> ## Documentation Index
> Fetch the complete documentation index at: https://anypay-docs-update-resources-2026-06-29.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Hooks

> React Hooks and utilities available in the Trails SDK

All hooks and utilities are available from the `0xtrails` package unless stated otherwise.

## TrailsProvider

<Warning>
  **Required for Hooks**: The `TrailsProvider` must wrap your application when using any Trails hooks. This provider configures the necessary context for all hook functionality.
</Warning>

```tsx theme={null}
import { TrailsProvider } from '0xtrails'

<TrailsProvider
  config={{
    trailsApiKey: "...",
    trailsApiUrl: "...",            // optional
    sequenceIndexerUrl: "...",      // optional
    sequenceNodeGatewayUrl: "...",  // optional
    intentProtocol: "v1.5",        // optional — defaults to v1.5
  }}
>
  <App />
</TrailsProvider>
```

### Apps without wagmi

<Note>
  Since `0xtrails@0.16.0`, Trails uses a built-in EVM runtime by default and no longer requires wagmi for the widget or the SDK hooks. See the [0.16 release notes](/sdk/changelog/0xtrails-0.16.0) for migration details.
</Note>

The SDK still uses `@tanstack/react-query` internally. If your app does not already include a `QueryClientProvider`, add one above `TrailsProvider`:

```tsx theme={null}
'use client'

import { TrailsProvider } from '0xtrails'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const queryClient = new QueryClient()

export const Providers = ({ children }: { children: React.ReactNode }) => {
  return (
    <QueryClientProvider client={queryClient}>
      <TrailsProvider config={{ trailsApiKey: 'YOUR_API_KEY' }}>
        {children}
      </TrailsProvider>
    </QueryClientProvider>
  )
}
```

### Apps with wagmi

If your app already uses wagmi, wrap your app with `WagmiProvider` and use wagmi hooks (`useAccount`, `useWalletClient`, etc.) to supply wallet state to Trails hooks. For widget integrations that should share the same wagmi session, pass [`wagmiAdapter`](/sdk/adapters/wagmi) on the widget's `adapters` prop — see [Adapters](/sdk/adapters/overview).

```bash theme={null}
pnpm i @0xtrails/adapter-wagmi
```

```tsx theme={null}
'use client'

import { TrailsProvider } from '0xtrails'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider, createConfig, http } from 'wagmi'
import { arbitrum, base, polygon } from 'wagmi/chains'

const wagmiConfig = createConfig({
  chains: [base, polygon, arbitrum],
  transports: {
    [base.id]: http(),
    [polygon.id]: http(),
    [arbitrum.id]: http(),
  },
})

const queryClient = new QueryClient()

export const Providers = ({ children }: { children: React.ReactNode }) => {
  return (
    <WagmiProvider config={wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <TrailsProvider config={{ trailsApiKey: 'YOUR_API_KEY' }}>
          {children}
        </TrailsProvider>
      </QueryClientProvider>
    </WagmiProvider>
  )
}
```

For custom EIP-1193 wallets without wagmi, use the `evmAdapter` helper on widget components — see [EIP-1193 adapter](/sdk/adapters/eip-1193).

### Configuration Options

