Quickstart
Quote, build, and execute your first xPath swap.
This quickstart uses xPath to quote a route, build transaction calldata, and submit the transaction from an EVM wallet.
Prerequisites
- Node.js 18+
- An xPath API key
- A wallet funded with the input token and source-chain gas
- Token addresses and decimals from the xPath token endpoints
Install viem:
npm install viemSet your API key in the environment:
export XPATH_API_KEY="YOUR_API_KEY"1. Request a Quote
Amounts are base-10 integer strings in the token's smallest unit. For a token with 6 decimals, 1000000 represents one token.
const API_URL = 'https://api.xpath.rath.fi'
const API_KEY = process.env.XPATH_API_KEY!
async function xpath(path: string, init?: RequestInit) {
const response = await fetch(`${API_URL}${path}`, {
...init,
headers: {
'api-key': API_KEY,
'Content-Type': 'application/json',
...init?.headers,
},
})
if (!response.ok) {
throw new Error(`xPath request failed: ${response.status} ${await response.text()}`)
}
const payload = await response.json()
if (payload.code !== 0) throw new Error(payload.message)
return payload.data
}
const sender = '0x1111111111111111111111111111111111111111'
const quoteParams = new URLSearchParams({
fromChain: '8453',
toChain: '42161',
fromToken: '0x4200000000000000000000000000000000000006',
toToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
amount: '1000000000000000',
sender,
receiver: sender,
slippage: '1',
routeMode: 'suggested',
})
const routes = await xpath(`/quote?${quoteParams}`)
let route = routes[0]
if (!route) throw new Error('No xPath route is currently available')
console.log('Quote ID:', route.quoteId)
console.log('Expected output:', route.amountOut)
console.log('Minimum output:', route.minAmountOut)
console.log('Providers:', route.providers.map((provider) => provider.name))Review amountOut, minAmountOut, fees, estimated time, and providers before selecting a route.
2. Build the Transaction
Build executable calldata from the selected route parameters:
let builtTx = await xpath('/build-path-by-id', {
method: 'POST',
body: JSON.stringify({
quoteId: route.quoteId,
simulation: true,
}),
})
console.log('Transaction target:', builtTx.to)
console.log('Approval target:', builtTx.allowanceTarget)/build-path-by-id preserves the exact selected quote. Use /build-path when you intentionally want to rebuild from route parameters; its required Amount and Sender fields are capitalized.
3. Approve and Execute
Before submitting an ERC-20 route, check the returned allowance target. xPath-wrapped EVM routes can return null for allowanceTarget; in that case, the transaction target is the spender.
import { erc20Abi } from 'viem'
const spender = builtTx.allowanceTarget ?? builtTx.to
const currentAllowance = await publicClient.readContract({
address: route.fromToken.address,
abi: erc20Abi,
functionName: 'allowance',
args: [walletClient.account.address, spender],
})
if (currentAllowance < BigInt(route.amount)) {
const approvalHash = await walletClient.writeContract({
address: route.fromToken.address,
abi: erc20Abi,
functionName: 'approve',
args: [spender, BigInt(route.amount)],
})
await publicClient.waitForTransactionReceipt({ hash: approvalHash })
const freshRoutes = await xpath(`/quote?${quoteParams}`)
route = freshRoutes[0]
if (!route) throw new Error('No fresh xPath route is currently available')
builtTx = await xpath('/build-path-by-id', {
method: 'POST',
body: JSON.stringify({ quoteId: route.quoteId, simulation: true }),
})
const freshSpender = builtTx.allowanceTarget ?? builtTx.to
const freshAllowance = await publicClient.readContract({
address: route.fromToken.address,
abi: erc20Abi,
functionName: 'allowance',
args: [walletClient.account.address, freshSpender],
})
if (freshAllowance < BigInt(route.amount)) {
throw new Error('The fresh route uses a different spender; approve it and rebuild again')
}
}
const hash = await walletClient.sendTransaction({
to: builtTx.to,
data: builtTx.data,
value: BigInt(builtTx.value ?? '0'),
gas: BigInt(builtTx.gasLimit),
})
console.log('Source transaction:', hash)The publicClient and walletClient must be configured for builtTx.chain. Quotes are short-lived, so refresh the quote and build result after an approval transaction or any user delay.
4. Track Status
Cross-chain routes should be tracked after the source transaction is submitted:
const statusParams = new URLSearchParams({
txHash: hash,
fromChain: String(builtTx.chain),
userAddress: sender,
})
const status = await xpath(`/status?${statusParams}`)
console.log(status.status)
console.log(status.destTxDetails?.txHash)Next Steps
- Read Core Concepts before implementing route selection.
- Follow the Same-Chain Swap or Cross-Chain Swap guide.
- Use Gasless Swaps with Permit2 when the user should not submit the route transaction.