Skip to main content
Summary: One LiquidityPool contract holds all listed assets. The controller is its sole owner. The pool tracks scaled supply/debt, indexes, reserves (cash), revenue, and flash-loan settlement. It emits no events.
LiquidityPool is a single contract that holds custody and accounting for every listed asset, keyed by asset address (PoolKey::Params(asset) / State(asset)). The controller is its owner; every mutating method is #[only_owner], so only the controller can call them. The pool emits no events — see Events. The stable interface is declared in interfaces/pool/src/lib.rs. Accounting verbs take a PoolAction { caller, position, amount, asset }; the asset field routes storage. Maintenance and admin calls take a leading asset: Address. Types are in Data types.

Lifecycle

__constructor(admin: Address)
create_market(params: MarketParamsRaw)        // #[only_owner]; keyed by params.asset_id
__constructor sets the owner (the controller). create_market registers one asset: it validates params, writes PoolKey::Params(asset) and a fresh PoolKey::State(asset) (indexes at RAY, zeroed totals, last_timestamp = now), and raises #2 if the asset is already listed.

Accounting verbs

Each takes a PoolAction. The controller pre-transfers supplied tokens, so supply ignores action.caller.
FunctionReturnsBehavior
supply(action, supply_cap)PoolPositionMutationAccrues interest, adds scaled supply, enforces supply_cap.
borrow(action, borrow_cap)PoolPositionMutationChecks reserves, enforces borrow_cap and max utilization, adds scaled debt, transfers to caller (save-before-transfer).
withdraw(action, is_liquidation, protocol_fee)PoolPositionMutationBurns scaled supply, transfers net to caller. amount = i128::MAX closes the full position. When is_liquidation, skips the max-utilization check and skims protocol_fee as revenue.
repay(action)PoolPositionMutationBurns scaled debt; refunds overpayment to caller.
create_strategy(action, fee, borrow_cap)PoolStrategyMutationBorrow-side open; requires fee ≤ amount; keeps fee as revenue and sends amount − fee.

Flash loan

flash_loan(asset, initiator: Address, receiver: Address,
           amount: i128, fee: i128, data: Bytes) -> MarketStateSnapshot
A single call (no begin/end split). It snapshots the pool balance, transfers amount to receiver, invokes receiver.execute_flash_loan(initiator, asset, amount, fee, pool, data), then pulls back amount + fee and asserts the final balance equals pre_balance + fee. The receiver must be a contract (#412); any repayment shortfall raises #402. The fee is recorded as protocol revenue.

Admin and maintenance

FunctionReturnsBehavior
update_indexes(asset)MarketStateSnapshotAccrues interest to now; no transfer.
add_rewards(asset, amount)MarketStateSnapshotLifts the supply index by amount (#37 if no suppliers).
claim_revenue(asset)PoolAmountMutationBurns claimable scaled revenue and transfers to the owner (controller), capped at available cash.
seize_position(asset, side, position)PoolPositionMutationBorrow side socializes bad debt into the supply index; Deposit side moves shares into revenue. No transfer.
update_params(asset, model)()Accrues at the old rate, then replaces the InterestRateModel.
upgrade(new_wasm_hash)()Replaces pool WASM.

Views

No auth. All take (asset).
FunctionReturnsMeaning
capital_utilisation(asset)i128Utilization (RAY).
reserves(asset)i128Protocol-tracked cash in asset-native units (not live token balance).
deposit_rate(asset)i128Current supply APR (RAY).
borrow_rate(asset)i128Current borrow APR (RAY).
protocol_revenue(asset)i128Reconstructed claimable revenue.
supplied_amount(asset)i128Reconstructed total supplied.
borrowed_amount(asset)i128Reconstructed total borrowed.
delta_time(asset)u64Milliseconds since the last sync.
get_sync_data(asset)PoolSyncDataRaw MarketParamsRaw plus PoolStateRaw, without accrual.
reserves(asset) returns persisted PoolStateRaw.cash — the liquidity source for borrows, withdrawals, and revenue claims. Direct token donations to the pool address are excluded. Flash-loan settlement uses live token balance internally; see Security model.

Defense-in-depth checks

Each mutating path validates locally so a future controller bug cannot silently corrupt pool state:
  • Rejects non-positive amounts.
  • Panics #30 PoolNotInitialized if the asset’s Params/State keys are absent.
  • Checks reserves before any outgoing transfer.
  • Guards scaled-balance underflow (#33).
  • Validates the rate model on create_market and update_params.
  • Verifies flash-loan repayment with a pre/post balance check.

Controller dependency

The controller depends on interfaces/pool, not the pool crate. Runtime coupling is on the ABI only, which keeps the controller–pool trust boundary explicit. See Security model.

Next

Controller ABI

The public entrypoints that call this interface.

Storage

The asset-keyed PoolKey layout, durability, and TTL.