Swapping - Fixed Output

If you want to run this example, be sure that you have followed the steps here to configure your Wallet Client.

SwapTokensForExactTokens - Provide a the number of tokens you want your swap to result in and receive the number of tokens you need to send. You’ll get a quote before executing the swap to ensure you agree with the variable input amount

Calculate the desired output (AmountOut)

For the fixed-output flow we pick how much CTK we want to receive and convert that to raw units with the token’s decimals. We’ll again be using the getTokenDecimal function function to do this. It’s also important to remember our swapPath example showing that AUSD is token1 in the pair.

1const { decimalsToken0, 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(decimalsToken0);
7console.log("Desired amount Out ", amountOut);
1Desired amount Out 1000000000000000000000n
Why?

swapTokensForExactTokens guarantees you receive a fixed amount of CTK (token0) units. The contract will calculate how much AUSD (token1) it needs in order to guarantee that amount.

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 * Quote how much token1 is required to guarantee the specificed token0 amount as an output.
3 * Returns `[amountInMax, amountOut]`
4 */
5async function quoteAmountsIn(
6 pair: `0x${string}`,
7 amountOut: bigint,
8 path: `0x${string}`[],
9) {
10 return await client.readContract({
11 address: pair,
12 abi: stableSwapAbi,
13 functionName: 'getAmountsIn',
14 args: [amountOut, path],
15 });
16}
1const [amountInMax, amountOut] = await quoteAmountsIn(
2 PAIR_CTK_AUSD,
3 amountOut,
4 [TOKEN0_CTK, TOKEN1_AUSD],
5);
6
7console.log('Quoted amountInMax (raw AUSD):', amountInMax)
1Quoted amountInMax (raw AUSD): 1000200000

This output means the pair will charge 1000.20 AUSD, which already includes any fees. 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. This means that we need to 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
amountOutAmount guaranteed to receive (1000 CTK raw)
amountInMaxThe ceiling of AUSD you’re willing to pay
swapPathAddresses of the tokens to swap: [0xa90...22dC,0x7BE...cd9D] (AUSD/CTK on Sepolia)
callerAddressWallet that will receive the CTK
deadlineTime allowance before the transaction reverts (+5 min in this example)
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

If you want, you can choose to open the returned hash on the Sepolia Explorer and wait for it to process. No matter what, if the transaction is successfully executed, the destination wallet balance should show +1000 CTK, and no more than 1000.20 AUSD was spent.

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