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

OptionTypeRequiredDescription
trailsApiKeystringYesYour Trails API key. Join the Trails Telegram group to request access.
trailsApiUrlstringNoCustom API endpoint URL if using a self-hosted instance.
sequenceIndexerUrlstringNoCustom Sequence indexer URL.
sequenceNodeGatewayUrlstringNoCustom Sequence node gateway URL.
sequenceMetadataUrlstringNoCustom Sequence metadata URL.
sequenceApiUrlstringNoCustom Sequence API URL.
walletConnectProjectIdstringNoWalletConnect project ID for wallet connections.
slippageTolerancestring | numberNoDefault slippage tolerance for swaps (e.g., 0.005 for 0.5%).
debugbooleanNoEnable debug logging (default: false).

Chains

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

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()

Chain Type

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

Tokens

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

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'
})

Token Type

The unified Token type is used throughout the Trails SDK:
type Token = {
  // Required properties
  symbol: string
  name: string
  decimals: number
  contractAddress: string // Use zeroAddress for native tokens

  // Optional properties
  tokenId?: string
  chainId?: number
  chainName?: string
  imageUrl?: string
  isCustomToken?: boolean
  isSufficientBalance?: boolean
  isNativeToken?: boolean

  // Balance fields (raw → formatted → display)
  balance?: string // Raw big number string (e.g., "1000000000000000000")
  balanceFormatted?: string // Formatted number without commas (e.g., "1.0")
  balanceDisplay?: string // Formatted with commas (e.g., "1,000.00")

  // USD Balance fields
  balanceUsd?: number // Raw number (e.g., 1000.50)
  balanceUsdFormatted?: string // Without commas/symbol (e.g., "1000.50")
  balanceUsdDisplay?: string // With commas/symbol (e.g., "$1,000.50")

  // Price fields (raw → formatted → display)
  priceUsd?: number // Raw number (e.g., 1.50)
  priceUsdFormatted?: string // Without commas/symbol (e.g., "1.50")
  priceUsdDisplay?: string // With commas/symbol (e.g., "$1.50")
}

Balances

import {
  useTokenBalances,
  getAccountTotalBalanceUsd,
  useAccountTotalBalanceUsd,
  getHasSufficientBalanceToken,
  useHasSufficientBalanceToken,
  getHasSufficientBalanceUsd,
  useHasSufficientBalanceUsd,
} from '0xtrails'
Balance hooks and utilities:
  • useTokenBalances(address, options?): Returns sorted token balances enriched with USD price
  • useTokenBalancesForMultipleAccounts(addresses, options?): Returns token balances for multiple accounts
  • 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

Example Usage

import { useTokenBalances, useAccountTotalBalanceUsd } from '0xtrails'
import type { Token } from '0xtrails'

const WalletBalance = ({ address }: { address: string }) => {
  const [currentPage, setCurrentPage] = useState(1)
  const [pageSize, setPageSize] = useState(10)

  const { sortedTokens, isLoadingBalances, page } = useTokenBalances(address, {
    page: currentPage,
    pageSize: pageSize,
  })
  const { totalBalanceUsd, totalBalanceUsdFormatted } = useAccountTotalBalanceUsd(address)

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

  return (
    <div>
      <h3>Total Balance: {totalBalanceUsdFormatted}</h3>
      {sortedTokens.map((token: Token) => (
        <div key={`${token.chainId}-${token.contractAddress}`}>
          {token.symbol}: {token.balanceDisplay} ({token.balanceUsdDisplay})
        </div>
      ))}
      
      {/* Pagination controls */}
      <div>
        <button 
          onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
          disabled={currentPage === 1}
        >
          Previous
        </button>
        <span>Page {currentPage}</span>
        <button 
          onClick={() => setCurrentPage(prev => prev + 1)}
          disabled={!page?.more}
        >
          Next
        </button>
      </div>
    </div>
  )
}

useTokenBalances

Signature:
function useTokenBalances(
  address: Address | null,
  options?: {
    page?: number
    pageSize?: number
  }
): UseTokenBalancesReturn
Return Type:
type UseTokenBalancesReturn = {
  sortedTokens: Token[] // Tokens sorted by USD balance (highest first)
  isLoadingBalances: boolean
  isLoadingPrices: boolean
  isLoadingSortedTokens: boolean
  balanceError: Error | null
  page: Page | undefined // Pagination info
}

type Page = {
  column: string
  pageSize: number
  more: boolean // Whether there are more results
}
Token balances are returned as Token[] with balance fields populated. See the Token Type above for all available fields including balance, balanceFormatted, balanceDisplay, balanceUsd, etc.

useTokenBalancesForMultipleAccounts

