Skip to main content
Trails is an intent-based orchestration layer that executes cross-chain transactions automatically. Developers specify what they want delivered; Trails handles routing, bridging, swapping, and gas, regardless of what chain or token the user starts from.

How it works

Trails is built on an intent address primitive: a unique contract address computed from the full transaction parameters. When a user deposits tokens into their intent address, Trails automatically executes every step needed to deliver the result: swaps, bridges, relaying, with no further input from the user.
  1. Intent definition: specify the origin token, destination token, amount, and target chain
  2. Intent address generation: Trails computes a unique address encoding all transaction parameters
  3. User deposits: the user transfers tokens to their intent address in a single confirmation
  4. Origin execution: a relayer executes the encoded origin transaction (swaps, bridges as needed)
  5. Destination settlement: bridged funds arrive and the final transaction executes on the destination chain
  6. Receipt: Trails confirms settlement; funds reach the recipient
The user signs once. Everything else is automatic.

Widget & SDK

The Trails Widget is a drop-in React component that handles the full payment UI. The Headless SDK gives you full control over the presentation while Trails handles the execution.
PathPackageBest for
Widget0xtrails/widgetDrop-in UI: wraps any button or element
Headless SDK0xtrailsCustom UI with full control over presentation

Pay

Accept any token from any chain and receive a specific token on a specific chain. Users pay from their full cross-chain balance.
import { TrailsWidget } from "0xtrails/widget";

<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="pay"
  toAddress="0xYourAddress"
  toAmount="25"
  toChainId={137}
  toToken="USDC"
  onCheckoutComplete={({ sessionId }) => {
    // verify server-side: getIntentReceipt({ intentId: sessionId })
  }}
>
  <button>Pay $25 USDC</button>
</TrailsWidget>

Fund

Deposit funds into any wallet or chain from any starting token. Fund mode includes a built-in onramp: users can fund directly from a credit card, bank account, or existing crypto balance without leaving your app.
import { TrailsWidget } from "0xtrails/widget";

<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0xUserDepositAddress"
  toChainId={137}
  toToken="USDC"
  toAmount="100"
/>
The Widget surfaces onramp options (card, bank) alongside crypto funding automatically when mode="fund" is set.

Swap

Exchange any token for any other token across chains.
import { useQuote } from "0xtrails";

const { quote, swap } = useQuote({
  fromTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH on Mainnet
  fromChainId: 1,
  toTokenAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",   // USDC on Polygon
  toChainId: 137,
  swapAmount: "1000000000000000000", // 1 ETH in wei
  tradeType: "EXACT_INPUT",
  slippageTolerance: "0.005",
  onStatusUpdate: (status) => console.log("Swap status:", status),
});

await swap();

Direct API

The Direct API lets you build the full intent lifecycle in server-side code, with no frontend required.

Install

pnpm install @0xtrails/api

Initialize

import { TrailsApi } from "@0xtrails/api";

const trails = new TrailsApi("YOUR_API_KEY");
API key obtained from the Trails Dashboard. All requests use X-Access-Key header. Base URL: https://trails-api.sequence.app/rpc/Trails/.

Step 1: Get a quote

const { intent, feeOptions } = await trails.quoteIntent({
  ownerAddress: "0xSenderAddress",
  originChainId: 1,
  originTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
  destinationChainId: 137,
  destinationTokenAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
  originTokenAmount: "1000000000000000000", // 1 ETH in wei
  destinationToAddress: "0xRecipientAddress",
  tradeType: "EXACT_INPUT",
  options: {
    slippageTolerance: "0.005",
  },
});
Quotes are valid for 5 minutes.

Step 2: Commit the quote

const { intentId } = await trails.commitIntent({ intent });
Once committed, you have 10 minutes to execute.

Step 3: Execute

Transfer method: send tokens to the intent deposit address, then confirm:
const depositTxHash = await walletClient.sendTransaction({
  to: intent.depositAddress,
  value: parseEther("1"),
});

await trails.executeIntent({
  intentId,
  depositTransactionHash: depositTxHash,
});
Permit method (ERC-2612, gasless): sign two off-chain signatures instead of a transfer:
await trails.executeIntent({
  intentId,
  depositSignature: {
    feeOption: feeOptions[0],
    permitSignature: "0x...",
    intentSignature: "0x...",
    nonce: 1,
    deadline: Math.floor(Date.now() / 1000) + 300,
  },
});

Step 4: Monitor until complete

const { intentReceipt, done } = await trails.waitIntentReceipt({ intentId });

if (done && intentReceipt.status === "SUCCEEDED") {
  console.log("Settled:", intentReceipt.transactionHash);
}
Poll getIntentReceipt for non-blocking status checks. Terminal states: SUCCEEDED, FAILED.

Supported chains and tokens

Trails supports all EVM-compatible chains. To query available tokens and supported networks:
const { tokens } = await trails.getTokenList({ chainId: 137 });
const prices = await trails.getTokenPrices({ tokenAddresses: ["0x3c499c..."] });
See Supported Chains for the full network list.

Get started

Trails quickstart

Integrate the Widget, Headless SDK, or Direct API in minutes.

API reference

Full endpoint reference, request/response schemas, and error codes.