RathRath Finance

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 viem

Set 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

On this page