Skip to main content
All hooks and utilities are available from the 0xtrails package unless stated otherwise.

TrailsProvider

Required for Hooks: The TrailsProvider must wrap your application when using any Trails hooks. This provider configures the necessary context for all hook functionality.
import { TrailsProvider } from '0xtrails'

<TrailsProvider
  config={{
    trailsApiKey: "...",
    trailsApiUrl: "...", // optional
    sequenceIndexerUrl: "...", // optional
    sequenceNodeGatewayUrl: "..." // optional
  }}
>
  <App />
</TrailsProvider>

Configuration Options

  • trailsApiKey (required): Your Trails API key. Join the Trails Telegram group to request access.
  • trailsApiUrl (optional): Custom API endpoint URL if using a self-hosted instance.
  • sequenceIndexerUrl (optional): Custom Sequence indexer URL.
  • sequenceNodeGatewayUrl (optional): Custom Sequence node gateway URL.

Chains

import { getSupportedChains, useSupportedChains } from '0xtrails'
  • getSupportedChains(): Promise<Chain[]>
  • useSupportedChains(): { supportedChains: Chain[], isLoadingChains: boolean }

Chain Type

type Chain = {
  id: number
  name: string
  chainId: number
  rpcUrls: string[]
  nativeCurrency: {
    name: string
    symbol: string
    decimals: number
  }
  blockExplorerUrls?: string[]
  imageUrl?: string
}

Utility Functions

import { getChainInfo, getAllChains } from '0xtrails'

// Get info for specific chain
const chain = getChainInfo(8453) // Base chain info

// Get all chains (including unsupported)
const allChains = getAllChains()

Tokens

import { getSupportedTokens, useSupportedTokens, useTokenList } from '0xtrails'
  • useTokenList(): { tokens: SupportedToken[] | undefined, isLoadingTokens: boolean }
  • useSupportedTokens({ chainId?: number }): { supportedTokens: SupportedToken[], isLoadingTokens: boolean }
  • getSupportedTokens(): Promise<SupportedToken[]>

SupportedToken Type

type SupportedToken = {
  id: string
  symbol: string
  name: string
  contractAddress: string
  decimals: number
  chainId: number
  chainName: string
  imageUrl: string
}

Additional Helpers

import { useTokenInfo, useTokenAddress } from '0xtrails'

// Returns basic info for an ERC-20 by address and chainId
const { tokenInfo, isLoading, error } = useTokenInfo({
  address: '0x...',
  chainId: 8453
})

// Get token address by symbol and chain
const tokenAddress = useTokenAddress({
  chainId: 8453,
  tokenSymbol: 'USDC'
})

Balances

import {
  useTokenBalances,
  getAccountTotalBalanceUsd,
  useAccountTotalBalanceUsd,
  getHasSufficientBalanceToken,
  useHasSufficientBalanceToken,
  getHasSufficientBalanceUsd,
  useHasSufficientBalanceUsd,
} from '0xtrails'
Balance hooks and utilities:
  • useTokenBalances(address): Returns sorted token balances enriched with USD price
  • useAccountTotalBalanceUsd(address): Returns total USD balance across tokens
  • useHasSufficientBalanceUsd(address, targetUsd): Check if account has sufficient USD balance
  • useHasSufficientBalanceToken(address, tokenAddress, tokenAmount, chainId): Check token balance

TokenBalance Type

type TokenBalance = {
  contractAddress: string
  accountAddress: string
  tokenID: string
  balance: string
  chainId: number
  contractType: string
  blockHash: string
  blockNumber: number
  updateID: number
  contractInfo: {
    chainId: number
    contractAddress: string
    contractType: string
    decimals: number
    logoURI: string
    name: string
    symbol: string
    extensions?: {
      link?: string
      description?: string
      ogImage?: string
    }
  }
}

Example Usage

import { useTokenBalances, useAccountTotalBalanceUsd } from '0xtrails'

const WalletBalance = ({ address }: { address: string }) => {
  const { tokenBalances, isLoading } = useTokenBalances(address)
  const { totalBalanceUsd } = useAccountTotalBalanceUsd(address)

  if (isLoading) return <div>Loading balances...</div>

  return (
    <div>
      <h3>Total Balance: ${totalBalanceUsd}</h3>
      {tokenBalances?.map((token) => (
        <div key={`${token.chainId}-${token.contractAddress}`}>
          {token.contractInfo.symbol}: {token.balance}
        </div>
      ))}
    </div>
  )
}

