> ## Documentation Index
> Fetch the complete documentation index at: https://docs.portalhq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Bridge & Swap with Li.Fi

> Learn how to bridge and swap tokens across multiple chains using Portal's React Native SDK with Li.Fi integration.

Portal's React Native SDK provides comprehensive cross-chain bridging and swapping capabilities through the `portal.trading.lifi` API. This guide covers getting quotes, finding routes, executing swaps and bridges, and tracking transaction status.

## Overview

The Li.Fi functionality allows you to:

* **Get quotes** for bridging or swapping tokens across chains
* **Find routes** to discover the best paths for your cross-chain transfers
* **Execute swaps and bridges** by signing and submitting transactions
* **Track transaction status** for cross-chain transfers

<Note>
  **Supported Chains:** Li.Fi integration currently supports EVM-compatible chains (Ethereum, Base, Arbitrum, Polygon, etc.) and Solana. Other non-EVM chains may not be supported for Li.Fi operations.
</Note>

## Prerequisites

Before using Li.Fi operations, ensure you have:

* A properly initialized Portal client
* An active wallet with the required token(s) on the source network (see [Create a wallet](./create-a-wallet))
* Li.Fi integration enabled in your Portal Dashboard (see [Li.Fi Integration](../../../integrations/Trading/lifi))

## High-Level Methods

### `tradeAsset`

Runs the end-to-end Li.Fi flow in one call:

1. Discover routes (`getRoutes`)
2. Select a route (`routeIndex`, default `0`)
3. Build each step (`getRouteStep`)
4. Sign and broadcast each step transaction
5. Wait for on-chain confirmation for that step
6. Poll Li.Fi status until terminal for that step
7. Continue to the next step

Steps are executed **sequentially** (in order), not in parallel.

**Confirmation behavior:**

`tradeAsset` uses **strict confirmation semantics**. Each step MUST be confirmed on-chain before proceeding to the next step. If `waitForConfirmation` returns `false`, times out, or throws, the entire flow fails and an error is thrown. There is no optimistic fallback.

### Signature

```typescript theme={null}
tradeAsset(
  params: LifiTradeAssetParams,
  options?: LifiTradeAssetOptions,
): Promise<LifiTradeAssetResult>
```

**Essential parameters**

| Name           | Required | Description                                                                                                                                             |
| -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fromChain`    | Yes      | Source chain, CAIP-2 (e.g. `'eip155:8453'`).                                                                                                            |
| `toChain`      | Yes      | Destination chain, CAIP-2.                                                                                                                              |
| `fromToken`    | Yes      | Source token symbol or address.                                                                                                                         |
| `toToken`      | Yes      | Destination token symbol or address.                                                                                                                    |
| `amount`       | Yes      | Amount in smallest units (e.g. wei).                                                                                                                    |
| `fromAddress`  | Yes      | Sender address.                                                                                                                                         |
| `toAddress`    | No       | Recipient; defaults to `fromAddress`.                                                                                                                   |
| `routeOptions` | No       | Slippage, ordering, bridges, etc. See [Route options](#route-options).                                                                                  |
| `routeIndex`   | No       | Route index from discovery; default `0`.                                                                                                                |
| `onProgress`   | No       | Fired for each major stage (`fetching_routes`, `signing`, `submitted`, `confirming`, `lifi_pending`, `complete`, `failed`, …).                          |
| `statusPoll`   | No       | Overrides the **built-in Li.Fi status polling** used **after** on-chain confirmation for each step (same shape as [`pollStatus`](#pollstatus-options)). |

Second argument `LifiTradeAssetOptions`:

| Name                     | Required | Description                                                                                                                                                                                                                                                                                                                           |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `signAndSendTransaction` | No       | Per-call signer override for route-step transactions.                                                                                                                                                                                                                                                                                 |
| `waitForConfirmation`    | No       | Per-call confirmation override. Called after each submitted step with `(txHash, network)`. **MUST return `true` or resolve for success.** Returning `false`, throwing an error, or timing out will **abort the entire flow** and throw an error. When omitted, the built-in EVM receipt poller is used with the same strict behavior. |
| `evmRequestFn`           | No       | Per-call EVM RPC function used by the built-in fallback EVM receipt poller when `waitForConfirmation` is not provided.                                                                                                                                                                                                                |
| `evmPollerOptions`       | No       | Per-call tuning for the built-in EVM receipt poller (`pollIntervalMs`, `timeoutMs`) when `waitForConfirmation` is not provided.                                                                                                                                                                                                       |

**Return value**

| Field    | Description                           |
| -------- | ------------------------------------- |
| `hashes` | Transaction hashes per executed step. |
| `steps`  | Step objects from the API.            |
| `route`  | The executed route.                   |

**Example (progress + per-call confirmation options)**

```typescript theme={null}
import Portal from '@portal-hq/core'
import type { LifiTradeAssetParams, LifiTradeAssetOptions } from '@portal-hq/core'