Fetches token balances for multiple wallet addresses in a single API call. More efficient than calling useTokenBalances multiple times. Signature:
function useTokenBalancesForMultipleAccounts(
  addresses: (Address | null)[],
  options?: {
    page?: number
    pageSize?: number
  }
): UseTokenBalancesForMultipleAccountsReturn
Return Type:
type UseTokenBalancesForMultipleAccountsReturn = {
  /** Per-account token balances data, keyed by lowercase address */
  balancesByAccount: {
    [account: string]: {
      tokenBalancesData: {
        tokens: Token[]
      } | undefined
      isLoading: boolean
      error: Error | null
    }
  }
  /** Combined loading state - true if any account is loading */
  isLoading: boolean
  /** Combined error - first error encountered */
  error: Error | null
}
Example:
import { useTokenBalancesForMultipleAccounts } from '0xtrails'

const MultiWalletBalances = ({ addresses }: { addresses: string[] }) => {
  const { balancesByAccount, isLoading, error } = useTokenBalancesForMultipleAccounts(addresses)

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

  return (
    <div>
      {addresses.map(address => {
        const accountData = balancesByAccount[address.toLowerCase()]
        const tokens = accountData?.tokenBalancesData?.tokens || []

        return (
          <div key={address}>
            <h4>{address.slice(0, 6)}...{address.slice(-4)}</h4>
            {tokens.map(token => (
              <div key={`${token.chainId}-${token.contractAddress}`}>
                {token.symbol}: {token.balanceDisplay}
              </div>
            ))}
          </div>
        )
      })}
    </div>
  )
}

Quotes and Swapping

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

useQuote Hook

The useQuote hook provides real-time quotes for token swaps, cross-chain transfers and ability to pass in calldata for executions - enabling you to use Trails headlessly.

Usage Example

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

export 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.fromAmount} {quote.originToken.symbol}
      </div>
      <div>
          To: {quote.toAmount} {quote.destinationToken.symbol}
      </div>
      <div>
          Fee: {quote.fees?.totalFeeAmountUsdDisplay}
      </div>
      <button onClick={handleSwap}>
        Execute Swap
      </button>
    </div>
  )
}

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
  swapProvider?: RouteProvider | null
  bridgeProvider?: RouteProvider | null
  checkoutOnHandlers?: Partial<CheckoutOnHandlers>
  paymasterUrl?: string
  selectedFeeOption?: FeeOption | null
  abortSignal?: AbortSignal
  apiKey?: string | null
  nodeGatewayEnv?: 'prod' | 'dev' | 'local' | 'cors-anywhere'
  isSmartWallet?: boolean | null
}

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

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

enum RouteProvider {
  AUTO = 'AUTO',
  CCTP = 'CCTP',
  LIFI = 'LIFI',
  RELAY = 'RELAY',
  SUSHI = 'SUSHI',
  ZEROX = 'ZEROX'
}

Constants

import { TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from '0xtrails'

TRAILS_ROUTER_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 { TrailsWidget } from "0xtrails"

import { encodeFunctionData } from 'viem'
import { TRAILS_ROUTER_PLACEHOLDER_AMOUNT } from '0xtrails'

export const Example = () => {
    const calldata = encodeFunctionData({
        abi: stakingABI,
        functionName: 'stake',
        args: [TRAILS_ROUTER_PLACEHOLDER_AMOUNT] // Will be replaced with actual amount
    })

    return (
        // 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 {
  useIntentTransactionHistory,
  getAccountTransactionHistory,
  useAccountTransactionHistory,
  useIntentTransactionHistory,
  getTxTimeDiff,
} from '0xtrails'

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

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

Usage

import { useAccountTransactionHistory } from '0xtrails'

export const TransactionList = () => {
    const { data, isLoading, error } = useAccountTransactionHistory({
        chainId: 1,
        accountAddress: '0x123...',
        // Optional:
        // pageSize: 10,
        // includeMetadata: true,
        // page: 0,
        // abortSignal: new AbortController().signal,
    })

    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>
    )
}

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
  abortSignal?: AbortSignal
}

useIntentTransactionHistory

React hook for fetching transaction history for a specific intent address. Usage:
const {
    transactions: transactionHistory,
    loading: isLoadingHistory,
    error: historyError,
    refetch: refetchIntentHistory,
  } = useIntentTransactionHistory({
      accountAddress: '0x123...',
      pageSize: 10,
      enabled: true,
  })

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"

Encoders

import { getERC20TransferData } from '0xtrails'

getERC20TransferData

Encodes ERC20 transfer calldata for token transfers. Signature: (params: { recipient: string, amount: bigint }) => string Usage:
const transferData = getERC20TransferData(
  {
    recipient: '0x1234567890123456789012345678901234567890',
    amount: 1000000n,
  }
)

Error Handling

