feat: add SmartProvider support to BrokerLiquidator#159
Open
ricklista wants to merge 13 commits into
Open
Conversation
c5205c0 to
f4a2e4a
Compare
Add `redeemSmartCollateral` to allow BOT role to redeem smart collateral LP tokens via whitelisted SmartProviders. Add `batchSetSmartProviders` for MANAGER role to manage the provider whitelist.
…r shared impl Move RELAYER and ORACLE from constructor immutables to storage variables (appended at end of layout to preserve upgrade compatibility). This allows all LendingBroker proxies to share a single implementation deployment. - Constructor now only takes moolah address - initialize() accepts relayer and oracle as additional params - Added setRelayer() and setOracle() manager-only setters - Updated deploy scripts and all tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…al liquidation When partial liquidation deducts both interest and principal from a fixed position, the old code unconditionally reset interestRepaid=0 and lastRepaidTime=now. This erased any unpaid interest from position tracking, causing getUserTotalDebt() to under-report debt. The fix introduces three branches: 1. All interest covered -> safe to reset (unchanged behavior) 2. Partial interest + formula can represent outstanding -> adjust interestRepaid precisely so outstanding = accruedInterest - paidInterest (exact) 3. Partial interest + formula too small (most principal repaid) -> set interestRepaid=0 without resetting lastRepaidTime to maximize preserved outstanding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… functions - liquidateSmartCollateral: return actual seized (collAmount) and repaid (loanToken balance diff) instead of placeholder values - flashLiquidateSmartCollateral: capture repaidAssets from onMoolahLiquidate callback via _lastRepaidAssets storage
…ation Remove partial interest tracking logic and always reset interestRepaid and lastRepaidTime after liquidation deduction.
Amount now clears interest first, then principal. New fixed position principal <= amount. Also fix constructor args after rebase and add exact-full and excess-capped conversion tests.
- [L03] Block smart collateral markets from being liquidated via normal liquidate() by adding _isSmartCollateral() check that detects StableSwapLPCollateral via minter() -> TOKEN() chain - [I01] Add sellBNB() function for selling native BNB received from smart collateral redemptions, mirroring Liquidator.sol - [I05] Add zero address check in batchSetSmartProviders() - [I07] Fix incorrect comment on BOT role constant
LendingBroker - Add repayAll(onBehalf) for full position clearing, gated by whenNotPaused. - Replace global checkPositionsMeetsMinLoan with per-position validators (_validateDynamicPosition, _validateFixedPosition); remove the post-cascade global check from onMoolahLiquidate so partial liquidations no longer revert when a single position lands in the (0, minLoan) band. - Reject non-divisible repaidShares in liquidate() to preserve the broker market's totalBorrowAssets:totalBorrowShares = 1:VIRTUAL_SHARES invariant. - Re-check amount == 0 after interest/principal clamping in convertDynamicToFixed to prevent zero-principal fixed positions when the user has no dynamic debt. - Add docstring notes that fixed-repay amount and liquidate caller-whitelist / cascade ordering match the actual behavior. Size optimization - Move repay / repayAll bodies into LendingBrokerOperatorLib (DELEGATECALL'd). - Move liquidation cascade orchestration to BrokerMath.executeLiquidationCascade. - Move repayAll / convertDynamicToFixed pre-compute math to BrokerMath helpers. - Consolidate duplicate errors and dead helpers. LendingBroker shrinks from 27,358 -> 22,462 bytes (back under EIP-170). Addresses audit findings: H1 (loan-token price drift stranding positions), M (positions stranded by minLoanValue change), M (liquidation cascade dead zone revert), M (non-divisible share liquidation drift), Info (zero-principal fixed via convert), Low (_validatePositions stricter than Moolah post-liq check), Info (fixed-repay natspec), Info (liquidate natspec outdated). Generated with Claude Code
- BrokerLiquidator: drop duplicate receive() — master's d309fdc and the branch each declared an identical receive(); Solidity allows only one. The documented receive() at the end of the contract is kept, so the liquidator can still receive native BNB. - BatchManagementUtils.t.sol: update _deployBroker() to the new LendingBroker 2-arg constructor and 8-arg initialize (RELAYER/ORACLE moved from immutables to storage in 22bb8cf). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2e7613d to
3d3d63c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add SmartProvider (LP token) liquidation support to BrokerLiquidator, move LendingBroker RELAYER/ORACLE from immutables to storage for shared implementation across proxies, fix interest tracking in partial liquidation, and improve
convertDynamicToFixedto prioritize interest deduction before principal.Change type
Contracts changed
src/broker/LendingBroker.solsrc/broker/interfaces/IBroker.solsrc/liquidator/BrokerLiquidator.solsrc/liquidator/IBrokerLiquidator.soltest/broker/BrokerMathDeductFixed.t.soltest/liquidator/mocks/MockSmartProvider.solInterface changes
LendingBroker:
(moolah, relayer, oracle, wbnb)→(moolah, wbnb)— relayer/oracle removed from immutablesinitialize(): appended_relayer,_oracleparameterssetRelayer(address)— one-time migration setter (DEFAULT_ADMIN_ROLE)setOracle(address)— one-time migration setter (DEFAULT_ADMIN_ROLE)convertDynamicToFixed(amount, termId)— amount now prioritizes clearing interest first, then principal; new fixed positionprincipal <= amountRelayerSet,OracleSetBrokerLiquidator:
receive()— accept native ETHwithdrawETH(address, uint256)— MANAGER-onlyliquidateSmartCollateral(id, borrower, smartProvider, seizedAssets, repaidShares, payload)— BOT-onlyflashLiquidateSmartCollateral(id, borrower, smartProvider, seizedAssets, token0Pair, token1Pair, swapToken0Data, swapToken1Data, payload)— BOT-onlyredeemSmartCollateral(smartProvider, lpAmount, minToken0Amt, minToken1Amt)— BOT-onlybatchSetSmartProviders(providers, status)— MANAGER-onlySmartProvidersChanged,SmartLiquidationStorage layout
LendingBroker — safe.
RELAYER(slot 18) andORACLE(slot 19) appended after existing storage (liquidationWhitelistat slots 16–17). No collisions, no reordering.BrokerLiquidator — new
smartProvidersmapping and_lastRepaidAssetsuint256 appended. No collision risk.Access control
LendingBroker:
setRelayer(),setOracle(): DEFAULT_ADMIN_ROLE only, one-time migration (reverts if already non-zero)BrokerLiquidator:
withdrawETH(),batchSetSmartProviders(): MANAGER roleliquidateSmartCollateral(),flashLiquidateSmartCollateral(),redeemSmartCollateral(): BOT roleRisk assessment
forge inspectwithdrawETHrestricted to MANAGERonMoolahLiquidatecallback uses reentrancy guardDeployment
script/broker/deploy_broker.s.sol,script/broker/deploy_brokerImpl.s.sol,script/broker/deploy_broker_20260408.s.solMOOLAH,WBNB(optional, defaults to address(0))setRelayer()andsetOracle()once per proxy to migrate from immutable valuesTest plan
forge test --mc LendingBrokerTestpasses (includes convertDynamicToFixed partial/full/excess tests)forge test --mc BrokerMathDeductFixedTestpasses (deductFixedPositionDebt interest tracking)forge test --mc BrokerLiquidatorTestpasses (smart collateral liquidation + flash liquidation)forge test --mc MarketFactoryTestpasses (constructor arg update)forge test --mc PositionManagerTestpasses (constructor arg update)