CenturionDEX
Launch App

Build a Swap Interface

Last modified:

Overview

This tutorial walks through building a minimal React application that:

  1. Connects a wallet
  2. Gets a swap quote from QuoterV2
  3. Executes the swap via SwapRouter02

By the end, you'll have a working swap page that talks to CenturionDEX v3 on-chain.

Prerequisites

1. Project Setup

npx create-react-app centurion-swap --template typescript
cd centurion-swap
npm install ethers@6 @centurion-dex/v3-sdk @centurion-dex/sdk-core

2. Constants

Create src/constants.ts:

import { Token, ChainId } from '@centurion-dex/sdk-core'
 
export const SWAP_ROUTER_ADDRESS = '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45'
export const QUOTER_ADDRESS = '0x61fFE014bA17989E743c5F6cB21bF9697530B21e'
 
export const WCTN = new Token(
  ChainId.MAINNET,
  '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
  18,
  'WCTN'
)
 
export const USDC = new Token(
  ChainId.MAINNET,
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  6,
  'USDC'
)
 
export const POOL_FEE = 3000 // 0.30%

3. Getting a Quote

Create src/quote.ts:

import { ethers } from 'ethers'
import { QUOTER_ADDRESS, WCTN, USDC, POOL_FEE } from './constants'
 
const QUOTER_ABI = [
  'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)',
]
 
export async function getQuote(
  provider: ethers.Provider,
  amountIn: bigint
): Promise<bigint> {
  const quoter = new ethers.Contract(QUOTER_ADDRESS, QUOTER_ABI, provider)
 
  const [amountOut] = await quoter.quoteExactInputSingle.staticCall({
    tokenIn: WCTN.address,
    tokenOut: USDC.address,
    amountIn,
    fee: POOL_FEE,
    sqrtPriceLimitX96: 0,
  })
 
  return amountOut
}

Key points:

4. Executing the Swap

Create src/swap.ts:

import { ethers } from 'ethers'
import { SWAP_ROUTER_ADDRESS, WCTN, USDC, POOL_FEE } from './constants'
 
const ROUTER_ABI = [
  'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96)) external payable returns (uint256 amountOut)',
]
 
export async function executeSwap(
  signer: ethers.Signer,
  amountIn: bigint,
  amountOutMinimum: bigint
): Promise<ethers.TransactionResponse> {
  const router = new ethers.Contract(SWAP_ROUTER_ADDRESS, ROUTER_ABI, signer)
  const recipient = await signer.getAddress()
 
  const tx = await router.exactInputSingle(
    {
      tokenIn: WCTN.address,
      tokenOut: USDC.address,
      fee: POOL_FEE,
      recipient,
      amountIn,
      amountOutMinimum,
      sqrtPriceLimitX96: 0,
    },
    { value: amountIn } // Send CTN which gets wrapped to WCTN by the router
  )
 
  return tx
}

Key points:

5. Putting It Together

In your React component:

async function handleSwap() {
  const provider = new ethers.BrowserProvider(window.ethereum)
  const signer = await provider.getSigner()
 
  const amountIn = ethers.parseEther('0.1') // 0.1 CTN
 
  // Step 1: Get quote
  const amountOut = await getQuote(provider, amountIn)
  console.log(`Expected output: ${ethers.formatUnits(amountOut, 6)} USDC`)
 
  // Step 2: Apply 1% slippage tolerance
  const amountOutMinimum = amountOut * 99n / 100n
 
  // Step 3: Execute
  const tx = await executeSwap(signer, amountIn, amountOutMinimum)
  const receipt = await tx.wait()
  console.log(`Swap confirmed in block ${receipt.blockNumber}`)
}

6. Error Handling

Common failure modes to handle:

try {
  const tx = await executeSwap(signer, amountIn, amountOutMinimum)
  await tx.wait()
} catch (error: any) {
  if (error.code === 'ACTION_REJECTED') {
    // User rejected the transaction in their wallet
  } else if (error.message?.includes('Too little received')) {
    // Slippage tolerance exceeded — price moved too much
    // Suggest the user increase slippage or retry
  } else if (error.message?.includes('Transaction too old')) {
    // Deadline passed — transaction was pending too long
  } else {
    // Unexpected error — log and display to user
    console.error('Swap failed:', error)
  }
}

Next Steps