Endpoints
| Endpoint | What it does |
|---|---|
multiply | Flash-borrow the debt token, swap it into the collateral token, supply the collateral. Opens or extends a leveraged position. |
swap_debt | Borrow a new debt token, swap into the existing debt token, repay the old debt. |
swap_collateral | Withdraw collateral, swap into a new collateral asset, resupply. |
repay_debt_with_collateral | Withdraw collateral, swap (or net) into the debt token, repay. Optionally close the position. |
migrate_from_blend | Repay Blend debt, withdraw collateral and supply, deposit as XOXNO collateral. One transaction. |
The aggregator boundary
Theswap argument is an opaque Bytes payload (ScVal XDR). The controller does not decode it or validate its route structure. It forwards the bytes verbatim to the configured aggregator:
sender = <controller address> and total_in set to the exact amount it commits to spend. The aggregator pulls total_in of the input token from the controller, performs the swap, and returns the output token. Inside the aggregator the XDR decodes to a StrategyPayload:
swap was a typed AggregatorSwap/BatchSwap with per-path amount_in/min_amount_out and controller-side path validation. Those types no longer exist.
Building the swap payload
You do not hand-assemble the XDR. Request a route from the swap aggregator’s quote service, which returns the encodedStrategyPayload bytes for the input/output pair and amount. Pass those bytes straight into the strategy call. See Stellar Aggregator for the quote API and token coverage.
When the route is multi-hop or split across venues, that complexity is entirely inside the bytes; the controller and your transaction see only swap: Bytes.
Position mode
PositionMode (Normal = 0, Multiply = 1, Long = 2, Short = 3) tags the account’s intent. multiply accepts only Multiply, Long, or Short and rejects Normal. An account’s mode is fixed at creation; the other three strategies do not change it.
Multiply
initial_payment from caller → flash-borrow debt_to_flash_loan of debt_token from the pool → execute_strategy swaps it (plus debt-denominated payment) into collateral_token → supply the combined collateral → re-check LTV, health factor, and dust floors.
Rules:
modeisMultiply,Long, orShort;collateral_token != debt_token.- The strategy borrow uses the
RiskIncreasingoracle policy. initial_paymentmay be in the collateral token (added directly), the debt token (added to the flash amount), or a third token — which requiresconvert_swapto convert it to the collateral token, else#500 ConvertStepsRequired.
Swap debt
amount of new_debt_token, swaps to existing_debt_token, repays the old debt; over-repayment is refunded to caller. The two debt tokens must differ.
Swap collateral
current_collateral, swaps to new_collateral, resupplies. Debt-free accounts use the RiskDecreasing oracle policy; accounts with debt use RiskIncreasing. The new collateral must pass deposit preflight (collateralizable on the account’s spoke, within position limits).
Repay debt with collateral
collateral_amount, swaps to debt_token (or nets directly when the tokens are the same — no route needed), repays. When close_position = true and all debt is cleared, withdraws all remaining collateral to caller.
Migrate from Blend
blend_pool must be governance-approved. Solvency is checked at strategy_finalize, not during the Blend calls.
This flow does not use the flash-loan receiver pattern. See Blend migration for the two-phase submit model, refund reconciliation, and parameter guide.
Strategy borrow vs flash loan
| Mechanism | flash_loan | Strategy borrow (multiply, migrate_from_blend) |
|---|---|---|
| Pool call | pool.flash_loan → receiver callback | pool.create_strategy |
| Fee | Configured bps | 0 for migration; configured for multiply |
| Debt outcome | Repaid in same callback | Permanent account debt |
| Solvency check | At callback end | At strategy_finalize |
Reentry and trust
Strategy swaps and Blendsubmit calls reuse the flash-loan reentrancy guard (FlashLoanOngoing). A compromised or misconfigured aggregator cannot call back into mutating controller entrypoints during the swap. The controller authorizes exactly one token pull (total_in of the input token) for the aggregator and verifies its own balance deltas afterward.
Failure modes
| Error | Cause | Fix |
|---|---|---|
#500 ConvertStepsRequired | multiply initial payment is a third token but convert_swap is None | Provide a convert_swap route for payment → collateral. |
#27 AggregatorNotSet | No aggregator configured | Governance must schedule set_aggregator. |
#42 BlendPoolNotApproved | blend_pool not on the allow-list | Governance must schedule approve_blend_pool. |
#102 HealthFactorTooLow | Post-strategy health factor below 1 | Reduce leverage or improve the quote (total_min_out). |
| Aggregator slippage revert | Output below total_min_out | Re-quote; the floor is inside the swap bytes. |
Next
Blend migration
Atomic Blend V2 to XOXNO position migration.
Flash loans
The single-asset flash loan receiver pattern.
Stellar Aggregator
The quote service that produces the swap bytes.

