Payments
Swap Tokens
Build a custom token swap interface that allows users to exchange tokens across chains using thirdweb Payments.
Create a basic swap interface with token selection and amount input:
import React, { useState, useEffect } from "react";import { Bridge } from "thirdweb";import { useActiveAccount } from "thirdweb/react";import { createThirdwebClient } from "thirdweb";import { ethereum, polygon } from "thirdweb/chains";const client = createThirdwebClient({clientId: "YOUR_CLIENT_ID",});function SwapInterface() {const account = useActiveAccount();const [fromToken, setFromToken] = useState("ETH");const [toToken, setToToken] = useState("MATIC");const [amount, setAmount] = useState("");const [quote, setQuote] = useState(null);const [isLoading, setIsLoading] = useState(false);const getQuote = async () => {if (!amount || !account) return;setIsLoading(true);try {const preparedQuote = await Bridge.Buy.prepare({originChainId: ethereum.id,originTokenAddress:"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETHdestinationChainId: polygon.id,destinationTokenAddress:"0x0000000000000000000000000000000000001010", // MATICamount: BigInt(amount * 1e18), // Convert to weisender: account.address,receiver: account.address,client,});setQuote(preparedQuote);} catch (error) {console.error("Failed to get quote:", error);}setIsLoading(false);};useEffect(() => {if (amount && account) {const timer = setTimeout(getQuote, 500);return () => clearTimeout(timer);}}, [amount, account]);const executeSwap = async () => {if (!quote || !account) return;try {for (const step of quote.steps) {for (const transaction of step.transactions) {const result = await sendAndConfirmTransaction({transaction,account,});// Wait for cross-chain completion if neededif (["buy", "sell", "transfer"].includes(transaction.action)) {let swapStatus;do {swapStatus = await Bridge.status({transactionHash: result.transactionHash,chainId: transaction.chainId,client,});if (swapStatus.status === "PENDING") {await new Promise((resolve) =>setTimeout(resolve, 3000),);}} while (swapStatus.status === "PENDING");}}}alert("Swap completed successfully!");setQuote(null);setAmount("");} catch (error) {console.error("Swap failed:", error);alert("Swap failed. Please try again.");}};return (<div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-lg"><h2 className="text-2xl font-bold mb-6 text-center">Token Swap</h2>{/* From Token */}<div className="mb-4"><label className="block text-sm font-medium mb-2">From</label><div className="flex gap-2"><inputtype="number"placeholder="0.0"value={amount}onChange={(e) => setAmount(e.target.value)}className="flex-1 p-3 border rounded-lg focus:ring-2 focus:ring-blue-500"/><selectvalue={fromToken}onChange={(e) => setFromToken(e.target.value)}className="p-3 border rounded-lg focus:ring-2 focus:ring-blue-500"><option value="ETH">ETH</option><option value="USDC">USDC</option><option value="WETH">WETH</option></select></div><p className="text-sm text-gray-500 mt-1">Ethereum</p></div>{/* Swap Arrow */}<div className="flex justify-center mb-4"><button className="p-2 rounded-full bg-gray-100 hover:bg-gray-200">↓</button></div>{/* To Token */}<div className="mb-6"><label className="block text-sm font-medium mb-2">To</label><div className="flex gap-2"><inputtype="text"placeholder="0.0"value={quote? (Number(quote.destinationAmount) / 1e18).toFixed(6): ""}readOnlyclassName="flex-1 p-3 border rounded-lg bg-gray-50"/><selectvalue={toToken}onChange={(e) => setToToken(e.target.value)}className="p-3 border rounded-lg focus:ring-2 focus:ring-blue-500"><option value="MATIC">MATIC</option><option value="USDC">USDC</option><option value="WETH">WETH</option></select></div><p className="text-sm text-gray-500 mt-1">Polygon</p></div>{/* Quote Info */}{quote && (<div className="mb-4 p-3 bg-blue-50 rounded-lg"><p className="text-sm"><span className="font-medium">Rate:</span> 1 {fromToken} ={" "}{(Number(quote.destinationAmount) /Number(quote.originAmount)).toFixed(6)}{" "}{toToken}</p><p className="text-sm"><span className="font-medium">Fee:</span>{" "}{((Number(quote.originAmount) - Number(amount) * 1e18) /1e18).toFixed(6)}{" "}{fromToken}</p><p className="text-sm"><span className="font-medium">Estimated time:</span>{" "}{Math.round(quote.estimatedExecutionTimeMs / 1000)}s</p></div>)}{/* Swap Button */}<buttononClick={executeSwap}disabled={!quote || isLoading || !account}className="w-full py-3 px-4 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed">{!account? "Connect Wallet": isLoading? "Getting Quote...": "Swap"}</button>{!account && (<p className="text-center text-sm text-gray-500 mt-3">Connect your wallet to start swapping</p>)}</div>);}
Build a more sophisticated swap interface with multiple route options:
import React, { useState, useEffect } from "react";import { Bridge } from "thirdweb";import { useActiveAccount } from "thirdweb/react";function AdvancedSwapInterface() {const account = useActiveAccount();const [routes, setRoutes] = useState([]);const [selectedRoute, setSelectedRoute] = useState(null);const [quotes, setQuotes] = useState([]);// Get available routesconst getRoutes = async () => {try {const availableRoutes = await Bridge.routes({originChainId: 1, // EthereumoriginTokenAddress:"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",client,});setRoutes(availableRoutes.slice(0, 5)); // Show top 5 routes} catch (error) {console.error("Failed to get routes:", error);}};// Get quotes for multiple routesconst getQuotes = async (amount) => {if (!amount || !account) return;const quotePromises = routes.map(async (route) => {try {const quote = await Bridge.Buy.prepare({originChainId: route.originToken.chainId,originTokenAddress: route.originToken.address,destinationChainId: route.destinationToken.chainId,destinationTokenAddress: route.destinationToken.address,amount: BigInt(amount * 1e18),sender: account.address,receiver: account.address,client,});return { route, quote };} catch (error) {return { route, error };}});const results = await Promise.all(quotePromises);setQuotes(results.filter((result) => result.quote));};useEffect(() => {getRoutes();}, []);return (<div className="max-w-2xl mx-auto p-6"><h2 className="text-2xl font-bold mb-6">Advanced Token Swap</h2>{/* Amount Input */}<div className="mb-6"><inputtype="number"placeholder="Enter amount to swap"onChange={(e) => getQuotes(e.target.value)}className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500"/></div>{/* Route Options */}<div className="space-y-3"><h3 className="text-lg font-medium">Available Routes</h3>{quotes.map(({ route, quote }, index) => (<divkey={index}className="border rounded-lg p-4 hover:bg-gray-50 cursor-pointer"onClick={() => setSelectedRoute({ route, quote })}><div className="flex justify-between items-center"><div><p className="font-medium">{route.originToken.symbol} →{" "}{route.destinationToken.symbol}</p><p className="text-sm text-gray-500">{route.originToken.name} to{" "}{route.destinationToken.name}</p></div><div className="text-right"><p className="font-medium">{(Number(quote.destinationAmount) /Math.pow(10, route.destinationToken.decimals)).toFixed(6)}</p><p className="text-sm text-gray-500">~{Math.round(quote.estimatedExecutionTimeMs / 1000)}s</p></div></div></div>))}</div>{selectedRoute && (<div className="mt-6 p-4 bg-blue-50 rounded-lg"><h4 className="font-medium mb-2">Selected Route</h4><p>{selectedRoute.route.originToken.symbol} →{" "}{selectedRoute.route.destinationToken.symbol}</p><buttononClick={() => executeSwap(selectedRoute.quote)}className="mt-3 w-full py-2 px-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Execute Swap</button></div>)}</div>);}
Token Swap Playground
Try building and testing swap interfaces with live data
- Cross-chain swaps - Exchange tokens across 50+ supported chains
- Real-time quotes - Get up-to-date pricing and execution estimates
- Route optimization - Find the best path for any token pair
- Price comparison - Compare multiple routes to maximize output
- Status tracking - Monitor cross-chain transaction progress
Gas Optimization Tip: For frequently traded pairs, consider caching route data to reduce API calls and improve UI responsiveness.
- Send a Payment - Learn about peer-to-peer transfers
- Get Routes - Explore route discovery APIs
- Token Prices - Access real-time token pricing
- Webhooks - Get notified when swaps complete