const portal = new Portal({
  apiKey: 'YOUR_PORTAL_CLIENT_API_KEY',
  backup: {
    /* ... */
  },
  gatewayConfig: {
    'eip155:8453': 'https://YOUR_RPC_URL',
    'eip155:42161': 'https://YOUR_RPC_URL',
  },
})

async function runTrade() {
  const addresses = await portal.addresses
  const fromAddress = addresses?.eip155
  if (!fromAddress) throw new Error('No EVM address')

  const params: LifiTradeAssetParams = {
    fromChain: 'eip155:8453',
    toChain: 'eip155:42161',
    fromToken: 'ETH',
    toToken: 'USDC',
    amount: '1000000000000',
    fromAddress,
    statusPoll: {
      everyMs: 10_000,
      initialDelayMs: 10_000,
      timeoutMs: 600_000,
    },
    onProgress: (status, data) => {
      console.log('[Li.Fi]', status, data.txHash ?? '')
    },
  }

  const options: LifiTradeAssetOptions = {
    waitForConfirmation: async (txHash, network) => {
      // Example: reuse Portal confirmation helper, or plug in custom logic
      return portal.waitForConfirmation(txHash, network)
    },
    evmPollerOptions: { pollIntervalMs: 4_000, timeoutMs: 600_000 },
  }

  try {
    const result = await portal.trading.lifi.tradeAsset(params, options)
    console.log('Hashes:', result.hashes)
    console.log('Executed steps:', result.steps.length)
    console.log('Route id:', result.route.id)
    return result
  } catch (e) {
    console.error('tradeAsset failed', e)
    throw e
  }
}
```

**Example (default confirmation behavior)**

```typescript theme={null}
const result = await portal.trading.lifi.tradeAsset({
  fromChain: 'eip155:8453',
  toChain: 'eip155:42161',
  fromToken: 'ETH',
  toToken: 'USDC',
  amount: '1000000000000',
  fromAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
})
```

Wrap `tradeAsset` in `try/catch`; failures (no route, step error, receipt timeout, Li.Fi terminal `FAILED`, …) throw after `onProgress` may report `'failed'`.

### Progress lifecycle

`onProgress` can emit statuses such as:

* `fetching_routes`
* `route_selected`
* `preparing_step`
* `signing`
* `submitted`
* `confirming`
* `lifi_pending`
* `step_done`
* `complete`
* `failed`

### `pollStatus`

Built-in Li.Fi status polling with retries and backoff. Use when you already have a **tx hash** (for example from a manual flow) and want the same polling behavior as inside `tradeAsset`, without implementing the loop yourself.

```typescript theme={null}
import Portal from '@portal-hq/core'