Quotes and Swapping

import { 
  prepareSend,
  useQuote, 
  TradeType,
  type UseQuoteReturn,
  type UseQuoteProps,
  type SwapReturn,
  type Quote
} from '0xtrails'

prepareSend

Prepares a send transaction with optimal routing. This is a lower-level function for advanced use cases. Signature:
function prepareSend(params: {
  fromTokenAddress: string
  fromChainId: number
  toTokenAddress: string
  toChainId: number
  amount: string | bigint
  tradeType: TradeType
  toRecipient?: string
  slippageTolerance?: string | number
}): Promise<PrepareSendResult>
Usage:
const result = await prepareSend({
  fromTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
  fromChainId: 1,
  toTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
  toChainId: 8453,
  amount: '1000000',
  tradeType: TradeType.EXACT_INPUT,
})

useQuote Hook

The useQuote hook provides real-time quotes for token swaps and cross-chain transfers.

Types

type UseQuoteProps = {
  walletClient?: any 
  fromTokenAddress?: string | null
  fromChainId?: number | null
  toTokenAddress?: string | null
  toChainId?: number | null
  toCalldata?: string | null
  swapAmount?: string | bigint
  toRecipient?: string | null
  tradeType?: TradeType | null
  slippageTolerance?: string | number | null
  onStatusUpdate?: ((transactionStates: TransactionState[]) => void) | null
  quoteProvider?: string | null
  checkoutOnHandlers?: Partial<CheckoutOnHandlers>
  paymasterUrl?: string
  selectedFeeToken?: {
    tokenAddress: string
    tokenSymbol?: string
  } | null
  abortSignal?: AbortSignal
  apiKey?: string | null
}

type UseQuoteReturn = {
  quote: Quote | null
  swap: (() => Promise<SwapReturn | null>) | null
  isLoadingQuote: boolean
  quoteError: unknown
  refetchQuote: (() => Promise<void>) | null
}

enum TradeType {
  EXACT_INPUT = 'EXACT_INPUT',  // User specifies exact input amount
  EXACT_OUTPUT = 'EXACT_OUTPUT' // User specifies exact output amount
}

Usage Example

import { useQuote, TradeType } from '0xtrails'
import { useWalletClient, useAccount } from 'wagmi'

const SwapComponent = () => {
  const { data: walletClient } = useWalletClient()
  const { address } = useAccount()

  const { quote, swap, isLoadingQuote, quoteError, refetchQuote } = useQuote({
    walletClient,
    fromTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
    fromChainId: 1, // Ethereum
    toTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', // USDC
    toChainId: 8453, // Base
    swapAmount: '1000000', // 1 USDC
    tradeType: TradeType.EXACT_INPUT,
    toRecipient: address,
    slippageTolerance: '0.005', // 0.5%
    onStatusUpdate: (states) => {
      console.log('Transaction states:', states)
    },
    checkoutOnHandlers: {
      triggerCheckoutStart: () => {
        console.log("Checkout started")
      },
      triggerCheckoutSignatureConfirmed: () => {
        console.log("Signature confirmed")
      },
      triggerCheckoutSignatureRequest: () => {
        console.log("Signature request")
      },
      triggerCheckoutComplete: (
        txStatus: "success" | "fail",
        accountAddress?: string,
      ) => {
        console.log(`Checkout complete for account ${accountAddress}: ${txStatus}`)
      },
      triggerCheckoutStatusUpdate: (transactionStates: TransactionState[]) => {
        console.log("Transaction states updated:", transactionStates)
      },
      triggerCheckoutQuote: (quote: any) => {
        console.log("Quote received:", quote)
      },
      triggerCheckoutSignatureRejected: (error: any) => {
        console.log("Signature rejected:", error)
      },
    }
  })

  // Quotes can become stale; refetch every ~30 seconds
  useEffect(() => {
    const id = setInterval(() => {
      refetchQuote?.()
    }, 30000)
    return () => clearInterval(id)
  }, [refetchQuote])

  const handleSwap = async () => {
    if (!swap) return
    
    try {
      const result = await swap()
      console.log('Swap completed:', result)
    } catch (error) {
      console.error('Swap failed:', error)
    }
  }

  if (isLoadingQuote) return <div>Getting quote...</div>
  if (quoteError) return <div>Error: {String(quoteError)}</div>
  if (!quote) return <div>No quote available</div>

  return (
    <div>
      <div>
        From: {quote.originAmountFormatted} {quote.originToken.symbol}
      </div>
      <div>
        To: {quote.destinationAmountFormatted} {quote.destinationToken.symbol}
      </div>
      <div>
        Fee: {quote.totalFeeAmountUsdDisplay}
      </div>
      <button onClick={handleSwap}>
        Execute Swap
      </button>
    </div>
  )
}