| Option                   | Type               | Required | Description                                                                                               |
| ------------------------ | ------------------ | -------- | --------------------------------------------------------------------------------------------------------- |
| `trailsApiKey`           | `string`           | Yes      | Your Trails API key. Get your API key from the [Trails Dashboard](https://dashboard.trails.build/landing) |
|                          |                    |          |                                                                                                           |
| `trailsApiUrl`           | `string`           | No       | Custom API endpoint URL if using a self-hosted instance.                                                  |
| `sequenceIndexerUrl`     | `string`           | No       | Custom Sequence indexer URL.                                                                              |
| `sequenceNodeGatewayUrl` | `string`           | No       | Custom Sequence node gateway URL.                                                                         |
| `sequenceMetadataUrl`    | `string`           | No       | Custom Sequence metadata URL.                                                                             |
| `sequenceApiUrl`         | `string`           | No       | Custom Sequence API URL.                                                                                  |
| `walletConnectProjectId` | `string`           | No       | WalletConnect project ID for wallet connections.                                                          |
| `slippageTolerance`      | `string \| number` | No       | Default slippage tolerance for swaps (e.g., `0.005` for 0.5%).                                            |
| `debug`                  | `boolean`          | No       | Enable debug logging (default: `false`).                                                                  |
| `intentProtocol`         | `"v1" \| "v1.5"`   | No       | Override intent protocol version (default: `"v1.5"`). See [Protocol v1.5](/architecture/v1-5).            |

## Wallet adapters

<Note>
  Added in [`0xtrails@0.16.0`](/sdk/changelog/0xtrails-0.16.0). Adapters are a **widget-only** feature — pass them on focused widget components (`Pay`, `Fund`, `Swap`, …), not on `TrailsProvider`. See the [Adapters](/sdk/adapters/overview) section for wagmi, EIP-1193, Solana, and thirdweb integrations.
</Note>

Hooks-based integrations do not use adapters. Supply wallet state directly to hooks such as `useQuote` and `useTrailsSendTransaction` via `walletClient` and `address` from your own wallet stack (wagmi, viem, ethers, etc.).

## Chains

```ts theme={null}
import { getSupportedChains, useSupportedChains } from '0xtrails'
```

* `getSupportedChains()`: `Promise<Chain[]>`
* `useSupportedChains()`: `{ supportedChains: Chain[], isLoadingChains: boolean }`

### Utility Functions

```ts theme={null}
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

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

### Public Clients

<Note>
  Added in [`0xtrails@0.16.0`](/sdk/changelog/0xtrails-0.16.0). Use these to get a viem `PublicClient` for any supported chain without configuring transports yourself — Trails resolves the RPC via the Sequence node gateway using your API key.
</Note>

```ts theme={null}
import { usePublicClient, usePublicClientGetter } from '0xtrails'
```

* `usePublicClient(chainId?: number)`: returns a viem `PublicClient | null` for the given chain.
* `usePublicClientGetter()`: returns a `(chainId: number, retryCount?: number) => PublicClient | null` you can call inside event handlers or effects.

```tsx theme={null}
import { usePublicClient, usePublicClientGetter } from '0xtrails'

const base = usePublicClient(8453)
const getClient = usePublicClientGetter()

async function readBalanceOn(chainId: number, address: `0x${string}`) {
  const client = getClient(chainId)
  return client?.getBalance({ address })
}
```

## Tokens

```ts theme={null}
import { getSupportedTokens, useSupportedTokens, useTokenList } from '0xtrails'
```

* `useTokenList()`: `{ tokens: Token[] | undefined, isLoadingTokens: boolean }`
* `useSupportedTokens({ chainId?: number })`: `{ supportedTokens: Token[], isLoadingTokens: boolean }`
* `getSupportedTokens()`: `Promise<Token[]>`

### Additional Helpers

```ts theme={null}
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:

```ts theme={null}
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

```ts theme={null}
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

```tsx theme={null}
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**:

```ts theme={null}
function useTokenBalances(
  address: Address | null,
  options?: {
    page?: number
    pageSize?: number
  }
): UseTokenBalancesReturn
```

**Return Type**:

```ts theme={null}
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](#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**:

```ts theme={null}
function useTokenBalancesForMultipleAccounts(
  addresses: (Address | null)[],
  options?: {
    page?: number
    pageSize?: number
  }
): UseTokenBalancesForMultipleAccountsReturn
```

**Return Type**:

```ts theme={null}
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**:

```tsx theme={null}
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

```ts theme={null}
import {
  useQuote,
  useTrailsSendTransaction,
  type UseQuoteReturn,
  type UseQuoteProps,
  type UseTrailsSendTransactionParameters,
  type UseTrailsSendTransactionReturnType,
  type ActionItem,
  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.

`from.amount` and `to.amount` are human-readable decimal strings such as `"1"` or `"0.25"`. Do not pass raw wei strings; the SDK scales amounts using the token decimals.

Pass `walletAddress` to preview quotes before a wallet is connected. If a connected `walletClient.account.address` is available, it becomes the quote owner and takes precedence over `walletAddress`. Executing with `send` still requires a real wallet client.

#### Usage Example

```tsx theme={null}
import { useEffect } from 'react'
import { useQuote } from '0xtrails'
import { useWalletClient, useAccount } from 'wagmi'

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

  const { quote, send, isLoadingQuote, quoteError, refetchQuote } = useQuote({
    walletClient,
    from: {
      token: 'USDC',
      chain: 'ethereum',
      amount: '1',
    },
    to: {
      token: 'USDC',
      chain: 'base',
      recipient: address,
    },
    slippageTolerance: '0.005', // 0.5%
    onStatusUpdate: (states) => {
      console.log('Transaction states:', states)
    },
  })

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

  const handleSwap = async () => {
    if (!send) return

    try {
      const result = await send()
      console.log('Swap completed:', result)
    } catch (error) {
      console.error('Swap failed:', error)
    }
  }

  if (isLoadingQuote) return <div>Getting quote...</div>
  if (quoteError) return <div>Error: {quoteError.message}</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

```ts theme={null}
type UseQuoteProps = {
  walletClient?: any
  walletAddress?: string | null
  from: {
    token: string
    chain: ChainIdentifier
    amount?: string // human-readable; EXACT_INPUT when set
    decimals?: number // optional source token decimals override
  }
  to: {
    token: string
    chain: ChainIdentifier
    recipient?: string | null
    approve?: string | null
    amount?: string // human-readable; EXACT_OUTPUT when set
    decimals?: number // optional destination token decimals override
    calls?: (Call | Call[])[] | null
    calldata?: string | null // deprecated; prefer calls or actions
  }
  slippageTolerance?: string | number | null
  onStatusUpdate?: ((transactionStates: TransactionState[]) => void) | null
  swapProvider?: RouteProvider | null
  bridgeProvider?: RouteProvider | null
  swapProviderFallback?: boolean
  bridgeProviderFallback?: boolean
  checkoutOnHandlers?: Partial<CheckoutOnHandlers>
  selectedFeeOption?: FeeOption | null
  abortSignal?: AbortSignal
  apiKey?: string | null
  nodeGatewayEnv?: 'prod' | 'dev' | 'local' | 'cors-anywhere'
  isSmartWallet?: boolean | null
  mode?: 'pay' | 'fund' | 'earn' | 'swap' | 'withdraw' | null
  intentProtocolVersion?: 'v1' | 'v1.5' | null
  actions?: ActionItem[]
}

type UseQuoteReturn = {
  quote: Quote | null
  send: ((options?: SendOptions) => Promise<SwapReturn | null>) | null
  isLoadingQuote: boolean
  quoteError: QuoteError | null
  quoteErrorPrettified: string
  refetchQuote: () => Promise<void>
  abort: () => void
  feeOptions: FeeOption[]
  isRecipientContract: boolean | null
  isSenderContractOnOrigin: boolean | null
  isSenderContractOnDestination: boolean | null
}

type ActionItem =
  | LendActionItem
  | SwapActionItem
  | DepositActionItem
  | CustomActionItem
  | AssertActionItem
```

### useTrailsSendTransaction Hook

`useTrailsSendTransaction` creates a button-driven transaction flow with the Trails modal. It accepts most `useQuote` options except `from`, `to`, `walletClient`, and `selectedFeeOption`; pass destination requirements when calling `sendTransaction`.

`tokenAmount` and `fromAmount` are human-readable decimal strings. Native `value` remains a raw wei `bigint`, matching wagmi.

```tsx theme={null}
import { useTrailsSendTransaction } from '0xtrails'

const { sendTransaction, isPending, error } = useTrailsSendTransaction()

sendTransaction({
  to: '0x1234567890123456789012345678901234567890',
  value: 1000000000000000n, // 0.001 native token
})
```

#### Types

```ts theme={null}
type TrailsSendTransactionVariables = {
  to?: `0x${string}`
  value?: bigint
  data?: `0x${string}`
  tokenAddress?: `0x${string}`
  tokenAmount?: string // human-readable destination amount
  tokenDecimals?: number
  fromTokenAddress?: `0x${string}`
  fromChainId?: number
  fromAmount?: string // human-readable origin amount
  fromDecimals?: number
}

type UseTrailsSendTransactionParameters =
  Omit<UseQuoteProps, 'from' | 'to' | 'walletClient' | 'selectedFeeOption'> & {
    calls?: DestinationCallsInput
    onSuccess?: (data: SwapReturn, variables: TrailsSendTransactionVariables) => void
    onError?: (error: Error, variables: TrailsSendTransactionVariables) => void
    onSettled?: (
      data: SwapReturn | undefined,
      error: Error | null,
      variables: TrailsSendTransactionVariables,
    ) => void
    receiptActionButtonText?: string
    onReceiptAction?: () => void
  }

type UseTrailsSendTransactionReturnType = {
  sendTransaction: (variables: TrailsSendTransactionVariables) => void
  sendTransactionAsync: (variables: TrailsSendTransactionVariables) => Promise<SwapReturn>
  retry: () => void
  data: SwapReturn | undefined
  error: Error | null
  isPending: boolean
  isSuccess: boolean
  isError: boolean
  isIdle: boolean
  status: 'idle' | 'pending' | 'error' | 'success'
  reset: () => void
  variables: TrailsSendTransactionVariables | undefined
}
```

## Earn Markets and Providers

```ts theme={null}
import {
  useEarnMarkets,
  useEarnProviders,
  useEarnBalances,
  type UseEarnMarketsParams,
  type UseEarnMarketsReturn,
  type UseEarnProvidersParams,
  type UseEarnProvidersReturn,
  type UseEarnBalancesParams,
  type UseEarnBalancesReturn,
} from '0xtrails'
```

### useEarnMarkets Hook

Fetch earn markets for `lend` and `deposit` actions. Use the returned market `id` as a `marketId`.

```tsx theme={null}
import { useEarnMarkets } from '0xtrails'

const { data, total, isLoading, error, refetch } = useEarnMarkets({
  chain: 'polygon',
  type: 'vault',
  provider: 'morpho',
  sortBy: 'rewardRateDesc',
  search: 'USDT'
})
```

#### Types

```ts theme={null}
type UseEarnMarketsParams = {
  chain?: ChainIdentifier
  provider?: ProviderId
  type?: 'lending' | 'vault'
  sortBy?: 'statusEnterAsc' | 'statusEnterDesc' | 'statusExitAsc' | 'statusExitDesc' | 'rewardRateAsc' | 'rewardRateDesc'
  limit?: number
  offset?: number
  search?: string
}

type UseEarnMarketsReturn = {
  data: EarnMarket[] | undefined
  total: number
  isLoading: boolean
  error: Error | null
  refetch: () => void
}
```

### useEarnProviders Hook

Fetch supported earn providers.

```tsx theme={null}
import { useEarnProviders } from '0xtrails'

const { data, total, isLoading, error, refetch } = useEarnProviders({
  limit: 50,
})
```

#### Types

```ts theme={null}
type UseEarnProvidersParams = {
  limit?: number
  offset?: number
}

type UseEarnProvidersReturn = {
  data: Provider[] | undefined
  total: number
  isLoading: boolean
  error: Error | null
  refetch: () => void
}
```

### useEarnBalances Hook

Fetch earn positions for a wallet. Use `chain` for one network or `chains` to batch multiple networks in one backend request.

```tsx theme={null}
import { useEarnBalances } from '0xtrails'

const { data, errors, isLoading, error, refetch } = useEarnBalances({
  walletAddress: '0x9ec762D784B653E168d93eA3078B023d8958dBc6',
  chain: 'polygon',
})

const { data: multiChainBalances } = useEarnBalances({
  walletAddress: '0x9ec762D784B653E168d93eA3078B023d8958dBc6',
  chains: ['polygon', 'ethereum', 'base'],
})
```

Restrict the lookup to one market:

```tsx theme={null}
const { data } = useEarnBalances({
  walletAddress,
  chain: 'polygon',
  marketId: 'polygon-usdc-aave-v3-lending',
})
```

#### Types

```ts theme={null}
type UseEarnBalancesParams =
  | {
      walletAddress: string
      chain: ChainIdentifier
      chains?: never
      marketId?: string
      arguments?: EarnBalanceQuery['arguments']
      enabled?: boolean
    }
  | {
      walletAddress: string
      chains: ChainIdentifier[]
      chain?: never
      marketId?: string
      arguments?: EarnBalanceQuery['arguments']
      enabled?: boolean
    }

type UseEarnBalancesReturn = {
  data: EarnBalances[] | undefined
  errors: EarnBalanceError[]
  isLoading: boolean
  error: Error | null
  refetch: () => void
}
```

### useResolveActions Hook

<Note>
  Added in [`0xtrails@0.16.0`](/sdk/changelog/0xtrails-0.16.0). Resolves an array of [composable actions](/sdk/composable-actions/overview) (`lend`, `deposit`, `swap`, `assertCondition`, `custom`) into the concrete `Call[]` you can pass to `useTrailsSendTransaction` or sign directly.
</Note>

```tsx theme={null}
import { useResolveActions, lend, useTrailsClient, usePublicClient } from '0xtrails'

const trailsClient = useTrailsClient()
const publicClient = usePublicClient(8453)

const { calls, isLoading, error } = useResolveActions({
  actions: [lend({ marketId: 'base-usdc-aave-v3-lending', amount: '100' })],
  chainId: 8453,
  destinationChain: 'base',
  userWalletAddress: '0xUserAddress',
  trailsClient,
  publicClient,
})
```

#### Types

```ts theme={null}
type UseResolveActionsProps = {
  actions: ActionItem[]
  chainId: number | undefined
  destinationChain: ChainIdentifier
  userWalletAddress: `0x${string}` | undefined
  trailsClient: TrailsClient | null
  publicClient: PublicClient | null
}

type UseResolveActionsReturn = {
  calls: Call[] | null
  isLoading: boolean
  error: QuoteError | null
}
```

## Fiat & Exchange Rates

<Note>
  Added in [`0xtrails@0.16.0`](/sdk/changelog/0xtrails-0.16.0). Use these to drive fiat-aware UI (currency selectors, USD-to-local-currency displays) without rolling your own pricing source.
</Note>

```ts theme={null}
import { useFiatCurrencyList, useExchangeRate } from '0xtrails'
```

### useFiatCurrencyList

Returns the list of fiat currencies the Trails API supports for the fiat on-ramp flow. Backed by `@tanstack/react-query`.

```tsx theme={null}
const { data: currencies, isLoading } = useFiatCurrencyList()
```

### useExchangeRate

Fetch the exchange rate between two currencies. Handles same-currency (returns `1`) and direction inversion (e.g. EUR → USD inverts the USD → EUR rate).

```tsx theme={null}
const { exchangeRate, isLoading, error } = useExchangeRate({
  fromCurrency: 'USD',
  toCurrency: 'EUR',
})
```

#### Options

```ts theme={null}
type UseExchangeRateOptions = {
  fromCurrency?: string // ISO 4217, default "USD"
  toCurrency?: string   // ISO 4217, default "USD"
  disabled?: boolean    // skip the query
}

type UseExchangeRateResult = {
  exchangeRate: number | undefined
  isLoading: boolean
  error: Error | null
}
```

## Onramp Providers

<Note>
  Added in [`0xtrails@0.16.0`](/sdk/changelog/0xtrails-0.16.0). Use these to drive your own fiat on-ramp UI (e.g. payment method picker, exchange selector) outside the Trails widget. Backed by Mesh.
</Note>

```ts theme={null}
import { useMeldPaymentMethods, useMeldServiceProviders } from '0xtrails'
```

### useMeldPaymentMethods

List the payment methods available for a given fiat currency (e.g. `CREDIT_DEBIT_CARD`, `APPLE_PAY`, `GOOGLE_PAY`, `SEPA`, `PAYPAL`).

```tsx theme={null}
const { data: methods, isLoading } = useMeldPaymentMethods('EUR')
```

### useMeldServiceProviders

List the on-ramp service providers available, optionally filtered.

```tsx theme={null}
const { data: providers } = useMeldServiceProviders({
  // any fields of GetMeldServiceProvidersRequest
})
```

## Constants

```ts theme={null}
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**:

```tsx theme={null}
import { Fund } from '0xtrails/widget'
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 (
        <Fund
            apiKey="YOUR_API_KEY"
            to={{
                recipient: "0x...", // Staking contract
                currency: "ETH",
                chain: "ethereum",
                calldata,
            }}
        >
            <button>Stake ETH</button>
        </Fund>
    )
}
```

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

```ts theme={null}
import {
  useIntentTransactionHistory,
  getAccountTransactionHistory,
  useAccountTransactionHistory,
  useIntentTransactionHistory,
  getTxTimeDiff,
} from '0xtrails'
```

### getAccountTransactionHistory

Gets transaction history for a wallet address interacting with Trails.

**Signature**:

```ts theme={null}
function getAccountTransactionHistory(params: {
  chainId: number
  accountAddress: string
  pageSize?: number
  includeMetadata?: boolean
  page?: number
}): Promise<TransactionHistoryResponse>
```

**Usage**:

```ts theme={null}
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.

