Skip to main content
Account strategies combine lending actions in one controller transaction. Token conversions use the configured swap aggregator, and the final account must pass its risk checks. Any failure reverts the whole transaction. This guide covers route-based strategies. Use Blend migration for moving a Blend position or Flash positions for a custom receiver callback.

Check shared requirements

The caller must own the target account’s position NFT or hold an active delegate grant. The protocol must be unpaused. Each action must satisfy the relevant market listing, pause, freeze, cap, position-count, cash, and utilization checks. Strategies fetch prices for account and strategy assets before execution. Finalization refreshes listed collateral LTV. While debt remains, it checks LTV coverage, health factor, and the configured USD collateral floor. Each collateral position with fewer than three decimals must also retain at least two base units after floor conversion. These are two units of the smallest token denomination, not two whole tokens. See account risk refresh and Health factor.

Obtain swap bytes

For conversions between different tokens, request a quote for the input token, output token, and amount from the Stellar Aggregator. Decode the quote’s base64 routeXdr into the strategy’s opaque swap: Bytes. The XOXNO SDK’s strategy builders accept the encoded routeXdr directly. Use the quote service’s payload instead of assembling XDR by hand. Select the quote URL, RPC, controller, and router from one trusted network configuration. Compare the quote service’s /api/v1/config network passphrase and router with that configuration. Check the route against your own input token, output token, input amount, and minimum output before preparation. mapQuoteResponseToStrategySwap performs the route checks when you pass these values in expected. A quote’s agreement with itself does not establish your intended trade. For a controller strategy, omit sender from the quote request. Start with maxSplits: 1 and maxHops: 2, then simulate the full controller call. A direct swap envelope or a sequence of LP-conversion transactions cannot replace the strategy’s atomic route bytes. The controller forwards these bytes to the configured aggregator:
The controller is sender. It authorizes the exact input amount, then checks its own token balances to determine input spent and output received. It ignores the aggregator’s reported output. The StrategyPayload format is defined in the aggregator reference.
The aggregator enforces the minimum output encoded in the route. The controller requires positive output and rejects invalid input spending, but those checks do not enforce a swap price. Set the quote’s minimum output to the intended slippage bound and simulate the complete strategy before signing.
For the same underlying token in different hubs, supported strategies use empty swap bytes and transfer between markets without a router call. Same-market collateral-funded repayment also requires empty bytes and nets supply against debt directly.

Multiply

Multiply opens or extends a leveraged position. The controller collects any initial payment, borrows the debt principal, converts the proceeds into collateral, and supplies the result before final risk checks. Debt remains on the account. The pool records the gross principal as debt and withholds the debt market’s flashloan_fee from the tokens sent to the controller. The fee applies even if that market has is_flashloanable = false. For gross principal P and pool fee rate B in basis points:
These values use token base units. Quote the conversion for the net input, plus any measured initial payment in the debt token. Router static and referral fees are separate and are included in the quote’s net output. Read both fee policies before showing the user the total cost. Pass account_id = 0 to create an account. For an existing account, spoke_id and mode must match its stored values. Only Multiply, Long, and Short are accepted; account mode remains fixed after creation. Multiply requires different market keys. Long and Short also require different underlying token addresses. To use one token across two hubs, choose Multiply with empty swap bytes. The optional initial payment can use: The controller measures the initial payment it receives. Token-address matches determine the handling above, even if hub ids differ. For same-token collateral and debt across hubs, the collateral match takes precedence. A third-token route consumes its measured receipt; the main route consumes net borrowed funds plus the measured debt-token payment. An initial payment emits InitialMultiplyPaymentEvent with the requested payment amount. Use balance changes to reconcile the receipt. Persist the returned account id.

Swap debt

Borrow amount of new_debt, convert it to existing_debt, and repay that debt. The pool refunds excess repayment to caller. Amounts use base units of the new debt token. As with multiply, debt records the gross principal, while the conversion receives the principal minus the pool strategy fee. Quote that net input. The market keys must differ, and the existing debt’s hub must be active. Matching token addresses across hubs use empty swap bytes and skip conversion.

Swap collateral

Withdraw amount of the current collateral, convert it to the new asset, and supply the result. The amount uses the current token’s native units. The markets must differ, and the current collateral’s hub must be active. Matching token addresses across hubs use empty swap bytes. The new collateral must be collateralizable on the account’s spoke and fit supply caps and position limits.

Repay debt with collateral

Withdraw collateral, convert it to the debt token, and repay. When collateral and debt are the same market, the pool nets supply against debt without a token withdrawal or router call. For matching token addresses across distinct hubs, it withdraws and repays without a conversion. Both cases require empty swap bytes. close_position = true withdraws the remaining balance of the selected collateral market to caller after all account debt is cleared. Other supplied markets remain on the account. Any remaining borrow position raises #122 CannotCloseWithRemainingDebt and reverts the transaction.

Prepare a collateral swap

This complete function uses the published @xoxno/sdk-js 1.0.228 interface. It swaps between different underlying tokens. Read token decimals from the selected market metadata; keep account ids and token amounts as strings. minimumOutput is your own lowest acceptable output, not a value copied from the quote. The function returns prepared, unsigned XDR.
Partial withdrawal burns supply shares with ceiling rounding. Near a full withdrawal, the pool can close the position and pay its floor-valued balance. Simulate the exact request; do not assume a quoted input guarantees that payout.

Authorize and simulate

The caller authorizes the controller call and any initial-payment transfers. The controller authorizes token pulls used by the configured aggregator. Swap execution runs under the flash-loan guard; guarded financial controller operations reject reentry with #400 FlashLoanOngoing. Simulate using the selected account, route, amounts, and closing option. Preparation supplies transaction resources and authorization entries. Check those entries against the local builder output before asking the wallet to sign. Follow Wallet integration to persist the selected network, signed XDR, and hash before submission. If the submission outcome is unknown, look up that hash on the original network; do not repeat or replace the operation automatically. Re-quote when the route’s amount or minimum output no longer matches the intended trade.

Verify the result

Read the account’s collateral, debt, and health factor after execution. Confirm caller balances for initial payments, refunds, and closing payouts. Controller position batches identify each strategy leg: Use the event reference to decode batches and the Controller ABI for complete signatures.

Common errors