> ## Documentation Index
> Fetch the complete documentation index at: https://xoxno.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Stellar lending vault integrations

> Build a yield vault or DeFindex-style strategy on top of the lending protocol: position model, auth recipe, fresh-balance views, preview limits, and lifecycle rules.

Build a contract that supplies an asset, tracks its value, and withdraws on demand. Covers DeFindex strategies, SEP-56 / OpenZeppelin `FungibleVault` wrappers, and fee vaults. **Read first:** [Supply and borrow](/stellar-lending/dev/supply-and-borrow), [Withdraw and repay](/stellar-lending/dev/withdraw-and-repay), [Interest and revenue](/stellar-lending/dev/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](/stellar-lending/dev/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:

1. **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.
2. **Balance views read no oracle.** `collateral_amount_for_token`, `borrow_amount_for_token`, and `get_market_index` stay callable through any oracle outage, so your `balance()` never bricks downstream accounting.
3. **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 is `caller` everywhere.

```rust theme={"system"}
// Deposit: pull tokens in, then supply. account_id = 0 opens an account;
// persist the returned id.
let id = controller.supply(&strategy, &stored_id_or_0, &0u32,
    &vec![&env, (asset.clone(), amount)]);

// Balance: one call, underlying units, interest accrued to now.
let value = controller.collateral_amount_for_token(&id, &asset);

// Withdraw: pay any recipient directly; returns actual amounts paid.
// amount = 0 closes the position; over-asking clamps to a full close.
let paid = controller.withdraw(&strategy, &id,
    &vec![&env, (asset.clone(), amount)], &Some(recipient));
```

## Authorize

When your contract calls `supply`, 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:

```rust theme={"system"}
env.authorize_as_current_contract(vec![&env,
    InvokerContractAuthEntry::Contract(SubContractInvocation {
        context: ContractContext {
            contract: asset.clone(),
            fn_name: Symbol::new(&env, "transfer"),
            args: (env.current_contract_address(), pool.clone(), amount).into_val(&env),
        },
        sub_invocations: Vec::new(&env),
    }),
]);
```

`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.                           |

Both are previews against live state: indexes keep accruing after the read, so leave a margin if you act in a later transaction.

## Lifecycle rules

* **Account deletion on full close.** Withdrawing the last position deletes the account. Reset your stored `account_id` to `0`; supplying with a stale id raises `#24 AccountNotFound`. Views degrade gracefully for missing accounts (`0`, empty maps, `i128::MAX` health factor).
* **Min borrow collateral.** While the vault account carries debt, LTV-weighted collateral must stay above the instance-level `MinBorrowCollateralUsd` floor (`get_min_borrow_collateral_usd`, default `$5 WAD`).
* **Pause.** The owner circuit breaker blocks `supply` and `withdraw` (previews return `0`). `repay` and `renew_account` stay 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 via `RestoreFootprint`, 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

<CardGroup cols={2}>
  <Card title="Controller ABI" icon="code" href="/stellar-lending/dev/controller-abi">
    Exact signatures for supply, withdraw, and every view used here.
  </Card>

  <Card title="Interest and revenue" icon="percent" href="/stellar-lending/dev/interest-and-revenue">
    How the supply index accrues the yield your vault reports.
  </Card>

  <Card title="DeFindex strategy" icon="vault" href="/stellar-lending/dev/defindex-strategy">
    The reference vault adapter: one account per vault, trait API, and test harness.
  </Card>

  <Card title="Accounts and risk" icon="user-shield" href="/stellar-lending/dev/accounts-and-risk">
    Account metadata, position limits, and spoke rules.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/stellar-lending/dev/errors">
    Every error code your integration can surface.
  </Card>
</CardGroup>