Constants

import { TRAILS_CONTRACT_PLACEHOLDER_AMOUNT } from '0xtrails'

TRAILS_CONTRACT_PLACEHOLDER_AMOUNT

This constant provides a placeholder value for amounts when encoding calldata in fund mode. It’s used to replace dynamic output amounts during contract execution. Use Case: When creating calldata for fund mode transactions where the final amount isn’t known until execution time, use this placeholder in your encoded function calls. The Trails system will replace it with the actual dynamic amount during contract execution. Example:
import { encodeFunctionData } from 'viem'
import { TRAILS_CONTRACT_PLACEHOLDER_AMOUNT } from '0xtrails'

// Encode calldata with placeholder amount
const calldata = encodeFunctionData({
  abi: stakingABI,
  functionName: 'stake',
  args: [TRAILS_CONTRACT_PLACEHOLDER_AMOUNT] // Will be replaced with actual amount
})

// Use in fund mode widget
<TrailsWidget
  apiKey="YOUR_API_KEY"
  mode="fund"
  toAddress="0x..." // Staking contract
  toCalldata={calldata}
  toChainId={1}
  toToken="ETH"
>
  <button>Stake ETH</button>
</TrailsWidget>
This pattern is essential for fund mode transactions where users choose their input amount, but the contract needs to receive the exact output amount after any swaps or bridging operations.

Transaction History

import {
  getIntentTransactionHistory,
  getAccountTransactionHistory,
  useAccountTransactionHistory,
  useIntentTransactionHistory,
  getTxTimeDiff,
} from '0xtrails'

getIntentTransactionHistory

Gets transaction history for a specific intent address. Signature:
function getIntentTransactionHistory(params: {
  intentAddress: string
  pageSize?: number
  page?: number
}): Promise<IntentTransactionHistoryResponse>
Usage:
const history = await getIntentTransactionHistory({
  intentAddress: '0x...',
  pageSize: 10,
  page: 0,
})

getAccountTransactionHistory

Gets transaction history for a wallet address interacting with Trails. Signature:
function getAccountTransactionHistory(params: {
  chainId: number
  accountAddress: string
  pageSize?: number
  includeMetadata?: boolean
  page?: number
}): Promise<TransactionHistoryResponse>
Usage:
const history = await getAccountTransactionHistory({
  chainId: 1,
  accountAddress: '0x...',
  pageSize: 10,
})

useAccountTransactionHistory

React hook for account transaction history with pagination. Usage:
const { transactions, isLoading, error, hasMore, loadMore } = 
  useAccountTransactionHistory({
    chainId: 1,
    accountAddress: address,
  })

useIntentTransactionHistory

React hook for intent transaction history. Usage:
const { transactions, isLoading, error } = useIntentTransactionHistory({
  intentAddress: '0x...',
})

getTxTimeDiff

Gets the time difference for a transaction in human-readable format. Signature: (timestamp: string | number) => string Usage:
const timeDiff = getTxTimeDiff(transaction.timestamp)
console.log(timeDiff) // "5 minutes ago"

useGetTransactionHistory

Hook for fetching transaction history from a user’s wallet address for a specific chain.
import { useGetTransactionHistory } from '0xtrails'

Types

type TransactionHistoryItemFromAPI = {
  txnHash: string
  blockNumber: number
  blockHash: string
  chainId: number
  metaTxnID: string | null
  transfers: Transfer[]
  timestamp: string
}