import {
  InsufficientBalanceError,
  UserRejectionError,
  getIsUserRejectionError,
  getIsBalanceTooLowError,
  getIsApiError,
  getIsRateLimitedError,
  getIsRequiredAmountNotMetError,
  getIsNoAvailableQuoteError,
  getIsQuoteFailedError,
  getIsQuoteTokenError,
  getIsQuoteInputError,
  getIsInsufficientLiquidityError,
  getIsWalletAlreadyConnectedError,
  getPrettifiedErrorMessage,
} from '0xtrails'

Error Classes

InsufficientBalanceError

Error thrown when a user has insufficient balance to complete a transaction.
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.
try {
  await swap()
} catch (error) {
  if (error instanceof UserRejectionError) {
    console.log('User rejected the transaction')
  }
}

Error Detection Utilities

All error detection functions have the same signature: (error: unknown) => boolean
FunctionDescription
getIsUserRejectionErrorUser rejected the transaction in their wallet
getIsBalanceTooLowErrorInsufficient balance for the transaction
getIsApiErrorAPI returned an error
getIsRateLimitedErrorRequest was rate limited
getIsRequiredAmountNotMetErrorRequired minimum amount not met
getIsNoAvailableQuoteErrorNo quote available for the requested swap
getIsQuoteFailedErrorQuote request failed
getIsQuoteTokenErrorInvalid token in quote request
getIsQuoteInputErrorInvalid input parameters for quote
getIsInsufficientLiquidityErrorInsufficient liquidity for the swap
getIsWalletAlreadyConnectedErrorWallet is already connected

getPrettifiedErrorMessage

Converts any error into a user-friendly message string. Signature: (error: unknown) => string
try {
  await swap()
} catch (error) {
  const userMessage = getPrettifiedErrorMessage(error)
  showNotification(userMessage)
  // e.g., "Insufficient balance" instead of technical error details
}

Example: Comprehensive Error Handling

import {
  getIsUserRejectionError,
  getIsBalanceTooLowError,
  getIsNoAvailableQuoteError,
  getIsRateLimitedError,
  getPrettifiedErrorMessage,
} from '0xtrails'

async function handleSwap() {
  try {
    await swap()
  } catch (error) {
    if (getIsUserRejectionError(error)) {
      // User cancelled - don't show error notification
      return
    }
    
    if (getIsBalanceTooLowError(error)) {
      showNotification('Not enough balance. Please add funds.')
      return
    }
    
    if (getIsNoAvailableQuoteError(error)) {
      showNotification('No route available for this swap. Try a different token pair.')
      return
    }
    
    if (getIsRateLimitedError(error)) {
      showNotification('Too many requests. Please wait a moment.')
      return
    }
    
    // Fallback to prettified message
    showNotification(getPrettifiedErrorMessage(error))
  }
}

useGetIntent

Hook to fetch intent data from the Trails API.
import { useGetIntent, type UseGetIntentParams, type UseGetIntentReturn } from '0xtrails'

Usage

import { useGetIntent } from '0xtrails'

export const IntentDetails = ({ intentId }: { intentId: string }) => {
  const { intent, isLoading, isError, error, refetch } = useGetIntent({
    intentId,
    enabled: true,
  })

  if (isLoading) return <div>Loading intent...</div>
  if (isError) return <div>Error: {error?.message}</div>
  if (!intent) return <div>Intent not found</div>

  return (
    <div>
      <p>Intent ID: {intent.intentId}</p>
      <p>Status: {intent.status}</p>
      <p>Origin Chain: {intent.quoteRequest.originChainId}</p>
      <p>Destination Chain: {intent.quoteRequest.destinationChainId}</p>
      <button onClick={refetch}>Refresh</button>
    </div>
  )
}

Types

type UseGetIntentParams = {
  intentId: string | undefined
  enabled?: boolean
}

type UseGetIntentReturn = {
  intent: Intent | null
  isLoading: boolean
  isError: boolean
  error: Error | null
  refetch: () => void
}

useIntentRecover

Hook for recovering funds from stuck or failed intent transactions. Trails automatically refunds upon reverting, but this hook handles situations where funds are stuck without an explicit revert. It automatically selects the intent address (origin or destination) with the highest balance for recovery.
import {
  useIntentRecover,
  type UseIntentRecoverParams,
  type UseIntentRecoverReturn,
} from '0xtrails'

Usage

import { useIntentRecover } from '0xtrails'
import { useWalletClient, useAccount } from 'wagmi'