```tsx theme={null}
import { useAccountTransactionHistory } from '0xtrails'
```

#### Usage

```tsx theme={null}
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

```ts theme={null}
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**:

```ts theme={null}
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**:

```ts theme={null}
const timeDiff = getTxTimeDiff(transaction.timestamp)
console.log(timeDiff) // "5 minutes ago"
```

## SDK Utilities

```ts theme={null}
import { SDK_VERSION, getVersion } from '0xtrails'
```

* `SDK_VERSION`: Current SDK version string
* `getVersion()`: Returns the current SDK version at runtime

### FundMethod Type

```ts theme={null}
import type { FundMethod } from '0xtrails'
```

`FundMethod` is exported as a strict union: `"wallet" | "direct-transfer" | "onramp-mesh" | "onramp-meld"`.

## Encoders

```ts theme={null}
import { getERC20TransferData } from '0xtrails'
```

### getERC20TransferData

Encodes ERC20 transfer calldata for token transfers.

**Signature**: `(params: { recipient: string, amount: bigint }) => string`

**Usage**:

```ts theme={null}
const transferData = getERC20TransferData(
  {
    recipient: '0x1234567890123456789012345678901234567890',
    amount: 1000000n,
  }
)
```

## Error Handling

```ts theme={null}
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.

```ts theme={null}
try {
  await send?.()
} 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.

```ts theme={null}
try {
  await send?.()
} 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`

| Function                           | Description                                   |
| ---------------------------------- | --------------------------------------------- |
| `getIsUserRejectionError`          | User rejected the transaction in their wallet |
| `getIsBalanceTooLowError`          | Insufficient balance for the transaction      |
| `getIsApiError`                    | API returned an error                         |
| `getIsRateLimitedError`            | Request was rate limited                      |
| `getIsRequiredAmountNotMetError`   | Required minimum amount not met               |
| `getIsNoAvailableQuoteError`       | No quote available for the requested swap     |
| `getIsQuoteFailedError`            | Quote request failed                          |
| `getIsQuoteTokenError`             | Invalid token in quote request                |
| `getIsQuoteInputError`             | Invalid input parameters for quote            |
| `getIsInsufficientLiquidityError`  | Insufficient liquidity for the swap           |
| `getIsWalletAlreadyConnectedError` | Wallet is already connected                   |

### getPrettifiedErrorMessage

Converts any error into a user-friendly message string.

**Signature**: `(error: unknown) => string`

```ts theme={null}
try {
  await send?.()
} catch (error) {
  const userMessage = getPrettifiedErrorMessage(error)
  showNotification(userMessage)
  // e.g., "Insufficient balance" instead of technical error details
}
```

### Example: Comprehensive Error Handling

```tsx theme={null}
import {
  getIsUserRejectionError,
  getIsBalanceTooLowError,
  getIsNoAvailableQuoteError,
  getIsRateLimitedError,
  getPrettifiedErrorMessage,
} from '0xtrails'

