Swaps is an optional feature surface. quoteSwap works end-to-end. swap enforces
the minOut floor before submit and fails closed (SWAP_FAILED) until the full EVM wallet
submit path is wired through your BFF. The core SDK (connect, pay, batch, sessions) does
not depend on this surface.
Enabling swaps
Pass swapChain to buckspayFacilitator. Without it, quoteSwap and swap are omitted
from the relayer and both methods throw if called:
import { buckspayFacilitator } from "@buckspay/relayer/buckspay-facilitator";
const relayer = buckspayFacilitator({
url: "/api/gasless", // your BFF - the facilitator API key stays server-side
network: "testnet",
swapChain: "base-sepolia"
});
quoteSwap - ships end-to-end
quoteSwap is signer-agnostic and delegates to /swap/quote:
const quote = await client.quoteSwap({
tokenIn: "0xSellTokenAddress",
tokenOut: "0xBuyTokenAddress",
amount: "1000000"
});
// quote: SwapQuote
swap - enforces the floor, fails closed
swap checks minOut before any submit. If the live quote falls below minOut, it throws
BuckspayError("SWAP_FAILED") without signing or submitting anything:
const receipt = await client.swap({
tokenIn: "0xSellTokenAddress",
tokenOut: "0xBuyTokenAddress",
amount: "1000000",
minOut: "985000" // floor: reject if the received amount would be below this
});
Full example
// Recipe 14 - SWAP (STRETCH, optional). Gasless token swap via the facilitator's existing
// /swap rail. NOTE: this is the first feature cut if the cycle tightens - the core surface does
// NOT depend on it. The submit leg uses the EVM wallet typed-data signature through your BFF.
import { createBuckspayClient, createRpcSimContext, type SwapQuote } from "@buckspay/core";
import { classicAccount } from "@buckspay/accounts/classic";
import { walletsKit } from "@buckspay/signers/wallets-kit";
import { buckspayFacilitator } from "@buckspay/relayer/buckspay-facilitator";
const client = createBuckspayClient(
{
network: "testnet",
account: classicAccount(),
signer: walletsKit({ network: "testnet" }),
// `swapChain` enables quoteSwap/swap; without it the relayer omits them (fails closed).
relayer: buckspayFacilitator({
url: "/api/gasless", // your BFF - the facilitator API key stays server-side
network: "testnet",
swapChain: "base-sepolia"
}),
gas: { mode: "sponsored" }
},
createRpcSimContext("https://soroban-testnet.stellar.org")
);
export async function quoteOnly(): Promise<SwapQuote> {
await client.connect();
// quoteSwap is signer-agnostic and works end-to-end today.
return client.quoteSwap({
tokenIn: "0xSellTokenAddress",
tokenOut: "0xBuyTokenAddress",
amount: "1000000"
});
}
export async function swapWithFloor(): Promise<void> {
await client.connect();
// minOut is enforced BEFORE any submit; a below-floor quote throws SWAP_FAILED.
const receipt = await client.swap({
tokenIn: "0xSellTokenAddress",
tokenOut: "0xBuyTokenAddress",
amount: "1000000",
minOut: "985000"
});
console.log(receipt.transferTx);
}
Next
Atomic batch
Settle multiple transfers all-or-nothing in one transaction.
Onboarding
How a user with zero XLM gets a payment-ready account.