Skip to main content
Call the controller to use lending. The controller owns the pool and authorizes its accounting calls. Signatures omit the Soroban-injected Env argument. Amounts use raw token units unless a scale is specified. Use the full HubAssetKey to identify a market. Construction uses __constructor(admin: Address) and leaves the controller paused. Types are defined in Data types. Error codes are listed in Errors. Events are listed in Events. Governance owns the controller. Configuration follows the governance execution rules.

Reading the tables

User and maintenance calls require the explicit caller, liquidator, or payer to authorize. The Auth column gives the additional account check. Admin calls require the controller owner to authorize. accept_ownership requires the pending owner. Pause: gated calls fail while the controller is paused. open calls remain available. Account, listing-flag, and pool checks still apply. Flash-loan guard: financial calls fail with #400 FlashLoanOngoing during a flash loan. Account renewal and delegate changes use their own authorization checks. The flash-position callback can use allowed controller calls; final account checks still apply.

Core lending

  • supply creates accounts in PositionMode::Normal; see account creation for the zero-id convention.
  • A caller who is neither owner nor delegate may only add to hub assets the account already holds a supply position in. New accounts skip the check because the caller becomes the owner.
  • to selects the recipient on borrow and withdraw; it defaults to caller. Debt is always booked to account_id.
  • On withdraw, a zero amount withdraws the entire position for that asset. The return value reports the resolved pool payout for each asset.
  • Anyone may repay an account’s debt; the caller’s signature is needed only to pull the tokens.
  • Amounts credited are measured from receipts. A token that delivers less than it is sent credits the delivered amount.

Liquidation

seize_mode is SeizeMode::Transfer (pool pays underlying, withholding the protocol fee) or SeizeMode::Credit(account_id) (seized supply shares move to a controller account). Credit(0) creates the receiving account and liquidate returns its id; Transfer returns 0. See Liquidations.

Leverage and strategies

All calls below are pause-gated. Account strategies check final solvency and publish position updates. flash_loan settles principal plus fee and creates no account debt. See Strategies.
swap and convert_swap are opaque route bytes. The published SDK 1.0.228 builders accept routeXdr for swap routes. Equal underlying tokens require empty route bytes. repay_debt_with_collateral nets supply against debt directly only when the full HubAssetKey matches. close_position = true requires an empty debt map before it withdraws all remaining collateral. multiply and swap_debt charge a strategy fee on gross new debt. flash_position mints fee-free debt and must finish with supply and positive debt in its declared debt market. For an existing account, multiply and flash_position require mode to match the stored mode (#25 AccountModeMismatch). Receiver requirements are described in Flash positions and Flash loans.

Account and delegation

  • add_delegate requires an active, governance-approved position manager. Delegates are capped at 16 (#45 RegistryCapReached).
  • remove_delegate remains available during a controller pause.
  • A delegate grant is bound to the owner who made it. Transferring the position NFT deactivates the previous owner’s grants immediately.
  • renew_account extends both the account’s storage TTL and the NFT Owner entry.

Permissionless maintenance

Caller-signed; none carries a privileged role.

Owner-gated configuration

Configuration calls require the owner, except accept_ownership, which requires the pending owner. get_app_version is public. In production, governance owns the controller and applies its proposal and timelock rules.
set_spoke_asset_flags and edit_asset_in_spoke can retain or tighten halt flags. They cannot clear a set flag (#317 SpokeAssetFlagRelaxation). Clear flags with timelocked AdminOperation::RelaxSpokeAssetFlags and the current get_spoke_asset_flags_epoch as expected_epoch. Governance checks the epoch at proposal time. The controller checks it again at execution. A later flag write invalidates the old proposal (#319 SpokeFlagsEpochMismatch). Listing and relisting advance the epoch. Explicit flag writes advance it even when the requested flags are unchanged. Risk-only edits that retain all flags do not advance it. Removing a listing preserves the epoch.

Views

Views require no authorization and do not persist economic state. Storage reads can renew TTL. get_market_indexes_detailed and get_liquidation_estimate accept at most MAX_VIEW_INPUTS = 256 inputs. A larger list fails with #16. Account risk Account state Simulation get_liquidation_estimate(account_id: u64, debt_payments: Vec<(HubAssetKey, i128)>, seize_mode: SeizeMode) -> LiquidationEstimate returns seized_collaterals, protocol_fees, refunds, max_payment_wad, and bonus_rate_bps.
seized_collaterals is gross of protocol_fees. These two fields use asset units for Transfer and RAY-scaled supply shares for Credit; their difference is the estimated net. refunds always uses native debt-token units, max_payment_wad is USD-WAD and bonus_rate_bps is BPS. A full Transfer close can pay one native unit less because the pool floors its payout; simulate execution for exact amounts.
Market and configuration get_collateral_amount and get_borrow_amount use half-up rounding. A full withdrawal floors its payout; a full debt repayment needs the ceiling-rounded debt. Do not use a display balance as an exact close amount. There is no max_withdraw, max_borrow, or max_supply view on the controller. Size an action against get_collateral_amount, get_ltv_collateral_usd, get_spoke_usage, and the pool’s get_reserves, or simulate the call.

Account creation and batching

  1. account_id = 0 creates an account. It applies to supply, multiply, flash_position, migrate_from_blend, and as SeizeMode::Credit(0) in liquidate. Each returns the resolved account id. Creating an account mints its position NFT; there is no separate account-creation entrypoint.
  2. The spoke id is fixed at creation. Calls accepting a spoke id check it against existing account metadata (#310 SpokeMismatch); others use the stored spoke.
  3. Position mode is fixed at creation.
  4. Zero has different meanings. On entry legs (supply, borrow, repay) zero is rejected. On withdraw it means “the entire position”.
  5. Payment batches sum duplicate legs by full HubAssetKey. They preserve first-appearance order. Overflow fails with #33 MathOverflow. flash_position collateral declarations instead require distinct tokens.
The account_id is the position NFT’s token_id. Ownership and transfer behavior are documented in Position NFT.