async function pollTransferStatus(portal: Portal, txHash: string) {
  const terminal = await portal.trading.lifi.pollStatus(
    {
      txHash,
      fromChain: 'eip155:8453',
      toChain: 'eip155:42161',
    },
    (statusUpdate) => {
      console.log('Status:', statusUpdate.status)
      return true
    },
    { everyMs: 10_000, timeoutMs: 600_000 },
  )

  console.log('Final status:', terminal.status)
}
```

### `pollStatus` options

| Name                   | Default                                           | Description                                    |
| ---------------------- | ------------------------------------------------- | ---------------------------------------------- |
| `everyMs`              | `10000`                                           | Time between requests (ms).                    |
| `initialDelayMs`       | `0` here / `10000` in `tradeAsset`’s `statusPoll` | Delay before the first request.                |
| `timeoutMs`            | `600000`                                          | Max total poll time.                           |
| `maxConsecutiveErrors` | `10`                                              | Abort after this many consecutive hard errors. |
| `backoff`              | `factor` 1.5, `maxIntervalMs` 15000               | Backoff between polls.                         |

Return `false` from the update callback to stop early; return `true` (or nothing) to continue.

## Route Options

The `routeOptions` parameter on `tradeAsset` (and the `options` field on `LifiRoutesRequest`) accepts a `LifiRoutesRequestOptions` object:

| Name                   | Type                      | Description                                                       |
| ---------------------- | ------------------------- | ----------------------------------------------------------------- |
| `slippage`             | `number`                  | Maximum acceptable slippage as a decimal (e.g. `0.005` for 0.5%). |
| `order`                | `'FASTEST' \| 'CHEAPEST'` | Route ordering preference.                                        |
| `insurance`            | `boolean`                 | Whether to include bridge insurance.                              |
| `bridges`              | `LifiToolsConfiguration`  | Allow/deny specific bridge tools.                                 |
| `exchanges`            | `LifiToolsConfiguration`  | Allow/deny specific exchange tools.                               |
| `allowSwitchChain`     | `boolean`                 | Allow routes that require switching chains mid-route.             |
| `allowDestinationCall` | `boolean`                 | Allow contract calls on the destination chain.                    |
| `fee`                  | `number`                  | Integrator fee percentage.                                        |
| `maxPriceImpact`       | `number`                  | Maximum price impact as a decimal.                                |
| `referrer`             | `string`                  | Referrer address for fee sharing.                                 |

***

## Getting a Quote

```ts theme={null}
import { LifiQuoteRequest } from '@portal-hq/trading'

async function getQuote(portal: Portal) {
  const addresses = await portal.addresses
  const fromAddress = addresses?.eip155

  if (!fromAddress) {
    throw new Error('No EVM address found')
  }

const request: LifiQuoteRequest = {
  fromChain: 'eip155:8453',
  toChain: 'eip155:42161',
  // Amount in wei (1 ETH = 1e18 wei)
  // 1_000_000_000_000 wei = 0.000001 ETH
  fromAmount: '1000000000000',
  fromToken: 'ETH',
  toToken: 'USDC',
  fromAddress,
}


  const response = await portal.trading.lifi.getQuote(request)
  const quote = response.data?.rawResponse

  if (!quote) return

  console.log('To amount:', quote.estimate?.toAmount)

  if (quote.transactionRequest) {
    await executeTransaction(
      portal,
      quote.transactionRequest,
      request.fromChain
    )
  }
}
```

***

## Finding Routes

```ts theme={null}
import { LifiRoutesRequest } from '@portal-hq/trading'

async function getRoutes(portal: Portal) {
  const addresses = await portal.addresses
  const fromAddress = addresses?.eip155

  if (!fromAddress) {
    throw new Error('No EVM address found')
  }

const request: LifiRoutesRequest = {
  fromChainId: 'eip155:8453',
  toChainId: 'eip155:42161',
  fromTokenAddress: 'ETH',
  toTokenAddress: 'USDC',

  // Amount in wei (1 ETH = 1e18 wei)
  // 1_000_000_000_000 wei = 0.000001 ETH
  fromAmount: '1000000000000',

  fromAddress,
}

  const response = await portal.trading.lifi.getRoutes(request)
  const routes = response.data?.rawResponse?.routes ?? []

  const route =
    routes.find(r => r.tags?.includes('RECOMMENDED')) ?? routes[0]

  if (!route) return

  await processRouteSteps(portal, route.steps, request.fromChainId)
}
```

***

## Getting Route Step Details

```ts theme={null}
import { LifiStep, LifiStepTransactionRequest } from '@portal-hq/trading'

