swap-tokens
Execute token swaps on EVM chains and Solana using generic AMM router interfaces.
Dependencies
import { useState } from "react";
import { useWalletClient, usePublicClient } from "wagmi";
import { parseUnits } from "viem";
// Generic swap router interface — plug in any AMM router address
const SWAP_ROUTER_ABI = [
{
name: "swapExactTokensForTokens",
type: "function",
inputs: [
{ name: "amountIn", type: "uint256" },
{ name: "amountOutMin", type: "uint256" },
{ name: "path", type: "address[]" },
{ name: "to", type: "address" },
{ name: "deadline", type: "uint256" },
],
outputs: [{ name: "amounts", type: "uint256[]" }],
},
] as const;
const ERC20_APPROVE_ABI = [
{
name: "approve",
type: "function",
inputs: [
{ name: "spender", type: "address" },
{ name: "amount", type: "uint256" },
],
outputs: [{ name: "", type: "bool" }],
},
] as const;
interface SwapParams {
routerAddress: `0x${string}`;
tokenIn: `0x${string}`;
tokenOut: `0x${string}`;
amountIn: string;
amountOutMin: string;
// ★ Use separate decimals for each token — they can differ (e.g. USDC=6, WETH=18)
decimalsIn?: number;
decimalsOut?: number;
}
export function useSwapTokens() {
const { data: walletClient } = useWalletClient();
const publicClient = usePublicClient();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function swap(params: SwapParams) {
if (!walletClient || !publicClient) throw new Error("Wallet not connected");
setLoading(true);
setError(null);
try {
const decimalsIn = params.decimalsIn ?? 18;
const decimalsOut = params.decimalsOut ?? 18;
const amountIn = parseUnits(params.amountIn, decimalsIn);
const amountOutMin = parseUnits(params.amountOutMin, decimalsOut);
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60 * 20);
// Step 1: approve router to spend tokenIn
const approveTx = await walletClient.writeContract({
address: params.tokenIn,
abi: ERC20_APPROVE_ABI,
functionName: "approve",
args: [params.routerAddress, amountIn],
});
await publicClient.waitForTransactionReceipt({ hash: approveTx });
// Step 2: execute swap
const swapTx = await walletClient.writeContract({
address: params.routerAddress,
abi: SWAP_ROUTER_ABI,
functionName: "swapExactTokensForTokens",
args: [
amountIn,
amountOutMin,
[params.tokenIn, params.tokenOut],
walletClient.account.address,
deadline,
],
});
return await publicClient.waitForTransactionReceipt({ hash: swapTx });
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
throw e;
} finally {
setLoading(false);
}
}
return { swap, loading, error };
}How Token Swaps Work
Automated Market Makers (AMMs)
Traditional exchanges match buyers with sellers. AMMs replace the order book with a liquidity pool — a smart contract holding reserves of two tokens.
The constant product formula: x * y = k
x= reserve of token Ay= reserve of token Bk= constant (never changes during a swap)
When you swap token A for token B, you add Δx to the pool.
The new y is k / (x + Δx), so you receive y - k/(x+Δx) of token B.
Effect: the more you buy, the worse your price gets (diminishing returns).
Slippage
The difference between the price you expect and the price you get.
Causes: price moves between when you sign and when the tx executes.
Always set a maximum acceptable slippage (e.g., 0.5% = amountOutMin = expectedOut * 0.995).
Price Impact
How much your swap moves the market price. A $100 swap in a $1M pool has 0.01% impact. The same swap in a $10k pool has ~1% impact. Check before transacting.
DEX Aggregators
Aggregators (1inch, Jupiter, etc.) split your swap across multiple pools to find the best price and minimize price impact. They do NOT hold your funds.
MEV and Frontrunning
MEV (Maximal Extractable Value): validators/searchers profit by reordering transactions.
Sandwich attack:
- Attacker sees your swap in the mempool
- Attacker buys token B first (drives price up)
- Your swap executes at the worse price
- Attacker sells token B for profit
Mitigations:
- Use private RPC / MEV-protected endpoints
- Set tight slippage tolerance
- Use commit-reveal schemes or batched swaps
Key Terms
- Path: the token route — e.g., [USDC → WETH → DAI] for a multi-hop swap
- Deadline: Unix timestamp after which the tx reverts (prevents stale execution)
- LP fee: small percentage (e.g., 0.3%) kept in the pool, distributed to liquidity providers
- ATA: Associated Token Account (Solana) — deterministic address for a (wallet, mint) pair