async function handleSwap() {
  try {
    await send?.()
  } 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.

```ts theme={null}
import { useGetIntent, type UseGetIntentParams, type UseGetIntentReturn } from '0xtrails'
```

### Usage

```tsx theme={null}
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

```ts theme={null}
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.

```ts theme={null}
import {
  useIntentRecover,
  type UseIntentRecoverParams,
  type UseIntentRecoverReturn,
} from '0xtrails'
```

### Usage

```tsx theme={null}
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

```ts theme={null}
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'
  }>
}
```

## useIntentRecoverWithAddress

Recovery hook designed for apps that handle signing externally (e.g., embedded wallets, or custodial solutions). Unlike `useIntentRecover` which requires a `walletClient`, this hook accepts a plain `walletAddress` and exposes `getDataToSign` so you can sign the EIP-712 typed data with any signing method.

Like `useIntentRecover`, it automatically selects the intent address (origin or destination) with the highest balance for recovery.

```ts theme={null}
import {
  useIntentRecoverWithAddress,
  type UseIntentRecoverWithSignatureParams,
  type UseIntentRecoverWithSignatureReturn,
} from '0xtrails'
```

### Usage

```tsx theme={null}
import { useIntentRecoverWithAddress } from '0xtrails'

export const RecoverComponent = ({ intentId }: { intentId: string }) => {
  const walletAddress = '0x...' // Your externally managed wallet address

  const {
    intent,
    isLoadingIntent,
    hasIntentBalance,
    recoverToken,
    selectedRecoveryTarget,
    getDataToSign,
    getRecoverTx,
    getRecoverStatus,
    intentError,
  } = useIntentRecoverWithAddress({
    intentId,
    walletAddress,
    refundToAddress: '0x...', // Optional: defaults to walletAddress
  })

  const handleRecover = async () => {
    try {
      // Step 1: Get EIP-712 typed data to sign
      const { payload, typedData, chainId } = await getDataToSign()

      // Step 2: Sign with your external wallet/signer
      const signature = await myCustomWallet.signTypedData({
        account: walletAddress,
        ...typedData,
      })

      // Step 3: Build the recovery transaction
      const recoverTx = await getRecoverTx({ signature, payload })

      // Step 4: Send the transaction with your external wallet
      const hash = await myCustomWallet.sendTransaction({
        to: recoverTx.to,
        data: recoverTx.data,
        chain: recoverTx.chain,
      })

      console.log('Recovery transaction sent:', hash)

      // Optionally check status
      const status = await getRecoverStatus({ txHash: hash, chainId })
      console.log('Recovery status:', status)
    } 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={handleRecover}>
        Recover Funds
      </button>
    </div>
  )
}
```

### Types

```ts theme={null}
type UseIntentRecoverWithSignatureParams = {
  intentId: string | undefined
  walletAddress?: `0x${string}` // The address that will sign the recovery payload
  refundToAddress?: `0x${string}` // Optional: address to receive recovered funds (defaults to walletAddress)
}