type TransactionHistoryItem = TransactionHistoryItemFromAPI & {
  explorerUrl?: string
  chainName?: string
}

type TransactionHistoryResponse = {
  page: {
    column: string
    pageSize: number
    more: boolean
  }
  transactions: TransactionHistoryItem[]
}

type GetAccountTransactionHistoryParams = {
  chainId: number
  accountAddress: string
  pageSize?: number
  includeMetadata?: boolean
  page?: number
}

Usage

import { useGetTransactionHistory } from '0xtrails'

function TransactionList() {
  const { data, isLoading, error } = useGetTransactionHistory({
    chainId: 1,
    accountAddress: '0x123...',
    // Optional:
    // pageSize: 10,
    // includeMetadata: true,
    // page: 0,
  })

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {String(error)}</div>

  return (
    <div>
      {data?.transactions?.map(tx => (
        <div key={tx.txnHash}>
          Transaction: {tx.txnHash} | Block: {tx.blockNumber} | Time: {tx.timestamp}
        </div>
      ))}
    </div>
  )
}

Encoders

import { getERC20TransferData } from '0xtrails'

getERC20TransferData

Encodes ERC20 transfer calldata for token transfers. Signature: (to: string, amount: bigint) => string Usage:
const transferData = getERC20TransferData(
  '0x1234567890123456789012345678901234567890',
  1000000n // 1 USDC (6 decimals)
)

Error Handling

import {
  InsufficientBalanceError,
  UserRejectionError,
  getIsUserRejectionError,
} from '0xtrails'

InsufficientBalanceError

Error thrown when a user has insufficient balance to complete a transaction. Usage:
try {
  await swap()
} catch (error) {
  if (error instanceof InsufficientBalanceError) {
    console.error('Insufficient balance for this transaction')
  }
}

UserRejectionError

Error thrown when a user rejects a transaction in their wallet. Usage:
try {
  await swap()
} catch (error) {
  if (error instanceof UserRejectionError) {
    console.log('User rejected the transaction')
  }
}

getIsUserRejectionError

Utility function to check if an error is a user rejection error. Signature: (error: unknown) => boolean Usage:
try {
  await swap()
} catch (error) {
  if (getIsUserRejectionError(error)) {
    console.log('User rejected the transaction')
  }
}

Advanced: Intent Functions

These are advanced functions for direct intent management. Most developers should use the TrailsWidget or useQuote hook instead.
import {
  calculateIntentAddress,
  calculateOriginAndDestinationIntentAddresses,
  commitIntent,
  getIntent,
  sendOriginTransaction,
} from '0xtrails'

Types

type OriginCallParams = {
  to: string
  value: bigint
  data: string
  chainId: number
}

type QuoteProvider = 'lifi' | 'relay' | 'cctp' | 'auto'

type TrailsFee = {
  amount: bigint
  amountUsd: number
  token: SupportedToken
}

calculateIntentAddress

Calculates the intent contract address for a given set of parameters. Signature: (params: IntentParams) => Promise<string> Usage:
const intentAddress = await calculateIntentAddress({
  fromChainId: 1,
  toChainId: 8453,
  // ... other params
})

calculateOriginAndDestinationIntentAddresses

Calculates both origin and destination intent addresses. Signature:
function calculateOriginAndDestinationIntentAddresses(
  params: IntentParams
): Promise<{ originAddress: string; destinationAddress: string }>
Usage:
const { originAddress, destinationAddress } = 
  await calculateOriginAndDestinationIntentAddresses(params)

commitIntent

Commits an intent to the blockchain. Signature: (params: CommitIntentParams) => Promise<CommitIntentResult> Usage:
const result = await commitIntent({
  walletClient,
  fromChainId: 1,
  toChainId: 8453,
  // ... other params
})

getIntent

Retrieves an existing intent from the blockchain. Signature: (intentAddress: string) => Promise<Intent> Usage:
const intent = await getIntent('0x...')

sendOriginTransaction

Sends the origin transaction for an intent. Signature: (params: SendOriginTxParams) => Promise<string> Usage:
const txHash = await sendOriginTransaction({
  walletClient,
  intentAddress: '0x...',
  // ... other params
})