Skip to main content
A flash loan sends one pool asset to a receiver contract and collects the principal plus a fee before the transaction ends. The receiver can use the funds during its callback; failed repayment reverts the transaction. Call the controller’s flash_loan. For a callback that leaves debt on a lending account, use Flash positions.

Check loan conditions

  • The market exists on an active hub and has is_flashloanable = true.
  • The protocol is unpaused and the pool has enough cash for the principal.
  • amount is positive and expressed in token-native units.
  • receiver is a deployed Wasm contract exporting execute_flash_loan.
  • No guarded financial operation is already running under the flash-loan guard.
The caller authorizes the loan and can differ from the receiver.

Calculate the fee

The market’s flashloan_fee is in basis points. The fee rounds half-up, with a one-native-unit minimum when a positive fee would otherwise round to zero. The maximum configurable rate is 500 BPS (5%). The pool collects amount + fee and records the fee as protocol revenue. Read the market parameters before constructing the request, and enforce a maximum fee in the receiver.

Implement the receiver

The pool transfers the principal, then invokes this callback:
Authenticate the configured trusted pool and validate the expected initiator, asset, principal, fee limit, and request data before spending funds or granting an allowance. Callback arguments alone do not establish authorization. After completing its work, the receiver approves the trusted pool to pull exactly amount + fee of the expected asset. Use checked arithmetic for that total and the allowance expiry. The receiver must hold enough tokens when the callback returns.
Repayment uses the receiver’s token allowance. A top-level wallet signature does not replace that approval. Do not send repayment directly to the pool: the pool requires its balance to remain unchanged during the callback, then collects repayment with transfer_from.
The controller’s FlashLoanOngoing guard blocks reentry into guarded financial operations, including supply, borrow, withdraw, repay, liquidation, strategies, and another flash loan. See the security model.

Build and authorize the call

asset selects the market as { hub_id, asset }. data is an opaque payload forwarded unchanged to the receiver.
Use @xoxno/sdk-js 1.0.228 for the TypeScript example. caller is the wallet’s G… public key. The CLI alias caller must resolve to that wallet. CLI invocation can submit the transaction; use the TypeScript function when your application needs prepared, unsigned XDR for a wallet. The example permits no additional caller-authorized receiver calls. If your receiver needs them, define a narrow StellarAuthPolicy for its exact calls, recipients, and amount limits. Check every requested signer. Do not disable validation to accommodate an unexpected authorization tree. The receiver grants its repayment allowance during the authenticated callback. Follow Wallet integration to sign the prepared XDR and persist the original network, signed XDR, and hash before submission. Resolve an unknown submission outcome by that hash; do not repeat or replace the loan automatically.

Verify settlement

The controller emits FlashLoanEvent at ["position", "flash_loan"] with { hub_id, asset, receiver, caller, amount, fee }. The pool emits PoolMarketStateBatchEvent at ["market", "batch_state_update"]. Read the charged fee from FlashLoanEvent. Successful settlement enforces:
The change in a revenue view can also include accrued interest and share rounding. Reconcile the receiver’s balance against its own work and repayment; its ending balance depends on the callback’s proceeds and costs.

Common errors

See the Controller ABI for the public entrypoint and Pool ABI for pool settlement.