type UseIntentRecoverWithSignatureReturn = {
  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
  /** Returns EIP-712 typed data for external signing */
  getDataToSign: () => Promise<{
    payload: Payload
    refundCall: PayloadCall
    chainId: number
    typedData: TypedData // EIP-712 typed data to pass to your signer
  }>
  getRecoverTx: (params: {
    signature: string
    payload: Payload
  }) => Promise<{
    to: `0x${string}`
    data: `0x${string}`
    chainId: number
    chain: Chain
  }>
  /** Check recovery transaction status */
  getRecoverStatus: (params: {
    txHash: `0x${string}`
    chainId: number
  }) => Promise<{
    status: 'success' | 'fail' | 'pending'
  }>
}
```

### Comparison with useIntentRecover

| Feature           | `useIntentRecover`             | `useIntentRecoverWithAddress`                          |
| ----------------- | ------------------------------ | ------------------------------------------------------ |
| Signing           | Uses `walletClient` internally | You sign externally via `getDataToSign`                |
| One-call recovery | `recover()` handles everything | Manual: `getDataToSign` → sign → `getRecoverTx` → send |
| Best for          | Standard wallets               | Embedded wallets, custodial signers                    |
| `signPayload`     | Yes                            | No (replaced by `getDataToSign`)                       |
| `recover`         | Yes                            | No (you control the full flow)                         |

## 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.

```ts theme={null}
import { useCommitIntent, useExecuteIntent } from '0xtrails'
```

### useCommitIntent

```tsx theme={null}
const { mutate: commitIntent, isPending, isError, error, data } = useCommitIntent()