export const RecoverComponent = ({ intentId }: { intentId: string }) => {
  const { data: walletClient } = useWalletClient()
  const { address } = useAccount()

  const {
    intent,
    isLoadingIntent,
    hasIntentBalance,
    recoverToken,
    selectedRecoveryTarget,
    signPayload,
    getRecoverTx,
    recover,
    getRecoverStatus,
    intentError,
  } = useIntentRecover({
    intentId,
    walletClient,
    refundToAddress: address, // Optional: defaults to connected wallet
  })

  // Option 1: Automatic flow (recommended)
  const handleAutoRecover = async () => {
    try {
      const { txHash, receipt, chainId } = await recover()
      console.log('Recovery successful:', txHash)

      // Optionally check status
      const status = await getRecoverStatus({ txHash, chainId })
      console.log('Recovery status:', status)
    } catch (error) {
      console.error('Recovery failed:', error)
    }
  }

  // Option 2: Manual flow for more control
  const handleManualRecover = async () => {
    try {
      // Step 1: Sign the recovery payload
      const { signature, payload } = await signPayload()

      // Step 2: Get the recovery transaction
      const recoverTx = await getRecoverTx({
        signedHash: signature,
        payload,
      })

      // Step 3: Execute the recovery transaction
      const txHash = await walletClient.sendTransaction({
        to: recoverTx.to,
        data: recoverTx.data,
        chain: recoverTx.chain,
      })

      console.log('Recovery transaction sent:', txHash)
    } catch (error) {
      console.error('Recovery failed:', error)
    }
  }

  if (isLoadingIntent) return <div>Loading intent...</div>
  if (intentError) return <div>Error: {intentError.message}</div>
  if (!hasIntentBalance) return <div>No balance to recover</div>

  return (
    <div>
      {recoverToken && (
        <p>
          Recovering {recoverToken.balanceFormatted} {recoverToken.symbol}
          ({recoverToken.balanceUsdDisplay}) from {selectedRecoveryTarget?.chainName}
        </p>
      )}
      <button onClick={handleAutoRecover}>
        Recover Funds
      </button>
    </div>
  )
}

Types

type UseIntentRecoverParams = {
  intentId: string | undefined
  walletClient?: WalletClient
  refundToAddress?: `0x${string}` // Optional: address to receive recovered funds (defaults to wallet address)
}

type UseIntentRecoverReturn = {
  intent: Intent | null
  isLoadingIntent: boolean
  isLoadingBalances: boolean
  intentError: Error | null
  balancesError: Error | null
  hasIntentBalance: boolean
  /** Token that will be recovered (the one with highest balance) */
  recoverToken: Token | null
  /** Information about which intent address will be recovered from */
  selectedRecoveryTarget: {
    address: string
    chainId: number
    chainName: string
    isOrigin: boolean
  } | null
  refetchIntent: () => void
  signPayload: () => Promise<{
    signature: string
    payload: Payload
    refundCall: PayloadCall
  }>
  getRecoverTx: (params: {
    signedHash: string
    payload: Payload
  }) => Promise<{
    to: `0x${string}`
    data: `0x${string}`
    chainId: number
    chain: Chain
  }>
  /** One-call recovery: signs, builds, and sends the recovery transaction */
  recover: () => Promise<{
    txHash: `0x${string}`
    receipt: TransactionReceipt
    chainId: number
  }>
  /** Check recovery transaction status */
  getRecoverStatus: (params: {
    txHash: `0x${string}`
    chainId: number
  }) => Promise<{
    status: 'success' | 'fail' | 'pending'
  }>
}

useCommitIntent / useExecuteIntent

Mutation hooks for committing and executing intents. These are lower-level hooks for advanced users who need direct control over the intent lifecycle.
import { useCommitIntent, useExecuteIntent } from '0xtrails'

useCommitIntent

const { mutate: commitIntent, isPending, isError, error, data } = useCommitIntent()

// Commit an intent
commitIntent(intent)

useExecuteIntent

const { mutate: executeIntent, isPending, isError, error, data } = useExecuteIntent()

// Execute an intent with deposit signature
executeIntent({
  intent,
  depositSignature,
})

TrailsClient

For advanced use cases, you can access the underlying Trails API client directly.
import { getTrailsClient, useTrailsClient, TrailsClient } from '0xtrails'

useTrailsClient Hook

import { useTrailsClient } from '0xtrails'

function MyComponent() {
  const trailsClient = useTrailsClient()

  const fetchIntent = async (intentId: string) => {
    const response = await trailsClient.getIntent({ intentId })
    return response.intent
  }

  // Use client for direct API calls
}

getTrailsClient Function

For non-React contexts:
import { getTrailsClient } from '0xtrails'

const client = getTrailsClient({
  apiKey: 'YOUR_API_KEY',
  hostname: 'https://api.trails.xyz', // optional
})

// Direct API calls
const quote = await client.quoteIntent({
  originChainId: 1,
  destinationChainId: 8453,
  // ... other params
})

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,
  sendOriginTransaction,
} from '0xtrails'

Types

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

type RouteProvider = 'AUTO' | 'CCTP' | 'LIFI' | 'RELAY' | 'SUSHI' | 'ZEROX'

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

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
})

sendOriginTransaction

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