Skip to main content
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.