async function getRouteStep(
  portal: Portal,
  step: LifiStep
): Promise<LifiStep | null> {
  const response = await portal.trading.lifi.getRouteStep(
    step as LifiStepTransactionRequest
  )

  return response.data?.rawResponse ?? null
}
```

***

## Executing Transactions

```ts theme={null}
import { ETHTransactionParam } from '@portal-hq/provider'
import { LifiTransactionRequest } from '@portal-hq/trading'

async function executeTransaction(
  portal: Portal,
  transactionRequest: LifiTransactionRequest,
  chainId: string
) {
  const ethTx: ETHTransactionParam = {
    from: transactionRequest.from,
    to: transactionRequest.to,
    value: transactionRequest.value ?? '0x0',
    data: transactionRequest.data,
  }

  const response = await portal.request(
    'eth_sendTransaction',
    [ethTx],
    chainId
  )

  console.log('Tx hash:', response.result)
}

```

> **Note**
> `LifiTransactionRequest` may include `gasLimit` and `gasPrice`.
> You can omit these fields to let Portal estimate gas automatically,
> or include them if you prefer to use Li.Fi's suggested values.

***

## Processing Multi-Step Routes

```ts theme={null}
async function processRouteSteps(
  portal: Portal,
  steps: LifiStep[],
  chainId: string
) {
  for (const step of steps) {
    const stepWithTx = await getRouteStep(portal, step)
    if (!stepWithTx?.transactionRequest) {
      throw new Error('Missing transactionRequest')
    }

    await executeTransaction(
      portal,
      stepWithTx.transactionRequest,
      chainId
    )
  }
}
```

***

## Tracking Transaction Status

```ts theme={null}
import { LifiStatusRequest } from '@portal-hq/trading'

async function getStatus(
  portal: Portal,
  txHash: string,
  fromChain: string
) {
  const request: LifiStatusRequest = {
    txHash,
    fromChain,
  }

  const response = await portal.trading.lifi.getStatus(request)
  const status = response.data?.rawResponse

  console.log('Status:', status?.status)
}
```

***

## Polling Until Completion

```ts theme={null}
async function pollUntilDone(
  portal: Portal,
  txHash: string,
  fromChain: string
) {
  while (true) {
    const response = await portal.trading.lifi.getStatus({
      txHash,
      fromChain,
    })

    const status = response.data?.rawResponse?.status
    if (status === 'DONE') return true
    if (status === 'FAILED') return false

    await new Promise(r => setTimeout(r, 2000))
  }
}
```

***

## Supported Networks

Portal's Li.Fi integration supports the following mainnet networks:

* Monad (`eip155:143`)
* Ethereum (`eip155:1`)
* Optimism (`eip155:10`)
* BSC (`eip155:56`)
* Gnosis (`eip155:100`)
* Unichain (`eip155:130`)
* Polygon (`eip155:137`)
* Sonic (`eip155:146`)
* Mantle (`eip155:5000`)
* Base (`eip155:8453`)
* Arbitrum (`eip155:42161`)
* Celo (`eip155:42220`)
* Avalanche (`eip155:43114`)
* Linea (`eip155:59144`)
* Berachain (`eip155:80094`)
* Katana (`eip155:747474`)
* Solana (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`)
* Bitcoin (`bip122:000000000019d6689c085ae165831e93-p2wpkh`)

For the complete list of networks Li.Fi supports across its ecosystem, refer to the [Li.Fi documentation](https://docs.li.fi). If you need a chain that isn't listed above, contact Portal support.

<Note>
  **Testnets are not supported.**
</Note>

## Next Steps

* Learn about [signing Ethereum transactions](./sign-a-transaction)
* Explore [sending tokens](./send-tokens)
* Check out the [Client API Li.Fi endpoints](../../../apis/client/api-reference-link)
