Skip to main content

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
Why?

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:

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)
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.

swapTokensForExactTokens

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