Swapping - Fixed Input

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.

1const { decimalsToken1 } = await getTokenDecimals(
2 client,
3 testnetPair,
4);
5// We define the amount of tokens that we would like to get out (in token1 units)
6const amountOut = 1000n * 10n ** BigInt(decimalsToken1);
7console.log("Desired amount Out ", amountOut);
1Desired amount Out 1000000000000000000000n
Why?

swapTokensForExactTokens guarantees you receive this many token1 units; the contract will calculate how much token0 it needs in return.

Getting a quote for AmountIn

Get a quote from the pair for how much AUSD it would require (including the current purchase fee) to receive the fixed amountOut that was configured.

1/**
2 * Helper: quote required token0 for a fixed token1 output.
3 */
4async function quoteAmountsIn(
5 pair: `0x${string}`,
6 amountOut: bigint,
7 path: `0x${string}`[],
8) {
9 return await client.readContract({
10 address: pair,
11 abi: stableSwapAbi,
12 functionName: 'getAmountsIn',
13 args: [amountOut, path],
14 });
15}
1const [amountInMax] = await quoteAmountsIn(
2 PAIR_AUSD_CTK,
3 amountOut,
4 [TOKEN0_AUSD, TOKEN1_CTK],
5);
6
7console.log('Quoted amountInMax (raw AUSD):', amountInMax)
1Quoted 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

1/**
2 * Give `pair` permission to spend `amount` of `tokenToApprove`.
3 * Returns the transaction hash of the on-chain approval.
4 */
5async function approveAllowance(
6 tokenToApprove: `0x${string}`,
7 pair: `0x${string}`,
8 amount: bigint,
9) {
10 // 1) Dry-run to catch reverts & obtain gas estimate
11 const { request } = await client.simulateContract({
12 address: tokenToApprove,
13 abi: ausdImplementationAbi, // standard ERC-20 ABI
14 functionName: "approve",
15 args: [pair, amount],
16 });
17
18 // 2) Send the real transaction
19 return await client.writeContract(request);
20}
1console.log(
2 `Approving contract ${TESTNET_PAIR_AUSD_CTK} to spend ${amountInMax} units of token ${inputTokenAddress}...`,
3);
4const approveAllowanceTxHash = await approveAllowance(
5 inputTokenAddress,
6 TESTNET_PAIR_AUSD_CTK,
7 amountInMax,
8);
9console.log("Approve transaction hash:", approveAllowanceTxHash);
1Approving pair to spend 1000200000 units of AUSD...
2Approve 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:

ArgMeaning
amountOut1000 CTK (raw units) – the guaranteed output
amountInMaxceiling AUSD you’re willing to pay
swapPath[AUSD, CTK]
callerAddressreceives the CTK
deadlinereverts if mined after this timestamp (here: +5 min)
1console.log("Executing swapTokensForExactTokens...");
2const { request: swapRequest } = await client.simulateContract({
3 address: TESTNET_PAIR_AUSD_CTK,
4 abi: stableSwapAbi,
5 functionName: "swapTokensForExactTokens",
6 args: [amountOut, amountInMax, swapPath, callerAddress, BigInt(deadline)],
7});
8const swapRequestTxHash = await client.writeContract(swapRequest);
9console.log("Swap transaction hash:", swapRequestTxHash);
1Executing swapTokensForExactTokens...
2Swap 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.

swapTokensForExactTokens

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