FungibleVault wrappers, and fee vaults. Read first: Supply and borrow, Withdraw and repay, Interest and revenue.
A reference implementation ships in the protocol repo at contracts/defindex-strategy: a complete DeFindexStrategyTrait adapter that maps each DeFindex vault to one controller supply account. Share accounting lives in the DeFindex vault contract; the strategy holds only a vault-to-account mapping. See DeFindex strategy for the trait API, deposit/withdraw flows, and integration tests.
The position model
Supply positions are not tokens. The controller stores them per account (account_id: u64), keyed by asset, as scaled shares (scaled_amount_ray = amount / supply_index, RAY = 27 decimals). Your contract is the account owner; the underlying value is scaled × supply_index, and the supply index grows as borrowers pay interest. There is no share token to hold, no emissions token to claim, and no harvest step — interest compounds into the index automatically, including admin-funded rewards (add_rewards).
Three guarantees make the integration a clean two-call loop:
- Views accrue to now.
collateral_amount_for_token(account_id, asset)returns the current underlying balance with interest simulated to the ledger timestamp — no keeper dependency, and the same fixed-point math the pool persists on the next mutation. What a view reports is what a deposit or withdrawal in the same transaction applies. - Balance views read no oracle.
collateral_amount_for_token,borrow_amount_for_token, andget_market_indexstay callable through any oracle outage, so yourbalance()never bricks downstream accounting. - Rounding is deterministic and conservative. Credits round down (floor), debits round up (ceil), neutral conversions half-up, at RAY precision. Measure your exact credited value by re-reading the view after the call instead of replicating formulas.
The core loop
One strategy instance per asset; your contract iscaller everywhere.
Authorize
When your contract callssupply, the controller runs exactly one nested token call: transfer(your_contract → pool, amount). Pre-authorize it with authorize_as_current_contract — no allowances, no other entries:
withdraw needs no auth entry: tokens flow from the pool toward the recipient. Your contract’s own require_auth as caller is satisfied by invoker authorization. Resolve the pool address once at deploy time from get_all_markets_detailed.
Preview limits before acting
A withdrawal can revert on pool cash, the market’s max-utilization cap, or the account’s LTV/HF gates (only if you also borrow). A deposit can revert on the supply cap. Two views model the exact enforcement so you can clamp instead of revert:| View | Returns |
|---|---|
max_withdraw(account_id, asset) | Largest withdrawal the next transaction can execute. Equals the full balance whenever a full close is feasible; 0 while paused. |
max_supply(asset) | Remaining supply-cap headroom; i128::MAX when uncapped; 0 while paused or the market is not active. |
Lifecycle rules
- Account deletion on full close. Withdrawing the last position deletes the account. Reset your stored
account_idto0; supplying with a stale id raises#24 AccountNotFound. Views degrade gracefully for missing accounts (0, empty maps,i128::MAXhealth factor). - Min borrow collateral. While the vault account carries debt, LTV-weighted collateral must stay above the instance-level
MinBorrowCollateralUsdfloor (get_min_borrow_collateral_usd, default$5 WAD). - Pause. The owner circuit breaker blocks
supplyandwithdraw(previews return0).repayandrenew_accountstay available. - Oracle posture. Mutating flows need a resolvable price; a debt-free account withdraws under the lenient risk-decreasing policy (stale or deviating prices tolerated, total feed loss not). Balance reporting is oracle-free.
- Storage rent. Account keys auto-extend on every touch; a dormant position should call
renew_account(owner, account_id)periodically (it works even while paused). Archived entries are restorable viaRestoreFootprint, not lost. - Anyone can supply into your account. Supply is not owner-gated (account_id is the routing key), so treat your protocol balance as monotone-up from external action — relevant for share-price accounting; use a first-deposit inflation guard as the reference strategy does.
Verify
Run your flows against the reference strategy’s integration tests (contracts/defindex-strategy/tests): deposit/balance/withdraw round trips, third-party payout, interest accrual without index maintenance, and the account-recreation path after a full exit.
Next
Controller ABI
Exact signatures for supply, withdraw, and every view used here.
Interest and revenue
How the supply index accrues the yield your vault reports.
DeFindex strategy
The reference vault adapter: one account per vault, trait API, and test harness.
Accounts and risk
Account metadata, position limits, and spoke rules.
Errors
Every error code your integration can surface.