// Commit an intent
commitIntent(intent)
```

### useExecuteIntent

```tsx theme={null}
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.

```ts theme={null}
import { getTrailsClient, useTrailsClient, TrailsClient } from '0xtrails'
```

### useTrailsClient Hook

```tsx theme={null}
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:

```ts theme={null}
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

<Warning>
  These are advanced functions for direct intent management. Most developers should use the focused components or `useQuote` hook instead.
</Warning>

```ts theme={null}
import {
  calculateOriginAndDestinationIntentAddresses,
  commitIntent,
  sendOriginTransaction,
} from '0xtrails'
```

### Types

```ts theme={null}
type OriginCallParams = {
  to: string
  value: bigint
  data: string
  chainId: number
}

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

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

### calculateOriginAndDestinationIntentAddresses

Calculates both origin and destination intent addresses.

**Signature**:

```ts theme={null}
function calculateOriginAndDestinationIntentAddresses(
  params: IntentParams
): Promise<{ originAddress: string; destinationAddress: string }>
```

**Usage**:

```ts theme={null}
const { originAddress, destinationAddress } =
  await calculateOriginAndDestinationIntentAddresses(params)
```

### commitIntent

Commits an intent to the blockchain.

**Signature**: `(params: CommitIntentParams) => Promise<CommitIntentResult>`

**Usage**:

```ts theme={null}
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**:

```ts theme={null}
const txHash = await sendOriginTransaction({
  walletClient,
  intentAddress: '0x...',
  // ... other params
})
```
