Executing Swaps
The SDK builds unsigned algosdk.Transaction[] arrays. You sign them with whatever wallet your users have connected, then submit to Algod. The SDK never touches private keys.
High-Level: client.buildSwapTxns
import { TaurusClient } from '@taurusswap/sdk';
import algosdk from 'algosdk';
const client = new TaurusClient();
const txns = await client.buildSwapTxns({
sender: 'SENDER_ADDRESS',
fromIndex: 0, // sell token 0 (USDC)
toIndex: 1, // buy token 1 (USDT)
amountIn: 10_000_000n, // 10 USDC
slippageBps: 50, // 0.5% — default if omitted
});
// txns is algosdk.Transaction[]
// Sign and submit (see Signing section below)Low-Level: buildSwapTxns
If you manage your own Algod client and pool state:
import { readPoolState, buildSwapTxns } from '@taurusswap/sdk';
const pool = await readPoolState(algod, POOL_APP_ID);
const txns = await buildSwapTxns(
algod,
POOL_APP_ID,
'SENDER_ADDRESS',
pool, // pass the pool state you already have
0, // tokenInIdx
1, // tokenOutIdx
10_000_000n, // amountInRaw
50, // slippageBps
);Transaction Group Layout
The group structure depends on whether the swap crosses any ticks:
Simple swap (no tick crossings)
┌──────────────────────────────────────────┐
│ Tx 0: Budget (optional opcode top-up) │
│ Tx 1: ASA Transfer user → pool │
│ amount: amountInRaw │
│ Tx 2: App call swap() │
│ args: [tokenInIdx, tokenOutIdx, │
│ amountInRaw, claimedOut, │
│ minAmountOut] │
│ inner tx: pool → user (output tokens) │
└──────────────────────────────────────────┘Crossing swap (≥1 tick boundary)
┌──────────────────────────────────────────┐
│ Tx 0–k: Budget top-up (k depends on │
│ number of crossings) │
│ Tx k+1: ASA Transfer user → pool │
│ Tx k+2: App call swap_with_crossings() │
│ args: [tokenInIdx, tokenOutIdx, │
│ amountInRaw, effectiveAmountIn, │
│ minAmountOut, trade_recipe] │
│ inner tx: pool → user (output tokens) │
└──────────────────────────────────────────┘The SDK selects the right path automatically based on quote.ticksCrossed.
Signing: Pera Wallet
import { PeraWalletConnect } from '@perawallet/connect';
const pera = new PeraWalletConnect();
async function swap() {
const txns = await client.buildSwapTxns({
sender: activeAddress,
fromIndex: 0,
toIndex: 1,
amountIn: 10_000_000n,
});
// Pera expects an array of { txn, signers? } objects per group
const signedTxns = await pera.signTransaction([
txns.map((txn) => ({ txn })),
]);
// signedTxns is Uint8Array[]
const { txid } = await client.algod.sendRawTransaction(signedTxns).do();
await algosdk.waitForConfirmation(client.algod, txid, 4);
console.log('Confirmed:', txid);
}Signing: use-wallet-react
import { useWallet } from '@txnlab/use-wallet-react';
function SwapButton() {
const { activeAddress, signTransactions, algodClient } = useWallet();
const handleSwap = async () => {
const txns = await client.buildSwapTxns({
sender: activeAddress!,
fromIndex: 0,
toIndex: 1,
amountIn: 10_000_000n,
});
// Encode to msgpack bytes for signing
const encoded = txns.map((t) => algosdk.encodeUnsignedTransaction(t));
const signedTxns = await signTransactions([encoded]);
const { txid } = await algodClient.sendRawTransaction(signedTxns).do();
await algosdk.waitForConfirmation(algodClient, txid, 4);
};
return <button onClick={handleSwap}>Swap</button>;
}Fully Atomic: executeSwap
The low-level executeSwap function combines quote + build + sign + submit into one call. Useful for server-side scripts or bots where you hold the key directly:
import { executeSwap } from '@taurusswap/sdk';
import algosdk from 'algosdk';
const account = algosdk.mnemonicToSecretKey('your mnemonic here ...');
const { txId, amountOut } = await executeSwap(
algod,
POOL_APP_ID,
account.addr,
0, // tokenInIdx
1, // tokenOutIdx
10_000_000n, // amountInRaw
50, // slippageBps
async (txns) => {
// signer callback — sign all txns and return Uint8Array[]
return txns.map((t) => t.signTxn(account.sk));
},
);
console.log('TX ID:', txId);
console.log('Received:', Number(amountOut) / 1e6, 'USDT');Error Handling
import {
TaurusClient,
SwapTooSmallError,
InsufficientLiquidityError,
InvalidSlippageError,
} from '@taurusswap/sdk';
try {
const txns = await client.buildSwapTxns({ sender, fromIndex: 0, toIndex: 1, amountIn });
// ...sign and submit
} catch (err) {
if (err instanceof SwapTooSmallError) {
// amountIn is below ~1000 microunits effective after fee
showToast('Amount too small. Try a larger trade.');
} else if (err instanceof InsufficientLiquidityError) {
// Trade exceeds pool depth
showToast('Insufficient liquidity. Try a smaller amount.');
} else if (err instanceof InvalidSlippageError) {
// slippageBps was outside 0–10 000
console.error('Bad slippage value');
} else if (String(err).includes('app call rejection')) {
// On-chain invariant check failed — pool state changed between quote and submit
showToast('Price moved. Please refresh the quote and try again.');
}
}Confirmation Pattern
async function swapAndWait(txns: algosdk.Transaction[], signedTxns: Uint8Array[]) {
const { txid } = await client.algod.sendRawTransaction(signedTxns).do();
// waitForConfirmation polls /v2/transactions/{txid}/pending
const result = await algosdk.waitForConfirmation(client.algod, txid, 4 /* rounds to wait */);
console.log('Confirmed in round:', result['confirmed-round']);
console.log('TX ID:', txid);
// Invalidate the cache so the next quote uses fresh state
client.invalidateCache();
return txid;
}