SwapTokensForExactTokens
SwapTokensForExactTokens - Useful when the user wants a fixed output amount and a variable input amount. You'll get a quote before executing the swap to ensure you agree with the variable input amount
Decide the desired output AmountOut
Because smart contracts store token balances in raw units, we convert the human amount into on-chain units using the token's decimals.
const { decimalsToken1 } = await getTokenDecimals(
client,
testnetPair,
);
// We define the amount of tokens that we would like to get out (in token1 units)
const amountOut = 1000n * 10n ** BigInt(decimalsToken1);
console.log("Desired amount Out ", amountOut);
Desired amount Out 1000000000000000000000n
swapTokensForExactTokens guarantees you receive this many token1 units; the contract will calculate how much token0 it needs in return.
Getting the quote for AmountIn
With amountOut fixed, get a quote from the pair for much AUSD it will require (including the current purchase fee).
/**
* Helper: quote required token0 for a fixed token1 output.
*/
async function quoteAmountsIn(
pair: `0x${string}`,
amountOut: bigint,
path: `0x${string}`[],
) {
return await client.readContract({
address: pair,
abi: stableSwapAbi,
functionName: 'getAmountsIn',
args: [amountOut, path],
});
}
const [amountInMax] = await quoteAmountsIn(
PAIR_AUSD_CTK,
amountOut,
[TOKEN0_AUSD, TOKEN1_CTK],
);
console.log('Quoted amountInMax (raw AUSD):', amountInMax)
Quoted amountInMax (raw AUSD): 1000200000
getAmountsIn returns an array matching the path. In our case the result means:
- 1000.2 AUSD (raw) → 1000 CTK (raw)
So the pair will charge 1000.20 AUSD, which already includes its purchase-fee markup. If you agree with this quote, the next step is to approve the pair to spend up to amountInMax AUSD before submitting the swap.
Approve the allowance
AgoraStableSwapPair cannot pull AUSD from your wallet unless the ERC-20 allowance is set first. We grant the pair permission to spend up to amountInMax AUSD, and—as a best-practice—simulate the transaction before broadcasting it
/**
* Give `pair` permission to spend `amount` of `tokenToApprove`.
* Returns the transaction hash of the on-chain approval.
*/
async function approveAllowance(
tokenToApprove: `0x${string}`,
pair: `0x${string}`,
amount: bigint,
) {
// 1) Dry-run to catch reverts & obtain gas estimate
const { request } = await client.simulateContract({
address: tokenToApprove,
abi: ausdImplementationAbi, // standard ERC-20 ABI
functionName: "approve",
args: [pair, amount],
});
// 2) Send the real transaction
return await client.writeContract(request);
}
console.log(
`Approving contract ${TESTNET_PAIR_AUSD_CTK} to spend ${amountInMax} units of token ${inputTokenAddress}...`,
);
const approveAlowanceTxHash = await approveAllowance(
inputTokenAddress,
TESTNET_PAIR_AUSD_CTK,
amountInMax,
);
console.log("Approve transaction hash:", approveAlowanceTxHash);
Approving pair to spend 1000200000 units of AUSD...
Approve transaction hash: 0x1d166bbc9ce4e0ba3000a42aa22360c14cb8a8ba8007d137915a3922e304e0f4
Swapping tokens
With allowance set, we trigger the atomic swap. The swap call will be swapTokensForExactTokens, and it will take the following args:
| Arg | Meaning |
|---|---|
amountOut | 1000 CTK (raw units) – the guaranteed output |
amountInMax | ceiling AUSD you're willing to pay |
swapPath | [AUSD, CTK] |
callerAddress | receives the CTK |
deadline | reverts if mined after this timestamp (here: +5 min) |
console.log("Executing swapTokensForExactTokens...");
const { request: swapRequest } = await client.simulateContract({
address: TESTNET_PAIR_AUSD_CTK,
abi: stableSwapAbi,
functionName: "swapTokensForExactTokens",
args: [amountOut, amountInMax, swapPath, callerAddress, BigInt(deadline)],
});
const swapRequestTxHash = await client.writeContract(swapRequest);
console.log("Swap transaction hash:", swapRequestTxHash);
Executing swapTokensForExactTokens...
Swap transaction hash: 0x5d27ab73a4759720aedd71b32075366a427c8ce21220cd9910b6352715550c3a
Open the hash on SnowTrace to watch it confirm.
Once mined, your wallet balance reflects +1000 CTK, and no more than 1000.20 AUSD was spent.

For the full example, please refer to our GitHub repository.