Skip to content

feat: smart collateral liquidation & BNB provider for fixed-term markets#172

Open
ricklista wants to merge 14 commits into
feature/brokerLiquidatorSupportSmartProviderfrom
feature/market-factory-bnb-provider-support
Open

feat: smart collateral liquidation & BNB provider for fixed-term markets#172
ricklista wants to merge 14 commits into
feature/brokerLiquidatorSupportSmartProviderfrom
feature/market-factory-bnb-provider-support

Conversation

@ricklista

Copy link
Copy Markdown
Contributor

Summary

Add smart collateral liquidation support to BrokerLiquidator for fixed-term broker markets, and extend MarketFactory to auto-configure BNBProvider for WBNB-based fixed-term markets and route smart provider whitelisting to brokerLiquidator (vs liquidator/publicLiquidator for common markets).

Change type

  • New contract
  • Upgrade (existing proxy)
  • Bug fix
  • Gas optimization
  • Configuration change
  • Migration / deploy script
  • Test
  • Dependency update

Contracts changed

Contract File Type
BrokerLiquidator src/liquidator/BrokerLiquidator.sol modified
IBrokerLiquidator src/liquidator/IBrokerLiquidator.sol modified
MarketFactory src/moolah/MarketFactory.sol modified
IBroker src/broker/interfaces/IBroker.sol modified
DeployBrokerLiquidator script/broker/deploy_brokerLiquidator.s.sol modified
LendingBrokerTest test/broker/LendingBroker.t.sol modified
MarketFactoryTest test/moolah/MarketFactoryTest.sol modified

Interface changes

IBrokerLiquidator — new functions:

  • withdrawETH(uint256 amount) external
  • liquidateSmartCollateral(bytes32, address, address, uint256, uint256, bytes) external returns (uint256, uint256)
  • flashLiquidateSmartCollateral(bytes32, address, address, uint256, address, address, bytes, bytes, bytes) external returns (uint256, uint256)
  • smartProviders(address) external view returns (bool)
  • batchSetSmartProviders(address[], bool) external

BrokerLiquidator — new functions/features:

  • receive() external payable — accept native BNB
  • withdrawETH(uint256) — MANAGER role
  • liquidateSmartCollateral(...) — BOT role, seize + redeem LP via SmartProvider
  • flashLiquidateSmartCollateral(...) — BOT role, flash liquidation with LP redemption + token swaps in callback
  • redeemSmartCollateral(...) — BOT role, standalone LP redemption
  • batchSetSmartProviders(...) — MANAGER role
  • New events: SmartProvidersChanged, SmartLiquidation

IBrokerLiquidator.MoolahLiquidateData struct — new fields appended:

  • swapSmartCollateral, smartProvider, minToken0Amt, minToken1Amt, token0Pair, token1Pair, swapToken0Data, swapToken1Data

IBroker — new function:

  • ORACLE() external view returns (IOracle)

MarketFactory — signature changes:

  • createFixedTermMarket(FixedTermMarketParams, bool) — added liquidatorSmartProvider param
  • batchCreateFixedTermMarkets(FixedTermMarketParams[], bool[]) — added liquidatorSmartProviders param

Storage layout

BrokerLiquidator (upgrade): Two new state variables appended after existing storage:

  • mapping(address => bool) public smartProviders (slot after brokerToMarketId)
  • uint256 internal _lastRepaidAssets (next slot)

No variables reordered or removed. No __gap in this contract. New variables are strictly appended — no storage collision risk.

MarketFactory: No new state variables. All new dependencies (brokerLiquidator, BNBProvider, etc.) are immutable (set in constructor, stored in bytecode). No storage impact.

Access control

BrokerLiquidator:

  • New BOT-gated functions: liquidateSmartCollateral, flashLiquidateSmartCollateral, redeemSmartCollateral
  • New MANAGER-gated functions: withdrawETH, batchSetSmartProviders
  • Existing access patterns unchanged

MarketFactory: Unchanged — createFixedTermMarket and batchCreateFixedTermMarkets remain OPERATOR-gated.

Risk assessment

Area Risk Note
Storage collision 🟢 None New BrokerLiquidator vars appended at end; MarketFactory has no new state vars
Fund safety 🟡 Low receive() + withdrawETH added — MANAGER-gated withdrawal mitigates risk. onMoolahLiquidate callback handles smart collateral swaps with external calls to whitelisted pairs. _lastRepaidAssets used as transient state across callback boundary
Access control 🟢 None All new functions use existing role gates (BOT, MANAGER). No new roles introduced
External call safety 🟡 Low flashLiquidateSmartCollateral callback makes external calls to token0Pair/token1Pair with user-supplied calldata — mitigated by pairWhitelist check. Native BNB sent via call{value} to whitelisted pairs

Deployment

  • Chain: BSC mainnet
  • Deploy script: script/broker/deploy_brokerLiquidator.s.sol (updated for payable cast)
  • Upgrade path: Deploy new BrokerLiquidator implementation, upgrade proxy via timelock. Deploy new MarketFactory implementation (constructor args include brokerLiquidator address), upgrade proxy via timelock.

Test plan

  • forge test passes (9/9 MarketFactoryTest)
  • testCreateFixedTermMarketWithBNBLoan — BNBProvider auto-set for WBNB loan markets
  • testCreateFixedTermMarketWithBNBCollateral — BNBProvider auto-set for WBNB collateral markets
  • testCreateFixedTermMarketWithSmartProvider — smart provider config routes to brokerLiquidator
  • testBatchCreateFixedTermMarkets — batch creation with mixed BNB + smart provider markets

ricklista and others added 14 commits April 16, 2026 11:36
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>
- Add RelayerSet/OracleSet event emissions in LendingBroker migration setters (#7)
- Rename withdrawERC20 to withdraw with native BNB support in BrokerLiquidator (#1)
- Add NatSpec documenting custody requirement for redeemSmartCollateral (#2)
… 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
…ixed-term markets

- BrokerLiquidator: add liquidateSmartCollateral, flashLiquidateSmartCollateral,
  redeemSmartCollateral, withdrawETH, receive(), and smart provider whitelist
- MarketFactory: auto-set BNBProvider for WBNB fixed-term markets, route smart
  provider config to brokerLiquidator for fixed-term vs liquidator/publicLiquidator
  for common markets
- IBroker: expose ORACLE() view
- Tests: add coverage for BNB provider and smart provider in fixed-term market creation
@ricklista ricklista force-pushed the feature/market-factory-bnb-provider-support branch from 20f1bf6 to 4567528 Compare April 30, 2026 02:20
@ricklista ricklista changed the base branch from master to feature/brokerLiquidatorSupportSmartProvider April 30, 2026 02:23
@ricklista ricklista force-pushed the feature/brokerLiquidatorSupportSmartProvider branch from 2e7613d to 3d3d63c Compare June 16, 2026 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants