# Get a set of routes for a request that describes a transfer of tokens
Source: https://docs.li.fi/api-reference/advanced/get-a-set-of-routes-for-a-request-that-describes-a-transfer-of-tokens
post /v1/advanced/routes
In order to execute any transfer, you must first request possible `Routes`. From the result set a `Route` can be selected and executed by retrieving the transaction for every included `Step` using the `/steps/transaction` endpoint.
**Attention**: This request is more complex and intended to be used via our [JavaScript SDK](https://docs.li.fi/integrate-li.fi-js-sdk/install-li.fi-sdk).
Need opinionated routing defaults? Include `options.preset` in the request body and review the
[API presets overview](/api-reference/presets/overview) for the supported configurations.
# Populate a step with transaction data
Source: https://docs.li.fi/api-reference/advanced/populate-a-step-with-transaction-data
post /v1/advanced/stepTransaction
This endpoint expects a full `Step` object which usually is retrieved by calling the `/advanced/routes` endpoint and selecting the most suitable `Route`. Afterwards the transaction for every required `Step` can be retrieved using this endpoint.
**Attention**: This request is more complex and intended to be used via our [JavaScript SDK](https://docs.li.fi/integrate-li.fi-js-sdk/install-li.fi-sdk).
# Check the status of a cross chain transfer
Source: https://docs.li.fi/api-reference/check-the-status-of-a-cross-chain-transfer
get /v1/status
Cross chain transfers might take a while to complete. Waiting on the transaction on the sending chain doesn't help here. For this reason we build a simple endpoint that let's you check the status of your transfer.
Important: The endpoint returns a `200` successful response even if the transaction can not be found. This behavior accounts for the case that the transaction hash is valid but the transaction has not been mined yet.
While none of the parameters `fromChain`, `toChain` and `bridge` are required, passing the `fromChain` parameter will speed up the request and is therefore encouraged.
If you want to learn more about how to use this endpoint please have a look at our [guide](/introduction/user-flows-and-examples/status-tracking).
# Error Codes
Source: https://docs.li.fi/api-reference/error-codes
Exhaustive list of possible error codes
## API status codes
API status code is the code returned by the server like 200, 404, 429, 500, 502.
## API error codes
API returns the following set of error codes:
* DefaultError = 1000,
* FailedToBuildTransactionError = 1001,
* NoQuoteError = 1002,
* NotFoundError = 1003,
* NotProcessableError = 1004,
* RateLimitError = 1005,
* ServerError = 1006,
* SlippageError = 1007,
* ThirdPartyError = 1008,
* TimeoutError = 1009,
* UnauthorizedError = 1010,
* ValidationError = 1011,
* RpcFailure = 1012,
* MalformedSchema = 1013,
## Tool errors
In addition to returning status and error codes, API may return error messages for underlying tools, describing an issue with specific tools. This can be caused by many reasons, from the tool simply not supporting the requested token pair or insufficient liquidity.
To better explain failure cases, we try to return errors in a predictable format. The `ToolError` interface looks like the following:
```TypeScript theme={"system"}
type ToolErrorType = 'NO_QUOTE'
interface ToolError {
errorType: ToolErrorType
code: string
action: Action
tool: string
message: string
}
```
### Possible codes:
`NO_POSSIBLE_ROUTE`: No route was found for this action.
`INSUFFICIENT_LIQUIDITY`: The tool's liquidity is insufficient.
`TOOL_TIMEOUT`: The third-party tool timed out.
`UNKNOWN_ERROR`: An unknown error occurred.
`RPC_ERROR`: There was a problem getting on-chain data. Please try again later.
`AMOUNT_TOO_LOW`:The initial amount is too low to transfer using this tool.
`AMOUNT_TOO_HIGH`: The initial amount is too high to transfer using this tool.
`FEES_HIGHER_THAN_AMOUNT`: The fees are higher than the initial amount — this would result in a negative resulting token.
`DIFFERENT_RECIPIENT_NOT_SUPPORTED`: This tool does not support different recipient addresses.
`TOOL_SPECIFIC_ERROR`: The third-party tool returned an error.
`CANNOT_GUARANTEE_MIN_AMOUNT`: The tool cannot guarantee that the minimum amount will be met.
### Example response:
```TypeScript theme={"system"}
{
"message": "Unable to find a quote for the requested transfer.",
"errors": [
{
"errorType": "NO_QUOTE",
"code": "INSUFFICIENT_LIQUIDITY",
"action": {
"fromChainId": 100,
"toChainId": 100,
"fromToken": {
"address": "0x4ecaba5870353805a9f068101a40e0f32ed605c6",
"decimals": 6,
"symbol": "USDT",
"chainId": 100,
"coinKey": "USDT",
"name": "USDT",
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png",
"priceUSD": "0.99872"
},
"toToken": {
"address": "0xddafbb505ad214d7b80b1f830fccc89b60fb7a83",
"decimals": 6,
"symbol": "USDC",
"chainId": 100,
"coinKey": "USDC",
"name": "USDC",
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png"
},
"fromAmount": "1",
"slippage": 0.03,
"fromAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0",
"toAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0"
},
"tool": "1inch",
"message": "The tool's liquidity is insufficient."
},
{
"errorType": "NO_QUOTE",
"code": "TOOL_TIMEOUT",
"action": {
"fromChainId": 100,
"toChainId": 100,
"fromToken": {
"address": "0x4ecaba5870353805a9f068101a40e0f32ed605c6",
"decimals": 6,
"symbol": "USDT",
"chainId": 100,
"coinKey": "USDT",
"name": "USDT",
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png",
"priceUSD": "0.99872"
},
"toToken": {
"address": "0xddafbb505ad214d7b80b1f830fccc89b60fb7a83",
"decimals": 6,
"symbol": "USDC",
"chainId": 100,
"coinKey": "USDC",
"name": "USDC",
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png"
},
"fromAmount": "1",
"slippage": 0.03,
"fromAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0",
"toAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0"
},
"tool": "openocean",
"message": "The third party tool timed out."
},
{
"errorType": "NO_QUOTE",
"code": "NO_POSSIBLE_ROUTE",
"action": {
"fromChainId": 100,
"toChainId": 100,
"fromToken": {
"address": "0x4ecaba5870353805a9f068101a40e0f32ed605c6",
"decimals": 6,
"symbol": "USDT",
"chainId": 100,
"coinKey": "USDT",
"name": "USDT",
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png",
"priceUSD": "0.99872"
},
"toToken": {
"address": "0xddafbb505ad214d7b80b1f830fccc89b60fb7a83",
"decimals": 6,
"symbol": "USDC",
"chainId": 100,
"coinKey": "USDC",
"name": "USDC",
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png"
},
"fromAmount": "1",
"slippage": 0.03,
"fromAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0",
"toAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0"
},
"tool": "superfluid",
"message": "No route was found for this action."
}
]
}
```
# Fetch all known tokens
Source: https://docs.li.fi/api-reference/fetch-all-known-tokens
get /v1/tokens
Retrieve LI.FI’s catalog of supported tokens, optionally filtered by chain or tag.
Use this endpoint to list tokens that LI.FI currently supports. Combine filters to narrow the response to specific chains, token tags, or both.
## Filter tokens
### Chain filter
Set the `chains` query parameter to a comma-separated list of chain identifiers (for example, `ETH,ARB,OP`). Only tokens live on those networks are returned.
### Tag filter
Use the `tags` query parameter to restrict results to tokens with specific LI.FI tags. Currently `stablecoin` is the only public tag.
Tags and chains can be combined. The response includes only tokens that satisfy all provided filters.
## Examples
### Fetch the complete token catalog
```bash theme={"system"}
curl "https://li.quest/v1/tokens"
```
### Fetch all stablecoins
```bash theme={"system"}
curl "https://li.quest/v1/tokens?tags=stablecoin"
```
### Fetch Arbitrum stablecoins
```bash theme={"system"}
curl "https://li.quest/v1/tokens?chains=ARB&tags=stablecoin"
```
More tags may be introduced over time as we add support for more token types.
# Fetch information about a Token
Source: https://docs.li.fi/api-reference/fetch-information-about-a-token
get /v1/token
This endpoint can be used to get more information about a token by its address or symbol and its chain.
If you want to learn more about how to use this endpoint please have a look at our [guide](/api-reference/fetch-information-about-a-token).
# Get gas price for the specified chainId
Source: https://docs.li.fi/api-reference/gas/get-gas-price-for-the-specified-chainid
get /v1/gas/prices/{chainId}
This endpoint can be used to get the most recent gas prices for the supplied chainId.
# Get gas prices for enabled chains
Source: https://docs.li.fi/api-reference/gas/get-gas-prices-for-enabled-chains
get /v1/gas/prices
This endpoint can be used to get the most recent gas prices for the enabled chains in the server.
# Get a gas suggestion for the specified chain
Source: https://docs.li.fi/api-reference/get-a-gas-suggestion-for-the-specified-chain
get /v1/gas/suggestion/{chain}
Endpoint to retrieve a suggestion on how much gas is needed on the requested chain. The suggestion is based on the average price of 10 approvals and 10 uniswap based swaps via LI.FI on the specified chain.
If `fromChain` and `fromToken` are specified, the result will contain information about how much `fromToken` amount the user has to send to receive the suggested gas amount on the requested chain.
# Get a list of filtered transfers
Source: https://docs.li.fi/api-reference/get-a-list-of-filtered-transfers
get /v1/analytics/transfers
This endpoint can be used to retrieve a list of transfers filtered by certain properties. Returns a maximum of 1000 transfers.
# Get a paginated list of filtered transfers
Source: https://docs.li.fi/api-reference/get-a-paginated-list-of-filtered-transfers
get /v2/analytics/transfers
A paginated version of the `GET /v1/analytics/transfers endpoint`. This endpoint can be used to retrieve a list of transfers filtered by certain properties.
# Get a quote for a token transfer
Source: https://docs.li.fi/api-reference/get-a-quote-for-a-token-transfer
get /v1/quote
This endpoint can be used to request a quote for a transfer of one token to another, cross chain or not.
The endpoint returns a `Step` object which contains information about the estimated result as well as a `transactionRequest` which can directly be sent to your wallet.
The estimated result can be found inside the `estimate`, containing the estimated `toAmount` of the requested `Token` and the `toAmountMin`, which is the guaranteed minimum value that the transfer will yield including slippage.
If you want to learn more about how to use this endpoint please have a look at our [guide](/introduction/user-flows-and-examples/requesting-route-fetching-quote).
For preset options, use the `preset` query parameter. See the [API presets overview](/api-reference/presets/overview). The [stablecoin preset](/api-reference/presets/stablecoin) is ideal for stablecoin transfers and can be customized. To see supported stablecoins, use `/v1/tokens?tags=stablecoin`.
# Get a quote for a token transfer
Source: https://docs.li.fi/api-reference/get-a-quote-for-a-token-transfer-1
get /v1/quote/toAmount
This endpoint is an alternative to the `v1/quote` endpoint, taking a `toAmount` value rather than `fromAmount`. This endpoint will calculate an appropriate `fromAmount` based on the specified `toAmount`, and use this value to generate the quote data.
This endpoint can be used to request a quote for a transfer of one token to another, cross chain or not.
The endpoint returns a `Step` object which contains information about the estimated result as well as a `transactionRequest` which can directly be sent to your wallet.
The estimated result can be found inside the `estimate`, containing the estimated required `fromAmount` of the sending `Token` to meet the `toAmountMin` of the receiving token, which is the guaranteed minimum value that the transfer will yield including slippage.
If you want to learn more about how to use this endpoint please have a look at our [guide](/introduction/user-flows-and-examples/requesting-route-fetching-quote).
# Get available bridges and exchanges
Source: https://docs.li.fi/api-reference/get-available-bridges-and-exchanges
get /v1/tools
This endpoint can be used to get information about the bridges and exchanges available trough our service
# Get information about all currently supported chains
Source: https://docs.li.fi/api-reference/get-information-about-all-currently-supported-chains
openapi.yaml GET /v1/chains
If you want to learn more about how to use this endpoint please have a look at our [guide](/sdk/chains-tools).
# Get integrator's collected fees data for all supported chains
Source: https://docs.li.fi/api-reference/get-integrators-collected-fees-data-for-all-supported-chains
get /v1/integrators/{integratorId}
This endpoint can be used to request all integrator's collected fees data by tokens for all supported chains.
The endpoint returns an `Integrator` object which contains the integrator id and an array of fee balances for all supported chains.
# Get status information about a lifuel transaction
Source: https://docs.li.fi/api-reference/get-status-information-about-a-lifuel-transaction
get /v1/gas/status
# Get the total amount of a token received on a specific chain, for cross-chain transfers.
Source: https://docs.li.fi/api-reference/get-the-total-amount-of-a-token-received-on-a-specific-chain-for-cross-chain-transfers
get /v1/analytics/transfers/summary
Calculates and returns the total received token amount per wallet address, per sending chain, within a specified time range, for a given receiving chain and receiving token. Only aggregates cross-chain transfers, meaning transfers with distinct sending and receiving chains.
# Get transaction request for withdrawing collected integrator's fees by chain
Source: https://docs.li.fi/api-reference/get-transaction-request-for-withdrawing-collected-integrators-fees-by-chain
get /v1/integrators/{integratorId}/withdraw/{chainId}
This endpoint can be used to get transaction request for withdrawing integrator's collected fees the specified chain. If a list of token addresses is provided, the generated transaction will only withdraw the specified funds.
If there is no collected fees for the provided token's addresses, the `400` error will be thrown.
The endpoint returns a `IntegratorWithdrawalTransactionResponse` object which contains the transaction request.
# In case a transaction was missed by a relayer, this endpoint can be used to force a tx to be re-fetched.
Source: https://docs.li.fi/api-reference/in-case-a-transaction-was-missed-by-a-relayer-this-endpoint-can-be-used-to-force-a-tx-to-be-re-fetched
get /v1/gas/refetch
# Overview
Source: https://docs.li.fi/api-reference/introduction
Fundamentals of LI.FI`s API.
**Building an AI agent?** Start with our [Agent Integration Guide](/agents/overview) for a streamlined overview of the essential endpoints, or use [llms.txt](/llms.txt) for machine-readable documentation.
## Base URL
LI.FI’s API is built on REST principles and is served over HTTPS.
The Base URL for all API endpoints is:
```javascript theme={"system"}
https://li.quest/v1
```
## Authentication
All LI.FI APIs do not require API key. API key is only needed for higher rate limits
Authentication to LI.FI's API is performed via the custom HTTP header `x-lifi-api-key` with an API key. If you are using the Client SDK, you will set the API when constructing a client, and then the SDK will send the header on your behalf with every request. If integrating directly with the API, you’ll need to send this header yourself like so:
```curl theme={"system"}
curl --location 'https://li.quest/v1/quote?fromChain=100&fromAmount=1000000&fromToken=0x4ecaba5870353805a9f068101a40e0f32ed605c6&fromAddress=0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0&toChain=137&toToken=0x2791bca1f2de4661ed88a30c99a7a9449aa84174&slippage=0.03' \
--header 'x-lifi-api-key: YOUR_CUSTOM_KEY'
```
API key can be tested using the following endpoint:
```javascript theme={"system"}
curl --location 'https://li.quest/v1/keys/test'
--header 'x-lifi-api-key: YOUR_CUSTOM_KEY'
```
Never expose your `x-lifi-api-key` in client-side environments such as browser-based JavaScript or direct Widget integrations. Using the API key on the client side can lead to unauthorized usage or abuse of your key, as it becomes publicly accessible in the browser's developer tools or network tab.
If you're using the LI.FI Widget, you **do not need to pass an API key**. The Widget operates securely without requiring a key in the frontend. For server-side integrations (e.g. SDK or API requests from your backend), always keep your key secret and secure.
## Rate Limit
Rate limit is counted per IP without API key and per API Key with authenticated requests.
Please refer to [Rate limits and API authentication](/api-reference/rate-limits) page.
## Error Message
Errors consist of three parts:
1. HTTP error code
2. LI.FI error code
3. Error message
Specific error codes and messages are defined on [Error Codes](/api-reference/error-codes) page
**Looking for one-click DeFi operations?** Use [Composer](/composer/overview) to deposit into vaults, stake, and lend — all through the same `/quote` endpoint. Set `toToken` to a vault token address and Composer handles the rest. See [Composer API Parameters](/composer/reference/api-parameters).
# OpenAPI Specification
Source: https://docs.li.fi/api-reference/openapi-spec
Download or reference the LI.FI API OpenAPI specification for integration with AI agents, SDKs, and tools
## LI.FI OpenAPI Specification
The LI.FI API follows the OpenAPI 3.0 specification. You can use this spec to:
* Generate client SDKs in any language
* Import into API testing tools (Postman, Insomnia, etc.)
* Integrate with AI agents and LLM tools
* Build automated documentation
## Access the spec
The full specification is available at the URLs below. Use whichever format suits your workflow.
Raw YAML hosted on this site
| Format | URL |
| ----------- | --------------------------------- |
| YAML (docs) | `https://docs.li.fi/openapi.yaml` |
## Specification details
* **OpenAPI Version**: 3.0.2
* **API Version**: 1.0.0
* **Endpoints**: 28+ paths covering quotes, routes, tokens, chains, and more
## API base URLs
| Environment | Base URL |
| ----------- | -------------------------- |
| Production | `https://li.quest` |
| Staging | `https://staging.li.quest` |
## For AI agents
AI agents can discover and consume this API through multiple formats:
| Resource | URL | Description |
| ------------ | ----------------------------------------------- | --------------------------------- |
| OpenAPI Spec | `https://docs.li.fi/openapi.yaml` | Full OpenAPI 3.0.2 specification |
| AI Plugin | `https://docs.li.fi/.well-known/ai-plugin.json` | Standard discovery format |
| llms.txt | `https://docs.li.fi/llms.txt` | Structured documentation for LLMs |
| MCP Server | `https://mcp.li.quest/mcp` | Model Context Protocol server |
| Agent Guide | [/agents/overview](/agents/overview) | Integration guide for AI agents |
## Quick links
* [API Introduction](/api-reference/introduction) - Get started with the LI.FI API
* [Rate Limits](/api-reference/rate-limits) - API keys and rate limits
* [Get a Quote](/api-reference/get-a-quote-for-a-token-transfer) - Request a cross-chain swap quote
# Parse transaction call data (BETA)
Source: https://docs.li.fi/api-reference/parse-transaction-call-data-beta
get /v1/calldata/parse
This endpoint allows to pass transaction call data. It will then parse the call data based on known and on-chain ABIs to provide a JSON overview of the internal transaction information.
# Perform multiple contract calls across blockchains (BETA)
Source: https://docs.li.fi/api-reference/perform-multiple-contract-calls-across-blockchains-beta
post /v1/quote/contractCalls
This endpoint can be used to bridge tokens, swap them and perform a number or arbitrary contract calls on the destination chain. You can find an example of it [here](https://github.com/lifinance/sdk/tree/main/examples).
This functionality is currently in beta. While we've worked hard to ensure its stability and functionality, there might still be some rough edges.
# API Presets
Source: https://docs.li.fi/api-reference/presets/overview
Preconfigured routing defaults for LI.FI quote and advanced route endpoints.
API presets provide tuned routing defaults for the LI.FI API. When you pass a preset, LI.FI loads the preset’s defaults and merges any request-specific options on top. Your explicit values always win, so you can start with a preset and selectively override the pieces you care about.
## Supported endpoints
* [`POST /v1/advanced/routes`](/api-reference/advanced/get-a-set-of-routes-for-a-request-that-describes-a-transfer-of-tokens)
* [`GET /v1/quote`](/api-reference/get-a-quote-for-a-token-transfer)
## How presets are applied
1. LI.FI loads the preset defaults.
2. LI.FI merges your request options on top of those defaults.
3. Any conflicting keys fall back to the values you provided in the request body or query string.
This keeps requests concise while preserving full control.
## Processing flow
1. Validate that the requested preset name is present and active.
2. Load the preset configuration (defaults, tool preferences, etc) or use the built-in default if it isn’t available.
3. Merge preset defaults into the request payload or query parameters.
4. Apply user-provided overrides so explicit values win.
5. Log the resolved preset for observability and troubleshooting.
If validation fails, for example because the preset name does not match the required pattern, the API returns HTTP `400`.
## Preset names
Presets can be referenced by:
* **Generic names** such as `stablecoin`
* **Specific names** such as `stablecoin-cheapest`
See all [available presets](/api-reference/presets/overview#available-presets).
A generic name can map to a specific preset. For example, `stablecoin` resolves to `stablecoin-cheapest` by default. LI.FI may promote other presets behind the same generic name when new configurations such as `stablecoin-fastest` or partner-specific variants `stablecoin-partnerX` become available.
## Using the preset parameter
### Advanced Routes
Provide the preset inside the `options` object.
```json theme={"system"}
{
"fromChainId": 1,
"toChainId": 137,
"fromAmount": "1000000000000000000",
"fromTokenAddress": "0xA0b86a33E6441b8C4C8C0C4C0C4C0C4C0C4C0C4C",
"toTokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"options": {
"preset": "stablecoin"
}
}
```
### Quote
Send the preset as a query parameter.
```
GET /v1/quote?fromChain=1&toChain=137&fromToken=0xA0b86a33E6441b8C4C8C0C4C0C4C0C4C0C4C0C4C&toToken=0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174&fromAddress=0x123...&fromAmount=1000000000000000000&preset=stablecoin
```
## Available presets
* [Stablecoin preset](/api-reference/presets/stablecoin)
* Commerce preset (coming soon)
More presets will be documented as they are released.
# Stablecoin preset
Source: https://docs.li.fi/api-reference/presets/stablecoin
Stablecoin-focused routing defaults for LI.FI quotes and advanced routes.
Use the stablecoin preset when you need LI.FI to favor tight slippage and reliable routing for stablecoin moves. It is built for experiences such as:
* Treasury and treasury-rebalance flows.
* Larger cross-chain stablecoin transfers that demand predictable execution.
* Integrations that require a safe default but still override specific fields when needed.
## Quick start
1. Add `preset=stablecoin` to your quote or advanced routes request.
2. Send the request as usual. LI.FI applies the preset defaults first, then merges any overrides you supply.
### Quote request (GET)
```bash theme={"system"}
curl "https://li.quest/v1/quote?fromChain=1&toChain=137&fromToken=0xA0b86a33E6441b8C4C8C0C4C0C4C0C4C0C4C0C4C&toToken=0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174&fromAddress=0x123...&fromAmount=1000000000000000000&preset=stablecoin"
```
Review the full query parameter schema in the [`GET /v1/quote` reference](/api-reference/get-a-quote-for-a-token-transfer).
### Advanced routes (POST)
```bash theme={"system"}
curl -X POST "https://li.quest/v1/advanced/routes" \
-H "Content-Type: application/json" \
-d '{
"fromChainId": 1,
"toChainId": 137,
"fromAmount": "1000000000000000000",
"fromTokenAddress": "0xA0b86a33E6441b8C4C8C0C4C0C4C0C4C0C4C0C4C",
"toTokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"options": {
"preset": "stablecoin"
}
}'
```
See the complete request body structure in the [`POST /v1/advanced/routes` reference](/api-reference/advanced/get-a-set-of-routes-for-a-request-that-describes-a-transfer-of-tokens).
### JavaScript/TypeScript
```ts theme={"system"}
const quoteRequest = {
fromChain: 1,
toChain: 137,
fromToken: "0xA0b86a33E6441b8C4C8C0C4C0C4C0C4C0C4C0C4C",
toToken: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
fromAddress: "0x123...",
fromAmount: "1000000000000000000",
preset: "stablecoin"
};
```
## Preset defaults
The stablecoin preset configures these defaults to protect execution quality:
* order: `CHEAPEST`
* slippage: `0.001` (0.1%)
* Price impact capped at 2%
* Shortlist of bridges that are prime for stablecoin transfers
* All exchanges enabled by default
If your request includes conflicting values, your request wins. For instance, when you supply `denyBridges` or similar list overrides, the preset’s defaults are replaced by the values you send. Include all bridges you want denied (existing and new) in the request payload.
This means you can override the preset defaults with your own values as shown below.
## Overrides example
```ts theme={"system"}
const routesRequestWithOverrides = {
fromChainId: 1,
toChainId: 137,
fromAmount: "1000000000000000000",
fromTokenAddress: "0xA0b86a33E6441b8C4C8C0C4C0C4C0C4C0C4C0C4C",
toTokenAddress: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
options: {
preset: "stablecoin",
slippage: 0.005, // loosen to 0.5% instead of the default 0.1%
order: "FASTEST" // override the default CHEAPEST order
}
};
```
Integrators are advised to use the stablecoin preset as is and only override its values if absolutely necessary, since the default options are carefully optimized for stablecoin transfers.
To see all available options you can override, check the [options schema](https://github.com/lifinance/types/blob/main/src/api.ts#L116).
## Tool preferences
For bridging, the preset prioritizes stablecoin-friendly paths such as:
* Mint-and-burn flows like Glacis, Mayan Swift, Mayan MCTP, and Celer
* Intent and solver-based options like Eco, Relay, Across, and Gaszip
* Additional tools as they are vetted and added over time
## Check supported stablecoins
Stablecoin support is driven by LI.FI’s token tagging. Use [`/v1/tokens`](/api-reference/fetch-all-known-tokens) with the `tags` filter to confirm which assets qualify.
### All stablecoins
```bash theme={"system"}
curl "https://li.quest/v1/tokens?tags=stablecoin"
```
### Stablecoins on a specific chain
```bash theme={"system"}
curl "https://li.quest/v1/tokens?chains=ARB&tags=stablecoin"
```
Combine the `tags` filter with `chains` filters to match your integration. Need the full parameter list? Check the [`GET /v1/tokens` reference](/api-reference/fetch-all-known-tokens).
## Related docs
* [API presets overview](/api-reference/presets/overview)
* [`GET /v1/quote`](/api-reference/get-a-quote-for-a-token-transfer)
* [`POST /v1/advanced/routes`](/api-reference/advanced/get-a-set-of-routes-for-a-request-that-describes-a-transfer-of-tokens)
# Rate Limits and API Authentication
Source: https://docs.li.fi/api-reference/rate-limits
To mitigate misuse and manage capacity on our API, we have implemented limits on LI.FI API usage.
Rate limits apply to requests made using your `x-lifi-api-key` and are calculated per API key across all endpoints. These limits help prevent abuse and ensure a smooth experience for everyone.
# Current Rate Limits
The default rate limits for production usage are as follows:
### Unauthenticated
| Endpoint | Rate Limit |
| --------------------------- | ------------------------- |
| `/quote` | 75 requests per two hours |
| `/advanced/routes` | 75 requests per two hours |
| `/advanced/stepTransaction` | 50 requests per two hours |
| Other public endpoints | 100 requests per minute |
### Authenticated
API keys created via the [LI.FI Partner Portal](https://li.fi/plans/) currently default to **100 requests per minute**.
Rate limits for quote-related endpoints (`/quote`, `/advanced/routes`, `/advanced/stepTransaction`) are enforced on a **two-hour rolling window**. For example, if your API key limit is 100 RPM, you can make up to 12,000 requests within any two-hour window for those endpoints.
> 🔒 Higher limits may be available for enterprise clients. Please see our [Plans page](https://li.fi/plans/) for more details.
# Handling Rate Limits
Every response includes your current rate limit in the headers. Keep in mind that limits can differ depending on the endpoint.
In the Partner Portal, you’ll see your requests-per-minute (RPM) limit. To give you flexibility during spikes, we don’t enforce it minute by minute. Instead, we multiply your RPM by 120 and apply it as a two-hour rolling window.
👉 Example: If your limit is 100 RPM, that means you can make up to 12,000 requests within any two-hour window — either all at once or spread out however you like.
### Rate Limit Information In Request Response
`ratelimit-reset`: in how many seconds will the rate limit reset (2 hours equal 7200)
`ratelimit-limit`: the total limit for the period of 2 hours
`ratelimit-remaining`: how much of the limit is still left until the reset
Here's how you can calculate your average RPM:
`(ratelimit-limit - ratelimit-remaining) / ((7200 - ratelimit-reset) / 60)`
If you exceed your limits, you'll receive a `429 Too Many Requests` response with error code `1005` (`RateLimitError`). When this occurs:
* The response will include details on when the rate limit resets
* Consider requesting a higher rate limit via the [Partner Portal](https://li.fi/plans/)
# Best Practices
To avoid hitting rate limits:
* Cache results from `GET /tokens`, `GET /chains`, and static endpoints
* Avoid polling frequently for the same data
* Batch or debounce user input that triggers API calls
# Abuse Prevention
To prevent abuse, LI.FI may temporarily block keys that:
* Consistently exceed rate limits
* Attempt to bypass limits through multiple keys or IPs
* Cause performance degradation to the service
# Using the API key
All LI.FI APIs do not require API key. API key is only needed for higher rate limits
Authentication to LI.FI's API is performed via the custom HTTP header `x-lifi-api-key` with an API key. If you are using the Client SDK, you will set the API when [creating a config](/sdk/configure-sdk), and then the SDK will send the header on your behalf with every request. If integrating directly with the API, you’ll need to send this header yourself like so:
```curl theme={"system"}
curl --location 'https://li.quest/v1/quote?fromChain=100&fromAmount=1000000&fromToken=0x4ecaba5870353805a9f068101a40e0f32ed605c6&fromAddress=0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0&toChain=137&toToken=0x2791bca1f2de4661ed88a30c99a7a9449aa84174&slippage=0.03' \
--header 'x-lifi-api-key: YOUR_CUSTOM_KEY'
```
API key can be tested using the following endpoint:
```javascript theme={"system"}
curl --location 'https://li.quest/v1/keys/test'
--header 'x-lifi-api-key: YOUR_CUSTOM_KEY'
```
Never expose your `x-lifi-api-key` in client-side environments such as browser-based JavaScript or direct Widget integrations. Using the API key on the client side can lead to unauthorized usage or abuse of your key, as it becomes publicly accessible in the browser's developer tools or network tab.
If you're using the LI.FI Widget, you **do not need to pass an API key**. The Widget operates securely without requiring a key in the frontend. For server-side integrations (e.g. SDK or API requests from your backend), always keep your key secret and secure.
# Need Higher Limits?
If you're building a high-volume integration or a production-grade product, we’re happy to support your scaling needs.
Please see our [Plans page](https://li.fi/plans/) for more details.
# Returns all possible connections between two chains.
Source: https://docs.li.fi/api-reference/returns-all-possible-connections-based-on-a-from-or-tochain
get /v1/connections
This endpoint gives information about all possible transfers between chains.
`fromChain` and `toChain` are required. Additional filters such as token, bridge, and exchange can be used to narrow the result further.
Information about which chains and tokens are supported can be taken from the response of the /v1/chains endpoint.
Information about which bridges and exchanges are supported can be taken from the response of the `/v1/tools` endpoint.
# Core
Source: https://docs.li.fi/changelog/backend
Additions and updates to the core LI.FI service
## New Chains
* **Somnia** added
* **Lighter** support added
* **Etherlink** now enabled on LI.FI
## New Bridges
* **Symbiosis** migrated to v2 with any-to-any swap support
* **LI.FI Intents** enabled on Polygon and BSC
* **Stargate** destination calls enabled on Flow and Flare
* **Relay V1** deprecated in favor of Relay V2
* **Mayan** Solana transaction generation improved
## New DEXs
* **Nordstern** extended to Ethereum
## Features
* **RWA token controls:** RWA tokens are now tagged via Coingecko categories and filtered out of quotes when a partner's RWA policy is not current. New company-level RWA policy endpoints are exposed for the Partner Portal.
* Improved scam-token detection and token verification
* **LI.FI Intents:** deposit-address status is now available through the `/v1/status` endpoint
* **Polymer:** resolved the 10 USDC fee threshold issue
* LI.FI Intents fill deadline extended to 44 hours
* Added RPC gas estimation timeouts to keep quotes responsive
## Reliability
* Fixed Sui multi-hop swap handling
* Added gas buffers for Stargate V2 on Sei and Plume
* Improved Hyperliquid, Glacis, and split-swap status parsing
* Aligned Across V4 destination receiver handling
* Fixed Polymer status mapping so pending transfers no longer settle to a terminal state prematurely
* Converted Bitcoin fees to satoshis for correct fee calculations
* Fixed Tron transaction address extraction and fee limit handling
* Prevented Composer destination call failures
* Updated the LI.FI Intents status schema to allow nullable transaction hashes
## New Chains
* **TRON** — full support for TRON transactions and routing
* **Arbitrum Nova** added
* **Base Sepolia** and **Arbitrum Sepolia** testnets added
## New Bridges
* **Mayan v2** integration shipped
* **NEAR Intents** expanded to include Tron
## New DEXs
* **Bitget** DEX added
* **OKX** new contracts deployed
* **Paraswap** API updated
* Removed **Odos** chains to keep routing clean
## Features
* **Hyperliquid builder codes** support
* **Custom Solana priority fees** for faster execution
* **Bitcoin simple transactions** — new flow for straightforward BTC transfers
* **Polymer Standard** limit increased to 10M
* **Transak session** request endpoint added
* Improved precision of integrator fees between quote and transaction generation
* Optional **Composer API key** support
## Earn API
### Breaking Changes
* **API paths simplified** - all Earn endpoints have dropped the `/earn` path segment. Update your base paths:
* `/v1/earn/chains` is now `/v1/chains`
* `/v1/earn/vaults` is now `/v1/vaults`
* `/v1/earn/vaults/{chainId}/{address}` is now `/v1/vaults/{chainId}/{address}`
* `/v1/earn/protocols` is now `/v1/protocols`
* `/v1/earn/portfolio/{userAddress}/positions` is now `/v1/portfolio/{userAddress}/positions`
* **`provider` field removed** from vault objects
* **Portfolio positions** - `protocolName` and `balanceUsd` are now nullable; handle `null` values in your integration
### New Features
* **Three new filters** on `GET /v1/vaults`: `isTransactional`, `isRedeemable`, and `isComposerSupported` let you narrow results to vaults that support specific capabilities
* **`address` field** added to portfolio position objects, returning the vault contract address for each position
* **Structured error responses** for `400` and `404` - validation errors now return a detailed `errors` array with per-field codes and paths
## Reliability
* Fixed status API not working for funds moved in the same block
* Improved Hyperliquid and Relay status parsing
* Improved Solana status error parsing
* `/quote/toAmount` now returns `1011` for same-token requests and caps the adjustment factor to prevent over-quoting
* Normalised x100 percentage values in status API responses
* Fixed inconsistent FeeForwarder address mapping in status step lookup
* Fixed Stargate V2 and Polymer status validation
* Filter token tax updates by GoPlus-supported chains
## Fee Infrastructure
* Upgraded fee handling on supported EVM chains from **FeeCollector** to **FeeForwarder** — fees are now forwarded directly to recipient wallets at execution time, with no manual withdrawal required. Chains without FeeForwarder fall back to the legacy contract automatically. No partner action needed. See [FeeForwarder](/introduction/integrating-lifi/fee-forwarder) for details.
## New Chains
* Added **Fogo (SVM)**, **Tempo**, and **Morph** support
* Enabled **Arc Testnet**, **OP Sepolia**, **Arbitrum Sepolia**, and **Base Sepolia** across Backend, Solver, Wallet, and Order services
## New Bridges
* **Hypercore:** Native Deposits, Native Withdrawals, and Spot Swapping now supported
* **Eco Bridge** enabled on HyperEVM and BSC, with updated Solana program ID
* **Symbiosis** expanded to BSC, SEI, ARB, and Morph
* **Polymer** added fee buffer and simultaneous mainnet/testnet support
* New **Across** swap facet and improved receiver address status parsing
* **Chainflip** updated to SDK v2.1.1
* **Titan** API endpoint updated
* New **LI.FI Intents DEX** implementation
## New DEXs
* **OKX** liquidity source filtering (`excludeDexIds`) and exposed liquidity list endpoint
* **Relay** token whitelisting
* **Houdini Swap** integration (including Solana)
* **OogaBooga** disabled
## Features
* Improved `/tools` endpoint performance
* Strengthened wallet compliance screening with the Hypernative integration
## Reliability
* Improved **Solana RPC** latency and stability
* Corrected **Hyperliquid** transaction status parsing
* Fixed impossible price impact (>100%) when input minus fees is lower than output
* Fixed **LI.FI Intents** status parsing and **LIFI Transfer** token casing inconsistencies
* **Across** temporarily disabled and re-enabled after fixing pending transaction parsing
* **Chainflip** temporarily disabled and fixed via SDK v2.1.1
## New Chains
* Added **Telos** and **Zcash** support
## New Bridges
* Expanded **MegaETH** bridge coverage (GasZip, Garden, native bridge)
* New two-step bridge flow to **Hyperliquid spot**
* Re-enabled the **LI.FI Intents bridge**
## New DEXs
* New routing path on **Symbiosis**
* Added **Kelp**, **Morpho vaults v2**, and **Neutrl** in Composer
## Features
* Dedicated Hyperliquid endpoints and improved cross-chain status tracking
* Launched For Agents documentation and LI.FI MCP server docs
* Strengthened wallet compliance screening with lower latency
## Reliability
* Improved token pricing accuracy and wallet balance display
* Better fee calculations across stable chains and bridge routes
* Improved gas recommendations for MegaETH
## New Chains
* Added **Viction** support
* Removed inactive chains to keep routing clean
## New Bridges
* **Allbridge** now available on Unichain and Linea
* **Glacis** expanded to Flow
* Continued **MegaETH** rollout with relay and native bridge
## New DEXs
* Added **Nordstern Finance**, **Cetus** on Sui, and **Eisen** on Monad
## Features
* **Smart slippage selection** for major and stable assets
* Revamped **dynamic stablecoin route fees**
* New **tool control options** for integrators to manage protocols and exchanges via API
* Improved **token search** performance
## Intents Stack
* **Wallet Service** and **Order Service** reached v1.0.0
* **Solver v2.1.0** with reliability and rebalancing upgrades
## Composer
* **Composer v0.3.0** with new protocol modules (Ether.fi, HypurrFi, Fluid, Spark, Royco, Cap)
## Reliability
* Improved transaction indexing and RPC reliability
* Better Bitcoin address support (xpub, UTXO handling)
* Fee accuracy and data consistency improvements
## New Chains
* Day-one support for **Stable**
* Added **MegaETH** and **Plume**
## New Bridges
* Added **Garden**, **CelerCircle**, and **CelerCircleFast** bridges
* Updated **Chainflip** integration
* Enabled **Stargate** on Plasma
## New DEXs
* Added **OKX aggregator** on Solana
* Enabled **SushiSwap** on Monad
## Features
* **API Config Presets** for optimized stablecoin routes
* **Token tagging** for stablecoins to improve routing
* Improved quote accuracy with better caching and validation
## Reliability
* Improved platform health monitoring
* Better token pricing accuracy and update frequency
* General infrastructure stability improvements
## New Chains
* Day-one support for **Monad**
## New Bridges
* Enabled **Across** and **GasZip** on Monad
* Upgraded **Glacis** contracts
* Added **Unit** withdrawals
## New DEXs
* Enabled **Magpie Fly**, **Kuru**, and **Monorail** aggregators
## Features
* Faster route sorting and display
* **OpenOcean** expanded to ETH, BSC, Scroll, and Monad
## Reliability
* API performance improvements across analytics endpoints
* Improved token data quality on Sui and Solana
* General platform stability improvements
## Milestones
* Reached **\$50B lifetime volume**
* Expanded to **24/7 technical support** coverage
## Ecosystem
* **Chains:** Flow, Hemi added; Hypercore available via API
* **Bridges:** Across destination swaps on HyperEVM; Solana support for Across; Hypercore to HyperEVM transfers; Pioneer Bridge; Eco Bridge USDT on Celo
* **DEX Aggregators:** GlueX, Magpie, Hyperflow Corewriter; Titan and OKX on Solana; Momentum on Sui; Plasma chain DEXs
* **Protocols:** Perena support added
## Features
* Mayan bridge split into three providers: `mayan`, `mayanWH`, `mayanMCTP`
* Route quality improvements and positive slippage collection
* Allow `/status` to work with `transactionId`
## Bug Fixes
* Fixed multiple Solana transaction errors (insufficient lamports, insufficient funds, transaction size limits)
* Fixed wSOL display in status responses
* Fixed gas amount USD values in contract calls
* Improved gas estimation and status parsing accuracy
# Monthly Updates
Source: https://docs.li.fi/changelog/monthly-updates
Monthly changelog of new chains, bridges, DEXs, features, and improvements shipped across the LI.FI platform.
## August 2026
### Fee Infrastructure
* **Partner Service:** Added intermediary fee support for multi-hop fee splits.
### Bridge Integrations and Coverage Updates
* **Frax HopV2:** Integrated Frax's LayerZero V2 OFT-based bridge for cross-chain transfers of supported Frax assets, with Fraxtal as the hub. This integration supports transfers into Tempo, but not from Tempo.
* **Eco:** Added HyperCore deposits as a destination.
* **Layerswap:** Improved Solana source-token support for native SOL and supported SPL tokens.
* **Celer cBridge:** Deprecated. CCTP + Celer routes are unaffected by this deprecation.
* **Symbiosis:** Added Hyperliquid and Lighter as destination chains, and enabled routing on Stable.
* **Glacis:** Added Tempo as a source chain.
* **Hop:** Deprecated.
### New DEXs and DEX Aggregators
* **Sushiswap:** Enabled on X Layer.
* **Pioneer:** Deprecated.
### Intents Stack
* **Breaking:** LI.FI Intents escrow contracts redeployed at new addresses via CREATE2, identical across every EVM chain: InputSettlerEscrowLIFI, OutputSettlerSimple, and the mainnet Polymer oracle. InputSettlerCompact and the testnet Polymer oracle are unchanged. Integrators calling these contracts directly must update.
* **LI.FI Intents:** Now live on Solana.
* Added sponsored (gasless) transaction support for Solana LI.FI Intents.
### Features and Improvements
* **Ethereum Sepolia Testnet:** Added support for development and testing.
* **EVM gasless quoting:** Added relay-fee calculation and eligibility filtering for gasless quotes; this update covers quoting, not a full EVM relayed-execution launch.
* **SDK and providers:** Improved Solana transaction confirmation, RPC error handling, SDK/Perps SDK dependency compatibility, and native permit handling for delegated EVM accounts.
* **Partner Portal:** Improved enterprise SSO connection setup, organization discovery, and first-login flows.
* **Jumper:** Advanced mode added shareable win cards, perps trading, and Solana quote simulation. Earn gained a 7-day/30-day APY toggle, an exposure filter, and an Insurance Risk tag.
* **Composer:** Added Origin Protocol (wOETH, wOUSD, wsuperOETHb), Symbiotic V2 curator vaults, and two new Ample XAUt vaults on Ethereum and Arbitrum.
* Added enterprise guides for Stablecoin 1:1 and Real World Assets integration flows.
* Raised the deny-pools limit to 100.
### Reliability
* Fixed Sui coin selection ignoring address balance, which caused full-balance quotes to fail to build.
* Fixed slippage protection being lost on swap-into-destination-call-bridge-into-swap routes.
* Fixed Across deposit resolution and status parsing when a source transaction carries more than one deposit.
* Fixed Symbiosis and LayerSwap refund substatus reporting.
* Added a gas buffer for Etherlink to prevent sequencer rejections.
* Fixed false Arbitrum "funds permanently locked" alerts.
* Fly's Solana routes remain temporarily disabled while a gas-estimation issue is investigated.
***
## July 2026
### New Chains
* **Injective:** Now supported, live on the LI.FI API as chain `1776`.
### New Bridges
* **Layerswap:** New bridge integration added, including Robinhood Chain support.
* **Paxos:** New bridge integration added.
* **Superset:** New bridge integration added.
* **Glacis:** New Solana bridging solutions added, plus 0g support and a migration to the new Glacis API as the default.
* **Polymer:** HyperCore deposits enabled as a destination, plus Morph and Cronos support and a standard-with-fees mode.
* **Mayan:** ETH Swift support added.
* **Relay:** Zaps destination calls added.
* **LI.FI Intents:** Enabled on Katana and MegaETH.
* **Robinhood Chain:** Bridge coverage expanded across Glacis, Symbiosis, Layerswap, Across v4 destination calls, Relay, and Across.
* **Stargate:** Gas fee buffer added for destination calls on Gnosis.
### New DEXs and DEX Aggregators
* **Rialto:** New DEX aggregator added.
* **Titan:** Ported to API v3.
* **Fly:** Extended to Telos, Katana, Tempo, and Robinhood Chain.
* **Bitget:** Avalanche support added.
* Enabled Nordstern, KyberSwap, and OpenOcean on Robinhood Chain.
### Earn API
* **Plume:** Earn data support added, covering Nest vaults.
### Features and Improvements
* **Breaking:** SDK v4.3.0 removed the `getWalletBalances` action and client method. Switch to `getTokenBalances` or `getTokenBalancesByChain`, which read balances directly from RPCs.
* **SDK v4.2.0:** Integrators can now pick the priority-fee tier used when building Solana transactions.
* **Widget v4.2.0:** Added Advanced settings with slippage and route-priority pages, a limit-order mode with expiry and partial-fill controls, redesigned amount cards, and a new appearance-change event. MetaMask Bitcoin added to the wallet list; the Phantom Bitcoin connector removed.
* **Widget v4.3.0 to v4.5.0:** Integrators can mark specific tokens as trusted to suppress the unverified-token warning. Checkout now quotes through the shared routes engine with a fiat-first Transak flow, restricted to EVM chains and tokens. Token balances are read from RPCs.
* **Partner Portal:** Per-API-key IP allowlists, a new audit log page with filters, pagination, and CSV export, and per-company SSO configuration.
* **Jumper:** Advanced mode live with limit orders through CoW Swap and 1inch and dedicated Swap and Bridge tabs, rolling out to allowlisted wallets. Portfolio DeFi positions now come from Zerion, with a transaction view and a profit-and-loss chart.
* Added a user-added-token indicator in token information and transfer-scope filtering in analytics.
* **Slippage:** Default slippage for major assets raised to 0.5%.
* **Sui:** Migrated to the Sui SDK 2.0.
* **Docs:** New Smart Deposit Addresses and Smart Slippage enterprise pages, the Tron Diamond added to the Smart Contract Addresses table, and a full ethers.js to viem migration across the integration guides and Composer samples.
### Reliability
* Fixed transfers stuck in `PENDING` on bridge and facet mismatches, which are now marked invalid.
* Fixed stuck Squid boost routes and LI.FI Intents batch order settlement.
* Fixed inflated Mayan relayer fee values and Bitget swap estimates.
* Added gas buffers for Symbiosis source chains and 1inch gas estimates.
* Improved route selection: switch-chain routes are dropped, single-signature routes are preferred, and Composer routes with excessive price impact are filtered out.
* Improved integrator fee precision on Solana swaps and between quote and transaction generation.
* Added parsing for transfer-recovered events on failed Across and Stargate V2 destination transactions.
* Improved pricing resilience across upstream oracles, and expanded dRPC, RouteMesh, and POKT coverage including Avalanche.
* Added native USDC support on Cronos and a 2 bps Near fee for stable routes.
***
## June 2026
### New Chains
* **Robinhood Chain:** Launched and live on the LI.FI API, with routing enabled across bridges, DEXs, and LI.FI Intents.
* **0g:** Now supported, with Stargate routing and DEX liquidity.
* **Cronos:** Now supported, with Circle CCTP live from day one for moving USDC into the Cronos ecosystem with zero slippage.
### New Bridges
* **Circle CCTP:** Enabled on Cronos from day one for USDC transfers.
* **Stargate:** Enabled on 0g.
### New DEXs and DEX Aggregators
* **ListaDAO StableSwap:** Added on Ethereum.
* **Elfomo:** New liquidity source added.
* **Kipseli:** New liquidity source added.
* **Nexroute:** New DEX added.
* Added Uniswap V3 and Algebra forks across supported chains, including 0g.
### Composer
* **Breaking:** The legacy Composer stack was decommissioned. The zap-pack endpoints (`POST /route`, `GET /zap-packs`) have been removed. Migrate to the current compose endpoints.
* **Simulate endpoint:** New `POST /compose` simulation returning balance deltas and gas estimates before execution.
* **Compound V3:** Protocol support added.
* Migrated Euler vault data to the Euler public API.
* Added a generic LI.FI fallback referrer across all zap routes.
### Intents Stack
* **Solana support** expanded across the Intents stack: Intent API Solana indexing, Solana order ID computation, and a new event ingestion pipeline.
* Completed audit hardening for the Solana LI.FI Intents contracts.
* Scaled escrow output amounts on-chain from the actual swap output.
### Earn API
* Added Compound, infiniFi, and Apyx vaults, ingested from DeFiLlama, to the protocol and vault listings.
### Features and Improvements
* **Token Service:** Rebuilt with Coingecko for improved price accuracy and coverage across 2M+ assets and 40+ chains, with real-time price streams. Live across all LI.FI-powered flows.
* **SDK v4.1.0:** Added an optional `private` flag to `getRoutes` for requesting private routes, and optional limit-order fields (`toAmount`, `validUntil`, `partiallyFillable`) on `getRoutes` and a step's action. New exported `LiFiStepRequest` type. Dual CJS and ESM build.
* **RWA access:** Ondo tokenized stocks are now available across the LI.FI partner network, giving partners access to tokenized real-world assets.
* **RWA controls:** Added custom RWA fee configuration and a custom stablecoin token list for integrators.
* **Partner Portal:** New analytics Fees dashboard, role assignment and removal APIs, organization MFA enforcement, and SSO settings.
* Exposed `verificationStatus` in token information.
### Reliability
* Fixed `getQuote` dropping `distributionFees`, so multi-recipient fee splits now reach the backend on `/quote` and `/quote/toAmount`.
* Fixed RWA opt-out still serving routes through other tools.
* Fixed `/v1/status` returning `422` for legacy Relay transactions served from cache.
* Fixed Solana status reporting an intermediate token instead of the final output on multi-hop Titan swaps.
* Anchored Somnia WBTC and USDso to canonical token prices.
* Re-enabled USDT on Tron through Near Intents.
* Fixed Mayan non-EVM transfers failing on BSC.
* Updated the Symbiosis reverted-transaction flow and status handling.
* Fixed Composer requests hanging during cache outages, and out-of-gas failures on a wallet's first Composer transaction.
***
## May 2026
### New Chains
* **Somnia:** Now supported.
* **Lighter:** Support added.
* **Etherlink:** Now enabled on LI.FI.
### New Bridges
* **Symbiosis:** Migrated to v2 with any-to-any swap support.
* **LI.FI Intents:** Enabled on Polygon and BSC.
* **Stargate:** Destination calls enabled on Flow and Flare.
* **Relay V1:** Deprecated in favor of Relay V2.
* **Mayan:** Improved Solana transaction generation.
### New DEXs and DEX Aggregators
* **Nordstern:** Extended to Ethereum.
### Composer
* **New protocols:** Added Midas and Paxos.
* **Aave v3 lifecycle operations:** Borrow, repay, rewards, and eMode, with a health-factor guard.
* **Plasma chain support** added, including Pendle and Fluid edges.
* Per-protocol default referrer payloads.
* Added Upshift vault limit validation and IPOR zaps.
### Intents Stack
* **TRON support** is rolling out across the Intents stack, including event indexing, solver registration, and the Wallet Service.
* **Solana support** is expanding across the Intents stack, including solver registration, quote origin support, and EVM-to-Solana order building.
* **Order Service:** `/orders/status` now surfaces `refundTxHash` and `refundedAt`.
### Features and Improvements
* **RWA token controls:** RWA tokens are tagged via Coingecko categories and filtered out of quotes when a partner's RWA policy is not current. New company-level RWA policy endpoints are exposed in the Partner Portal.
* Improved scam-token detection and token verification.
* **Partner Portal:** TRON partner fee collection wallet setup and a Terms of Service acceptance banner.
### Reliability
* Fixed Sui multi-hop swap handling.
* Added gas buffers for Stargate V2 on Sei and Plume.
* Improved Hyperliquid, Glacis, and split-swap status parsing.
* Aligned Across V4 destination receiver handling.
* Fixed Polymer status mapping so pending transfers no longer settle prematurely, and resolved the 10 USDC Polymer fee threshold issue.
* Converted Bitcoin fees to satoshis for correct fee calculations.
* Prevented Composer destination call failures.
* Extended the LI.FI Intents fill deadline to 44 hours and allowed nullable transaction hashes in the Intents status schema.
***
## April 2026
### New Chains
* **TRON:** Full support shipped and live on the LI.FI API, including on Jumper.
* **Arbitrum Nova:** Now supported.
* **Base Sepolia** and **Arbitrum Sepolia:** Testnets enabled.
### New Bridges
* **Mayan v2:** New bridge version integrated.
* **NEAR Intents:** Expanded to include Tron routing.
### New DEXs and DEX Aggregators
* **Bitget:** New DEX added.
* **OKX:** New contracts deployed.
* **Paraswap:** API updated.
* **Odos:** Chains removed to keep routing clean.
### Earn API
* **Breaking:** All Earn endpoints dropped the `/earn` path segment. For example, `/v1/earn/vaults` is now `/v1/vaults`. Update base paths in your integration.
* **Breaking:** Removed the `provider` field from vault objects.
* **Breaking:** `protocolName` and `balanceUsd` on portfolio positions are now nullable — handle `null` values.
* Added three new filters on `GET /v1/vaults`: `isTransactional`, `isRedeemable`, and `isComposerSupported`.
* Added `address` field to portfolio position objects, returning the vault contract address for each position.
* Structured error responses for `400` and `404` — validation errors now return a detailed `errors` array with per-field codes and paths.
### Features and Improvements
* **Widget and SDK v4:** New major versions released.
* **Hyperliquid builder codes** support added.
* **Custom Solana priority fees** for faster execution.
* **Bitcoin simple transactions:** New flow for straightforward BTC transfers.
* **Polymer Standard** limit increased to 10M.
* **Transak** session request endpoint added.
* **Partner Portal:** Shipped a banner and modal notifying users of the FeeCollector → FeeForwarder transition.
* **Solver:** Ethereum routes enabled for USDC, WETH, and ETH.
* **Jumper:** Learn page search, reading progress bar, and table of contents; multi-address portfolio queries; Solana positions; improved wSOL support; TRON is live on Jumper.
* **Composer:** Optional API key support added.
### Reliability
* Fixed status API not working for funds moved in the same block.
* Improved Hyperliquid, Relay, and Solana status parsing.
* `/quote/toAmount` now returns `1011` for same-token requests and caps the adjustment factor to prevent over-quoting.
* Normalised percentage values in status API responses.
* Fixed Stargate V2 and Polymer status validation.
* Improved integrator fee precision between quote and transaction generation.
***
## March 2026
### Fee Infrastructure
* **FeeForwarder:** LI.FI has upgraded fee handling on supported EVM chains. Fees are now forwarded directly to recipient wallets at execution time, with no manual withdrawal required. Chains without FeeForwarder deployed continue to use the legacy FeeCollector contract. No changes to API responses or partner integrations.
### New Chains
* **Fogo (SVM):** New Solana-based chain added.
* **Tempo:** Added as a supported chain.
* **Morph:** Enabled with Symbiosis routing.
* **Arc Testnet, OP Sepolia, Arbitrum Sepolia, Base Sepolia:** Testnet support added across Backend, Solver, Wallet, and Order services, with Polymer enabled as the initial bridge for Arc Testnet ↔ OP Sepolia.
### New Bridges
* **Hypercore:** Native Deposits, Native Withdrawals, and Spot Swapping now supported.
* **Eco Bridge:** Enabled on HyperEVM and BSC, with updated Solana program ID.
* **Symbiosis:** Expanded to BSC, SEI, and ARB, plus Morph support.
* **Polymer:** Added fee buffer and simultaneous mainnet/testnet support.
* **Across:** New swap facet implementation and improved receiver address status parsing.
* **Chainflip:** Updated to SDK v2.1.1 with pending transaction fix.
* **Titan:** API endpoint updated with new simulation argument.
* **LI.FI Intents:** New DEX implementation for intent-based routing.
### New DEXs and DEX Aggregators
* **OKX:** Added liquidity source filtering (`excludeDexIds`) and exposed a liquidity list endpoint.
* **Relay:** Token whitelisting added.
* **Houdini Swap:** New integration on Jumper, including Solana support.
* **OogaBooga:** Disabled.
### Features and Improvements
* **Composer:** New protocols added including Seamless staking, Avant zaps, Auto Finance (Tokemak), YO Protocol, Yearn, and Upshift. Added referrer support for Ether.fi, Veda, and Spark, plus Linea slot-finder support.
* **Intents Stack:** Reliable solver registration, origin/destination chain filters on `GET /orders`, strict solver-address enforcement, and configurable `maxPriceImpact` via environment for the Ledger service.
* **Wallet Service:** Added RPC failover and fallback across all RPC calls for more reliable transaction submission.
* **Partner Portal:** Token-level fee withdrawal (PAR-300), company integration limit overrides, and multicompany invite improvements.
* **Partner Service:** New fees resolve endpoint and company-level integration limit overrides.
* **Gasless Service:** Migrated to the new Gelato SDK.
* **Jumper:** Sitemap indexing improvements, theme flickering fixes, SVM + EVM wallet support on Earn, and Houdini Swap integration (including Solana).
* Improved `/tools` endpoint performance and Codex pricing.
### Reliability
* Improved Solana RPC call latency and stability.
* Hyperliquid transaction status parsing corrected; completed txs no longer marked as pending.
* Fixed impossible price impact (>100%) when input minus fees is lower than output.
* Fixed LI.FI Intents status parsing and LIFI Transfer token casing inconsistencies.
* Across temporarily disabled and re-enabled after fixing pending transaction parsing.
* Chainflip temporarily disabled and fixed via SDK v2.1.1.
***
## February 2026
### New Chains
* **Telos:** Now supported, with routing and liquidity rolling out across products.
* **Zcash:** Now supported with reliable token pricing.
### New Bridges
* **MegaETH:** Expanded bridge coverage with GasZip, Garden, and native bridge support.
* **Hyperliquid spot:** New two-step bridge flow for cleaner execution and tracking.
* **Intents bridge:** Re-enabled the LI.FI Intents bridge.
### New DEXs and DEX Aggregators
* **Symbiosis:** New routing path enabled for improved options.
* **Composer:** Added Kelp, Morpho vaults v2, and Neutrl protocol support.
### Features and Improvements
* Dedicated Hyperliquid endpoints and improved cross-chain status tracking.
* Launched the **For Agents** documentation tab for AI agents and crawlers.
* Added LI.FI MCP server docs, OpenAPI surfacing through llms.txt, and AI-friendly redirects.
* New guides for debugging, API presets, intermediate tokens, smart slippage, and Bitcoin formats.
* Strengthened wallet compliance screening with lower latency.
* Partner Portal and Partner Service updates with improved analytics and configurable rate limits.
### Reliability
* Improved token pricing accuracy and wallet balance display.
* Better fee calculations across stable chains and bridge routes.
* Improved gas recommendations for MegaETH.
* General performance and stability improvements across the platform.
***
## January 2026
### New Chains
* **Viction:** Now supported.
* Removed inactive chains to keep routing clean.
### New Bridges
* **Allbridge:** Now available on Unichain and Linea.
* **Glacis:** Expanded to support Flow.
* **MegaETH:** Continued rollout with relay and native bridge support.
### New DEXs and DEX Aggregators
* **Nordstern Finance** DEX added.
* **Cetus** DEX added on Sui.
* **Eisen** added on Monad.
### Features
* **Smart slippage selection** for major and stable assets.
* **Dynamic stablecoin route fees** revamped for better pricing.
* New **tool control options** for integrators to manage allowed protocols and exchanges via the API.
* Improved **token search** performance.
### Intents Stack
* **Wallet Service** and **Order Service** reached their v1.0.0 milestones with improved performance and reliability.
* **Solver v2.1.0** shipped with reliability and rebalancing upgrades.
### Composer
* **Composer v0.3.0:** Expanded protocol support with new modules including Ether.fi staking, HypurrFi, Fluid, Spark, Royco, and Cap.
### Reliability
* Improved transaction indexing resilience and RPC reliability across chains.
* Better Bitcoin address support including xpub and improved UTXO handling.
* General fee accuracy and data consistency improvements.
***
## December 2025
### New Chains
* **Stable:** Day-one support shipped and enabled on production.
* **MegaETH:** Now supported.
* **Plume:** Now supported.
### New Bridges
* **Garden bridge** added.
* **CelerCircle** and **CelerCircleFast** bridges added.
* **Chainflip** integration updated.
* **Stargate** enabled on Plasma.
### New DEXs and DEX Aggregators
* **OKX aggregator** added on Solana.
* **SushiSwap** enabled on Monad.
### Features
* **API Config Presets** for optimized stablecoin routes and quotes.
* **Token tagging** for stablecoins to improve routing accuracy.
* Improved quote accuracy with better caching and validation.
### Reliability
* Improved platform health monitoring and alerting.
* Better token pricing accuracy and update frequency.
* General infrastructure stability improvements.
***
## November 2025
### New Chains
* **Monad:** Day-one support shipped with rapid follow-up improvements after launch.
### New Bridges
* **Across on Monad** enabled with reliability improvements.
* **GasZip on Monad** enabled including inbound support.
* **Glacis** contracts upgraded and re-enabled.
* **Unit** withdrawals added.
### New DEXs and DEX Aggregators
* **Magpie Fly** enabled.
* **Kuru** aggregator added.
* **Monorail** aggregator added.
### Features
* Faster route sorting and display for quicker results.
* **OpenOcean** expanded to ETH, BSC, Scroll, and Monad.
* SDK improvements: Permit2 refactor and fixes for multi-step estimation, slippage handling, and approvals.
### Reliability
* API performance improvements across analytics and transfer endpoints.
* Improved token data quality on Sui and Solana.
* General platform stability improvements.
***
## October 2025
### Milestones
* Reached **\$50B lifetime volume**.
* Technical support expanded to **24/7 coverage**.
### New Chains
* **Flow** and **Hemi** added.
* **Hypercore** infrastructure now available via API.
### New Bridges
* Across destination swaps on HyperEVM.
* Solana support for Across.
* Hypercore to HyperEVM transfers.
* Pioneer Bridge enabled.
* Eco Bridge USDT on Celo.
### New DEXs and DEX Aggregators
* GlueX, Magpie, Hyperflow Corewriter on EVM.
* Titan, OKX on Solana.
* Momentum on Sui.
* Plasma chain DEXs enabled.
### Features
* **Perena** protocol support added.
* Significant **Widget bundle size reduction** and new "All Networks" tab.
* SDK now supports **all Bitcoin address types** including nested SegWit.
* Route quality improvements and positive slippage collection.
* Mayan bridge split into three distinct providers for clearer routing: `mayan`, `mayanWH`, `mayanMCTP`.
* Partner Portal now supports **multi-user accounts**.
### Bug Fixes
* Fixed multiple Solana transaction errors (insufficient lamports, insufficient funds, transaction size limits).
* Fixed wSOL display in status responses.
* Fixed gas amount USD values in contract calls.
* Improved gas estimation and status parsing accuracy.
# Partner Portal
Source: https://docs.li.fi/changelog/partner-portal
Additions and updates to the LI.FI Partner Portal
## Partner Portal
* **TRON fee collection:** Set up your TRON partner fee collection wallet directly in the portal, with a new news card highlighting TRON support
* **Terms of Service banner:** The dashboard now prompts integrators when ToS acceptance is pending or stale
## Partner Service
* **RWA policy endpoints:** New endpoints and an `rwaPolicy` field on the integrator schema let partners manage real-world asset token policies
## Partner Portal
* **FeeForwarder migration notice:** Added an in-portal banner and modal notifying users of the transition from FeeCollector to FeeForwarder for fee handling
* Security dependency updates
## Partner Portal
* **Token-level fee withdrawal** (PAR-300) — withdraw fees per token rather than per wallet
* **Company integration limit overrides** — support for per-company integration caps
* Multicompany invite flow improvements
* Fixed add-wallet dialog crash when adding the last default wallet
* Fixed error page content overflow and non-verified JWT decoding in analytics
## Partner Service
* New **fees resolve endpoint** with enhanced response schema
* **OpenAPI documentation** published for the Partner Service
* **Company update endpoint** for max integration limit override
* Configurable default API key rate limit
## Improvements
* Updated analytics and improved Partner Portal experience
* Configurable API key rate limits for integrators
## Improvements
* Partner Portal onboarding improvements and performance fixes
## Improvements
* Partner Portal quality-of-life improvements
## Improvements
* Improved Partner Portal onboarding experience
## New Features
* **Multi-user accounts** in Partner Portal
# SDK
Source: https://docs.li.fi/changelog/sdk
Additions and updates to the LI.FI SDK
## Major Release
* **v4 released** — new major version of the SDK, now adopted by Jumper
## Improvements
* Improved error handling and reliability in the Intents stack
* Better fee handling and fallback logic for solver-based routes
## Features
* **Smart slippage selection** for major and stable assets
* Improved Bitcoin address support including xpub and better UTXO handling
## Changes
* Updated Chainflip integration
## Features
* **Permit2 refactor** for improved token approval flows
* Fixes for multi-step route estimation, slippage handling, and approvals
## Features
* Support for **all Bitcoin address types** including nested SegWit
* Updated **OKX v6** integration
# Widget
Source: https://docs.li.fi/changelog/widget
Additions and updates to the LI.FI widget
## Major Release
* **v4 released** — new major version of the Widget, now adopted by Jumper
## Improvements
* Improved slippage calculation and reporting accuracy
## New Features
* **180 kB bundle size reduction** for faster load times
* **"All Networks" tab** is now live
* **Multi-step route badges** for improved route visibility
* Support for **Porto wallet**
# Dynamic Pricing
Source: https://docs.li.fi/enterprise/dynamic-pricing
Flexible, case-by-case commercial models for LI.FI's enterprise features.
Pricing for LI.FI's enterprise features is agreed per integration, not a fixed rate card. LI.FI tailors the commercial model to your volume, chains, and the mix of flows you run.
## Why pricing is per integration
Enterprise integrations vary widely. A stablecoin-heavy payments flow and a volatile-asset trading flow have very different economics, so a single flat rate either overcharges the thin-margin flows or underprices the rest. LI.FI sets pricing against the characteristics of your integration instead.
## What shapes the rate
* **Volume.** Higher, committed volume moves the rate.
* **Route type.** Same-chain and cross-chain are priced differently.
* **Token type.** Stablecoin-to-stablecoin flows can carry a lower rate than volatile-asset swaps.
* **Feature mix.** Which enterprise features you enable, and how they are applied.
## Transparency
Every resolved fee is returned with the quote, so the applied rate is visible to you and auditable per transaction. Your own integrator fee is separate and paid in full to your configured fee wallet. See [Monetizing the integration](/introduction/integrating-lifi/monetizing-integration) for how integrator fees are collected.
## How to set it up
Pricing is agreed when your integration is configured, and it can be adjusted later.
Tell us your volume and the flows you run. We propose a commercial model that fits.
# Overview
Source: https://docs.li.fi/enterprise/overview
LI.FI's enterprise features for higher success rates, tighter pricing, and simpler user flows.
LI.FI runs a set of execution-quality features for integrators who need higher success rates, tighter pricing, and simpler user flows than the standard quote path provides.
Each feature is enabled per integrator. You can adopt one or run several together. Pricing and rollout are handled case by case, so the starting point is a short conversation about your volume, chains, and the flows you want to improve.
## The features
Pre-broadcast simulation that filters routes which would revert, so the quotes your users receive are more likely to execute.
A slippage tolerance set per token from market data, in place of a single flat value applied to every trade.
A swap completed with a single token transfer to a unique deposit address. No approval, no router transaction.
Like-for-like stablecoin swaps executed at par, so the amount out matches the amount in.
Routing for onchain stocks and permissioned real-world assets, with eligibility resolved before a route is returned.
Flexible, case-by-case commercial models tailored to your volume, chains, and flows.
## Who it's for
* Integrators seeing on-chain failures from reverted transactions or mis-set slippage.
* Custodial and exchange-style products that want a transfer-based flow instead of approve-and-execute.
* Payments and treasury products moving stablecoins at size, where per-hop leakage is the margin.
* Platforms offering onchain stocks or permissioned real-world assets, where the asset itself governs who can hold it.
* Teams moving higher-value volume, where a small reduction in failed transactions is material.
* Stablecoin and RWA issuers who want their asset distributed across LI.FI's integrator network.
## How to enable
These features sit behind a per-integrator gate. To scope enablement for your integration, tell us your chains, volume, and the flows you want to improve. We confirm which features fit and enable them for your integrator key.
Start a conversation about LI.FI's enterprise features for your integration.
# Quote Simulation
Source: https://docs.li.fi/enterprise/quote-simulation
Pre-broadcast simulation that filters routes which would revert, so the quotes returned to your users are more likely to execute.
Quote Simulation dry-runs a swap against live chain state before the route is returned, and filters routes that would revert. The quotes your users receive are more likely to execute.
## The problem
A quote can look valid and still revert when the user broadcasts it. State moves between quote time and execution: balances change, pool reserves shift, a token behaves in a way the quote didn't account for. The user pays gas for a failed transaction, and your product carries the support cost and the loss of trust.
## How LI.FI solves it
LI.FI replays the exact transaction the user would send against current on-chain state and checks whether it reverts. Routes that fail the check are filtered out, so the set of routes you receive is pre-validated against live conditions.
The simulation runs on the LI.FI side during route generation. It doesn't require the user to hold the input token or grant an approval first, so there's no extra step in your flow and no change to how the user signs.
Simulation reduces reverted transactions but does not guarantee execution. It filters routes that would revert at quote time, but it can't prevent a failure if on-chain state changes between the quote and when the user broadcasts the transaction.
## What you see as an integrator
Nothing to configure and nothing new to handle. Quote Simulation raises the quality of the routes LI.FI returns rather than exposing a parameter you set. The observable effect is that a route which would fail on-chain is absent from the response.
Quote Simulation isn't an integrator-controllable flag. LI.FI enables it per integrator, and there's no request field to toggle it on or off.
## Scope
* **EVM chains only.** Non-EVM chains aren't simulated.
* **Same-chain swaps only.** Cross-chain routes are out of scope for simulation.
* **Higher-value trades.** Simulation applies above a value threshold, so low-value quotes aren't slowed by it. The threshold is configurable per integrator.
* Requires the user's address on the quote request, which is the normal case for execution.
## Availability
Quote Simulation is an enterprise feature, enabled per integrator.
Share your chains and typical trade sizes. We confirm coverage and enable simulation for your integrator key.
# Real World Assets (RWA)
Source: https://docs.li.fi/enterprise/real-world-assets
Routing for onchain stocks and permissioned real-world assets, with eligibility resolved before a route is returned.
LI.FI routes tokenized real-world assets: onchain stocks, and permissioned assets whose token contracts govern who may hold and receive them.
## Onchain Stocks
LI.FI supports tokenized equities from:
* **Ondo**
* **xStocks**
* **Robinhood**
Support for further issuers is coming.
### How quoting works
Onchain stocks are quoted by licensed solvers through [LI.FI Intents](/lifi-intents/introduction), LI.FI's intent and solver marketplace. Trading tokenized equities requires a licensed counterparty, so these assets aren't routed through open liquidity pools. Solvers that hold the necessary licenses publish quotes for the assets they're permitted to trade, and the order server matches your user's intent against those quotes.
What this means for your integration:
* **Pricing comes from the solver network.** The quote is a licensed solver's price for that asset and size, rather than a rate derived from pool liquidity.
* **Coverage follows solver licensing.** Which equities are quotable depends on what the active licensed solvers are permitted to trade, so availability moves as solvers and their permissions change.
* **Available directly via the LI.FI API.** Onchain stocks are accessible through the LI.FI API, with LI.FI Intents supplying the quotes underneath. There's no separate integration to build.
## Permissioned RWAs
Some real-world assets enforce eligibility at the token level — an allowlist, an identity registry, a jurisdiction check, or a transfer-agent gate. LI.FI resolves that eligibility before returning a route.
LI.FI identifies the token as a permissioned RWA and the rule set it enforces.
LI.FI runs the check with [LI.FI Composer](/composer/overview) — onchain, as a call against the token's allowlist or identity registry, or offchain, against the issuer's own eligibility source.
If the check passes, the route is returned and executes normally. If it doesn't, no route is returned and the response carries a message stating why.
Because the rules apply to the receiving address, the same pair can be routable for one user and not another. Pass the actual recipient address on the quote request, not a placeholder.
LI.FI routes within the rules the token enforces. It doesn't onboard users, perform KYC or AML checks, or grant allowlist status — those remain with the issuer or transfer agent, and with you as the integrator.
## What you see as an integrator
* **The endpoints you already call.** Both asset types are available directly through the LI.FI API. Onchain stocks are quoted by licensed solvers via LI.FI Intents, and permissioned RWAs come back with the eligibility check already resolved.
* **A declined route is an eligibility outcome, not an error.** Surface the returned message to the user rather than treating it as a failure.
* **Issuer timing still applies.** Lock-ups, redemption windows, and settlement schedules are set by the issuer. Routing works within them and can't shorten them.
## Scope
* **Assets are supported individually.** Coverage is a defined list per asset and issuer rather than open-ended.
* **Recipient address required.** Quotes for permissioned assets need the real recipient to resolve eligibility.
* **Availability varies by asset and jurisdiction.** Which assets you can offer, and to whom, follows the issuer's own distribution rules.
## Availability
RWA routing is an enterprise feature, enabled per integrator and per asset.
Tell us which tokenized assets and issuers you work with, and the chains you need them on. We confirm coverage and enable RWA routing for your integrator key.
## For RWA issuers
If you issue a tokenized asset, LI.FI can add it as a supported asset and make it reachable from the wallets, exchanges, and fintech apps already integrated with LI.FI. Users buy in with whatever they hold, on whatever chain they hold it, and your asset's transfer rules are enforced as part of routing.
Distribution is set up per asset. LI.FI's team scopes it with you: the asset and its transfer rules, the chains it's live on, and the jurisdictions you distribute into.
Get in touch about listing your asset. Share the asset, its transfer rules, and the chains it's live on, and we'll scope distribution across LI.FI's integrator network.
# Smart Deposit Addresses
Source: https://docs.li.fi/enterprise/smart-deposit-address
A swap, bridge, or vault deposit completed with a single token transfer to a unique deposit address. No approval, no router transaction.
Smart Deposit Addresses let a user complete a swap with a single token transfer to a unique deposit address. There's no approval and no router transaction to sign.
The same mechanism now covers more than a same-chain swap: cross-chain routes, Solana and Bitcoin as source chains, deposits that end inside a yield vault, and composed routes into tokens no single bridge reaches. What the sender does never changes. They send a transfer.
## The problem
The standard swap flow asks the user to sign twice: an ERC-20 approval, then the router transaction. That means approval-management UX, extra gas, and a drop-off point where users abandon the flow. For custodial and exchange-style products that can only send a plain transfer, approve-and-execute doesn't fit at all.
## How LI.FI solves it
LI.FI derives a unique deposit address for the trade. The sender transfers the input tokens to that address with an ordinary transfer, and LI.FI executes the swap from there and delivers the output to the recipient. If the trade can't complete under the supported flow, LI.FI returns the funds to the configured sender/refund address.
| | Standard flow | Smart Deposit Addresses |
| ---------------------------- | --------------------------------- | ----------------------- |
| Signatures | Approval, then router transaction | A single transfer |
| Approval UX | Managed by your app | None |
| Fits a transfer-only product | No | Yes |
## What you see as an integrator
* **Standard quote and route flow.** When Smart Deposit Addresses are enabled for your integrator key, the option appears in routing. The returned step is a plain transfer to the deposit address, with no approval step.
* **Status by deposit address.** Track the trade using the deposit address rather than a source transaction hash. Query the status endpoint with the deposit address and its source chain:
```bash curl theme={"system"}
GET /v1/status?depositAddress={address}&fromChain={chainId}
```
A response while the trade is in flight looks like this:
```json theme={"system"}
{
"status": "PENDING",
"substatusMessage": "Waiting for funds to arrive at the deposit address."
}
```
The `status` moves through `PENDING` to `DONE` once execution completes, or to `FAILED` if the trade could not complete and the funds were returned.
Query `depositAddress` together with `fromChain`. Use this in place of a transaction hash when tracking a deposit-address trade.
## Supported networks
| Network | As source | As destination |
| --------------- | --------------------------------- | ------------------------------------------------------- |
| Ethereum | Yes | Yes |
| Arbitrum | Yes | Yes |
| Base | Yes | Yes |
| Optimism | Yes | Yes |
| Polygon | Yes | Yes |
| BSC | Yes | Yes |
| Avalanche | Yes | Yes |
| Plasma | Yes | Yes |
| Tempo | Yes | Yes |
| Robinhood Chain | Yes | Yes, including tokenized stocks through composed routes |
| Solana | Yes, one signature, any SPL token | Yes |
| Bitcoin | Yes, in beta | Not supported |
Coverage moves. If the network you need isn't listed, ask your LI.FI contact rather than assuming it's unavailable: a large set of further chains is already assessed and can be scheduled on request.
## Supported routes
| Route | Notes |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| Same-chain swaps on EVM | The original flow. |
| EVM to EVM, cross-chain | |
| EVM to Solana | |
| Solana to EVM | One signed Solana transaction. Native SOL or any SPL token as the source. |
| Bitcoin to EVM | Beta, enabled per integrator. |
| Vault deposits | The journey ends inside a curated ERC-4626 yield vault rather than at a token balance. |
| Composed routes | Long-tail tokens that no single bridge reaches, including tokenized stocks. |
| Amount-flexible deposits | Deposit any amount at or above a dynamic minimum, at a rate held for 24 hours. Cross-chain routes only, opt-in per request. |
## Token support
| Supported | Where |
| ------------------------------------------- | ------------------------------------------- |
| ERC-20 to ERC-20 | Every live route |
| Native SOL and any SPL token | As the source, through the Solana lane |
| SPL tokens | As the destination |
| ERC-4626 vault shares | As the destination |
| Tokenized stocks and other long-tail tokens | As the destination, through composed routes |
Native EVM tokens such as ETH and BNB aren't supported as the input, and neither are tokens that tax transfers. The expected sender and refund address is required on the quote request.
## How the deposit address behaves
* Each quote gets a unique, single-use deposit address, valid for 24 hours.
* Failed executions and expired deposits are refunded to the sender automatically, with LI.FI covering the gas.
* Multiple deposits to the same address accumulate and execute cumulatively.
## Availability
Smart Deposit Addresses are an enterprise feature, enabled per integrator. The Solana lane is gated separately, on its own list, so being enabled for the EVM routes doesn't enable Solana. Ask for it by name.
Tell us your chains and the flow you want to support. We confirm coverage and enable Smart Deposit Addresses for your integrator key.
# Smart Slippage
Source: https://docs.li.fi/enterprise/smart-slippage
A slippage tolerance set per token from market data, in place of a single flat value applied to every trade.
Smart Slippage sets a slippage tolerance per token, informed by that token's market behavior, in place of a single flat value applied to every trade.
## The problem
One flat slippage value can't fit every token. Set it tight and volatile tokens revert, so the user pays gas for a failed swap. Set it loose and stable tokens give away value that a tighter bound would have kept. A single number can't be right for both.
| | Flat slippage | Smart Slippage |
| --------------- | -------------------------------------------- | ------------------------------------ |
| Volatile tokens | Reverts when the value is too tight | Widened to the level the token needs |
| Stable tokens | Gives away value when the value is too loose | Tightened to protect price |
| Basis | One number for every trade | Per-token, from market data |
## How LI.FI solves it
LI.FI resolves a recommended slippage for the tokens a route touches and either applies it as the server-side default for the quote, or surfaces it to you as an advisory value so your client can decide.
When a route crosses more than one token, LI.FI takes the highest recommended slippage among those tokens, so the most volatile token in the path doesn't cause a revert. A slippage value you pass yourself is always respected and takes precedence over the recommendation.
## What you see as an integrator
When Smart Slippage is enabled for your integrator key, each step in the route response carries a `recommendedSlippage` field. Read it per step and apply it in your client, or let LI.FI apply it as the default when you don't set your own.
The field has three states, and the distinction matters:
* **A number.** A recommendation resolved for that step, expressed as a decimal fraction. For example, `0.005` means 0.5 percent.
* **`null`.** The feature is active for your integrator, but no recommendation resolved for that step. This lets you tell "no data for this token" apart from "feature not enabled."
* **Field absent.** The feature isn't enabled for your integrator, so no recommendation was produced.
An enabled step in the route response includes the field:
```json theme={"system"}
{
"steps": [
{
"type": "swap",
"recommendedSlippage": 0.005
}
]
}
```
Your own slippage always wins. If you pass a slippage value on the request, LI.FI uses it and doesn't override it with a recommendation.
## Scope
* Enabled per integrator. When it's off, the `recommendedSlippage` field is absent and quote behavior is unchanged.
* Applies to both same-chain and cross-chain routes.
* Recommendations reflect current market data. Where LI.FI has no current data for a token, the value resolves to `null` for that step rather than falling back to a guess.
## Availability
Smart Slippage is an enterprise feature, enabled per integrator.
Tell us which tokens and chains you route most. We confirm coverage and enable Smart Slippage for your integrator key.
# Stablecoin 1:1
Source: https://docs.li.fi/enterprise/stablecoin-1-1
Like-for-like stablecoin swaps executed at par, so the amount out matches the amount in.
Stablecoin 1:1 executes like-for-like stablecoin swaps at par. A user who sends 100,000 USDC receives 100,000 USDT, rather than whatever a liquidity pool happens to quote for that size.
## The problem
Two stablecoins that track the same dollar are economically the same asset, but a standard swap doesn't treat them that way. The trade routes through pool liquidity, and the pool charges for it: a fee tier, price impact against available depth, and slippage between quote and execution. A USDC-to-USDT move that should be flat comes back a few basis points short.
That leakage scales with size and frequency. On a payments or treasury flow, a few basis points per hop is often the entire margin, and it is paid again every time funds are rebalanced.
| | Pool-routed swap | Stablecoin 1:1 |
| ----------------------------- | ------------------------------ | ----------------- |
| Rate | Set by pool depth and fee tier | Par, 1:1 |
| Price impact | Grows with trade size | None |
| Slippage exposure on the rate | Between quote and execution | None |
| Output at size | Falls short of the input | Matches the input |
## How LI.FI solves it
For an eligible pair, LI.FI prices the swap at par instead of routing it through pool liquidity. The amount out is fixed to the amount in at quote time, and the rate doesn't move with trade size, so a large transfer receives the same rate as a small one.
Fees, where they apply, are charged and shown separately from the rate. The 1:1 guarantee is on the exchange rate, not a claim that the transfer is free. See [Dynamic Pricing](/enterprise/dynamic-pricing) for how stablecoin flows are priced.
Amounts are matched in nominal terms, not raw units. Stablecoins carry different decimal precision, so 100,000 USDC in returns 100,000 DAI out, even though the two are expressed at different scales on-chain.
## How it works
LI.FI guarantees the rate by working with solvers through [LI.FI Intents](/lifi-intents/introduction), LI.FI's intent and solver marketplace. Solvers quote eligible stablecoin pairs at par and commit to filling at that rate, so what you receive is a commitment rather than an estimate that can move by the time the trade settles.
Par pricing is one of the fill terms agreed with solvers. The same arrangement covers the other SLAs LI.FI holds solvers to on eligible flow, including fill reliability and settlement time.
## What you see as an integrator
* **The standard quote and route flow.** When Stablecoin 1:1 is enabled for your integrator key, eligible pairs are priced at par in the quote you already request. There's no separate endpoint and no new request field.
* **A fixed output amount.** The quoted amount out matches the amount in for the pair, net of any fee shown on the quote. Price impact on the rate is zero, so there's nothing for a slippage tolerance to protect against.
* **A normal fallback.** A pair, chain, or size that isn't eligible is quoted through standard routing with the usual pricing and slippage behavior. Read the quoted rate rather than assuming par.
## Scope
* **Like-for-like stablecoins only.** Both sides must track the same unit of account. Stablecoin-to-volatile-asset trades are out of scope and are priced normally.
* **Eligible pairs and chains are confirmed per integrator.** Coverage depends on which stablecoins and chains you move, and is agreed when the feature is enabled.
* **Par pricing assumes both assets hold their peg.** LI.FI does not quote at par against an asset trading away from its peg.
## Availability
Stablecoin 1:1 is an enterprise feature, enabled per integrator.
Tell us which stablecoins and chains you move, and your typical transfer sizes. We confirm eligible pairs and enable Stablecoin 1:1 for your integrator key.
## For stablecoin issuers
If you issue a stablecoin, LI.FI can enable 1:1 pricing for it on the LI.FI API. Users across the wallets, exchanges, and fintech apps integrated with LI.FI then move into and out of your token at par, instead of paying pool-driven leakage on every swap that touches it.
Setup is per asset. LI.FI's team agrees the eligible pairs, the chains, and the fill terms with you and the solver network.
Get in touch about 1:1 pricing for your stablecoin. Share the token, the chains it's live on, and the pairs you want quoted at par.
# API latency and optimization
Source: https://docs.li.fi/guides/latency
API latency and optimization guide
## Where Latency Comes From and How LI.FI Routing Works
LI.FI's routing engine aggregates quotes from multiple bridges and decentralized exchanges (DEXes). This process involves real-time requests to external protocols and tools, including:
* Bridge and DEX aggregators
* On-chain simulations for security and execution checks
* Off-chain services that may introduce their own delays
Latency can vary depending on the number of providers queried, the responsiveness of those providers, and whether on-chain simulations are enabled.
The LI.FI routing flow includes two main components:
1. **Swap Step Resolution** – Fetches quotes from DEXes.
2. **Route Composition** – Combines swaps and bridges to construct optimal cross-chain paths.
Both steps involve third-party systems and introduce potential delays. By default, LI.FI waits a short time for responses before returning the best available result. However, integrators can configure timing strategies to better control this behavior.
***
## Optimizing Response Timing
You can optimize how quickly you receive quotes by customizing:
### Choosing Between `/quote` and `/advanced/routes`
* Use `/quote` for **faster responses**. It returns a single best route. It combines route finding and transaction generation into a single call which cuts down on client to server latency.
* Use `/advanced/routes` to **retrieve multiple route options**. Those calls are quite fast to show results to the user quickly. In order to execute one of the routes a call to `/stepTransaction` is needed to generate the transaction data.
### Disabling Simulation
* By default, responses with transaction data include on-chain simulation checks.
* To improve speed, set the simulation option to `false` . You pass the `skipSimulation` flag as a query parameter to the `/quote` or `/stepTransaction` endpoint:
```json theme={"system"}
/v1/advanced/stepTransaction?skipSimulation=true
/v1/quote?skipSimulation=true&...
```
* **Note**: Disabling simulation reduces verification but improves response time. It is especially recommended when you simulate/gasEstimate the transaction either way in your system.
### Selecting Timing Strategies
LI.FI allows you to control how long it waits for results using timing strategies. Instead of only specify a timeout they are allow more advanced configuration to ensure that results of multiple tools get considered.
Timing strategies are applied in two ways when generating routes:
* `swapStepTimingStrategies`: applied when requesting same chain exchanges
* `routeTimingStrategies`: applied on the full route that can consist of multiple tools (e.g. swap+bridge)
#### Timing Strategy Format
A timing strategies consists of the following properties:
```json theme={"system"}
{
"strategy": "minWaitTime",
"minWaitTimeMs": 600,
"startingExpectedResults": 4,
"reduceEveryMs": 300
}
```
* **strategy:** Currently only `minWaitTime` exists
* **minWaitTimeMs:** Minimum time to wait for responses (e.g. 600ms)
* **startingExpectedResults:** Number of expected quotes (e.g. 4)
* **reduceEveryMs:** Frequency of reducing expectations (e.g. every 300ms)
> When this strategy is applied, we give all tool 600ms (minWaitTimeMs) to return a result. If we received 4 or more (startingExpectedResults) results during this time we return those and don't wait for other tools.\
> If less than 4 results are present we wait another 300ms and check if now at least 3 results are present.
#### Passing Strategies in API calls
In `POST /v1/advanced/routes` requests:
```json theme={"system"}
{
...
"options": {
"timing": {
"swapStepTimingStrategies": [
{
"strategy": "minWaitTime",
"minWaitTimeMs": 600,
"startingExpectedResults": 4,
"reduceEveryMs": 300
}
],
"routeTimingStrategies": [
{
"strategy": "minWaitTime",
"minWaitTimeMs": 1500,
"startingExpectedResults": 6,
"reduceEveryMs": 500
}
]
}
}
}
```
In `GET /v1/quote` requests:
```
/v1/quote?...
&swapStepTimingStrategies=minWaitTime-600-4-300
&routeTimingStrategies=minWaitTime-1500-6-500
```
The passed strategies in those examples are the default strategies we apply.
#### Timing Strategy Examples
**Maximize Results**\
Returns the best routes even if it takes longer:
```json theme={"system"}
{
"strategy": "minWaitTime",
"minWaitTimeMs": 900,
"startingExpectedResults": 5,
"reduceEveryMs": 300
}
```
**Balanced Approach**\
Waits a moderate amount of time to return a mix of speed and completeness:
```json theme={"system"}
{
"strategy": "minWaitTime",
"minWaitTimeMs": 900,
"startingExpectedResults": 1,
"reduceEveryMs": 300
}
```
**Fastest Possible Response**\
Returns first available result with no delay:
```json theme={"system"}
{
"strategy": "minWaitTime",
"minWaitTimeMs": 0,
"startingExpectedResults": 1,
"reduceEveryMs": 300
}
```
The timing strategies are only used for how long LI.FI will wait for third party providers to respond, the total response time from LI.FI API will be the sum of roundTripTime+parsing+strategies+simulation.
The handing always waits for at least one result or all external calls to fail.
# Chain Overview
Source: https://docs.li.fi/introduction/chains
A list of supported chains
LI.FI offers bridging and swaps between most EVM chains, native Bitcoin, Solana, SUI, Tron and Stellar.
The list of supported chains can also be found on our [API](/api-reference/get-information-about-all-currently-supported-chains).
# Monetizing the integration
Source: https://docs.li.fi/introduction/integrating-lifi/monetizing-integration
As an integrator, you can monetize LI.FI and collect fees from our Widget/SDK/API integration.
Any dApp that integrates LI.FI's Widget, SDK, or our API can now take fees from the volume they put through LI.FI.
## How it works
When using LI.FI Widget or calling our SDK/API to request quotes for a transaction, you can pass a fee parameter specifying the percentage fee you'd be taking from the requested transaction.
### EVM
Fees on EVM chains are forwarded directly to your configured fee wallet at transaction execution time. Fees arrive in your wallet as soon as the user's transaction is confirmed.
See [FeeForwarder](/introduction/integrating-lifi/fee-forwarder) for full details on how this works, including how to withdraw any legacy fees that were collected before this upgrade.
Only the designated fee-collection wallet receives forwarded fees. If the fee-collection wallet is updated, only fees from transactions after the update will be sent to the new wallet.
### Sui
The fees are sent to the fee wallet directly and do not need to be claimed.
### Solana
The fees are sent to the fee wallet directly and do not need to be claimed.
### Bitcoin
The fees are sent to the fee wallet directly and do not need to be claimed.
When collecting fees on Bitcoin, the transaction data must remain unaltered.
This data contains critical transfer and user refund instructions that are
specific to each bridge integration. Modifying the transaction data may lead
to irreversible loss of funds.
## How to set-up fee wallets
To set up your account and to start collecting fees, please set up your integration on [https://portal.li.fi/](https://portal.li.fi/).
Fees are collected on every chain and for every token individually. On all supported chains, fees are sent directly to your configured fee wallet at execution time. You can view your fee activity and balances in the [Partner Portal](https://portal.li.fi/).
## How to set up fee collection
The fee parameter is the percent of the integrator's fee, that is taken from every transaction. The parameter expects a float number e.g 0.02 refers to 2% of the transaction volume. The maximum fee amount should be less than 100%. Also, you should pass your custom integrator string to ensure the fees are collected to the right account. LI.FI will receive a percentage share of the collected fees depending on the use case and volume.
See examples of how to set up fee collection for different environments:
Detailed examples of how to configure fees in the LI.FI Widget, including
simple and advanced configurations.
Learn about how to configure fees and monetize your LI.FI SDK integration.
# Partnership Requests
Source: https://docs.li.fi/introduction/integrating-lifi/partnership-opportunities
Explore partnership opportunities.
## Explore Integration Opportunities
Fill out our [integration form](https://li.fi/contact-us/) to discover how you can partner with us and integrate LI.FI into your platform.
## Official Business Partnership Contacts
For any business partnership discussions, please ensure you are only communicating with the following team members from LI.FI:
**Head of DeFi Sales**
: [@Cerberus0x](https://t.me/andreilifi)
: [andrei@li.finance](mailto:andrei@li.finance)
: @0xCerberus
**Head of Integrations**
: [@sarthak\_arora](https://t.me/sarthak_arora)
: [sarthak@li.finance](mailto:sarthak@li.finance)
: @\_sarthakarora
# Universal Market Access for Digital Assets
Source: https://docs.li.fi/introduction/introduction
One Integration. Every Chain. Every Asset. Every Liquidity Source.
LI.FI is the routing and execution layer that connects any application to all on-chain liquidity across chains, bridges, DEXs, solvers, and yield protocols through a single integration.
## Why LI.FI
Blockchain infrastructure is fragmented across:
* Dozens of chains and rollups
* Multiple bridge protocols
* DEX aggregators per ecosystem
* Different token standards (USDC, USDC.e, wrapped assets, native gas tokens)
* Emerging intent and solver networks
* RWAs and hundreds of stablecoins
* Yield opportunities and money markets
* Perps DEXs & Orderbooks
Integrating these systems individually is expensive, brittle, and difficult to maintain. Data management and customer support tooling is painful on top.
**LI.FI abstracts this complexity behind a single integration.**
***
## What LI.FI Provides
LI.FI is the routing and orchestration layer that enables:
* Same-chain swaps
* Cross-chain swaps & bridging
* Cross-chain contract calls
* Multi-step transaction flows (bridge → swap → zap → deposit)
* Finding and buying perps
* Finding and zapping into yield opportunities
All through one unified API and SDK.
***
## How It Works
LI.FI sits between your application and the fragmented liquidity landscape.
Bridges, DEXs, Solvers, Perp Orderbooks, and Yield protocols into a single interface.
Token standards and chain differences so your app never has to handle them directly.
Optimal routes based on price, speed, gas cost, and execution reliability.
Transactions with built-in monitoring and fallback logic to maximise success.
You integrate once. LI.FI handles the routing logic and infrastructure maintenance.
***
## Core Capabilities
Access liquidity across major chains and ecosystems without maintaining individual integrations.
Routes are optimised for best price, gas efficiency, execution success rate, and speed.
If a provider fails or a route becomes invalid, LI.FI automatically re-routes to maximise transaction success.
Canonical token mapping prevents failures from wrapped assets and bridged variants (e.g. USDC vs USDC.e).
Build programmable multi-step flows: swap + bridge, bridge + deposit, cross-chain zaps, and full DeFi workflows.
***
## When to Use LI.FI
LI.FI is built for any application that requires reliable on-chain execution:
* Wallets & Wallet-as-a-Service
* Exchanges & trading desks
* Fintechs, neobanks & Banking-as-a-Service
* DeFi applications
* On-ramp / off-ramp providers
* AI agents executing on-chain
If your users need to move assets across chains or interact with fragmented liquidity, LI.FI removes the infrastructure burden.
***
## Without LI.FI vs. With LI.FI
| | Without LI.FI | With LI.FI |
| ----------------------- | ----------------------------------- | ------------------------------- |
| **Bridges** | Integrate one per chain pair | Covered by a single integration |
| **DEX aggregators** | Integrate one per ecosystem | Included out of the box |
| **Token mapping** | Build and maintain manually | Handled automatically |
| **Failed transactions** | Monitor and recover yourself | Built-in fallback routing |
| **New chain support** | Re-integrate each time | Continuous expansion by LI.FI |
| **Data feeds** | Aggregate and map dozens of sources | Unified data layer |
| **Maintenance** | Ongoing, high overhead | Managed by LI.FI |
| **Time to market** | Weeks to months per integration | Days |
***
## Next Steps
Integrate LI.FI into your application using the JavaScript/TypeScript SDK for full control over routes, execution, and token management.
Explore the LI.FI REST API to fetch quotes, execute cross-chain transfers, and track transaction status directly via HTTP.
Drop in the ready-made swap and bridge widget for an instant, customizable cross-chain UI in your app.
# For Bridges
Source: https://docs.li.fi/introduction/learn-more/for-bridges
Bridge integration requirements
## Overview
Integrating a new bridge involves significant effort, including backend and smart contract implementation, comprehensive testing, and understanding of specific edge cases. Given the high volume of traffic on our platform, we must also perform stress testing and optimize each integration, such as identifying data that can be cached.
Our prioritization for bridge integrations is strictly guided by the needs of our enterprise customers. Despite having over 60 employees, meeting these demands remains a challenge. As such, our capacity to accommodate additional requests is limited by these priorities.
## Strategies to Improve Your Chances of Integration
* **Demonstrate Performance Superiority**:
Provide data showing that your solution outperforms at least one of the top two bridges listed on our [dashboard](https://dune.com/lifi/lifi-bridge-and-dex-aggregation-overview).
* **Align with Existing API Standards**:
Ensure your API closely mirrors the structure of an already integrated bridge, with matching endpoints, inputs, and outputs. This alignment will facilitate a more straightforward integration process, as it would allow us to adapt existing bridge implementation files with minimal changes. A list of implemented bridges can be found here: List: Chains, Bridges, DEX Aggregators, Solvers.
# For DEXs/Aggregators/Solvers
Source: https://docs.li.fi/introduction/learn-more/for-dexs
DEX/Aggregator/Solver integration requirements
## Eligibility for Integration
### As a solver
Solvers need to have the same user experience as DEXs and have similar flow to already integrated DEXs
### As a DEX or DEX aggregator
* **Architectural Limitations**
Currently, we do not support integrations for limit orders, order books, RFQ, or intent execution due to limitations in our backend and smart contract architecture. There is no timeline for adding support for these features.
* **Existing Aggregator Integrations**
If your project is already integrated with other aggregators we support (e.g., 1inch, 0x, Paraswap, DODO, OpenOcean), we are unlikely to integrate as your liquidity source is already a part of our routing.
## DEX Integration Requirements
### UniswapV2 forks
We can efficiently integrate UniswapV2 and V3 forks within a short period of time. However, to proceed, the following information must be provided for each chain on which your DEX operates:
```javascript theme={"system"}
{
name: 'Honeyswap',
chainId: 100,
webUrl: 'https://app.honeyswap.org/',
tokenlistUrl: 'https://tokens.honeyswap.org/',
routerAddress: '0x1C232F01118CB8B424793ae03F870aa7D0ac7f77',
factoryAddress: '0xA818b4F111Ccac7AA31D0BCc0806d64F2E0737D7',
initCodeHash: '0x3f88503e8580ab941773b59034fb4b2a63e86dbc031b3633a925533ad3ed2b93',
baseTokens: [
{
address: '0x71850b7e9ee3f13ab46d67167341e4bdc905eef9',
symbol: 'HNY',
decimals: 18,,
},
...
],
},
```
Your logo:
At least 300x300px and with a white or transparent background.
### Non-UniswapV2 Forks
If your DEX is not a UniswapV2 clone, an API connection is required. Provide documentation and ensure the following technical standards:
* **The ability to handle at least 1 request per second.**
* **You have legible error messages.**
* **Your avg. API response time is \< 1 second**
Furthermore:
* **Maintain a support channel and stay responsive throughout the integration process and beyond.**
* **Stable API without breaking changes, with at least 3 weeks’ notice for updates**
# Getting Integrated by LI.FI
Source: https://docs.li.fi/introduction/learn-more/getting-integrated-by-lifi
Are you a bridge, DEX, aggregator, or solver looking to get added into LI.FI routing?
## Integration Approach and Prioritization
LI.FI is a neutral, multi-chain liquidity aggregator committed to integrating trusted and secure solutions. We frequently receive requests from decentralized exchanges, bridges, aggregators, and solvers for integration. However, each new integration adds complexity and maintenance requirements, which can impact our ability to support additional projects.
Our prioritization strategy is driven by customer demand. We focus on integrating the ecosystems, bridges, and DEXs that our customers request most frequently.
If your project is selected for integration, you will need to meet the following technical requirements for [Bridges](/introduction/learn-more/for-bridges) and [DEXs/Aggregators/Solvers](/introduction/learn-more/for-dexs).
## To get the integration started:
Please get in touch with our [team](/introduction/integrating-lifi/partnership-opportunities) for parnership opportunities.
# Token risk screening with Hypernative
Source: https://docs.li.fi/introduction/learn-more/hypernative-token-screening
How LI.FI uses Hypernative's token reputation API to detect and filter risky or unverified tokens across its token universe.
LI.FI integrates Hypernative's token reputation API into the LI.FI Core backend to automatically detect and filter risky or unverified tokens from the LI.FI token universe. This integration is part of LI.FI's Enterprise Readiness initiative and is designed to improve the safety of swaps, bridges, and on-chain interactions built on top of LI.FI.
## What Hypernative does
Hypernative provides a token screening API that accepts token identifiers and returns a verdict on whether the token should be accepted or denied. LI.FI uses this to augment its existing token validation pipeline with an additional layer of security.
Key characteristics:
* Tokens are screened via Hypernative's token reputation API endpoint.
* The primary output is a classification verdict: **Accept** or **Deny**.
* A denied verdict does not necessarily mean a token is fraudulent. Hypernative may deny tokens based on certain market conditions or other risk signals.
## How LI.FI uses Hypernative
LI.FI integrates Hypernative directly into the Core backend token validation stack, which underpins all token lists and routing decisions exposed via LI.FI's APIs and SDKs.
At a high level:
* Tokens in LI.FI's internal token collection are periodically screened via the Hypernative token reputation API.
* The screening result is stored as part of each token's metadata inside LI.FI's backend.
* That metadata is exposed downstream so integrators can filter or annotate tokens based on Hypernative's verdicts.
## Rate limits and screening strategy
Not every token query results in an immediate live Hypernative lookup. LI.FI relies on cached screening results for known tokens. Newly discovered tokens may have a short delay before a Hypernative verdict becomes available.
Treat Hypernative-related fields as "best effort" rather than guaranteed for every token at all times. Design your clients and backends accordingly.
## Exposed metadata
LI.FI will extend token metadata to include a field indicating Hypernative's verdict for a given token. Initially, only the Accept/Deny verdict will be surfaced — the reasoning behind a denial is not yet available, as LI.FI does not currently have access to that data from Hypernative's API.
As Hypernative's API capabilities expand, LI.FI expects to be able to expose the underlying reasoning in token metadata, giving integrators more context for making decisions about flagged tokens.
## Behavior in LI.FI APIs and SDKs
Initially, LI.FI will not alter the behavior of its APIs or SDKs based on Hypernative's screening results. The verdict is surfaced in token metadata and passed through to integrators as-is, so they can implement whatever custom business logic suits their use case — whether that means filtering tokens, displaying risk warnings, or blocking certain actions entirely.
## Limitations and considerations
While Hypernative significantly improves token safety, it is not a replacement for full due diligence.
Hypernative's reputation coverage may not include every token on every chain, especially newly deployed or illiquid assets.
There may be delays between token creation and the availability of a Hypernative verdict due to rate limits.
As with any reputation system, there is a non-zero risk of misclassification. LI.FI therefore maintains manual review workflows for critical cases.
Treat Hypernative metadata as one strong signal in a broader risk management strategy, alongside your own policies and any additional security tools.
## Getting support
If you have questions about interpreting Hypernative-based metadata or want to understand how to integrate this into your existing LI.FI setup:
* Reach out via your existing LI.FI partner channel or enterprise support contact.
* Refer to the [Hypernative documentation](https://docs.hypernative.xyz) for details on the underlying token reputation API.
For issues relating to missing or unexpected screening results, share the token address, chain ID, and a timestamped example request so the LI.FI team can investigate how the Hypernative integration handled that asset.
# Security and Audits
Source: https://docs.li.fi/introduction/learn-more/security-and-audits
Security is fundamental to our operations at LI.FI. Our approach combines multiple layers of defense, independent verification, and complete transparency with our users and partners.
## Security Team
We maintain a dedicated security team specializing in blockchain and DeFi security. This team is augmented by independent security researchers who provide external perspectives on potential vulnerabilities. Our objective is to identify and remediate security issues before they impact our users.
## Web2 Security Testing
We conduct annual penetration testing on our Web2 infrastructure, including APIs, web applications, and backend systems. These assessments are performed by specialized third-party security firms that provide independent evaluation of our security posture.
These tests are conducted annually and cover vulnerability scanning, manual penetration testing, authentication flows, API security, and infrastructure configuration review.
## Web3 Security
### Smart Contract Audits
All smart contracts deployed by LI.FI undergo independent security audits prior to production deployment. This applies to new contracts, upgrades, and material changes—any code going on-chain receives external audit review.
Our policy for Web3 smart contracts is that no code reaches production without independent security review. We engage multiple audit firms as different auditing teams bring varied expertise and methodologies, which strengthens our overall security assurance.
All audit reports are publicly available for review:
**[LI.FI Smart Contract Audit Reports](https://github.com/lifinance/contracts/tree/main/audit/reports)**
We maintain full transparency in our security practices. Users entrusting us with their assets can independently verify our security measures through our public audit disclosures.
### Automated Security Testing
We employ proactive Web3 automated security testing to continuously assess our smart contracts for potential vulnerabilities. Our automated testing infrastructure utilizes Olympix, which provides continuous security analysis and threat detection throughout the development and deployment lifecycle.
### Bug Bounty Program
We maintain an active bug bounty program offering rewards up to **\$1,000,000 USD** for critical vulnerabilities.
Security researchers are invited to participate through our program:
**[LI.FI Bug Bounty (Cantina)](https://cantina.xyz/bounties/260585d8-a3e8-4d70-8077-b6f3f5f0391b)**
The program encompasses smart contract vulnerabilities and other critical security issues. We have found that collaboration with the security research community provides valuable external scrutiny and strengthens our security posture.
### Smart Contract Monitoring
Beyond audits, we employ real-time monitoring of our smart contracts. Our internal monitoring systems track for anomalous patterns and suspicious activity. We also maintain partnerships with firms that provide independent monitoring capabilities—Hexagate being one example—which provides an additional layer of oversight.
Throughout 2024, we have expanded our monitoring infrastructure to include automated threat detection, anomaly detection using baseline behavioral models, transaction analysis for potential exploits, and emergency pause mechanisms for high-risk scenarios.
We continue to enhance our automated response capabilities, including implementing automated pause features that can activate immediately when specific risk thresholds are exceeded.
## Incident Response
We maintain established protocols for security incident management, including defined escalation procedures, communication frameworks for affected stakeholders, and post-incident analysis to implement corrective measures.
## Reporting Security Issues
We encourage responsible disclosure of security vulnerabilities. Security researchers and users who identify potential security issues can contact us through our dedicated channel:
**Security Contact**: [https://help.li.fi/](https://help.li.fi/)
All vulnerabilities may qualify for rewards through our [bug bounty program](https://cantina.xyz/bounties/260585d8-a3e8-4d70-8077-b6f3f5f0391b), with awards up to \$1,000,000 USD. All security reports are reviewed and addressed according to established protocols.
## Standards and Compliance
Our security practices align with recognized industry standards, including smart contract security best practices, OWASP guidelines for web application security, and secure development lifecycle methodologies. Our team receives ongoing security training to maintain current knowledge of evolving threat landscapes.
We maintain two non-negotiable commitments: mandatory independent audits for all smart contract deployments, and public disclosure of all audit reports.
## Our Approach
Security in the DeFi ecosystem requires continuous evolution. As the threat landscape changes, our security measures adapt accordingly. We maintain ongoing evaluation of new security tools, enhanced processes, and improved defensive capabilities.
Our security philosophy centers on defense in depth combined with operational transparency. We employ multiple layers of security controls, independent verification mechanisms, and public disclosure of security practices. This approach forms the foundation of trust with our users and partners.
***
*Last Updated: October 2025*
# Bitcoin Providers
Source: https://docs.li.fi/introduction/lifi-architecture/bitcoin-overview
Bitcoin Architecture
LI.FI offers seamless native Bitcoin bridging and swaps between native Bitcoin, major EVM chains and Solana.
## Requesting a quote
A quote for Bitcoin can be requested using the same endpoints as EVM. The only difference will be the transaction data when source chain is Bitcoin.
`fromAddress` when source chain is Bitcoin supports flexible formats for maximum convenience:
* **Single Bitcoin address**: `bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh`
* **Multiple addresses** (semicolon-separated): `address1;address2;address3`
* **Extended public key (xpub)**: `xpub6CUG...`
* **Multiple xpubs**: `xpub1,xpub2`
* **Combination of xpubs and addresses**: `xpub1;address1;address2`
**UTXO Collection**: The system builds transaction inputs by collecting UTXOs from:
* Provided addresses directly, OR
* Addresses derived from the provided xpub(s) that contain UTXOs
The `fromAddress` must have enough UTXOs to cover the requested transaction amount, otherwise no quote will be returned. UTXOs are checked and combined in the most efficient way to build the transaction.
**Transaction Signing**: All input addresses (wallets) that contribute UTXOs to the transaction must sign the PSBT. This ensures that funds from each participating address are properly authorized.
**Refund Address**: The third output in the PSBT is a refund/change output that will be returned to the address that contributed the most significant input to the transaction.
## Executing a transaction
### Transaction data
After retrieving the quote, the funds need to be sent to the BTC vault address provided in the response, along with a memo.
* Memo Functionality: Similar to Thorchain, LI.FI uses memos for BTC to EVM swaps. The memo in the BTC transaction specifies the swap's destination address and chain.
* Transaction Handling: The transaction that leaves BTC and goes to EVM needs to be sent to an EVM address. The memo ensures that the swap details are correctly processed by the validators.
NOTE: Only send transactions in a timely manner (\~30min). It is always
recommended to request an up-to-date quote to ensure to get the latest
information.
**Risk of modifying Bitcoin transaction data**
Modifying PSBT or raw Bitcoin transaction data received from our API (for
example removing outputs, changing amounts, or editing opcodes/scripts) can
invalidate signatures or spending conditions and lead to irreversible loss of
funds.
Do not alter PSBTs unless you are an expert and have explicitly confirmed with
us the modification you intend to make.
`data` in transactionRequest object is PSBT (partially signed bitcoin
transaction) and memo needs to be retrieved from PSBT by decoding it.
### Retrieving memo from PSBT
PSBT can be decoded using any library like [bitcoinjs](https://github.com/bitcoinjs/bitcoinjs-lib) or [scure-btc-signer](https://github.com/paulmillr/scure-btc-signer).
Here's an example using `bitcoinjs`
```typescript theme={"system"}
const psbtHex = transactionRequest.data;
// Create PSBT object from hex data
const psbt = Psbt.fromHex(psbtHex, { network: networks.bitcoin });
// Find OP_RETURN output in the transaction outputs
const opReturnOutput = psbt.txOutputs.find((output) => {
if (output?.script) {
// Convert the output script to hex string for checking
const scriptHex = Array.from(output.script)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
// Check if script starts with OP_RETURN opcode (0x6a)
return scriptHex.startsWith("6a");
}
return false;
});
// If an OP_RETURN output exists, decode its script data as UTF-8 text (memo)
const memo = opReturnOutput?.script
? new TextDecoder().decode(
new Uint8Array(Object.values(opReturnOutput.script))
)
: undefined;
```
# Smart Contract Addresses
Source: https://docs.li.fi/introduction/lifi-architecture/smart-contract-addresses
The LI.FI Diamond entryway contract address is `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` on most supported networks. Please note, on some networks the address is different. You can find the ABI on [Github](https://github.com/lifinance/lifi-contract-types/blob/main/dist/diamond.json).
More information and open-source code for each facet contract can be found on our [GitHub](https://github.com/lifinance/contracts/blob/main/docs/README.md).
Contract address of each facet and deployment information can be found in our [GitHub repo](https://github.com/lifinance/contracts/tree/main/deployments).
## Stellar
Stellar does not appear in the table above. On Stellar, LI.FI uses a single Soroban contract instead of the Diamond.
| Field | Value |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Chain ID | `1201081091099710` |
| Contract | `CDCNXZHYWRDHNAQEY5EX3WCF7BCWVJBS64ZBVUGRTQBFTHUKG6NAXFTR` |
| Explorer | [View on Stellar Expert](https://stellar.expert/explorer/public/contract/CDCNXZHYWRDHNAQEY5EX3WCF7BCWVJBS64ZBVUGRTQBFTHUKG6NAXFTR) |
Do not use this address as an approval spender. Approve the `estimate.approvalAddress` of the included step that pulls the funds. See [Token approvals](/introduction/lifi-architecture/stellar-overview#token-approvals) on the Stellar Providers page.
# Smart Contract Architecture
Source: https://docs.li.fi/introduction/lifi-architecture/smart-contract-overview
## Architecture
The LI.FI Contract is built using the **EIP-2535** (Multi-facet Proxy) standard. The contract logic lives behind a single contract that in turn uses `DELEGATECALL` to call facet contracts that contain the business logic.
All business logic is built using facet contracts that live in `src/Facets`.
For more information on EIP-2535 you can view the entire EIP [here](https://eips.ethereum.org/EIPS/eip-2535).
## Contract Flow
A basic example would be a user bridging from one chain to another using Stargate Protocol. The user would interact with the LI.FIDiamond contract which would pass the Stargate-specific call to the StargateV2Facet which then passes required calls + parameters to Stargate's contracts.
The basic flow is illustrated below.
## Diamond Helper Contracts
The LI.FI Diamond contract is deployed along with some helper contracts that facilitate things like upgrading facet contracts, look-ups for methods on facet contracts, ownership checking and withdrawals of funds. For specific details please check out [EIP-2535](https://eips.ethereum.org/EIPS/eip-2535).
# Solana Providers
Source: https://docs.li.fi/introduction/lifi-architecture/solana-overview
## Introduction
LI.FI offers seamless integration with the Solana blockchain through multiple bridges and exchanges.
We only support single step transactions per ecosystem at this point. Currently expanding to support two step transactions across the two ecosystems.
The native SOL is represented using the System Program address `11111111111111111111111111111111` when making requests to the backend. Native Solana doesn’t really have an address, so this is a representation specific to our system.
Wrapped Solana should use the wSOL address `So11111111111111111111111111111111111111112`
Solana chainID in LI.FI BE is `1151111081099710`.
## Mayan integration
Enables users to perform blue chip coins transfer between Solana and EVM chains supported by Mayan. This integration also supports the native USDC bridge CCTP.
Mayan is split into three different keys by provider:
`mayan` - Swift
`mayanMCTP` - CCTP
`mayanWH` - Wormhole
***
## AllBridge integration
Allbridge enables users to perform cost efficient stable coin transfers between Solana (USDC) and other supported Ethereum Virtual Machine (EVM) compatible chains, including ETH, POL, BSC (only USDT), OPT, AVA, ARB, and BAS.
## Jupiter integration
Enables users to perform Solana swaps of wide range of tokens.
Only Jupiter verified tokens are supported by LI.FI.
## Architectural differences with EVM
Solana works the same way in the LI.FI BE as any other chain so quote requests are the same except for token addresses and chainID.
Onchain tx submission is different from EVM because of fundamental differences between EVM and SVM. More detailed information and tx examples can be found on the [solana example page](/introduction/user-flows-and-examples/solana-tx-execution).
# Stellar Providers
Source: https://docs.li.fi/introduction/lifi-architecture/stellar-overview
Stellar Architecture
LI.FI offers same-chain swaps on Stellar, bridging into Stellar from EVM chains, Solana, Bitcoin and Tron, and bridging USDC out of Stellar to major EVM chains.
Stellar chainId in LI.FI is `1201081091099710`. The chain key is `xlm` and the chain type is `STL`.
`GET /v1/chains` returns EVM chains only by default. Pass `chainTypes` to include Stellar, for example `GET /v1/chains?chainTypes=EVM,STL`.
All Stellar tokens, including native XLM, are addressed by their Soroban contract (Stellar Asset Contract, SAC) `C...` address. Native XLM is represented as `CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA` (7 decimals).
Stellar token addresses are case-sensitive. Always pass them exactly as returned by the API.
## Requesting a quote
A quote for Stellar can be requested using the same endpoints as EVM. Only the chain ids, token addresses and the transaction data differ.
`fromAddress` when the source chain is Stellar must be an ed25519 `G...` account address. Muxed `M...` addresses and Soroban contract `C...` addresses are not supported as senders.
The LI.FI SDK also resolves SEP-2 federation addresses (`name*domain.com`) to a `G...` address before it builds a request. It refuses a federation record that carries a memo, because a route request cannot carry one.
`toAddress` when Stellar is the destination chain accepts a `G...` account address. When bridging out of Stellar, `toAddress` is a regular address on the destination chain (for example an EVM address).
The sender account must exist on the network. Stellar accounts only exist once they are funded, so no quote is returned for an unfunded sender account.
## Token approvals
Not every Stellar route requires a token approval. Depending on the tool, the executing contract either moves funds directly under the authorization carried by your signed transaction, or pulls them through a token allowance.
The decision is made per included step, never from the route-level estimate. Scan the quote's `includedSteps` and treat a step as needing an approval when both of these hold:
* `estimate.skipApproval` is not `true`.
* `estimate.approvalAddress` is a Soroban contract (`C...`) address.
For that step, approve its `estimate.approvalAddress` as the spender, its `action.fromToken.address` as the token, and at least its `estimate.fromAmount` as the amount. A Stellar route pulls at most one allowance today, so the first step that meets both conditions is the one to approve.
Soroban token allowances always have an expiration ledger (a mandatory TTL). Unlimited or indefinite approvals do not exist on Stellar — every allowance eventually expires and will need to be renewed.
Because allowances are never indefinite, it is also safe to approve a buffer: we recommend approving about 10% on top of the required amount so the allowance still covers positive slippage from a preceding swap.
**Do not approve the route-level `approvalAddress`**
The `estimate.approvalAddress` on the top-level step is a summary. When no included step pulls funds it falls back to a placeholder — a `G...` account, or the EVM diamond address — and neither can call `transfer_from`. Approving it grants an allowance that nothing ever consumes.
Take the token from the included step too, not from the route. A route that swaps and then bridges through CCTP needs the allowance on the intermediate asset the swap produces, not on the route's `fromToken`.
**Approve before requesting transaction data**
An approval is a separate Stellar transaction that consumes your account's sequence number, and the transaction envelope LI.FI returns is built against your account's current sequence number.
Submit the approval and wait for it to be confirmed before requesting the transaction data. An approval submitted after you already hold an envelope invalidates that envelope through the sequence advance, and an unconfirmed approval still in flight does the same.
## Soroswap integration
Registered as the exchange key `soroswap`. Enables users to perform same-chain swaps on Stellar. Soroswap aggregates liquidity across Stellar AMMs and splits the trade across venues for the best output.
For same-chain swaps on Stellar, `toAddress` must equal `fromAddress`. The router measures the swap output through the sender's balance change, so no transaction is returned for a different recipient.
## Polymer integration
Polymer is registered as two tools, `polymer` ("Polymer (Fast)") and `polymerStandard` ("Polymer (Standard)"). Both bridge USDC into Stellar from EVM chains using Circle's Cross-Chain Transfer Protocol (CCTP). They differ in the Circle attestation they settle on: `polymer` is faster, `polymerStandard` is cheaper.
Receiving a non-XLM asset on Stellar requires the recipient account to hold a trustline for that asset. If the recipient has no trustline, no quote is returned.
## Circle CCTP integration
Registered as the tool key `cctp` ("Circle CCTP"). It is the only bridge that routes out of Stellar today. It carries USDC to 10 chains: Ethereum, Arbitrum, Avalanche, Base, Injective, Monad, OP Mainnet, Polygon, Unichain, and World Chain.
A route out of Stellar that starts from a non-USDC asset swaps on Soroswap first, then bridges the resulting USDC. That route pulls a token allowance, and the allowance belongs on the intermediate USDC, not on the route's `fromToken`. See [Token approvals](#token-approvals).
## NearIntents integration
Registered as the tool key `near` ("NearIntents"). Enables users to bridge XLM and USDC into Stellar from other supported ecosystems, including EVM chains, Solana, Bitcoin, and Tron.
## Executing a transaction
### Transaction data
`transactionRequest.data` is a base64-encoded, unsigned Stellar `TransactionEnvelope` XDR. The envelope is built against a fresh sequence number of the sender account, and the network fee and Soroban resources are already set from simulation.
To execute the transaction:
1. Request the transaction data through the regular quote flow.
2. Sign the envelope with the sender account's key.
3. Submit the signed transaction to the Stellar network.
**Envelope validity window**
The envelope is only valid for about 5 minutes (enforced through timebounds) and becomes invalid as soon as the sender account's sequence number advances.
* Sign and submit promptly after receiving the transaction data.
* Request a new transaction if the envelope is stale.
* Do not modify the envelope. It is assembled from simulation, and any change can invalidate it.
### Gas
The estimated network fee is returned in the step's `estimate.gasCosts` in stroops (XLM, 7 decimals).
### Account reserve
Every Stellar account must hold a minimum XLM balance, the account reserve, and cannot spend it. Two consequences follow:
* A route that starts on Stellar cannot spend the sender's whole XLM balance.
* A route that bridges into Stellar delivers XLM to the receiver and holds back a buffer for the account reserve and for the destination step fees.
## Status tracking
Transaction status can be tracked using the regular [status tracking](/introduction/user-flows-and-examples/status-tracking) flow.
When polling `/status` for a transaction that originates on Stellar, always pass `fromChain=XLM` (or the Stellar chain id `1201081091099710`). Stellar transaction hashes are 64-character hex strings and are otherwise indistinguishable from Bitcoin transaction hashes.
# Sui Providers
Source: https://docs.li.fi/introduction/lifi-architecture/sui-overview
Sui Architecture
LI.FI offers seamless same chain swaps on SUI and bridging between SUI and major EVM chains and Solana.
# System Overview
Source: https://docs.li.fi/introduction/lifi-architecture/system-overview
LI.FI architectural overview
## Introduction
LI.FI is a multi-chain liquidity aggregation platform that connects decentralized applications (dApps) with various liquidity sources, including bridges, decentralized exchanges (DEXs), and solvers. This architecture enables seamless cross-chain and same-chain trading by facilitating price discovery, smart order routing, and efficient execution.
***
## Key Components of LI.FI Architecture
### 1. **dApp Interface (Integrators)**
* **Function**: What the end-user interacts with. dApps initiate quote requests and route selection to LI.FI's API.
* **Process**: A user sends a request from the dApp for the best trading route or quote, which is forwarded to LI.FI API for processing. Once the optimal route is selected, the dApp submits a transaction to execute the trade.
### 2. **LI.FI API - Aggregation and Routing Layer**
* **Purpose**: This off-chain layer performs core price discovery and smart order routing by interfacing with various liquidity sources.
* **Functionality**:
* **Fetch Pricing**: LI.FI API retrieves quotes from multiple sources, including bridges, DEXs, and solvers, to determine the best price and route.
* **Return Quote**: Once the optimal route is identified, LI.FI API returns the quote to the dApp.
* **Order Routing**: The dApp submits the transaction with the selected route, which LI.FI API processes by routing it to the LI.FI Diamond Contract on-chain.
### 3. **LI.FI Diamond Contract**
* **Function**: Acts as the primary on-chain entry point, handling the execution of transactions based on the chosen route from LI.FI API.
* **Role**:
* Routes the transaction to the appropriate **facet contract** (bridge, DEX, or solver) based on the chosen liquidity source.
* Acts as the router for on-chain executions, allowing modular connections to different liquidity sources.
### 4. **LI.FI Facet Contracts**
Specialized on-chain contracts that interface with respective liquidity sources:
* **Bridge Facet Contracts**
* Route transactions to specific bridge contracts (e.g., Bridge A or Bridge B) for cross-chain transfers.
* Ensure bridge compatibility and secure asset transfer across blockchains.
* **DEX Facet Contracts**
* Route transactions to specific DEX contracts for same-chain swaps.
* Optimize execution based on DEX-specific parameters for efficient liquidity utilization.
* **Solver Facet Contracts**
* Route transactions to solver contracts for accessing extended liquidity sources.
* Enable advanced routing and pricing calculations based on solver protocols.
### 5. **Bridge/DEX/Solver Contracts**
Final execution of trades occurs on the blockchain network through interactions with selected liquidity providers (bridge, DEX, or solver contracts).
***
## End-to-End Order Flow
1. **Initiate Request**
* A user initiates a quote request from the dApp for a multi-chain or same-chain trade.
2. **Quote and Route Discovery**
* LI.FI BE receives the request and queries bridges, DEX aggregators, and solvers to gather pricing data and route options.
* The optimal route is determined and returned to the dApp.
3. **Transaction Submission**
* The user selects the best route in the dApp, which submits the transaction to LI.FI Diamond contract with the selected quote data.
4. **On-Chain Execution**
* LI.FI Diamond contract forwards the transaction to the respective LI.FI Facet contract for execution.
* The facet contract executes the transaction with the specific bridge, DEX, or solver on-chain contract, finalizing the trade or transfer.
5. **Output Return**
* Once the transaction is complete, the resulting assets are returned to the user.
# Tron Providers
Source: https://docs.li.fi/introduction/lifi-architecture/tron-overview
Tron Architecture
LI.FI offers bridging between Tron and other supported ecosystems, such as EVM chains and Solana.
Tron chainId in LI.FI is `728126428` (chain key `TRN`).
Native TRX is represented as `T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb` (6 decimals). TRC20 tokens are addressed by their Base58 contract address (`T...`).
Tron addresses are Base58 and case-sensitive. Always pass them exactly as returned by the API. The hex form (`41...`) is not accepted.
Same-chain swaps on Tron are not supported yet. Quotes with Tron as the source chain are always cross-chain.
## Requesting a quote
A quote for Tron can be requested using the same endpoints as EVM. Only the chain ids, token addresses and the transaction data differ.
`fromAddress` when the source chain is Tron must be a Base58 `T...` account address.
`toAddress` when Tron is the destination chain is a Base58 `T...` address. When bridging out of Tron, `toAddress` is a regular address on the destination chain (for example an EVM address).
## Token approvals
Sending a TRC20 token requires a token allowance: the executing contract pulls the tokens from your account, so give an allowance to the step's `estimate.approvalAddress` for at least `fromAmount` before executing. Native TRX is passed as the transaction's call value and needs no approval.
To check whether a route needs an approval, scan the quote's `includedSteps`: whenever a step's `estimate.skipApproval` is not `true`, approve that step's `estimate.approvalAddress`.
An approval is a separate Tron transaction (TRC20 `approve(spender, amount)`). Submit it and wait for it to be confirmed before broadcasting the LI.FI transaction.
## Executing a transaction
### Transaction data
`transactionRequest` carries the unsigned Tron transaction in two forms:
* `data` — the hex-encoded protobuf `raw_data` of an unsigned `TriggerSmartContract` transaction (the `raw_data_hex` of a Tron node), prefixed with `0x`.
* `customData.tronTransaction` — the same transaction as a TronWeb transaction object: `{ visible, txID, raw_data, raw_data_hex }`. `customData.contractType` is `TriggerSmartContract`.
Inside the object, `raw_data_hex` and `txID` are plain lowercase hex without the `0x` prefix, so `data` equals `0x` + `raw_data_hex`.
`to` is the LI.FI contract on Tron, `value` is the TRX call value in SUN and `gasLimit` is the transaction's `fee_limit` in SUN (see [Gas](#gas)). Both are hex-encoded strings, matching `raw_data.fee_limit` in the transaction object.
To execute the transaction with [TronWeb](https://tronweb.network/):
1. Request the transaction data through the regular quote flow.
2. Sign `customData.tronTransaction` with the sender account's key.
3. Broadcast the signed transaction to the Tron network.
```typescript theme={"system"}
import { TronWeb } from "tronweb";
const tronWeb = new TronWeb({
fullHost: "https://api.trongrid.io",
privateKey: SENDER_PRIVATE_KEY,
});
const { transactionRequest } = quote;
const transaction = transactionRequest.customData.tronTransaction;
const signedTransaction = await tronWeb.trx.sign(transaction);
const { result, txid } = await tronWeb.trx.sendRawTransaction(signedTransaction);
```
`data` alone is enough to rebuild the transaction without TronWeb: the transaction id is the SHA-256 hash of the `raw_data` bytes (strip the `0x` prefix first), and the signature is made over that id. `customData.tronTransaction` saves you the deserialization.
**Transaction validity window**
The transaction is valid until `raw_data.expiration`, about 10 minutes after it was built. The network rejects a transaction broadcast after its expiration.
* Sign and broadcast promptly after receiving the transaction data.
* Request a new transaction if the one you hold is stale.
* If signing takes longer (for example with an MPC signer), extend the expiration **before** signing. TronWeb recomputes `txID` and `raw_data_hex` for you:
```typescript theme={"system"}
const extendedTransaction = await tronWeb.transactionBuilder.extendExpiration(
transaction,
extraSeconds
);
const signedTransaction = await tronWeb.trx.sign(extendedTransaction);
```
The expiration cannot be more than 24 hours after the current block. Do not modify any other field of `raw_data`.
### Gas
Tron charges bandwidth and energy. The estimated costs are returned in the step's `estimate.gasCosts` as two `SEND` entries in SUN (TRX, 6 decimals): one for bandwidth and one for energy. Resources staked by the sender account are consumed first; the remainder is burned in TRX.
`transactionRequest.gasLimit` is the transaction's `fee_limit`: the maximum TRX the transaction is allowed to burn for energy. It is a safety ceiling, not the expected cost.
## Status tracking
Transaction status can be tracked using the regular [status tracking](/introduction/user-flows-and-examples/status-tracking) flow.
When polling `/status` for a transaction that originates on Tron, always pass `fromChain=TRN` (or the Tron chain id `728126428`). Tron transaction hashes are 64-character hex strings and are otherwise indistinguishable from Bitcoin transaction hashes.
# Product Stack
Source: https://docs.li.fi/introduction/product-stack
The layered architecture behind LI.FI's universal market access.
LI.FI is built in layers. Each layer does a distinct job, and together they make it possible to move, swap, stake, and deploy assets across any chain in a single step.
Plug-and-play swap and bridge interfaces any app can embed. Works out of the box, and fully customisable to match your product, from layout and branding to wallet handling. Compatible with React, Next.js, Vue, Svelte, and any modern web stack.
[Explore the Widget →](/widget/overview)
Knows where assets are, what they cost, and what yields are available across every supported chain. Finds the best route to execute any action and combines multiple steps into a single transaction where possible.
Powered by [LI.FI Composer](/composer/overview). Goes beyond swapping and bridging. Users can deposit into yield vaults, stake into protocols, and chain together multiple actions across chains, all in one transaction.
Most comprehensive meta-level aggregation in the market. Connects to dozens of DEXs and bridges across every major chain. Users get the best available price without needing to know which protocol powers it. Edge cases like wrapped token variants and non-EVM differences are handled quietly in the background.
A competitive network of solvers and market makers who bid to fill user trades. More competition means better prices and faster execution. LI.FI's own aggregation layer fills any gaps they leave. Solvers and market makers can plug in with their own liquidity and tap into LI.FI's order flow.
[Sign up as a Solver →](https://intents.li.fi/)
LI.FI's own solver. Ensures trades always execute, even when third-party solvers can't fill. Also helps enterprises launch and distribute tokenised assets (RWAs, stablecoins) across LI.FI's network at scale.
A DEX aggregator built for new and emerging chains. Gives users best-price swaps across every DEX on a chain from day one, and gives new chains instant access to competitive liquidity, including from private market makers.
# Solana Ecosystem Coverage
Source: https://docs.li.fi/introduction/solana-ecosystem
Overview of LI.FI's Solana ecosystem coverage including bridges, DEXs, and key features
LI.FI offers comprehensive Solana support with 12 integrated bridges, 4 meta-aggregator DEX integrations, and seamless interoperability between Solana and 40+ chains.
For technical implementation details, see the [Solana Providers](/introduction/lifi-architecture/solana-overview) page. For transaction examples, see [Solana Transaction Example](/introduction/user-flows-and-examples/solana-tx-execution).
***
## Bridges
LI.FI integrates 12 bridges for Solana connectivity:
Near Intents, Mayan Swift, Mayan CCTP, Mayan fastMCTP, MayanWH, Allbridge, Relay, Gaszip, Across V4, Glacis, Chainflip, Unit
* **Glacis (LI.FI exclusive)**: Interop token mint/burn aggregator with 1:1 native bridging (OFT, NTT, etc.)
* **Intent-based bridges**: Mayan Swift, Across, Relay, Gaszip, Near Intents
* **Unit**: Native Solana token deposits with abstracted deposit addresses
***
## DEXs
LI.FI aggregates liquidity across 4 major Solana meta-aggregators:
Jupiter, DFlow, Titan, OKX
This provides coverage of every major underlying DEX including Raydium, Orca, Meteora, Phoenix, Lifinity, and PropAMMs, ensuring best pricing across all available liquidity sources.
***
## Key Features
### Gasless Transactions
LI.FI supports gasless Solana transactions through `svmSponsor`:
* **Swaps**: Fully sponsored, no SOL needed
* **Bridges (no account creation)**: Sponsored for Gaszip, Unit, and Across
* **Bridges (with account creation)**: Coming soon
### Relayer Costs in Input Token
Users can pay relayer fees using their input token, removing the need to hold native SOL for relay costs.
### Refuel
LI.Fuel via Allbridge enables users to receive destination gas tokens as part of a bridge transaction.
### Destination Swaps
Destination swaps are available through select bridges, enabling one-click bridge + swap flows.
### Jito Bundles
Jito Bundle functionality is available for partners, enabling more complex swap combinations with zero dust.
***
## Major Integrators
Phantom, Kamino Finance, MetaMask, Binance Wallet, Titan, BackPack, KuCoin, AliPay, Robinhood, Crypto.com, OneKey, Hyperliquid
***
## Integration Options
LI.FI's Solana support is available through all standard integration methods:
* [API](/api-reference)
* [SDK](/sdk/overview)
* [Widget](/widget/overview)
# Stellar Ecosystem Coverage
Source: https://docs.li.fi/introduction/stellar-ecosystem
Overview of LI.FI's Stellar ecosystem coverage including bridges, DEXs, and key features
LI.FI offers Stellar support with 4 integrated bridges and interoperability between Stellar and 26 chains.
For technical implementation details, see the [Stellar Providers](/introduction/lifi-architecture/stellar-overview) page. For the full list of supported chains, see [Chain Overview](/introduction/chains).
***
## Bridges
LI.FI integrates 4 bridges for Stellar connectivity:
Polymer (Fast), Polymer (Standard), Circle CCTP, NearIntents
* **Polymer (Fast)**: USDC transfers into Stellar from 19 EVM chains, settled on Circle's fast attestation.
* **Polymer (Standard)**: The same 19 EVM chains into Stellar, settled on Circle's standard attestation for a lower fee.
* **Circle CCTP**: USDC transfers out of Stellar to 10 EVM chains, including Ethereum, Arbitrum, Base, Polygon, and Avalanche.
* **NearIntents**: Intent-based routing into Stellar from 14 chains, including Ethereum, Solana, Bitcoin, Tron, and major L2s.
Each bridge routes in one direction today. Polymer and NearIntents route **into** Stellar. Circle CCTP routes **out of** Stellar. Same-chain swaps on Stellar are available through Soroswap, and a Soroswap swap can precede a CCTP transfer out, for example XLM to USDC to Base.
***
## DEXs
LI.FI integrates 1 DEX aggregator on Stellar:
* **Soroswap**: Aggregates liquidity across Stellar AMMs and splits a trade across venues for the best output. It powers same-chain swaps on Stellar.
***
## Key Features
### Soroban Asset Contract addressing
Every Stellar token, native XLM included, is addressed by its Stellar Asset Contract (SAC) `C...` address. One addressing scheme covers native XLM, wrapped classic assets, and Soroban-native tokens.
### Expiring token allowances
Soroban allowances always carry an expiration ledger. Indefinite approvals do not exist on Stellar, so a route grants only the allowance it needs and that allowance expires by itself.
### Federation addresses
The LI.FI SDK resolves SEP-2 federation addresses (`name*domain.com`) to a `G...` account address before it builds a request.
### Account reserve handling
A route that bridges into Stellar delivers XLM to the receiver and holds back a buffer for the account reserve and for the destination step fees.
### Wallet coverage
The LI.FI Widget connects Freighter, xBull, Lobstr, Rabet, Hana, Klever, OneKey, and Bitget through the [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit).
***
## Availability
Stellar support is available on the LI.FI API, the [SDK](/sdk/overview), and the [Widget](/widget/overview). Stellar routing runs on the public Stellar network only.
***
## Integration Options
LI.FI's Stellar support is available through all standard integration methods:
* [API](/api-reference)
* [SDK](/sdk/overview)
* [Widget](/widget/overview)
# EVM Providers
Source: https://docs.li.fi/introduction/tools
A list of providers/tools LI.FI aggregates
The list of supported tools can also be found on our [API](/api-reference/get-available-bridges-and-exchanges).
# TRON Ecosystem Coverage
Source: https://docs.li.fi/introduction/tron-ecosystem
Overview of LI.FI's TRON ecosystem coverage including bridges and cross-chain connectivity
LI.FI offers TRON support with 4 integrated bridges and seamless interoperability between TRON and 65 chains.
For the full list of supported chains, see [Chain Overview](/introduction/chains). For bridge and exchange tooling, see [Tools](/introduction/tools).
***
## Bridges
LI.FI integrates 4 bridges for TRON connectivity:
GasZip, Symbiosis, Near Intents, Allbridge
* **GasZip**: Intent-based bridging to 60 chains including major EVM networks and Solana
* **Symbiosis**: Cross-chain liquidity protocol connecting TRON to 22 chains across EVM ecosystems
* **Near Intents**: Intent-based routing between TRON and 13 chains including Ethereum, Solana, Bitcoin, and major L2s
* **Allbridge**: Multi-chain bridge connecting TRON to 12 chains including Ethereum, Solana, Sui, and major L2s
***
## DEXs
LI.FI does not currently list native TRON DEX aggregators in the tools API. On-chain swap routing on TRON is primarily available through cross-chain bridge flows.
***
## Key Features
### Cross-Chain Bridging
TRON is supported as a first-class TVM chain (chain ID `728126428`) with routing through GasZip, Symbiosis, Near Intents, and Allbridge.
### Intent-Based Routing
Near Intents enables intent-based quotes between TRON and major ecosystems including Ethereum, Solana, Bitcoin, and popular L2s.
### Broad Chain Connectivity
Bridge integrations connect TRON to 65 unique destination and source chains across EVM, Solana, Bitcoin, and Sui.
***
## Availability
TRON support is live on the LI.FI API and [Jumper](https://jumper.exchange).
***
## Integration Options
LI.FI's TRON support is available through all standard integration methods:
* [API](/api-reference)
* [SDK](/sdk/overview)
* [Widget](/widget/overview)
For the Tron transaction format, token approvals, execution and status tracking, see [Tron Providers](/introduction/lifi-architecture/tron-overview).
# Bitcoin Transaction Example
Source: https://docs.li.fi/introduction/user-flows-and-examples/bitcoin-tx-example
## Requesting Bitcoin-specific information via the API
### Chains
```JS theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/chains?chainTypes=UTXO' \
--header 'accept: application/json'
```
### Tools
```JS theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/tools?chains=20000000000001' \
--header 'accept: application/json'
```
### Tokens
```JS theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/tokens?chains=BTC' \
--header 'accept: application/json'
```
### Token details
```JS theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/token?chain=20000000000001&token=bitcoin' \
--header 'accept: application/json'
```
## Requesting a Quote
### Bitcoin to Ethereum
The quote and advanced route calls target the same transfer but differ in shape: `GET /quote` accepts query parameters such as `fromChain` and `fromToken`, whereas `POST /advanced/routes` expects a JSON body with fields like `fromChainId` and `fromTokenAddress`.
```javascript /quote theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/quote?fromAddress=bc1qmdpxhzarlxrygtvlxrkkl0eqguszkzqdgg4py5&fromAmount=500000&fromChain=BTC&fromToken=bitcoin&toAddress=0x39333638696578786b61393361726b63717a6773&toChain=1&toToken=0x0000000000000000000000000000000000000000' \
--header 'accept: application/json'
```
```javascript /advanced/routes theme={"system"}
curl --request POST \
--url https://li.quest/v1/advanced/routes \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"toTokenAddress": "0x0000000000000000000000000000000000000000",
"fromTokenAddress": "bitcoin",
"fromChainId": 20000000000001,
"fromAmount": "10000000",
"toChainId": 1,
"fromAddress": "YOUR_BTC_WALLET",
"toAddress": "YOUR_EVM_WALLET"
}
'
```
### Ethereum to Bitcoin
```javascript /quote theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/quote?fromChain=1&toChain=20000000000001&fromToken=0x0000000000000000000000000000000000000000&toToken=bitcoin&fromAddress=0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0&toAddress=bc1qmdpxhzarlxrygtvlxrkkl0eqguszkzqdgg4py5&fromAmount=500000000000000000' \
--header 'accept: application/json'
```
```javascript /advanced/routes theme={"system"}
curl --request POST \
--url https://li.quest/v1/advanced/routes \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"toTokenAddress": "bitcoin",
"fromTokenAddress": "0x0000000000000000000000000000000000000000",
"fromChainId": 1,
"fromAmount": "500000000000000000",
"toChainId": 20000000000001,
"fromAddress": "YOUR_EVM_WALLET",
"toAddress": "YOUR_BTC_WALLET"
}
'
```
## Executing the transaction
### Building Custom Bitcoin Transactions
Partners may want to build custom Bitcoin transactions to select specific UTXOs for various reasons such as coin control, UTXO consolidation, or fee optimization.
**Requirements when building custom transactions:**
* Preserve the exact output structure and order from the API response
* Ensure selected UTXOs have sufficient value to cover:
* Bridge deposit amount (1st output)
* Refund output if required by the bridge (3rd output, must be above dust threshold)
* Integrator fees (additional outputs)
**Critical:** The output order and structure cannot be modified. Deviating from the API response structure will result in stuck or failed transactions.
### Transaction Data
**BTC to Ethereum, Avalanche, or BNB Smart Chain (BSC)**
After retrieving the quote, the funds need to be sent to the BTC vault address provided in the response, along with a memo.
**Memo Functionality:** Similar to Thorchain, LI.FI uses memos for BTC to EVM swaps. Depending on the tool, the memo in the BTC transaction specifies the bridge-specific tx data to execute and internal LI.FI details for the tracking.
**Transaction Handling:** The transaction that leaves BTC and goes to EVM needs to be sent to an EVM address. The memo ensures that the swap details are correctly processed by the validators.
NOTE: Only send transactions in a timely manner (\~30 min). It is always
recommended to request an up-to-date quote to ensure to get the latest
information.
**Risk of modifying Bitcoin transaction data**
Modifying PSBT or raw Bitcoin transaction data received from our API (for
example removing outputs, changing amounts, or editing opcodes/scripts) can
invalidate signatures or spending conditions and lead to irreversible loss of
funds.
Do not alter PSBTs unless you are an expert and have explicitly confirmed with
us the modification you intend to make.
The following is an example of transaction data
```JS theme={"system"}
"transactionRequest": {
"to": "bc1qawcdxplxprc64fh38ryy4crndmfgwrffpac743", //thorswap vault to send BTC to
"data": "=:ETH.USDC:0x29DaCdF7cCaDf4eE67c923b4C22255A4B2494eD7::lifi:0|0x4977d81c2a5d6bd8",
"value": "500000"
}
```
### Extracting Transaction Data from PSBT
When building custom Bitcoin transactions with selected UTXOs, partners need to extract the memo and other transaction details from the PSBT (Partially Signed Bitcoin Transaction) returned by the API.
**Key Information:**
* The memo is contained in the **OP\_RETURN output** (2nd output) of the PSBT
* This memo must be preserved exactly as provided - it contains bridge-specific calldata and LI.FI tracking details
* The depositor address is in the 1st output
* The refund address (if required) is in the 3rd output
Partners can parse the PSBT to extract these values and reconstruct the transaction with their selected UTXOs while maintaining the exact output structure.
### Bitcoin transaction requirements per tool
**Critical: Output Structure Cannot Be Modified**
The output order and structure provided by the API must be replicated exactly. Changing the order, removing outputs, or modifying amounts will result in stuck transactions. Partners building custom transactions must preserve the exact structure while only changing the input UTXOs.
The general requirements for the outputs structure are the following:
* **1st output:** Bridged amount sent to the bridge depositor address
* **2nd output:** OP\_RETURN containing the memo with bridge-specific and LI.FI tracking details (must be preserved exactly)
* **3rd output:** Refund output back to sender's address (optional for some bridges, mandatory for others - see details below)
* **Remaining outputs:** Integrator-specific fee transfers
**Dust Threshold Requirements:**
All outputs containing value must exceed the dust threshold, which is determined by the output address type:
* Pay To Witness Public Key Hash (p2wpkh) - 294 sats
* Pay To Witness Script Hash (p2wsh) - 330 sats
* Pay To Script Hash (p2sh) - 540 sats
* Pay To Public Key Hash (p2pkh) - 546 sats
* Pay To Taproot (p2tr) - 330 sats
#### Bridge Requirements Summary
##### **Thorswap**
The memo (OP\_RETURN output) contains Thorswap calldata to be executed and LI.FI tracking id. It's important to keep both to avoid stuck or failed transactions.
```JS theme={"system"}
// Memo example
=:ETH.USDC:0x29DaCdF7cCaDf4eE67c923b4C22255A4B2494eD7::lifi:0|0x4977d81c2a5d6bd8
```
##### **Unit**
Unit bridge doesn't have any bridge-specific details stored in memo so it includes only LI.FI tracking details.
```JS theme={"system"}
// Memo example
// =|lifi02bf57fe
```
##### **Symbiosis**
Symbiosis bridge doesn't have any bridge-specific details stored in memo so it includes only LI.FI tracking details.
```JS theme={"system"}
// Memo example
=|lifi02bf57fe
```
Incorrect transaction structure will cause stuck transfers that require manual refund intervention. Ensure the output order matches the API response exactly.
##### **Relay**
The memo (OP\_RETURN output) contains Relay calldata to be executed and LI.FI tracking id. It's important to keep both to avoid stuck or failed transactions.
```JS theme={"system"}
// Memo example
0x986c2efd25b8887e9c187cfe2162753567339b6313e7137b749e83d4a1a79b03=|lifi92c9cbbc5
```
##### **Chainflip**
Chainflip PSBT requires to have three outputs as described above. The refund output is required, if it's skipped, the transaction will not be correctly processed.
The memo (OP\_RETURN output) contains Chainflip payload to be executed and LI.FI tracking id. It's important to keep both to avoid stuck or failed transactions.
```JS theme={"system"}
// Memo example
0x01071eb6638de8c571c787d7bc24f98bfa735425731c6400f4c5ef05000000000000000000000000ff010002001e0200=|lifi92c9cbbc5
```
**CRITICAL: Chainflip Fund Loss Risk**
Chainflip transactions with incorrect output structure will result in **permanent, unrecoverable loss of funds**. Unlike other bridges, Chainflip cannot manually refund stuck transactions.
**Mandatory Requirements:**
* All three outputs must be present in exact order: (1) deposit amount, (2) OP\_RETURN memo, (3) refund output
* Refund output (3rd output) is mandatory and must be above dust threshold
* Do not modify, remove, or reorder any outputs from the API response
Failure to follow these requirements exactly will result in irreversible fund loss with no recovery option.
# Quote vs Route
Source: https://docs.li.fi/introduction/user-flows-and-examples/difference-between-quote-and-route
Difference between /quote and /advanced/routes
## /quote
/quote endpoint returns **the best single-step route only**. So only one route is returned and it includes transaction data that is needed to be sent onchain to execute the route.
## /advanced/routes
The `/advanced/routes` endpoint allows more complex routes, in which the user needs to bridge funds first and then needs to trigger a second transaction on the destination chain to swap into the desired asset.
After retrieving the routes, the tx data needs to be generated and retrieved using the `/advanced/stepTransaction` endpoint. This endpoint expects a full Step object which usually is retrieved by calling the `/advanced/routes` endpoint and selecting the most suitable Route.
The `/advanced/stepTransaction` endpoint needs to be called to retrieve transaction data for every Step. Internally both endpoints use the same routing algorithm, but with the described different settings.
The `/advanced/routes` endpoint can return single-step routes only by using the `allowChainSwitch: false` parameter in the request.
# End-to-end Transaction Example
Source: https://docs.li.fi/introduction/user-flows-and-examples/end-to-end-example
**Want to go beyond swaps and bridges?** With [Composer](/composer/overview), you can deposit into vaults, stake, and lend — all in a single transaction using the same API pattern shown below. See the [Composer Quickstart](/composer/quickstart).
## Step by step
```ts TypeScript theme={"system"}
import axios from 'axios';
const getQuote = async (
fromChain: number,
toChain: number,
fromToken: string,
toToken: string,
fromAmount: string,
fromAddress: string,
) => {
const result = await axios.get('https://li.quest/v1/quote', {
params: {
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
fromAddress,
},
});
return result.data;
};
const fromChain = 42161; // Arbitrum
const fromToken = 'USDC';
const toChain = 100; // Gnosis
const toToken = 'USDC';
const fromAmount = '1000000';
const fromAddress = '0xYOUR_WALLET_ADDRESS';
const quote = await getQuote(fromChain, toChain, fromToken, toToken, fromAmount, fromAddress);
```
This step is only needed if `/advanced/routes` endpoint was used. `/quote` already returns the transaction data within the response. Difference between `/quote` and `/advanced/routes` is described [here](/introduction/user-flows-and-examples/difference-between-quote-and-route)
Before any transaction can be sent, it must be made sure that the user is allowed to send the requested amount from the wallet. This example uses the classic `approve()` approach. To reduce approval transactions using off-chain EIP-712 signatures, see the [Permit & Permit2 Approval Flow](/introduction/user-flows-and-examples/permit2-approval-flow).
```ts TypeScript theme={"system"}
import { erc20Abi, zeroAddress, type Address } from 'viem';
import type { PublicClient, WalletClient } from 'viem';
// Get the current allowance and update it if needed
const checkAndSetAllowance = async (
publicClient: PublicClient,
walletClient: WalletClient,
tokenAddress: Address,
approvalAddress: Address,
amount: bigint,
) => {
// Transactions with the native token don't need approval
if (tokenAddress === zeroAddress) {
return;
}
const [account] = await walletClient.getAddresses();
const allowance = await publicClient.readContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'allowance',
args: [account, approvalAddress],
});
if (allowance < amount) {
const hash = await walletClient.writeContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'approve',
args: [approvalAddress, amount],
account,
chain: walletClient.chain,
});
await publicClient.waitForTransactionReceipt({ hash });
}
};
await checkAndSetAllowance(
publicClient,
walletClient,
quote.action.fromToken.address as Address,
quote.estimate.approvalAddress as Address,
BigInt(fromAmount),
);
```
After receiving a quote, the transaction has to be sent to trigger the transfer.
Firstly, the wallet has to be configured. The transaction executes on the source chain, so the following example connects your wallet to Arbitrum:
```ts TypeScript theme={"system"}
import { createPublicClient, createWalletClient, http } from 'viem';
import { mnemonicToAccount } from 'viem/accounts';
import { arbitrum } from 'viem/chains';
const account = mnemonicToAccount('YOUR_PERSONAL_MNEMONIC');
const publicClient = createPublicClient({ chain: arbitrum, transport: http() });
const walletClient = createWalletClient({ account, chain: arbitrum, transport: http() });
```
Afterward, the transaction can be sent using the `transactionRequest` inside the previously retrieved quote:
```ts TypeScript theme={"system"}
import type { Address, Hex } from 'viem';
const hash = await walletClient.sendTransaction({
to: quote.transactionRequest.to as Address,
data: quote.transactionRequest.data as Hex,
value: BigInt(quote.transactionRequest.value),
gas: BigInt(quote.transactionRequest.gasLimit),
gasPrice: BigInt(quote.transactionRequest.gasPrice),
});
await publicClient.waitForTransactionReceipt({ hash });
```
If two-step route was used, the second step has to be executed after the first step is complete. Fetch the status of the first step like described in next step and then request transactionData from the `/advanced/stepTransaction` endpoint.
To check if the token was successfully sent to the receiving chain, the /status endpoint can be called:
```ts TypeScript theme={"system"}
const getStatus = async (
bridge: string,
fromChain: number,
toChain: number,
txHash: string,
) => {
const result = await axios.get('https://li.quest/v1/status', {
params: {
bridge,
fromChain,
toChain,
txHash,
},
});
return result.data;
};
const result = await getStatus(quote.tool, fromChain, toChain, hash);
```
## Full example
```ts TypeScript theme={"system"}
import axios from 'axios';
import {
createPublicClient,
createWalletClient,
erc20Abi,
http,
zeroAddress,
type Address,
type Hex,
} from 'viem';
import { mnemonicToAccount } from 'viem/accounts';
import { arbitrum } from 'viem/chains';
const API_URL = 'https://li.quest/v1';
// Get a quote for your desired transfer
const getQuote = async (
fromChain: number,
toChain: number,
fromToken: string,
toToken: string,
fromAmount: string,
fromAddress: string,
) => {
const result = await axios.get(`${API_URL}/quote`, {
params: {
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
fromAddress,
},
});
return result.data;
};
// Check the status of your transfer
const getStatus = async (
bridge: string,
fromChain: number,
toChain: number,
txHash: string,
) => {
const result = await axios.get(`${API_URL}/status`, {
params: {
bridge,
fromChain,
toChain,
txHash,
},
});
return result.data;
};
const fromChain: number = 42161; // Arbitrum
const fromToken = 'USDC';
const toChain: number = 100; // Gnosis
const toToken = 'USDC';
const fromAmount = '1000000';
// Set up your wallet on the source chain
const account = mnemonicToAccount('YOUR_PERSONAL_MNEMONIC');
const publicClient = createPublicClient({ chain: arbitrum, transport: http() });
const walletClient = createWalletClient({ account, chain: arbitrum, transport: http() });
// Get the current allowance and update it if needed
const checkAndSetAllowance = async (
tokenAddress: Address,
approvalAddress: Address,
amount: bigint,
) => {
// Transactions with the native token don't need approval
if (tokenAddress === zeroAddress) {
return;
}
const allowance = await publicClient.readContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'allowance',
args: [account.address, approvalAddress],
});
if (allowance < amount) {
const approveHash = await walletClient.writeContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'approve',
args: [approvalAddress, amount],
});
await publicClient.waitForTransactionReceipt({ hash: approveHash });
}
};
const run = async () => {
const quote = await getQuote(
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
account.address,
);
await checkAndSetAllowance(
quote.action.fromToken.address as Address,
quote.estimate.approvalAddress as Address,
BigInt(fromAmount),
);
const hash = await walletClient.sendTransaction({
to: quote.transactionRequest.to as Address,
data: quote.transactionRequest.data as Hex,
value: BigInt(quote.transactionRequest.value),
gas: BigInt(quote.transactionRequest.gasLimit),
gasPrice: BigInt(quote.transactionRequest.gasPrice),
});
await publicClient.waitForTransactionReceipt({ hash });
// Only needed for cross chain transfers
if (fromChain !== toChain) {
let result;
do {
result = await getStatus(quote.tool, fromChain, toChain, hash);
if (result.status !== 'DONE' && result.status !== 'FAILED') {
await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5s
}
} while (result.status !== 'DONE' && result.status !== 'FAILED');
}
};
run().then(() => {
console.log('DONE!');
});
```
# LI.FI Composer
Source: https://docs.li.fi/introduction/user-flows-and-examples/lifi-composer
One-click DeFi operations across any chain — bundle swaps, bridges, deposits, and staking into a single transaction.
**This page has moved.** Composer now has its own dedicated documentation section with quickstarts, integration guides, recipes, and a full reference.
What is Composer, why use it, and supported protocols
Execute your first Composer transaction in under 5 minutes
Architecture deep-dive: Onchain VM, eDSL, and transaction lifecycle
Step-by-step guide to integrating Composer via the REST API
# Messaging flow
Source: https://docs.li.fi/introduction/user-flows-and-examples/messaging-flow
LI.FI Messaging flow Documentation
# LI.FI Messaging Flow Documentation
## Overview
LI.FI Messaging Flow enables seamless interactions with centralized and hybrid exchanges that use message-based APIs (such as Hyperliquid) instead of traditional on-chain transactions. **This flow delivers gasless, approval-free operations for cross-chain transfers involving protocols that operate with off-chain signed messages.**
Traditional DeFi operations require users to send on-chain transactions, manage gas fees, and approve token spending for each interaction. Messaging flow eliminates these friction points by using off-chain signed messages (EIP-712) that are relayed to destination protocols through LI.FI's backend infrastructure.
## Key Benefits of Messaging Flow
* **No Token Approvals Required**: Unlike transaction-based flows, messaging flow doesn't require users to approve token spending
* **Gasless Operations**: Users sign messages off-chain without paying gas fees for the message itself (some operation might require fee payments, not gas)
* **Asynchronous Execution**: Messages are relayed and processed asynchronously, with status tracking via `taskId`
* **Seamless Integration**: Works out-of-the-box with LI.FI API, SDK, and Widget
***
## How Messaging Flow Works
The messaging flow operates through a multi-step process that replaces traditional on-chain transactions with off-chain signed messages:
1. **Quote/Route Generation**: User requests a quote or route with `executionType=message` (will generate ONLY message-based routes) or `executionType=all` (both transaction and messages options)
2. **Message Creation**: LI.FI generates an EIP-712 typed message containing the operation details
3. **User Signature**: User signs the message off-chain in their wallet (no gas required)
4. **Message Relay**: Signed message is submitted to LI.FI's `/v1/advanced/relay` endpoint
5. **Backend Processing**: LI.FI backend validates and forwards the message to the destination protocol (e.g., Hyperliquid)
6. **Task Tracking**: Backend returns a `taskId` for tracking the asynchronous operation
7. **Status Monitoring**: Status can be checked via `/v1/status` endpoint using the `taskId` parameter
### Flow Diagram
```
User Wallet → Sign EIP-712 Message (off-chain, no gas)
↓
LI.FI SDK/API → POST /v1/advanced/relay
↓
LI.FI Backend → Validates & relays to protocol
↓
Returns taskId → Track via GET /v1/status?taskId=...
↓
Protocol Execution → (e.g., Hyperliquid withdrawal)
```
***
## Key Differences from Transaction Flow
| Aspect | Transaction Flow | Messaging Flow |
| ------------------- | ---------------------------------- | ----------------------------------- |
| **Execution Type** | On-chain transaction | Off-chain signed message |
| **Gas Fees** | User pays gas for each transaction | No gas for signing messages |
| **Token Approvals** | Required (separate transaction) | Not required (`skipApproval: true`) |
| **Status Tracking** | `txHash` | `taskId` |
| **User Action** | Send transaction | Sign typed message |
### Important Parameters
* **`estimate.skipApproval`**: Automatically set to `true` for messaging flows, indicating no approval transaction is needed
* **`estimate.executionType`**: Set to `"message"` to identify steps that use messaging flow
* **`typedData`**: Contains the EIP-712 message structure that users need to sign
***
## The executionType Parameter
The `executionType` parameter controls which types of routes are returned by the LI.FI API. This optional parameter is available in:
* `GET /v1/quote`
* `POST /v1/advanced/routes`
### Values
* **`transaction`** (default): Returns only routes using traditional on-chain transactions, **excluding** messaging flow routes
* **`message`**: Returns only routes that use messaging flow
* **`all`**: Returns both transaction-based and message-based routes
### Example Usage
**Get only message-based routes:**
```bash theme={"system"}
curl -X GET 'https://li.quest/v1/quote?fromChain=1337&toChain=999&fromToken=0x8F254b963e8468305d409b33aA137C6700000000&toToken=0x9FDBdA0A5e284c32744D2f17Ee5c74B284993463&fromAddress=0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0&fromAmount=1000000&executionType=message'
```
**Get all available routes (both types):**
```bash theme={"system"}
curl -X GET 'https://li.quest/v1/quote?fromChain=1337&toChain=999&fromToken=0x8F254b963e8468305d409b33aA137C6700000000&toToken=0x9FDBdA0A5e284c32744D2f17Ee5c74B284993463&fromAddress=0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0&fromAmount=1000000&executionType=all'
```
***
## The /relay Endpoint
### POST /v1/advanced/relay
**Purpose**: Submit signed EIP-712 messages for relaying to destination protocols.
**Endpoint**: `https://li.quest/v1/advanced/relay`
### Request Body
The request body is a `RelayRequest` object containing:
* **Step Information**: Standard LI.FI step data (tool, action, estimate)
* **Typed Data**: Array of signed EIP-712 messages
* **Signature**: User's signature for each message
### Request Schema
```typescript theme={"system"}
{
id: string // Step ID
type: 'lifi' // Step type
tool: string // Bridge tool (e.g., 'hyperliquidSA')
toolDetails: { // Tool metadata
key: string
name: string
logoURI: string
}
action: { // Transfer action details
fromChainId: number
toChainId: number
fromToken: Token
toToken: Token
fromAmount: string
fromAddress: string
toAddress: string
slippage?: number
}
estimate: { // Estimated results
fromAmount: string
toAmount: string
toAmountMin: string
tool: string
executionDuration: number
approvalAddress: string
skipApproval: true // Always true for messaging flow
feeCosts: FeeCost[]
gasCosts: GasCost[]
executionType: string
}
includedSteps: Step[] // Nested steps
typedData: TypedData[] // EIP-712 messages with signatures
}
```
### Response
**Success Response** (`200 OK`):
```json theme={"system"}
{
"status": "ok",
"data": {
"taskId": "0x3078316542363633386445386335373163373837443762433234463938624641373335343235373331437c313735393438383039323538347c65646461643630632d373730392d346165312d623431652d3834643834333064306135623a30"
}
}
```
**Error Response** (`400 Bad Request`):
```json theme={"system"}
{
"status": "error",
"data": {
"code": 400,
"message": "Invalid request"
}
}
```
### Response Fields
* **`status`**: Either `"ok"` or `"error"`
* **`data.taskId`**: Unique hex-encoded identifier for tracking the message relay operation
* **`data.code`**: Error code (only present when status is "error")
* **`data.message`**: Error message (only present when status is "error")
***
## Status Tracking with taskId
After relaying a message, you receive a `taskId` that uniquely identifies the operation. Use this to track the message processing status.
### GET /v1/status
**Endpoint**: `https://li.quest/v1/status`
**Query Parameters**:
* **`taskId`** (optional): The task ID returned from `/relay` endpoint
* **`txHash`** (optional): Transaction hash (for traditional transactions)
* **`toChain`** (optional): Destination chain ID or key
* **`bridge`** (optional): Bridge tool identifier
* **`fromChain`** (optional): Source chain ID or key
**Note**: You must provide either `taskId` or `txHash`. For messaging flow, use `taskId`.
### Example Request
```bash theme={"system"}
curl -X GET 'https://li.quest/v1/status?taskId=0x3078316542363633386445386335373163373837443762433234463938624641373335343235373331437c313735393438383039323538347c65646461643630632d373730392d346165312d623431652d3834643834333064306135623a30'
```
### Response Format
The status endpoint returns the current state of the transfer:
```json theme={"system"}
{
"status": "DONE",
"substatus": "COMPLETED",
"sending": {
"txHash": "0x...",
"amount": "1000000",
"token": {
/* token details */
},
"chainId": 1337,
"timestamp": 1234567890
},
"receiving": {
"txHash": "0x...",
"amount": "1000000",
"token": {
/* token details */
},
"chainId": 999,
"timestamp": 1234567890
}
}
```
## Current Usage & Supported Protocols
Messaging flow is currently used for interactions with the following protocols:
### 1. Hyperliquid (Primary Use Case)
**Protocol**: [Hyperliquid](https://hyperliquid.xyz/)
**Operation**: Withdrawals from Hyperliquid to EVM chains
**Bridge Tool**: `hyperliquidSA`
**Message Type**: `SendAsset`
**How it works**:
1. User has tokens on Hyperliquid spot account
2. Signs a `SendAsset` message to withdraw to an EVM chain
3. LI.FI relays the message to Hyperliquid's API
4. Hyperliquid processes the withdrawal and sends tokens to destination chain
### 2. Unit Protocol
**Protocol**: [Unit Protocol](https://unit.network/)
**Operation**: Withdrawals to Hyperliquid via Unit bridge
**Bridge Tool**: `unit`
**Message Type**: `SpotSend`
**Chain IDs**:
* From: 1337 (Hyperliquid/Hypercore)
* To: EVM chains, Bitcoin, Solana
***
## Supported Message Types
### Hyperliquid
LI.FI Messaging Flow supports two EIP-712 message types for Hyperliquid operations. Each message type follows a specific structure and is used for different operations.
#### 1. SpotSend
**Purpose**: Spot token transfers
**Use Case**: Used by Unit protocol for deposits to Hyperliquid
**Bridge Tool**: `unit`
**Message Structure**:
```typescript theme={"system"}
{
type: 'spotSend',
signatureChainId: '0x1',
hyperliquidChain: 'Mainnet',
destination: '0x...', // Recipient address
token: 'USOL:0x49b67c39...', // Token identifier
amount: '1.0', // Amount to transfer
time: 1234567890 // Timestamp in milliseconds
}
```
#### 2. SendAsset
**Purpose**: Asset transfers between DEXs (spot accounts)
**Use Case**: Used for Hyperliquid withdrawals to EVM chains
**Bridge Tool**: `hyperliquidSA`
**Message Structure**:
```typescript theme={"system"}
{
type: 'sendAsset',
signatureChainId: '0x1',
hyperliquidChain: 'Mainnet',
destination: '0x2000...00fe', // System address
sourceDex: 'spot', // Source DEX type
destinationDex: 'spot', // Destination DEX type
token: 'USOL:0x49b67c39...', // Token identifier
amount: '1.0', // Amount to transfer
fromSubAccount: '', // Agent wallet address (if used)
nonce: 1757944034747 // Timestamp/nonce
}
```
**Note**: All messages follow the EIP-712 typed data standard and include domain information:
```typescript theme={"system"}
{
domain: {
name: 'HyperliquidSignTransaction',
version: '1',
chainId: 999,
verifyingContract: '0x0000000000000000000000000000000000000000'
},
types: { /* EIP712Domain and message types */ },
primaryType: 'HyperliquidTransaction:SendAsset',
message: { /* message content */ }
}
```
***
## Integration Guide
### Using the API Directly
**Step 1: Get a Quote/Route**
Request a quote with `executionType=message` or `executionType=all`:
```bash theme={"system"}
curl -X GET 'https://li.quest/v1/quote?fromChain=1337&toChain=999&fromToken=0x8F254b963e8468305d409b33aA137C6700000000&toToken=0x9FDBdA0A5e284c32744D2f17Ee5c74B284993463&fromAddress=0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0&fromAmount=1000000&executionType=all'
```
**Step 2: Identify Message Steps**
Check the route for steps with `estimate.executionType === "message"` and `estimate.skipApproval === true`.
**Step 3: Sign the Message**
Use the `typedData` from the route to request a signature from the user's wallet:
```typescript theme={"system"}
const signature = await walletClient.signTypedData({
domain: typedData.domain,
types: typedData.types,
primaryType: typedData.primaryType,
message: typedData.message,
})
```
**Step 4: Relay the Message**
Submit the signed message to the `/relay` endpoint:
```bash theme={"system"}
curl -X POST 'https://li.quest/v1/advanced/relay' \
-H 'Content-Type: application/json' \
-H 'x-lifi-api-key: YOUR_API_KEY' \
-d '{
"id": "step-id",
"typedData": [{
...typedData,
"signature": "0x..."
}],
...stepData
}'
```
**Step 5: Track Status**
Use the returned `taskId` to check status:
```bash theme={"system"}
curl -X GET 'https://li.quest/v1/status?taskId=RETURNED_TASK_ID'
```
***
### Best Practices
1. **Always check `estimate.skipApproval`**: If true, skip approval transaction
2. **Validate signatures**: Ensure the message is signed correctly before relaying
3. **Store taskId**: Save the taskId returned from `/relay` for status tracking
4. **Poll status endpoint**: Check status periodically until completion
***
## Limitations & Considerations
### Current Limitations
* **Supported Protocols**: Currently limited to Hyperliquid and Unit protocol
### Future Enhancements
As the messaging flow matures, additional protocols and chains may be supported. Protocol teams interested in integration can contact the LI.FI team.
***
## FAQ
**Q: Do I need to do anything special to use messaging flow?**
A: No. If you're using the LI.FI SDK or Widget, messaging routes are automatically included and handled. For direct API usage, set `executionType=all` to see message routes.
**Q: Why does my route have `skipApproval: true`?**
A: This indicates the route uses messaging flow and doesn't require a token approval transaction.
**Q: How long does it take for a message to be processed?**
A: Processing time varies by protocol. For Hyperliquid, withdrawals typically complete within a few seconds.
**Q: Can I cancel a message after relaying?**
A: Once a message is relayed and accepted by the protocol, it cannot be cancelled through LI.FI. Check with the specific protocol for their cancellation policies.
**Q: How do I know if a route uses messaging flow?**
A: Check the `estimate.executionType` field. If it's `"message"`, the route uses messaging flow.
***
## Next Steps
* **Integrate**: Use the LI.FI SDK, Widget, or API to access messaging flow routes
* **Test**: Try a small withdrawal from Hyperliquid using the messaging flow
* **Monitor**: Use the `taskId` to track your operations via the status endpoint
* **Contact**: Reach out to the [LI.FI team](https://li.fi/contact-us/) for protocol integration requests
For more information, visit the [LI.FI Documentation](https://docs.li.fi/).
# Permit & Permit2 Approval Flow
Source: https://docs.li.fi/introduction/user-flows-and-examples/permit2-approval-flow
LI.FI supports three token approval strategies on EVM chains. In addition to the classic `approve()` transaction, integrators can use **EIP-2612 Permits** or **Uniswap Permit2** to authorize token transfers via off-chain signatures, reducing the number of on-chain transactions required.
If you use the **LI.FI SDK** or **Widget**, Permit2 is handled automatically. This guide is for integrators building directly against the API who want to understand or implement the permit flow themselves.
## Overview of Approval Strategies
| Strategy | On-chain Transactions | How It Works |
| -------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Classic `approve()`** | 1 approval tx + 1 swap/bridge tx | User sends an ERC-20 `approve()` to the LI.FI Diamond, then submits the swap/bridge transaction. |
| **EIP-2612 Native Permit** | 1 swap/bridge tx only | User signs an off-chain EIP-712 message. The signature is submitted alongside the swap calldata to the `Permit2Proxy`, which calls `permit()` on the token contract. Only works with tokens that implement EIP-2612. |
| **Uniswap Permit2** | 1 one-time approval + 1 swap/bridge tx (signature only) | User approves the Permit2 contract once (unlimited). For each subsequent transaction, the user signs an off-chain EIP-712 message authorizing a specific transfer. Works with any ERC-20 token. |
### Why Permit2?
With the classic approval model, every new dApp interaction requires a separate `approve()` transaction. Permit2 replaces this with a single, one-time unlimited approval to the canonical Permit2 contract. All subsequent authorizations happen through gasless EIP-712 signatures with per-transfer granularity (exact amount, deadline, nonce).
## Architecture
All permit-based flows go through the **Permit2Proxy** periphery contract, which acts as an intermediary between the user and the LI.FI Diamond:
### Key Addresses
| Contract | Address | Notes |
| ------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **LI.FI Diamond** | `0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE` | Most EVM chains. Some networks use a different address — always query `GET /v1/chains` or check [Smart Contract Addresses](/introduction/lifi-architecture/smart-contract-addresses). |
| **Uniswap Permit2** | `0x000000000022D473030F116dDEE9F6B43aC78BA3` | Most EVM chains. Several networks use a different deployment (e.g. zkSync, Abstract, Lens, Flare, Sophon, XDC) — always read `permit2` from `GET /v1/chains`. |
| **Permit2Proxy** | Chain-specific | Query `GET /v1/chains` — each chain object includes `permit2` and `permit2Proxy` fields. |
### Discovering Addresses via the API
```ts theme={"system"}
const response = await fetch('https://li.quest/v1/chains');
const { chains } = await response.json();
const arbitrum = chains.find((c) => c.id === 42161);
console.log(arbitrum.permit2); // "0x000000000022D473030F116dDEE9F6B43aC78BA3"
console.log(arbitrum.permit2Proxy); // chain-specific Permit2Proxy address
console.log(arbitrum.diamondAddress); // "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE"
```
## API Flow: Permit2 (Standard Self-Execute)
This is the most common permit flow. It works with **any ERC-20 token** on chains where Permit2 is deployed.
Request a quote as usual. The response includes `estimate.approvalAddress` (the Diamond address for classic approve) and the chain metadata you need.
```ts theme={"system"}
const quote = await fetch('https://li.quest/v1/quote?' + new URLSearchParams({
fromChain: '42161',
toChain: '10',
fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toToken: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', // USDC on Optimism
fromAmount: '10000000', // 10 USDC
fromAddress: '0xYourWalletAddress',
})).then(r => r.json());
```
The user needs to approve the **Permit2 contract** (not the Diamond) once. This only needs to happen if the user hasn't already approved Permit2 for this token.
Resolve the Permit2 and Permit2Proxy addresses from the chains API — do not hardcode them, as several networks use non-canonical deployments.
```ts theme={"system"}
import { createPublicClient, createWalletClient, http, maxUint256, parseAbi } from 'viem';
import { arbitrum } from 'viem/chains';
const publicClient = createPublicClient({ chain: arbitrum, transport: http() });
const walletClient = createWalletClient({ chain: arbitrum, transport: http(), account });
const chainsResponse = await fetch('https://li.quest/v1/chains').then(r => r.json());
const fromChain = chainsResponse.chains.find((c) => c.id === quote.action.fromChainId);
const permit2Address = fromChain.permit2;
const permit2ProxyAddress = fromChain.permit2Proxy;
const erc20Abi = parseAbi([
'function allowance(address owner, address spender) view returns (uint256)',
'function approve(address spender, uint256 amount) returns (bool)',
]);
const tokenAddress = quote.action.fromToken.address;
const allowance = await publicClient.readContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'allowance',
args: [account.address, permit2Address],
});
if (allowance < BigInt(quote.action.fromAmount)) {
const hash = await walletClient.writeContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'approve',
args: [permit2Address, maxUint256],
});
await publicClient.waitForTransactionReceipt({ hash });
}
```
Permit2 uses unordered nonces. The `Permit2Proxy` contract provides a `nextNonce()` helper that finds the next unused nonce for the signer.
```ts theme={"system"}
const permit2ProxyAbi = parseAbi([
'function nextNonce(address owner) view returns (uint256)',
'function callDiamondWithPermit2(bytes diamondCalldata, ((address token, uint256 amount) permitted, uint256 nonce, uint256 deadline) permit, bytes signature) external',
]);
const nonce = await publicClient.readContract({
address: permit2ProxyAddress,
abi: permit2ProxyAbi,
functionName: 'nextNonce',
args: [account.address],
});
```
Construct the EIP-712 typed data for `PermitTransferFrom`. The `spender` is the **Permit2Proxy** (not the Diamond).
```ts theme={"system"}
const deadline = BigInt(Math.floor(Date.now() / 1000) + 30 * 60); // 30 minutes
const permitTransferFrom = {
permitted: {
token: tokenAddress,
amount: BigInt(quote.action.fromAmount),
},
spender: permit2ProxyAddress,
nonce,
deadline,
};
const signature = await walletClient.signTypedData({
account,
primaryType: 'PermitTransferFrom',
domain: {
name: 'Permit2',
chainId: arbitrum.id,
verifyingContract: permit2Address,
},
types: {
TokenPermissions: [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
PermitTransferFrom: [
{ name: 'permitted', type: 'TokenPermissions' },
{ name: 'spender', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
},
message: permitTransferFrom,
});
```
Wrap the Diamond calldata inside a `callDiamondWithPermit2` call targeting the **Permit2Proxy**, not the Diamond.
```ts theme={"system"}
import { encodeFunctionData } from 'viem';
const diamondCalldata = quote.transactionRequest.data;
const txData = encodeFunctionData({
abi: permit2ProxyAbi,
functionName: 'callDiamondWithPermit2',
args: [
diamondCalldata,
[
[permitTransferFrom.permitted.token, permitTransferFrom.permitted.amount],
permitTransferFrom.nonce,
permitTransferFrom.deadline,
],
signature,
],
});
const txHash = await walletClient.sendTransaction({
to: permit2ProxyAddress,
data: txData,
value: BigInt(quote.transactionRequest.value ?? 0),
gasLimit: BigInt(quote.transactionRequest.gasLimit ?? 0),
});
```
Track the transaction status as you normally would using the `/status` endpoint.
```ts theme={"system"}
const getStatus = async (txHash) => {
const result = await fetch(`https://li.quest/v1/status?txHash=${txHash}`);
return result.json();
};
let status;
do {
status = await getStatus(txHash);
if (status.status === 'PENDING') await new Promise(r => setTimeout(r, 5000));
} while (status.status !== 'DONE' && status.status !== 'FAILED');
```
## API Flow: EIP-2612 Native Permit
EIP-2612 permits are only available for tokens that implement the `permit()` function (e.g., USDC, AAVE, UNI). No prior `approve()` transaction is needed at all.
Not all tokens support EIP-2612. DAI uses a non-standard permit signature that LI.FI does not currently support. If the token does not implement EIP-2612, fall back to classic `approve()` or Permit2.
### Detecting EIP-2612 Support
There is no on-chain registry or ERC-165 interface for EIP-2612. The only reliable method is to probe the token contract for the required functions. If `nonces()` and `DOMAIN_SEPARATOR()` both return successfully, the token supports EIP-2612.
```ts theme={"system"}
const eip2612DetectAbi = parseAbi([
'function nonces(address owner) view returns (uint256)',
'function DOMAIN_SEPARATOR() view returns (bytes32)',
]);
async function supportsEIP2612(tokenAddress: string): Promise {
try {
await Promise.all([
publicClient.readContract({
address: tokenAddress,
abi: eip2612DetectAbi,
functionName: 'nonces',
args: [account.address],
}),
publicClient.readContract({
address: tokenAddress,
abi: eip2612DetectAbi,
functionName: 'DOMAIN_SEPARATOR',
}),
]);
return true;
} catch {
return false;
}
}
```
Tokens deployed with OpenZeppelin v4.9+ or v5.x also expose `eip712Domain()` ([EIP-5267](https://eips.ethereum.org/EIPS/eip-5267)), which returns all domain fields in a single call. For older tokens, read `name()`, `version()`, and `DOMAIN_SEPARATOR()` separately and recompute the separator to validate it.
Same as the standard flow: request a quote, then use the `transactionRequest.data` as your diamond calldata.
EIP-2612 tokens track nonces per-owner. Read the current nonce from the token contract.
```ts theme={"system"}
const eip2612Abi = parseAbi([
'function nonces(address owner) view returns (uint256)',
'function name() view returns (string)',
'function version() view returns (string)',
'function DOMAIN_SEPARATOR() view returns (bytes32)',
]);
const [nonce, name, version] = await Promise.all([
publicClient.readContract({
address: tokenAddress, abi: eip2612Abi,
functionName: 'nonces', args: [account.address],
}),
publicClient.readContract({
address: tokenAddress, abi: eip2612Abi, functionName: 'name',
}),
publicClient.readContract({
address: tokenAddress, abi: eip2612Abi, functionName: 'version',
}),
]);
```
The `spender` is the **Permit2Proxy** address.
```ts theme={"system"}
const deadline = BigInt(Math.floor(Date.now() / 1000) + 30 * 60);
const permitSignature = await walletClient.signTypedData({
account,
primaryType: 'Permit',
domain: {
name,
version,
chainId: arbitrum.id,
verifyingContract: tokenAddress,
},
types: {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
},
message: {
owner: account.address,
spender: permit2ProxyAddress,
value: BigInt(quote.action.fromAmount),
nonce,
deadline,
},
});
```
```ts theme={"system"}
import { parseSignature, encodeFunctionData } from 'viem';
const { v, r, s } = parseSignature(permitSignature);
const permit2ProxyEip2612Abi = parseAbi([
'function callDiamondWithEIP2612Signature(address tokenAddress, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s, bytes diamondCalldata) external payable',
]);
const txData = encodeFunctionData({
abi: permit2ProxyEip2612Abi,
functionName: 'callDiamondWithEIP2612Signature',
args: [
tokenAddress,
BigInt(quote.action.fromAmount),
deadline,
Number(v),
r,
s,
quote.transactionRequest.data,
],
});
const txHash = await walletClient.sendTransaction({
to: permit2ProxyAddress,
data: txData,
value: BigInt(quote.transactionRequest.value ?? 0),
});
```
## SDK Usage
The `@lifi/sdk` handles Permit2 automatically during route execution. No manual signature construction is needed.
```ts theme={"system"}
import { createConfig, EVM, executeRoute } from '@lifi/sdk';
import { createWalletClient, http } from 'viem';
import { arbitrum } from 'viem/chains';
createConfig({
integrator: 'your-integrator-id',
providers: [
EVM({
getWalletClient: () => Promise.resolve(walletClient),
}),
],
});
// The SDK automatically:
// 1. Checks if Permit2 is deployed on the source chain
// 2. Approves the Permit2 contract if needed (one-time)
// 3. Signs a PermitTransferFrom message per transaction
// 4. Encodes the calldata for Permit2Proxy
await executeRoute({ route });
```
To disable Permit2 and force classic `approve()` transactions:
```ts theme={"system"}
await executeRoute({
route,
executionOptions: {
disableMessageSigning: true,
},
});
```
## When Permit2 Is Not Used
The SDK skips Permit2 and falls back to classic `approve()` when:
* The source chain does not have Permit2 deployed (`chain.permit2` is not set)
* The source chain does not have a Permit2Proxy (`chain.permit2Proxy` is not set)
* The source token is the chain's native token (ETH, MATIC, etc.)
* Message signing is disabled (`disableMessageSigning: true`)
* The transaction uses batched execution (EIP-5792)
* The step's estimate has `skipApproval: true` or `skipPermit: true` (rare, optional fields only present on certain chain-specific steps such as Hyperliquid)
## Reference
### Permit2Proxy Contract Functions
| Function | Description |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| `callDiamondWithPermit2(diamondCalldata, permit, signature)` | Transfers tokens via Permit2 `permitTransferFrom`, approves the Diamond, and forwards calldata. |
| `callDiamondWithEIP2612Signature(token, amount, deadline, v, r, s, diamondCalldata)` | Calls `permit()` on the EIP-2612 token, transfers tokens, approves the Diamond, and forwards calldata. |
| `nextNonce(owner)` | Returns the next available Permit2 nonce for the given address. |
### EIP-712 Type Definitions
**PermitTransferFrom** (Permit2 standard flow):
```json theme={"system"}
{
"TokenPermissions": [
{ "name": "token", "type": "address" },
{ "name": "amount", "type": "uint256" }
],
"PermitTransferFrom": [
{ "name": "permitted", "type": "TokenPermissions" },
{ "name": "spender", "type": "address" },
{ "name": "nonce", "type": "uint256" },
{ "name": "deadline", "type": "uint256" }
]
}
```
**Permit** (EIP-2612 native permit):
```json theme={"system"}
{
"Permit": [
{ "name": "owner", "type": "address" },
{ "name": "spender", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "nonce", "type": "uint256" },
{ "name": "deadline", "type": "uint256" }
]
}
```
### EIP-712 Domain
**Permit2** (for `PermitTransferFrom`):
```json theme={"system"}
{
"name": "Permit2",
"chainId": "",
"verifyingContract": ""
}
```
**EIP-2612** (for native `Permit` -- domain varies per token):
```json theme={"system"}
{
"name": "",
"version": "",
"chainId": "",
"verifyingContract": ""
}
```
# Fetching a Quote/Route
Source: https://docs.li.fi/introduction/user-flows-and-examples/requesting-route-fetching-quote
Guide to make a quote and route request
## Using SDK
```TypeScript theme={"system"}
import { getRoutes } from '@lifi/sdk';
const routesRequest: RoutesRequest = {
fromChainId: 42161, // Arbitrum
toChainId: 10, // Optimism
fromTokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toTokenAddress: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
fromAmount: '10000000', // 10 USDC
};
const result = await getRoutes(routesRequest);
const routes = result.routes;
```
When you make a route request, you receive an array of route objects containing the essential information to determine which route to take for a swap or bridging transfer. At this stage, transaction data is not included and must be requested separately.
Additionally, if you would like to receive just one best option that our smart routing API can offer, it might be better to request a quote using getQuote.
## Using API
To generate a quote based on the amount you are sending, use the /quote endpoint. This method is useful when you know the exact amount you want to send and need to calculate how much the recipient will receive.
```TypeScript theme={"system"}
const getQuote = async (fromChain, toChain, fromToken, toToken, fromAmount, fromAddress) => {
const result = await axios.get('https://li.quest/v1/quote', {
params: {
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
fromAddress,
}
});
return result.data;
}
const fromChain = 42161;
const fromToken = 'USDC';
const toChain = 10;
const toToken = 'USDC';
const fromAmount = '1000000';
const fromAddress = YOUR_WALLET_ADDRESS;
const quote = await getQuote(fromChain, toChain, fromToken, toToken, fromAmount, fromAddress);
```
# Solana Transaction Example
Source: https://docs.li.fi/introduction/user-flows-and-examples/solana-tx-execution
## Requesting Solana specific information via the API
### Chains
```javascript theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/chains?chainTypes=SVM' \
--header 'accept: application/json'
```
### Tokens
```javascript theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/tokens?chains=SOL&chainTypes=SVM' \
--header 'accept: application/json'
```
### Token details
```javascript theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/token?chain=SOL&token=BONK' \
--header 'accept: application/json'
```
## Requesting a Quote or Routes
```javascript /quote theme={"system"}
curl --request GET \
--url 'https://li.quest/v1/quote?fromChain=ARB&toChain=SOL&fromToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&toToken=7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs&fromAddress=YOUR_EVM_WALLET&toAddress=YOUR_SOL_WALLET&fromAmount=1000000000' \
--header 'accept: application/json'
```
```javascript /advanced/routes theme={"system"}
curl --request POST \
--url https://li.quest/v1/advanced/routes \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"fromChainId": "ARB",
"fromAmount": "1000000000",
"toChainId": "SOL",
"fromTokenAddress": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
"toTokenAddress": "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs",
"fromAddress": "YOUR_EVM_WALLET",
"toAddress": "YOUR_SOL_WALLET"
}'
```
### Response
The key difference between **EVM -> SOL** and **SOL -> EVM** transfers is the structure of the transactionRequest. For **SOL -> EVM** transfers, it contains only a data parameter, which represents `base64` encoded Solana transaction data:
```json theme={"system"}
"transactionRequest": {
"data": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAsUBmw6CY1QcV7385AuJb6tDdM71YrLbjDGeWn6/zWFZAEcjcsOlIINY3LYFWBe38OO1l26BSpzB1L1bYnVNorsXkDqoJZ5Mb5PNE07yLa8RJGvFV55ILi1+vklkapJoW1yUKv7UyXP9sO3ptc4QOktFqSHRb9AYoDxZXcodBKfc4vN6ai03uOqBMXcmI4cih1E71LnDKMQljw0rqlnVVKOn98YHXWKE3PmeT4MetR4/Ep7+sfN+1vkcpHlwGeEHZgK4EIcmnLsIpOTZxLFhBBVIsDwUJkuCB/B43O01pI8fuLzyjGxJMo5db7lPEcx8Ns2BJ8kYOoL0ob3fnQ0eN3JwPzibblpkKkSjSk1qpqwB4d5rSn1PrbBHf6rOIO/O/W6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABceQrkgrAQHdCyf7CIDjBD/Y4pzKA7iTYhaafiX1eik37c9HIG4v1EeQo1ENAm3KHS+LCOKkZ4WQntGZQgIyu7ixIazui3zX0pmHiw3K3u/XdzSJfZ+ugLzVfJnnOn3v2RmLUngAdF+k4G2bOshpHaEkSZUr1Y1/vT3G9R/qU8zKFLzTYC6vfOspR18AlAfdoQQSzchbXIKs9TVzmL7XEYAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAADG+nrzvtutOj1l82qryXQxsbvkwtL24OR8pgIDRS9dYceCg/3UzgsruALUdoCSm3XiyBz8VtBPnGEIhrYpcBQ26zvkSidWkhDjuJPqY+9JDKulE8Bq2dVUioc+URRKUUsG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqTLHwyN3CxGdzcZNzliJWl0TJu3X7nF6oB9sysdDGG/6Ag8ABQJAQg8ADhMAAAcQBAMMAQgRAgYLDRIFChMJccw/qau6fVaf/aDz3hWN+Sh9oNuBvkrLv0ttxmLuxpa1pXsmnZ0BxMAAAAAAAAAAAAAAAABVIAjA9ocML3flzB0uub3/A+MOoAUAAAAAAAAAAAAAAAAnkbyh8t5GYe2IowyZp6lEmqhBdOkDAAAAAAAA"
}
```
# Transaction Status Tracking
Source: https://docs.li.fi/introduction/user-flows-and-examples/status-tracking
Complete guide to checking cross-chain transaction statuses using the LI.FI API
# Transaction Status
This guide explains how to check the status of cross-chain and swap transactions using the `/status` endpoint provided by LI.FI.
***
## Querying the Status Endpoint
To fetch the status of a transfer, the `/status` endpoint can be queried with:
1. sending transaction hash
2. receiving transaction hash
3. transactionId
Only one of the above values are required and need to be passed in `txHash` param.
### Required:
* `txHash`
### Optional:
* `fromChain`: Speeds up the request (recommended)
* `toChain`
* `bridge`
For swap transactions, set `fromChain` and `toChain` to the same value. The `bridge` parameter can be omitted.
```typescript theme={"system"}
const getStatus = async (txHash: string) => {
const result = await axios.get('https://li.quest/v1/status', {
params: { txHash },
});
return result.data;
};
```
## Sample Response
```json theme={"system"}
{
"transactionId": "0x0959ee0fbb37a868752d7ae40b25dbfa3b7d72f499fa8386fd5f4105b18b62bd",
"sending": {
"txHash": "0x5862726dbc6643c6a34b3496bb15e91f11771f6756ccf83826304846bbc93c0v",
"txLink": "https://etherscan.io/tx/0x5862726dbc6643c6a34b3496bb15e91f11771f6756ccf83826304846bbc93c0v",
"amount": "60000000000000000000000",
"token": {
"symbol": "ORDS",
"priceUSD": "0.012027801612559667"
},
"gasPrice": "23079962248",
"gasUsed": "231727",
"gasAmountUSD": "14.0296",
"amountUSD": "721.6681",
"includedSteps": [
{
"tool": "feeCollection",
"fromAmount": "60000000000000000000000",
"toAmount": "59820000000000000000000"
},
{
"tool": "1inch",
"fromAmount": "59820000000000000000000",
"toAmount": "275101169247651913"
}
]
},
"receiving": {
"txHash": "0x2862726dbc6643c6a34b3496bb15e91f11771f6756ccf83826604846bbc93c0v",
"amount": "275101169247651913",
"token": {
"symbol": "ETH",
"priceUSD": "2623.22"
},
"gasAmountUSD": "14.0296",
"amountUSD": "721.6509"
},
"lifiExplorerLink": "https://scan.li.fi/tx/0x5862726dbc6643c6a34b3496bb15e91f11771f6756ccf83826304846bbc93c0e",
"fromAddress": "0x14a980237fa9797fa27c5152c496cab65e36da4f",
"toAddress": "0x14a980237fa9797fa27c5152c496cab65e36da4f",
"tool": "1inch",
"status": "DONE",
"substatus": "COMPLETED",
"substatusMessage": "The transfer is complete.",
"metadata": {
"integrator": "example_integrator"
}
}
```
***
## Status Values
| Status | Description |
| ----------- | ------------------------------------------- |
| `NOT_FOUND` | Transaction doesn't exist or not yet mined. |
| `INVALID` | Hash is not tied to the requested tool. |
| `PENDING` | Bridging is still in progress. |
| `DONE` | Transaction completed successfully. |
| `FAILED` | Bridging process failed. |
***
## Substatus Definitions
### PENDING
* `WAIT_SOURCE_CONFIRMATIONS`: Waiting for source chain confirmations
* `WAIT_DESTINATION_TRANSACTION`: Waiting for destination transaction
* `BRIDGE_NOT_AVAILABLE`: Bridge API is unavailable
* `CHAIN_NOT_AVAILABLE`: Source/destination chain RPC unavailable
* `REFUND_IN_PROGRESS`: Refund in progress (if supported)
* `UNKNOWN_ERROR`: Status is indeterminate
### DONE
* `COMPLETED`: Transfer was successful
* `PARTIAL`: Only partial transfer completed (common for across, hop, stargate, amarok)
* `REFUNDED`: Tokens were refunded
### FAILED
* `NOT_PROCESSABLE_REFUND_NEEDED`: Cannot complete, refund needed
* `OUT_OF_GAS`: Transaction ran out of gas
* `SLIPPAGE_EXCEEDED`: Received amount too low
* `INSUFFICIENT_ALLOWANCE`: Not enough allowance
* `INSUFFICIENT_BALANCE`: Not enough balance
* `EXPIRED`: Transaction expired
* `UNKNOWN_ERROR`: Unknown or invalid state
* `REFUNDED`: Tokens were refunded
# Chains and Tools
Source: https://docs.li.fi/sdk/chains-tools
Request all available chains, bridges, and exchanges.
Get an overview of which options (chains, bridges, DEXs) are available at this moment.
## Get available chains
### `getChains`
Fetches a list of all available chains supported by the SDK.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `params` (`ChainsRequest`, optional): Configuration for the requested chains.
* `chainTypes` (`ChainType[]`, optional): List of chain types.
* `options` (`RequestOptions`, optional): Additional request options.
**Returns**
A Promise that resolves to an array of `ExtendedChain` objects.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { ChainType, getChains } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
try {
const chains = await getChains(client, { chainTypes: [ChainType.EVM] });
console.log(chains);
} catch (error) {
console.error(error);
}
```
## Get available bridges and DEXs
### `getTools`
Fetches the tools available for bridging and swapping tokens.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `params` (`ToolsRequest`, optional): Configuration for the requested tools.
* `chains` (`(ChainKey | ChainId)[]`, optional): List of chain IDs or keys.
* `options` (`RequestOptions`, optional): Additional request options.
**Returns**
A Promise that resolves to `ToolsResponse` and contains information about available bridges and DEXs.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getTools } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
try {
const tools = await getTools(client);
console.log(tools);
} catch (error) {
console.error(error);
}
```
## Get available connections
A connection is a pair of two tokens (on the same chain or on different chains) that can be exchanged via our platform.
Read more [Getting all possible Connections](/api-reference/returns-all-possible-connections-based-on-a-from-or-tochain)
### `getConnections`
Gets all the available connections for swapping or bridging tokens.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `connectionRequest` (`ConnectionsRequest`): Configuration of the connection request.
* `fromChain` (`number | string`): The source chain ID or key.
* `fromToken` (`string`, optional): The source token address.
* `toChain` (`number | string`): The destination chain ID or key.
* `toToken` (`string`, optional): The destination token address.
* `allowBridges` (`string[]`, optional): Allowed bridges.
* `denyBridges` (`string[]`, optional): Denied bridges.
* `preferBridges` (`string[]`, optional): Preferred bridges.
* `allowExchanges` (`string[]`, optional): Allowed exchanges.
* `denyExchanges` (`string[]`, optional): Denied exchanges.
* `preferExchanges` (`string[]`, optional): Preferred exchanges.
* `allowProtocols` (`string[]`, optional): Allowed protocols.
* `denyProtocols` (`string[]`, optional): Denied protocols.
* `allowSwitchChain` (`boolean`, optional): Whether connections that require chain switch (multiple signatures) are included. Default is true.
* `allowDestinationCall` (`boolean`, optional): Whether connections that include destination calls are included. Default is true.
* `chainTypes` (`ChainType[]`, optional): Types of chains to include.
* `options` (`RequestOptions`, optional): Request options.
**Returns**
A Promise that resolves to a `ConnectionsResponse`.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getConnections } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const connectionRequest = {
fromChain: 1,
fromToken: '0x0000000000000000000000000000000000000000',
toChain: 10,
toToken: '0x0000000000000000000000000000000000000000',
};
try {
const connections = await getConnections(client, connectionRequest);
console.log('Connections:', connections);
} catch (error) {
console.error('Error:', error);
}
```
For more detailed information on each endpoint and their responses, please refer to the [LI.FI API](/api-reference/introduction) documentation.
# Configure SDK
Source: https://docs.li.fi/sdk/configure-sdk
Get started and set up LI.FI SDK in just a few lines of code.
## Create Client
To get started, you need to create an SDK client for the LI.FI SDK. This client contains the shared settings and data required for the proper functioning of other SDK features that developers will use. The client can be configured with providers and used throughout your application.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
```
## Parameters
| Parameter | Required | Default | Description |
| ----------------------- | -------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `integrator` | Yes | | LI.FI SDK requires an integrator option to identify partners and allows them to monitor their activity on the partner dashboard, such as the transaction volume, enabling better management and support. Usually, the integrator option is your dApp or company name. This string must consist only of letters, numbers, hyphens, underscores, and dots and be a maximum of 23 characters long. |
| `apiKey` | No | | Unique API key for accessing LI.FI API services. Necessary for higher rate limits. Read more [Rate Limits & API Key](/api-reference/rate-limits) |
| `apiUrl` | No | `https://li.quest/v1` | The base URL for the LI.FI API. This is the endpoint through which all API requests are routed. It can be changed to the staging environment to test new features, for example. |
| `userId` | No | | A unique identifier for the user of your application. This can be used to track user-specific data and interactions within the LI.FI. |
| `routeOptions` | No | | Custom options for routing, applied when using `getQuote`, `getRoutes`, and `getContractCallsQuote` endpoints. These options can be configured once during SDK initialization or passed each time those functions are called. |
| `rpcUrls` | No | | A mapping of chain IDs to arrays of RPC URLs. These URLs might be used for transaction execution and data retrieval. |
| `providers` | No | | An array of SDK providers (e.g., `EthereumProvider()`, `SolanaProvider()`). Providers can also be set later using `client.setProviders()`. |
| `executionOptions` | No | | Default execution options applied to all route executions. Individual executions can override these options. |
| `preloadChains` | No | `true` | A flag to enable preloading of chain data. When enabled, chains are fetched from the API on client creation. |
| `chainsRefetchInterval` | No | | Interval in milliseconds to refetch chain data from the API. |
| `disableVersionCheck` | No | `false` | A flag to disable version checking of the SDK. By default, the SDK checks its version on initialization and logs a message in the console if a new version is available, prompting the user to update the SDK. |
| `debug` | No | `false` | A flag to enable debug logging in the SDK. |
| `requestInterceptor` | No | | A function to intercept and modify outgoing API requests before they are sent. |
| `storage` | No | | A custom storage implementation for persisting SDK data (e.g., chain data, route execution state). Uses `LocalStorageAdapter` in browser environments and `InMemoryStorage` in Node.js by default. |
To learn how to use `routeOptions` for monetization, including configuring
fees, see the [Monetize the SDK](/sdk/monetize-sdk) guide.
Setting up providers is not required if you are using the SDK solely to access
the LI.FI API without quote/route SDK execution functionality and plan to
handle the execution independently. Providers can be added later using the
`client.setProviders()` method.
## Setting custom RPC URLs
```typescript theme={"system"}
import { createClient, ChainId } from '@lifi/sdk';
const client = createClient({
integrator: "Your dApp/company name",
rpcUrls: {
[ChainId.ARB]: ["https://arbitrum-example.node.com/"],
[ChainId.SOL]: ["https://solana-example.node.com/"],
},
});
```
In a production app, it is recommended to pass through your authenticated RPC provider URL (Alchemy, Infura, Ankr, etc).
If no RPC URLs are provided, LI.FI SDK will default to public RPC providers.
Public RPC endpoints (especially Solana) can sometimes rate-limit users depending on location or during periods of heavy load, leading to issues such as incorrectly displaying balances or errors with transaction simulation.
## Client Methods
The SDK client provides various methods to access configuration and manage providers. All methods are called on the client instance.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
```
## Client API
| Method | Description |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `config` | Returns the current SDK configuration (read-only). |
| `providers` | Returns the array of configured providers (read-only). |
| `getProvider(type: ChainType)` | Returns the provider for the specified chain type, or `undefined` if not found. |
| `setProviders(providers: SDKProvider[])` | Sets the providers in the SDK client. If a provider already exists for a chain type, it will be updated with the new information. |
| `setChains(chains: ExtendedChain[])` | Sets the chains in the SDK client. Useful for preloading or overriding chain data. |
| `getChains()` | Returns a promise that resolves to the list of available chains. |
| `getChainById(chainId: ChainId)` | Returns a promise that resolves to the chain configuration for the specified chain ID. Throws an error if the chain is not found. |
| `getRpcUrls()` | Returns a promise that resolves to the RPC URLs mapping for all configured chains. |
| `getRpcUrlsByChainId(chainId: ChainId)` | Returns a promise that resolves to the array of RPC URLs for the specified chain ID. Throws an error if no RPC URLs are found for the chain. |
## Example: Using Client Methods
```typescript theme={"system"}
import { createClient, ChainId } from '@lifi/sdk';
const client = createClient({
integrator: "Your dApp/company name",
});
// Get all available chains
const chains = await client.getChains();
// Get a specific chain
const ethereumChain = await client.getChainById(ChainId.ETH);
// Get RPC URLs for all chains
const rpcUrls = await client.getRpcUrls();
// Get RPC URLs for a specific chain
const arbitrumRpcUrls = await client.getRpcUrlsByChainId(ChainId.ARB);
// Access configuration
console.log(client.config.integrator);
console.log(client.config.apiUrl);
// Set providers (see Configure SDK Providers section)
client.setProviders([
// ... your providers
]);
```
# Multi-VM Support
Source: https://docs.li.fi/sdk/configure-sdk-providers
Seamlessly connecting every ecosystem for your needs
## Introduction to SDK Ecosystem Providers
The LI.FI SDK supports different blockchain ecosystems, allowing you to integrate with EVM, Solana, Bitcoin, Sui, Tron, and Stellar networks. Internally, providers act as abstractions for each ecosystem, handling crucial tasks such as address resolution, balance checking, and transaction handling during route/quote execution.
These ecosystem providers are designed with modularity in mind and are fully tree-shakable, ensuring that they do not add unnecessary weight to your bundle if not used.
The SDK offers six provider packages, each with similar configuration options respective to their ecosystems:
* `@lifi/sdk-provider-ethereum` - For EVM-compatible chains
* `@lifi/sdk-provider-solana` - For Solana
* `@lifi/sdk-provider-bitcoin` - For Bitcoin (UTXO)
* `@lifi/sdk-provider-sui` - For Sui
* `@lifi/sdk-provider-tron` - For Tron
* `@lifi/sdk-provider-stellar` - For Stellar
```typescript theme={"system"}
import { EthereumProvider } from '@lifi/sdk-provider-ethereum';
import { SolanaProvider } from '@lifi/sdk-provider-solana';
import { BitcoinProvider } from '@lifi/sdk-provider-bitcoin';
import { SuiProvider } from '@lifi/sdk-provider-sui';
import { TronProvider } from '@lifi/sdk-provider-tron';
import { StellarProvider } from '@lifi/sdk-provider-stellar';
```
The setup for all providers focuses on utilizing a wallet client, wallet adapter, or a similar wallet interface concept depending on the ecosystem-specific libraries and standards. This unified approach simplifies managing wallets and transactions across EVM-compatible, Solana, Bitcoin, Sui, Tron, and Stellar chains.
### Different types of wallets/accounts
To execute `GET /quote` or `POST /advanced/routes` via a specific provider, that provider must be capable of signing transactions. SDK providers support signing transactions over the following types of wallets/accounts:
* **Local Accounts (e.g. private key/mnemonic wallets).**
Local accounts are wallets managed using private keys or mnemonic phrases. This setup is often used in backend services or scenarios where automated signing and transaction management are required.
* **JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc.).**
Using JSON-RPC accounts involves connecting through a Web3 provider, e.g. `window.ethereum`, and managing the user's account within the browser or mobile context. This setup is popular among dApps UIs and is often used together with libraries like `Wagmi`, `@solana/wallet-adapter-react`, or `@mysten/dapp-kit`.
These account types and interaction methods allow developers to choose the most suitable approach for integrating the SDK with their applications.
## Setup EVM Provider
The EVM provider execution logic is built based on the `Viem` library, using some of its types and terminology.
**Options available for configuring the EVM provider:**
* `getWalletClient`: A function that returns a Viem `Client` instance (typically a `WalletClient` created via `createWalletClient`).
* `switchChain`: A hook for switching between different networks. Returns an updated `Client` for the target chain.
* `disableMessageSigning`: An optional boolean to disable EIP-712 message signing (e.g., for Permit approvals). Useful for wallets or smart accounts that do not support typed data signing.
* `fallbackTransportConfig`: Optional Viem fallback transport configuration.
* `safeApiKey`: Optional Safe API key for Safe multisig wallets.
### Local Accounts
When using local accounts, developers need a predefined list of chains they plan to interact with in order to switch chains during transaction execution. These chains can be either from the `viem/chains` package or fetched from LI.FI API and adopted to viem's `Chain` type.
Here's a basic example using chains from `viem/chains`:
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { EthereumProvider } from '@lifi/sdk-provider-ethereum';
import type { Chain } from 'viem';
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { arbitrum, mainnet, optimism, polygon, scroll } from 'viem/chains';
const account = privateKeyToAccount('PRIVATE_KEY');
const chains = [arbitrum, mainnet, optimism, polygon, scroll];
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http(),
});
const client = createClient({
integrator: 'Your dApp/company name',
});
client.setProviders([
EthereumProvider({
getWalletClient: async () => walletClient,
switchChain: async (chainId) =>
// Switch chain by creating a new wallet client
createWalletClient({
account,
chain: chains.find((chain) => chain.id == chainId) as Chain,
transport: http(),
}),
}),
]);
```
### JSON-RPC Accounts
The best way to interact with JSON-RPC accounts and pass `WalletClient` to the `EthereumProvider` is to use the [Wagmi](https://wagmi.sh/) library. Developers can configure Wagmi chains either by using chains from the `viem/chains` package or fetching chains from the LI.FI API and adapting them to Viem's `Chain` type.
Below is a simplified example of how to set up the EVM provider using chains from the LI.FI API in conjunction with Wagmi and React.
We provide a `useSyncWagmiConfig` hook that synchronizes fetched chains with the Wagmi configuration and updates connectors. Please note that we do not initialize the Wagmi configuration with connectors. Additionally, we set `reconnectOnMount` to `false` since the `reconnect` action will be called within `useSyncWagmiConfig` hook after the chains are synchronized with the configuration and connectors.
```typescript theme={"system"}
import { ChainType, createClient, getChains } from '@lifi/sdk';
import { EthereumProvider } from '@lifi/sdk-provider-ethereum';
import { useSyncWagmiConfig } from '@lifi/wallet-management';
import { useQuery } from '@tanstack/react-query';
import { getWalletClient, switchChain } from '@wagmi/core';
import { type FC, type PropsWithChildren } from 'react';
import { createClient as createViemClient, http } from 'viem';
import { mainnet } from 'viem/chains';
import type { Config, CreateConnectorFn } from 'wagmi';
import { WagmiProvider, createConfig as createWagmiConfig } from 'wagmi';
import { injected } from 'wagmi/connectors';
// List of Wagmi connectors
const connectors: CreateConnectorFn[] = [injected()];
// Create Wagmi config with default chain and without connectors
const wagmiConfig: Config = createWagmiConfig({
chains: [mainnet],
client({ chain }) {
return createViemClient({ chain, transport: http() });
},
});
// Create SDK client
const client = createClient({
integrator: 'Your dApp/company name',
});
// Set up EVM provider using Wagmi actions and configuration
client.setProviders([
EthereumProvider({
getWalletClient: () => getWalletClient(wagmiConfig),
switchChain: async (chainId) => {
const chain = await switchChain(wagmiConfig, { chainId });
return getWalletClient(wagmiConfig, { chainId: chain.id });
},
}),
]);
export const CustomWagmiProvider: FC = ({ children }) => {
// Load EVM chains from LI.FI API using getChains action from LI.FI SDK
const { data: chains } = useQuery({
queryKey: ['chains'] as const,
queryFn: async () => {
const chains = await getChains(client, {
chainTypes: [ChainType.EVM],
});
return chains;
},
});
// Synchronize fetched chains with Wagmi config and update connectors
useSyncWagmiConfig(wagmiConfig, connectors, chains);
return (
{children}
);
};
```
### Update provider configuration
Additionally, providers allow for dynamic updates to its initial configuration via the `setOptions` function.
Here's an example of how to modify the initial configuration for `EthereumProvider`:
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { EthereumProvider } from '@lifi/sdk-provider-ethereum';
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { arbitrum, mainnet } from 'viem/chains';
const account = privateKeyToAccount('PRIVATE_KEY');
const mainnetClient = createWalletClient({
account,
chain: mainnet,
transport: http(),
});
const evmProvider = EthereumProvider({
getWalletClient: async () => mainnetClient,
});
const client = createClient({
integrator: 'Your dApp/company name',
});
client.setProviders([evmProvider]);
const arbitrumClient = createWalletClient({
account,
chain: arbitrum,
transport: http(),
});
evmProvider.setOptions({
getWalletClient: async () => arbitrumClient,
});
```
### Support for Ethers.js and other alternatives
Developers can still use Ethers.js or any other alternative Web3 library in their project and [convert](https://viem.sh/docs/ethers-migration) `Signer`/`Provider` objects to Viem's `WalletClient` before passing it to the EVM provider configuration.
## Setup Solana Provider
The Solana provider execution logic is built based on the [@solana/kit](https://github.com/solana-labs/solana-web3.js) library and the [Wallet Standard](https://github.com/wallet-standard/wallet-standard), using some of their types and terminology.
**Options available for configuring the Solana provider:**
* `getWallet`: A function that returns a [Wallet Standard](https://github.com/wallet-standard/wallet-standard) `Wallet` instance.
* `skipSimulation`: An optional boolean to skip transaction simulation before sending (default: `false`).
### Local Wallet
Standard Solana libraries do not offer a built-in method for creating a wallet-standard wallet directly from a private key. To address this limitation, we provide the `KeypairWalletAdapter`. This custom adapter enables users to create a wallet from a base58-encoded secret key.
It is worth noting that the `KeypairWalletAdapter` is designed specifically for backend or testing purposes and should not be used in user-facing code to prevent the risk of exposing your private key.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { SolanaProvider, KeypairWalletAdapter } from '@lifi/sdk-provider-solana';
const wallet = new KeypairWalletAdapter('BASE58_SECRET_KEY');
await wallet.connect();
const client = createClient({
integrator: 'Your dApp/company name',
});
client.setProviders([
SolanaProvider({
getWallet: async () => wallet,
}),
]);
```
### JSON-RPC Wallet
To interact with user wallets and pass a wallet-standard `Wallet` to the Solana provider, we recommend using the [@solana/wallet-adapter-react](https://github.com/anza-xyz/wallet-adapter) library. The `wallet.adapter` from `useWallet()` implements the wallet-standard `Wallet` interface. Unlike Wagmi, Solana configuration for React does not have global configurations. Therefore, we need to use React hooks to update the SDK configuration at runtime.
Below is a simplified example of how to set up the Solana provider.
```typescript SDKProviders.tsx theme={"system"}
import { createClient } from '@lifi/sdk';
import { SolanaProvider } from '@lifi/sdk-provider-solana';
import { useWallet } from '@solana/wallet-adapter-react';
import type { Wallet } from '@wallet-standard/base';
import { useEffect } from 'react';
const client = createClient({
integrator: 'Your dApp/company name',
});
export const SDKProviders = () => {
const { wallet } = useWallet();
useEffect(() => {
// Configure SDK Providers
client.setProviders([
SolanaProvider({
async getWallet() {
return wallet?.adapter as unknown as Wallet;
},
}),
]);
}, [wallet?.adapter]);
return null;
};
```
```typescript SolanaProvider.tsx theme={"system"}
import type { Adapter } from '@solana/wallet-adapter-base';
import { WalletAdapterNetwork } from '@solana/wallet-adapter-base';
import {
ConnectionProvider,
WalletProvider,
} from '@solana/wallet-adapter-react';
import { clusterApiUrl } from '@solana/web3.js';
import { type FC, type PropsWithChildren } from 'react';
import { SDKProviders } from './SDKProviders.js';
const endpoint = clusterApiUrl(WalletAdapterNetwork.Mainnet);
/**
* Wallets that implement either of these standards will be available automatically.
*
* - Solana Mobile Stack Mobile Wallet Adapter Protocol
* (https://github.com/solana-mobile/mobile-wallet-adapter)
* - Solana Wallet Standard
* (https://github.com/solana-labs/wallet-standard)
*
* If you wish to support a wallet that supports neither of those standards,
* instantiate its legacy wallet adapter here. Common legacy adapters can be found
* in the npm package `@solana/wallet-adapter-wallets`.
*/
const wallets: Adapter[] = [];
export const SVMBaseProvider: FC = ({ children }) => {
return (
{/* Configure Solana SDK provider */}
{children}
);
};
```
## Setup Sui Provider
The Sui provider execution logic is built based on the [@mysten/sui](https://sdk.mystenlabs.com/typescript) v2 library, using some of its types and terminology.
**Options available for configuring the Sui provider:**
* `getClient`: A function that returns a `ClientWithCoreApi` instance (from `@mysten/sui/client`).
* `getSigner`: A function that returns a `Signer` instance (from `@mysten/sui/cryptography`).
### JSON-RPC Wallet
To interact with user wallets (like Sui Wallet, etc.) and pass a client and signer to the Sui provider, we recommend using the [@mysten/dapp-kit-react](https://sdk.mystenlabs.com/dapp-kit) library.
The legacy `@mysten/dapp-kit` package (`SuiClientProvider` / `WalletProvider` / `createNetworkConfig`) is deprecated and only supports the sunset JSON-RPC API. New integrations should use `@mysten/dapp-kit-react` with `createDAppKit` / `DAppKitProvider`.
Below is a simplified example of how to set up the Sui provider with user wallets.
```typescript SDKProviders.tsx theme={"system"}
import { createClient } from '@lifi/sdk';
import { SuiProvider } from '@lifi/sdk-provider-sui';
import { CurrentAccountSigner, useDAppKit } from '@mysten/dapp-kit-react';
import { useEffect } from 'react';
const client = createClient({
integrator: 'Your dApp/company name',
});
export const SDKProviders = () => {
const dAppKit = useDAppKit();
useEffect(() => {
// Configure Sui SDK provider
client.setProviders([
SuiProvider({
// getClient returns a ClientWithCoreApi from @mysten/sui/client
async getClient() {
return dAppKit.getClient();
},
// getSigner returns a Signer bound to the connected account
async getSigner() {
return new CurrentAccountSigner(dAppKit);
},
}),
]);
}, [dAppKit]);
return null;
};
```
```typescript SuiProvider.tsx theme={"system"}
import { createDAppKit, DAppKitProvider } from '@mysten/dapp-kit-react';
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { getJsonRpcFullnodeUrl } from '@mysten/sui/jsonRpc';
import { type FC, type PropsWithChildren } from 'react';
import { SDKProviders } from './SDKProviders.js';
const dAppKit = createDAppKit({
networks: ['mainnet'],
createClient: (network) =>
new SuiGrpcClient({
network,
baseUrl: getJsonRpcFullnodeUrl(network),
}),
autoConnect: true,
storage: localStorage,
storageKey: 'lifi-sui-dapp-kit',
});
export const SuiBaseProvider: FC = ({ children }) => {
return (
{/* Configure Sui SDK provider */}
{children}
);
};
```
## Setup UTXO (Bitcoin) Provider
The Bitcoin provider execution logic is built based on the [Bigmi](https://github.com/lifinance/bigmi) library, using some of its types and terminology.
**Options available for configuring the UTXO provider:**
* `getWalletClient`: A function that returns a `Client` instance (from `@bigmi/core`).
### JSON-RPC Wallet
To interact with user wallets like Phantom, Xverse, use the `getConnectorClient` action to return the Bigmi Client object required by the SDK.
```typescript SDKProvider.tsx theme={"system"}
import { createClient } from '@lifi/sdk';
import { BitcoinProvider } from '@lifi/sdk-provider-bitcoin';
import { getConnectorClient } from '@bigmi/client';
import { useConfig } from '@bigmi/react';
import { useEffect } from 'react';
const client = createClient({
integrator: 'Your dApp/company name',
});
export const SDKProviders = () => {
const bigmiConfig = useConfig();
useEffect(() => {
// Configure SDK Provider
client.setProviders([
BitcoinProvider({
async getWalletClient() {
return getConnectorClient(bigmiConfig)
},
}),
]);
}, [bigmiConfig]);
return null;
};
```
```typescript UTXOProvider.tsx theme={"system"}
import { bitcoin, createClient, http } from '@bigmi/core'
import { createConfig, phantom, type CreateConnectorFn, type Config } from '@bigmi/client'
import { BigmiProvider } from '@bigmi/react'
import { type FC, type PropsWithChildren } from 'react'
import { SDKProviders } from './SDKProviders.js'
const connectors: CreateConnectorFn[] = [phantom()]
const bigmiConfig = createConfig({
chains: [bitcoin],
connectors,
client({ chain }) {
return createClient({ chain, transport: http() })
}
}) as Config
export const UTXOProvider: FC = ({ children }) => {
return (
{children}
)
}
```
## Setup Tron Provider
The Tron provider execution logic is built based on the [TronWeb](https://tronweb.network/) library and the [@tronweb3/tronwallet-abstract-adapter](https://github.com/tronweb3/tronwallet-adapter) adapter interface.
**Options available for configuring the Tron provider:**
* `getWallet`: A function that returns an `Adapter` instance (from `@tronweb3/tronwallet-abstract-adapter`).
* `multicallBatchSize`: An optional number to configure the batch size for multicall balance requests.
### JSON-RPC Wallet
To interact with user wallets (like TronLink), pass an `Adapter` to the Tron provider using the [@tronweb3/tronwallet-adapter-react-hooks](https://github.com/tronweb3/tronwallet-adapter) library.
Below is a simplified example of how to set up the Tron provider.
```typescript SDKProviders.tsx theme={"system"}
import { createClient } from '@lifi/sdk';
import { TronProvider } from '@lifi/sdk-provider-tron';
import { useWallet } from '@tronweb3/tronwallet-adapter-react-hooks';
import { useEffect } from 'react';
const client = createClient({
integrator: 'Your dApp/company name',
});
export const SDKProviders = () => {
const { wallet } = useWallet();
useEffect(() => {
if (wallet?.adapter) {
client.setProviders([
TronProvider({
async getWallet() {
return wallet.adapter;
},
}),
]);
}
}, [wallet?.adapter]);
return null;
};
```
## Setup Stellar Provider
The Stellar provider execution logic is built based on the [@stellar/stellar-sdk](https://github.com/stellar/js-stellar-sdk) library, using some of its types and terminology.
**Options available for configuring the Stellar provider:**
* `getWallet`: A function that returns a `StellarWallet` instance.
* `networkPassphrase`: An optional network passphrase for the Stellar network this provider targets. It defaults to the exported `DEFAULT_NETWORK_PASSPHRASE`, which is the public network.
Balance reads always follow the `networkPassphrase` option. Signing prefers that option and falls back to the connected wallet's own passphrase. The provider refuses to sign when the two disagree, so a wallet on the wrong network fails before the user is asked to sign.
Stellar has no wallet-adapter standard comparable to Wagmi or the Solana Wallet Standard, so the provider takes a small wallet interface rather than a library type:
```typescript theme={"system"}
interface StellarWallet {
// The connected G-address that signs and pays
address: string;
// The network passphrase the wallet signs against
networkPassphrase: string;
signTransaction: (
xdr: string,
opts?: { networkPassphrase?: string; address?: string }
) => Promise<{ signedTxXdr: string; signerAddress?: string }>;
// Optional. LI.FI router routes use source-account auth, so the SDK never calls this.
signAuthEntry?: (
authEntry: string,
opts?: { networkPassphrase?: string; address?: string }
) => Promise<{ signedAuthEntry: string; signerAddress?: string }>;
}
```
The provider also resolves SEP-2 federation addresses (`name*domain.com`) to a `G...` address, and rejects muxed `M...` and contract `C...` addresses as senders.
### JSON-RPC Wallet
To discover and connect browser wallets such as Freighter, xBull, or Lobstr, we recommend the [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit), then adapting its signing surface to `StellarWallet`.
Below is a simplified example. It is the same adapter the LI.FI Widget uses internally.
```typescript stellarKit.ts theme={"system"}
import { Networks, StellarWalletsKit } from '@creit.tech/stellar-wallets-kit';
import { FreighterModule } from '@creit.tech/stellar-wallets-kit/modules/freighter';
import { LobstrModule } from '@creit.tech/stellar-wallets-kit/modules/lobstr';
import { xBullModule } from '@creit.tech/stellar-wallets-kit/modules/xbull';
export const networkPassphrase = Networks.PUBLIC;
StellarWalletsKit.init({
modules: [new FreighterModule(), new xBullModule(), new LobstrModule()],
network: networkPassphrase,
});
// Call this from your "connect wallet" handler with a product id from
// StellarWalletsKit.refreshSupportedWallets().
export const connectWallet = async (walletId: string): Promise => {
StellarWalletsKit.setWallet(walletId);
const { address } = await StellarWalletsKit.fetchAddress();
return address;
};
```
```typescript SDKProviders.ts theme={"system"}
import { createClient } from '@lifi/sdk';
import { StellarProvider } from '@lifi/sdk-provider-stellar';
import type { StellarWallet } from '@lifi/sdk-provider-stellar';
import { StellarWalletsKit } from '@creit.tech/stellar-wallets-kit';
import { networkPassphrase } from './stellarKit.js';
const client = createClient({
integrator: 'Your dApp/company name',
});
// Your app supplies the address of the account the user connected.
export const setStellarProvider = (getAddress: () => string | undefined) => {
client.setProviders([
StellarProvider({
networkPassphrase,
async getWallet(): Promise {
const address = getAddress();
if (!address) {
throw new Error('Wallet not connected');
}
return {
address,
// The wallet's own network, so the SDK can refuse to sign a mismatch.
networkPassphrase: (await StellarWalletsKit.getNetwork())
.networkPassphrase,
signTransaction: (xdr, opts) =>
StellarWalletsKit.signTransaction(xdr, {
address,
networkPassphrase,
...opts,
}),
};
},
}),
]);
};
```
Only some wallets implement `getNetwork`. When a wallet does not, read the passphrase from your own configuration instead, and keep it equal to the `networkPassphrase` option.
### Update provider configuration
Like every other provider, the Stellar provider accepts dynamic updates through `setOptions`:
```typescript theme={"system"}
import { StellarProvider } from '@lifi/sdk-provider-stellar';
const stellarProvider = StellarProvider();
stellarProvider.setOptions({
getWallet: async () => wallet,
});
```
# Execute Routes/Quotes
Source: https://docs.li.fi/sdk/execute-routes
We allow you to execute any on-chain or cross-chain swap and bridging transfer and a combination of both.
The LI.FI SDK offers functionality to execute routes and quotes. In this guide, you'll learn how to utilize the SDK's features to handle complex cross-chain transfers, manage execution settings, and control the transaction flow.
## Execute route
Let's say you have obtained the route. Refer to [Request Routes/Quotes](/sdk/request-routes) for more details.
Please make sure you've configured SDK with EVM/Solana providers. Refer to [Configure SDK Providers](/sdk/configure-sdk-providers) for more details.
Now, to execute the route, we can use the `executeRoute` function. Here is a simplified example of how to use it:
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { executeRoute, getRoutes } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const result = await getRoutes(client, {
fromChainId: 42161, // Arbitrum
toChainId: 10, // Optimism
fromTokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toTokenAddress: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
fromAmount: '10000000', // 10 USDC
// The address from which the tokens are being transferred.
fromAddress: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0',
});
const route = result.routes[0];
const executedRoute = await executeRoute(client, route, {
// Gets called once the route object gets new updates
updateRouteHook(route) {
console.log(route);
},
});
```
The `executeRoute` function internally manages allowance and balance checks, chain switching, transaction data retrieval, transactions submission, and transactions status tracking.
* **Parameters:**
* `client` (`SDKClient`): The SDK client instance.
* `route` (`Route`): The route to be executed.
* `executionOptions` (`ExecutionOptions`, optional): An object containing settings and callbacks for execution.
* **Returns:**
* `Promise`: Resolves when execution is done or halted and rejects when it is failed.
### Execution Options
All execution options are optional, but we recommend reviewing their descriptions to determine which ones may be beneficial for your use case.
Certain options, such as [acceptExchangeRateUpdateHook](/sdk/execute-routes#acceptexchangerateupdatehook), can be crucial for successfully completing a transfer if the exchange rate changes during the process.
#### `updateRouteHook`
The function is called when the route object changes during execution. This function allows you to handle route updates, track execution status, transaction hashes, etc. See [Monitor route execution](/sdk/execute-routes#monitor-route-execution) section for more details.
* **Parameters**:
* `updatedRoute` (`RouteExtended`): The updated route object.
#### `updateTransactionRequestHook`
The function is intended for advanced usage, and it allows you to modify swap/bridge transaction requests or token approval requests before they are sent, e.g., updating gas information.
* **Parameters**:
* `updatedTxRequest` (`TransactionRequestParameters`): The transaction request parameters need to be updated.
* **Returns**: `Promise`: The modified transaction parameters.
#### `acceptExchangeRateUpdateHook`
This function is called whenever the exchange rate changes during a swap or bridge operation. It provides you with the old and new amount values. To continue the execution, you should return `true`. If this hook is not provided or if you return `false`, the SDK will throw an error. This hook is an ideal place to prompt your users to accept the new exchange rate.
* **Parameters**: An `ExchangeRateUpdateParams` object with the following properties:
* `toToken` (`Token`): The destination token.
* `oldToAmount` (`string`): The previous amount of the target token.
* `newToAmount` (`string`): The new amount of the target token.
* **Returns**: `Promise`: Whether the update is accepted.
* **Throws:** `TransactionError: Exchange rate has changed!`
#### `getContractCalls`
A hook used to dynamically provide contract calls during execution. This is primarily used for [Composer](/composer/overview) integrations where contract call data needs to be generated at execution time based on the actual bridged amounts.
* **Parameters**: A `ContractCallParams` object with the following properties:
* `fromChainId` (`number`): The source chain ID.
* `toChainId` (`number`): The destination chain ID.
* `fromTokenAddress` (`string`): The source token address.
* `toTokenAddress` (`string`): The destination token address.
* `fromAddress` (`string`): The sender address.
* `toAddress` (`string`, optional): The receiver address.
* `fromAmount` (`bigint`): The source amount.
* `toAmount` (`bigint`): The destination amount.
* `slippage` (`number`, optional): The slippage tolerance.
* **Returns**: `Promise`: An object containing the `contractCalls` array and optional `patcher` flag.
#### `adjustZeroOutputFromPreviousStep`
A boolean flag that, when set to `true`, adjusts zero output amounts from a previous step. This can be useful in multi-step routes where intermediate steps may report zero output.
* **Type**: `boolean`
* **Default**: `undefined`
#### `executeInBackground`
A boolean flag indicating whether the route execution should continue in the background without requiring user interaction. See [Update route execution](/sdk/execute-routes#update-route-execution) and [Resume route execution](/sdk/execute-routes#resume-route-execution) sections for details on how to utilize this option.
* **Type**: `boolean`
* **Default**: `false`
In previous SDK versions, `switchChainHook` and `disableMessageSigning` were part of `ExecutionOptions`. In v4, these options have been moved to `EthereumProviderOptions` and should be configured when setting up the [EVM provider](/sdk/configure-sdk-providers#setup-evm-provider). Note that `switchChainHook` has been renamed to `switchChain` on the provider.
EIP-7702 delegated smart wallets currently require source-chain native gas because gasless or relayer routes are not offered for this wallet type. See [EIP-7702 delegated wallet troubleshooting](/faqs/troubleshooting#eip-7702-delegated-smart-wallets).
## Manage route execution
After starting route execution, there might be use cases when you need to adjust execution settings, stop execution and come back later, or move execution to the background. We provide several functions to achieve that.
### Update route execution
The `updateRouteExecution` function is used to update the settings of an ongoing route execution.
One common use case is to push the execution to the background, for example, when a user navigates away from the execution page in your dApp. When this function is called, the execution will continue until it requires user interaction (e.g., signing a transaction or switching the chain). At that point, the execution will halt, and the `executeRoute` promise will be resolved.
To move the execution back to the foreground and make it active again, you can call `resumeRoute` with the same route object. The execution will then resume from where it was halted.
```typescript theme={"system"}
import { updateRouteExecution } from '@lifi/sdk';
updateRouteExecution(route, { executeInBackground: true });
```
* **Parameters:**
* `route` (`Route`): The active route to be updated.
* `executionOptions` (`ExecutionOptions`, **required**): An object containing settings and callbacks for execution.
### Resume route execution
The `resumeRoute` function is used to resume a halted, aborted, or failed route execution from the point where it stopped. It is crucial to call `resumeRoute` with the latest active route object returned from the `executeRoute` function or the most recent version of the updated route object from the `updateRouteHook`.
#### Common Use Cases
* **Move Execution to Foreground**: When a user navigates back to the execution page in your dApp, you can call this function to move the execution back to the foreground. The execution will resume from where it was halted.
* **Page Refresh**: If the user refreshes the page in the middle of the execution process, calling this function will attempt to resume the execution.
* **User Interaction Errors**: If the user rejects a chain switch, declines to sign a transaction, or encounters any other error, you can call this function to attempt to resume the execution.
```typescript theme={"system"}
import { resumeRoute } from '@lifi/sdk';
const route = await resumeRoute(client, route, { executeInBackground: false });
```
* **Parameters:**
* `client` (`SDKClient`): The SDK client instance.
* `route` (`Route`): The route to be resumed to execution.
* `executionOptions` (`ExecutionOptions`, optional): An object containing settings and callbacks for execution.
* **Returns:**
* `Promise`: Resolves when execution is done or halted and rejects when it is failed.
### Stop route execution
The `stopRouteExecution` function is used to stop the ongoing execution of an active route. It stops any remaining user interaction within the ongoing execution and removes the route from the execution queue. However, if a transaction has already been signed and sent by the user, it will be executed on-chain.
```typescript theme={"system"}
import { stopRouteExecution } from '@lifi/sdk';
const stoppedRoute = stopRouteExecution(route);
```
* **Parameters:**
* `route` (`Route`): The route that is currently being executed and needs to be stopped.
* **Returns:**
* `Route`: The route object that was stopped.
## Monitor route execution
Monitoring route execution is important and we provide tools for tracking progress, receiving data updates, accessing transaction hashes, and explorer links.
### Brief description of steps
A `route` object includes multiple `step` objects, each representing a set of transactions that should be completed in the specified order. Each step can include multiple transactions that require a signature, such as an allowance transaction followed by the main swap or bridge transaction.
### Understanding the `execution` object
Each `step` within a `route` has an `execution` object. This object contains all the necessary information to track the execution progress of that step. The `execution` object has an `actions` array where each entry represents a sequential stage in the execution. The latest action entry contains the most recent information about the execution stage.
### Actions array
The `actions` array within the `execution` object details each step's progression. Each `ExecutionAction` object has a `type` and `status` and might also include a transaction hash and a link to a blockchain explorer after the user signs the transaction.
The possible action types are:
* `CHECK_ALLOWANCE` - Checking token allowance
* `RESET_ALLOWANCE` - Resetting token allowance (when current allowance must be reset before setting a new one)
* `SET_ALLOWANCE` - Setting token allowance
* `PERMIT` - ERC-2612 permit message signing
* `NATIVE_PERMIT` - Native permit message signing
* `SWAP` - Swap transaction
* `CROSS_CHAIN` - Bridge transaction
* `RECEIVING_CHAIN` - Waiting for destination chain confirmation
You can use `getActionMessage()` and `getSubstatusMessage()` from `@lifi/sdk` to get human-readable messages for each action type and status.
### Tracking progress
To monitor the execution progress, you leverage the `updateRouteHook` callback and iterate through the route steps, checking their `execution` objects. Look at the `actions` array to get the latest information about the execution stage. The most recent entry in the `actions` array will contain the latest transaction hash, status, and other relevant details.
### Example to access transaction hashes
```typescript theme={"system"}
const getTransactionLinks = (route: RouteExtended) => {
route.steps.forEach((step, index) => {
step.execution?.actions.forEach((action) => {
if (action.txHash) {
console.log(
`Transaction Hash for Step ${index + 1}, Action ${action.type}:`,
action.txHash
);
}
});
});
};
const executedRoute = await executeRoute(client, route, {
updateRouteHook(route) {
getTransactionLinks(route);
},
});
```
### Get active routes
To get routes that are currently being executed (active), you can use `getActiveRoutes` and `getActiveRoute` functions.
```typescript theme={"system"}
import { getActiveRoute, getActiveRoutes, RouteExtended } from '@lifi/sdk';
const activeRoutes: RouteExtended[] = getActiveRoutes();
const routeId = activeRoutes[0].id;
const activeRoute = getActiveRoute(routeId);
```
## Execute quote
To execute a quote using the `executeRoute`, you need to convert it to a route object first. We provide `convertQuoteToRoute` helper function to transform quote objects to route objects. This applies to both standard and contract call quotes.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { convertQuoteToRoute, executeRoute, getQuote } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const quoteRequest = {
fromChain: 42161, // Arbitrum
toChain: 10, // Optimism
fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
fromAmount: '10000000', // 10 USDC
// The address from which the tokens are being transferred.
fromAddress: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0',
};
const quote = await getQuote(client, quoteRequest);
const route = convertQuoteToRoute(quote);
const executedRoute = await executeRoute(client, route, {
// Gets called once the route object gets new updates
updateRouteHook(route) {
console.log(route);
},
});
```
## Manual route execution
In addition to using the `executeRoute` function, you can execute routes and quotes manually. This approach requires developers to handle the logic for obtaining transaction data, switching chains, sending transactions, and tracking transaction status independently.
Initially, when route objects are requested, they do not include transaction data. This is because multiple route options are provided, and generating transaction data for all options would substantially delay the response. Each route consists of multiple steps, and once a user selects a route, transaction data for each step should be requested individually using the `getStepTransaction` function (see example below). Each step should be executed sequentially, as each step depends on the outcome of the previous one.
On the other hand, quote objects are returned with transaction data included, so the `getStepTransaction` call is not necessary, and they can be executed immediately.
After sending a transaction using the obtained transaction data, you can track the status of the transaction using the `getStatus` function. This function helps you monitor the progress and completion of each transaction. Read more [Status of a Transaction](/api-reference/check-the-status-of-a-cross-chain-transfer).
Here's a simplified example. For the sake of simplicity, this example omits balance checks, transaction replacements, error handling, chain switching, etc. However, in a real implementation, you should include these additional functionalities to have a robust solution and ensure reliability.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { getStepTransaction, getStatus } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
// Simplified example function to execute each step of the route sequentially
async function executeRouteSteps(route) {
for (const step of route.steps) {
// Request transaction data for the current step
const stepWithTransaction = await getStepTransaction(client, step);
// Send the transaction (e.g. using Viem)
const transactionHash = await sendTransaction(stepWithTransaction.transactionRequest);
// Monitor the status of the transaction
let status;
do {
const result = await getStatus(client, {
txHash: transactionHash,
fromChain: step.action.fromChainId,
toChain: step.action.toChainId,
bridge: step.tool,
});
status = result.status;
console.log(`Transaction status for ${transactionHash}:`, status);
// Wait for a short period before checking the status again
await new Promise(resolve => setTimeout(resolve, 5000));
} while (status !== 'DONE' && status !== 'FAILED');
if (status === 'FAILED') {
console.error(`Transaction ${transactionHash} failed`);
return;
}
}
console.log('All steps executed successfully');
}
```
#### `getStepTransaction`
* **Parameters:**
* `client` (`SDKClient`): The SDK client instance.
* `step` (`LiFiStep`): The step object for which we need to get transaction data.
* `options` (`RequestOptions`, optional): An object containing request options, such as `AbortSignal`, which can be used to cancel the request if necessary.
* **Returns:**
* `Promise`: A promise that resolves to the step object containing the transaction data.
#### `getStatus`
* **Parameters:**
* `client` (`SDKClient`): The SDK client instance.
* `params` (`GetStatusRequest`): The parameters for checking the status include the transaction hash, source and destination chain IDs, and the DEX or bridge name.
* `options` (`RequestOptions`, optional): An object containing request options, such as `AbortSignal`, which can be used to cancel the request if necessary.
* **Returns:**
* `Promise`: A promise that resolves to a status response containing all relevant information about the transfer.
# Install LI.FI SDK
Source: https://docs.li.fi/sdk/installing-the-sdk
Integrate our LI.FI SDK to your dApp/Wallet/Swap UI
The LI.FI SDK package provides access to the LI.FI API to find and execute the best on-chain and cross-chain routes across various bridges and exchanges.
## Installation
```typescript yarn theme={"system"}
yarn add @lifi/sdk
```
```typescript pnpm theme={"system"}
pnpm add @lifi/sdk
```
```typescript bun theme={"system"}
bun add @lifi/sdk
```
```typescript npm theme={"system"}
npm install @lifi/sdk
```
### Ecosystem Providers
To execute transactions, install the provider packages for the ecosystems you need:
```typescript yarn theme={"system"}
yarn add @lifi/sdk-provider-ethereum viem # For EVM chains
yarn add @lifi/sdk-provider-solana @wallet-standard/base # For Solana
yarn add @lifi/sdk-provider-bitcoin @bigmi/core # For Bitcoin
yarn add @lifi/sdk-provider-sui @mysten/sui # For Sui
yarn add @lifi/sdk-provider-tron @tronweb3/tronwallet-abstract-adapter # For Tron
yarn add @lifi/sdk-provider-stellar # For Stellar (no ecosystem library needed)
```
```typescript pnpm theme={"system"}
pnpm add @lifi/sdk-provider-ethereum viem # For EVM chains
pnpm add @lifi/sdk-provider-solana @wallet-standard/base # For Solana
pnpm add @lifi/sdk-provider-bitcoin @bigmi/core # For Bitcoin
pnpm add @lifi/sdk-provider-sui @mysten/sui # For Sui
pnpm add @lifi/sdk-provider-tron @tronweb3/tronwallet-abstract-adapter # For Tron
pnpm add @lifi/sdk-provider-stellar # For Stellar (no ecosystem library needed)
```
```typescript npm theme={"system"}
npm install @lifi/sdk-provider-ethereum viem # For EVM chains
npm install @lifi/sdk-provider-solana @wallet-standard/base # For Solana
npm install @lifi/sdk-provider-bitcoin @bigmi/core # For Bitcoin
npm install @lifi/sdk-provider-sui @mysten/sui # For Sui
npm install @lifi/sdk-provider-tron @tronweb3/tronwallet-abstract-adapter # For Tron
npm install @lifi/sdk-provider-stellar # For Stellar (no ecosystem library needed)
```
Provider packages are only required if you want to execute routes/quotes through the SDK. If you only need to request routes, quotes, or other API data, the core `@lifi/sdk` package is sufficient.
Each provider bundles its ecosystem library, but you configure a provider by handing it a wallet or client object that **you** construct. Anything you import in your own code must be a direct dependency — package managers that do not hoist transitive packages, such as pnpm, will otherwise fail to resolve it. That is why the ecosystem library is installed alongside the provider above.
The Stellar provider is the exception: it takes a small `StellarWallet` interface it defines itself. To discover and connect browser wallets such as Freighter, xBull, or Lobstr, also install [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit) (`@creit.tech/stellar-wallets-kit`), which the Stellar examples import.
Check out our complete examples in the [SDK repository](https://github.com/lifinance/sdk/tree/main/examples), and feel free to [file an issue](https://github.com/lifinance/sdk/issues) if you encounter any problems.
## Quick Start
Firstly, create SDK client with your integrator string.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
```
Now you can interact with the SDK and for example request a quote.
```typescript theme={"system"}
import { ChainId, getQuote } from '@lifi/sdk';
const quote = await getQuote(client, {
fromAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
fromChain: ChainId.ARB,
toChain: ChainId.OPT,
fromToken: '0x0000000000000000000000000000000000000000',
toToken: '0x0000000000000000000000000000000000000000',
fromAmount: '1000000000000000000',
});
```
You can learn more about [configuring the SDK](/sdk/configure-sdk) in the next section.
# Migrate from v2 to v3
Source: https://docs.li.fi/sdk/migrate-v2-to-v3
Migration guide for upgrading LI.FI SDK v2 to v3
Looking to upgrade to the latest version? See the [Migrate from v3 to v4](/sdk/migrate-v3-to-v4) guide for the most recent breaking changes.
## Overview
**LI.FI SDK v3** has undergone a major update, improving compatibility with popular libraries like [Viem](https://viem.sh/) and adding new features, including support for multiple ecosystems, starting with [Solana](https://github.com/anza-xyz/wallet-adapter). Consequently, as detailed in this guide, you need to be aware of some breaking changes and deprecations. We also recommend reviewing the updated documentation for additional new features not covered here.
To get started, install the latest version of LI.FI SDK.
```typescript yarn theme={"system"}
yarn add @lifi/sdk
```
```typescript pnpm theme={"system"}
pnpm add @lifi/sdk
```
```typescript bun theme={"system"}
bun add @lifi/sdk
```
```typescript npm theme={"system"}
npm install @lifi/sdk
```
## Configuration
We have made significant changes to how the LI.FI SDK is configured. It is no longer class-based, so you don't need to create, maintain, and share a LiFi class instance to use SDK functionality. Instead, it is now function-based, allowing you to configure it once and update the configuration from any place without needing to maintain or share the configuration object's reference. You can simply import the necessary functions from the package wherever needed.
In this example, you can call the `getQuote` function from anywhere using SDK v3, whereas with SDK v2, you had to share a created `LiFi` class instance and call `getQuote` as a method of that class.
```typescript theme={"system"}
// SDK v2
import { ChainId, LiFi } from '@lifi/sdk';
const lifi = new LiFi({
integrator: 'Your dApp/company name',
});
const quote = await lifi.getQuote({
fromAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
fromChain: ChainId.ARB,
toChain: ChainId.OPT,
fromToken: '0x0000000000000000000000000000000000000000',
toToken: '0x0000000000000000000000000000000000000000',
fromAmount: '1000000000000000000',
});
// SDK v3
import { ChainId, createConfig, getQuote } from '@lifi/sdk';
createConfig({
integrator: 'Your dApp/company name',
});
const quote = await getQuote({
fromAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
fromChain: ChainId.ARB,
toChain: ChainId.OPT,
fromToken: '0x0000000000000000000000000000000000000000',
toToken: '0x0000000000000000000000000000000000000000',
fromAmount: '1000000000000000000',
});
```
Due to these changes, the configuration-related functions from the `LiFi` interface are no longer needed. The previous interface included:
```typescript theme={"system"}
interface LiFi {
getConfig(): Config
getConfigAsync(): Promise
getRpcProvider(chainId: number, archive: boolean): Promise
setConfig(configUpdate: ConfigUpdate): Config
// ...
}
```
Now, you can import the configuration object directly and make any necessary changes to it. This simplifies the process and improves code maintainability.
```typescript theme={"system"}
// SDK v2
import { ChainId, LiFi } from '@lifi/sdk';
const lifi = new LiFi({
integrator: 'Your dApp/company name',
});
lifi.setConfig({
integrator: 'Your dApp/company name',
});
// SDK v3
import { config } from '@lifi/sdk';
config.set({
integrator: 'Your dApp/company name',
});
```
## Renamed methods
All methods previously supported by the class-based approach are now individual functions that you can import directly from the `@lifi/sdk` package. Some of these methods have been renamed to better reflect their functionality or to have a more concise name.
* `getContractCallQuote` -> `getContractCallsQuote`
* `getTokenApproval` -> `getTokenAllowance`
* `bulkGetTokenApproval` -> `getTokenAllowanceMulticall`
* `approveToken` -> `setTokenAllowance`
* `moveExecutionToBackground` -> `updateRouteExecution`
See [Request contract call Quote](/sdk/request-routes#request-contract-call-quote), [Manage route execution](/sdk/execute-routes#manage-route-execution) and [Token Management](/sdk/token-management) for more details.
## Moving from Ethers.js to Viem
Since the first version of the LI.FI SDK, we have relied on the `Ethers.js` library for all EVM-related interactions. However, as the industry evolves, `Viem` is gaining wide adoption and starting to replace `Ethers.js`.
To ensure our product remains robust and future-proof, we have decided to transition our entire stack to a more reliable solution - [`Viem`](https://viem.sh/) ([`Wagmi`](https://wagmi.sh/) for the Widget).
Please see the [Ethers v5 → viem Migration Guide](https://viem.sh/docs/ethers-migration) for more details. Among other notable changes, this transition also replaces all BigNumber utilities we previously used in v2 with the native `bigint` primitive.
## Multiple ecosystem support
LI.FI SDK v3 now supports two ecosystems — EVM and Solana — with more on the way. To accommodate these new ecosystems, we have introduced the concept of ecosystem providers in SDK v3. We extracted all EVM-related functionality from SDK v2 and integrated it into an EVM provider. Additionally, a Solana provider has been added to enable Solana-related functionality across all SDK functions.
These ecosystem providers are designed with modularity in mind and are fully tree-shakable, ensuring that they do not add unnecessary weight to your bundle if not used.
Previously, it was necessary to pass an `Ethers.js` Signer object to `executeRoute` and other functions for EVM interactions. With the introduction of ecosystem providers, you only need to configure them once when setting up the SDK. After that, you can use `executeRoute` and other functions without passing any additional options. The SDK will automatically determine the appropriate ecosystem provider and notify you if no suitable provider is configured.
Read more [Introduction to SDK Ecosystem Providers](/sdk/configure-sdk-providers#introduction-to-sdk-ecosystem-providers).
## Examples
Check out our complete examples in the [SDK repository](https://github.com/lifinance/sdk/tree/main/examples), and feel free to [file an issue](https://github.com/lifinance/sdk/issues) if you encounter any problems.
## Changelog
For a detailed view of all the changes, please see the [CHANGELOG](https://github.com/lifinance/sdk/blob/main/CHANGELOG.md).
# Migrate from v3 to v4
Source: https://docs.li.fi/sdk/migrate-v3-to-v4
Migration guide for upgrading LI.FI SDK v3 to v4
## Overview
**LI.FI SDK v4** introduces a client-based architecture that provides better type safety, improved modularity, and clearer separation of concerns. The SDK now uses a client instance pattern instead of global configuration, making it easier to manage multiple SDK instances and improving testability.
To get started, install the latest version of LI.FI SDK.
```typescript yarn theme={"system"}
yarn add @lifi/sdk
```
```typescript pnpm theme={"system"}
pnpm add @lifi/sdk
```
```typescript bun theme={"system"}
bun add @lifi/sdk
```
```typescript npm theme={"system"}
npm install @lifi/sdk
```
## Configuration
The most significant change in v4 is the move from a global configuration pattern to a client-based architecture. Instead of calling `createConfig()` and using global functions, you now create a client instance and pass it to all SDK functions.
### Before (v3)
```typescript theme={"system"}
// SDK v3
import { createConfig, getQuote, ChainId } from '@lifi/sdk';
createConfig({
integrator: 'Your dApp/company name',
});
const quote = await getQuote({
fromAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
fromChain: ChainId.ARB,
toChain: ChainId.OPT,
fromToken: '0x0000000000000000000000000000000000000000',
toToken: '0x0000000000000000000000000000000000000000',
fromAmount: '1000000000000000000',
});
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { createClient, getQuote, ChainId } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const quote = await getQuote(client, {
fromAddress: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
fromChain: ChainId.ARB,
toChain: ChainId.OPT,
fromToken: '0x0000000000000000000000000000000000000000',
toToken: '0x0000000000000000000000000000000000000000',
fromAmount: '1000000000000000000',
});
```
## Function Signatures
All SDK action functions now require the client as the first parameter. This includes:
* `getRoutes(client, {...})`
* `getQuote(client, {...})`
* `getContractCallsQuote(client, {...})`
* `getChains(client, {...})`
* `getTools(client, {...})`
* `getConnections(client, {...})`
* `getTokens(client, {...})`
* `getToken(client, chain, token)`
* `getTokenBalance(client, walletAddress, token)`
* `getTokenBalances(client, walletAddress, tokens)`
* `getTokenBalancesByChain(client, walletAddress, tokensByChain)`
* `getWalletBalances(client, walletAddress)`
* `getStatus(client, {...})`
* `getStepTransaction(client, step)`
* `executeRoute(client, route, options)`
* `resumeRoute(client, route, options)`
* `getGasRecommendation(client, {...})`
* `getTransactionHistory(client, {...})`
* `getNameServiceAddress(client, name, chainType?)`
* `getRelayerQuote(client, {...})`
* `relayTransaction(client, {...})`
* `getRelayedTransactionStatus(client, {...})`
* `patchContractCalls(client, {...})`
### Example Migration
### Before (v3)
```typescript theme={"system"}
// SDK v3
import { getRoutes, executeRoute } from '@lifi/sdk';
const result = await getRoutes({
fromChainId: 42161,
toChainId: 10,
fromTokenAddress: '0x...',
toTokenAddress: '0x...',
fromAmount: '10000000',
});
const executedRoute = await executeRoute(result.routes[0], {
updateRouteHook(route) {
console.log(route);
},
});
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { createClient, getRoutes, executeRoute } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const result = await getRoutes(client, {
fromChainId: 42161,
toChainId: 10,
fromTokenAddress: '0x...',
toTokenAddress: '0x...',
fromAmount: '10000000',
});
const executedRoute = await executeRoute(client, result.routes[0], {
updateRouteHook(route) {
console.log(route);
},
});
```
## Provider Packages
In v4, ecosystem providers have been moved to separate packages for better modularity and tree-shaking. You need to install the provider packages separately and import providers from their respective packages.
### Install Provider Packages
```typescript yarn theme={"system"}
yarn add @lifi/sdk-provider-ethereum @lifi/sdk-provider-solana @lifi/sdk-provider-bitcoin @lifi/sdk-provider-sui @lifi/sdk-provider-tron
```
```typescript pnpm theme={"system"}
pnpm add @lifi/sdk-provider-ethereum @lifi/sdk-provider-solana @lifi/sdk-provider-bitcoin @lifi/sdk-provider-sui @lifi/sdk-provider-tron
```
```typescript npm theme={"system"}
npm install @lifi/sdk-provider-ethereum @lifi/sdk-provider-solana @lifi/sdk-provider-bitcoin @lifi/sdk-provider-sui @lifi/sdk-provider-tron
```
```typescript bun theme={"system"}
bun add @lifi/sdk-provider-ethereum @lifi/sdk-provider-solana @lifi/sdk-provider-bitcoin @lifi/sdk-provider-sui @lifi/sdk-provider-tron
```
Only install the provider packages for the ecosystems you need. For example, if you only support EVM chains, you only need `@lifi/sdk-provider-ethereum`.
### Provider Configuration
### Before (v3)
```typescript theme={"system"}
// SDK v3
import { createConfig, EVM, Solana } from '@lifi/sdk';
createConfig({
integrator: 'Your dApp/company name',
providers: [
EVM({
getWalletClient: () => Promise.resolve(walletClient),
}),
Solana({
getWalletAdapter: () => Promise.resolve(walletAdapter),
}),
],
});
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { createClient } from '@lifi/sdk';
import { EthereumProvider } from '@lifi/sdk-provider-ethereum';
import { SolanaProvider } from '@lifi/sdk-provider-solana';
const client = createClient({
integrator: 'Your dApp/company name',
});
client.setProviders([
EthereumProvider({
getWalletClient: () => Promise.resolve(walletClient),
}),
SolanaProvider({
getWallet: () => Promise.resolve(wallet), // wallet-standard Wallet
}),
]);
```
## Provider Names
Provider factory names have been updated to be more descriptive:
* `EVM` → `EthereumProvider` (from `@lifi/sdk-provider-ethereum`)
* `Solana` → `SolanaProvider` (from `@lifi/sdk-provider-solana`)
* `UTXO` → `BitcoinProvider` (from `@lifi/sdk-provider-bitcoin`)
* `Sui` → `SuiProvider` (from `@lifi/sdk-provider-sui`)
* **New**: `TronProvider` (from `@lifi/sdk-provider-tron`) -- adds Tron ecosystem support
## Solana Provider: `getWalletAdapter` → `getWallet`
The Solana provider now uses [`@solana/kit`](https://github.com/solana-labs/solana-web3.js) and the [Wallet Standard](https://github.com/wallet-standard/wallet-standard) instead of `@solana/web3.js` and the legacy wallet adapter.
### Before (v3)
```typescript theme={"system"}
// SDK v3
import { Solana } from '@lifi/sdk';
Solana({
getWalletAdapter: () => Promise.resolve(walletAdapter),
});
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { SolanaProvider } from '@lifi/sdk-provider-solana';
SolanaProvider({
getWallet: () => Promise.resolve(wallet), // wallet-standard Wallet
});
```
## Sui Provider: `getWallet` → `getClient` + `getSigner`
The Sui provider now uses `@mysten/sui` v2 and requires a `ClientWithCoreApi` and a `Signer` instead of a wallet object.
### Before (v3)
```typescript theme={"system"}
// SDK v3
import { Sui } from '@lifi/sdk';
Sui({
getWallet: () => Promise.resolve(wallet),
});
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { SuiProvider } from '@lifi/sdk-provider-sui';
SuiProvider({
getClient: () => Promise.resolve(suiClient),
getSigner: () => Promise.resolve(signer),
});
```
## Configuration Management
### Before (v3)
```typescript theme={"system"}
// SDK v3
import { config } from '@lifi/sdk';
// Update configuration
config.set({
integrator: 'Updated name',
});
// Set providers
config.setProviders([...]);
// Get chains
const chains = await config.getChains();
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { createClient } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
// Access configuration (read-only)
console.log(client.config.integrator);
// Set providers
client.setProviders([...]);
// Get chains
const chains = await client.getChains();
// Get specific chain
const chain = await client.getChainById(1);
// Get RPC URLs
const rpcUrls = await client.getRpcUrls();
```
## Execution Model: Process → Action
The execution tracking model has been renamed for clarity. What was previously called a "process" is now called an "action."
### Type and field renames
* `Execution.process` → `Execution.actions`
* `Process` → `ExecutionAction`
* `ProcessType` → `ExecutionActionType`
* `getProcessMessage()` → `getActionMessage()` + `getSubstatusMessage()`
### New action types
The `TOKEN_ALLOWANCE` process type has been split into more granular action types:
* `CHECK_ALLOWANCE` - Checking token allowance
* `RESET_ALLOWANCE` - Resetting token allowance
* `SET_ALLOWANCE` - Setting token allowance
Additionally, the `NATIVE_PERMIT` action type has been added.
### Before (v3)
```typescript theme={"system"}
// SDK v3
step.execution?.process.forEach((process) => {
if (process.txHash) {
console.log(process.type, process.txHash);
}
});
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { getActionMessage } from '@lifi/sdk';
step.execution?.actions.forEach((action) => {
if (action.txHash) {
console.log(action.type, action.txHash);
}
const message = getActionMessage(action.type, action.status);
console.log(message);
});
```
## ExecutionOptions Changes
The `switchChainHook` and `disableMessageSigning` options have been removed from `ExecutionOptions` and moved to `EthereumProviderOptions`, which is configured when setting up the EVM provider. Note that `switchChainHook` has been renamed to `switchChain` on the provider (as shown in the example below).
### Before (v3)
```typescript theme={"system"}
// SDK v3
executeRoute(route, {
switchChainHook: async (chainId) => { ... },
disableMessageSigning: true,
});
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { EthereumProvider } from '@lifi/sdk-provider-ethereum';
client.setProviders([
EthereumProvider({
getWalletClient: () => Promise.resolve(walletClient),
switchChain: async (chainId) => { ... },
disableMessageSigning: true,
}),
]);
executeRoute(client, route, {
// switchChainHook and disableMessageSigning are no longer here
});
```
## Token Allowance Functions
Token allowance functions are now exported from the Ethereum provider package instead of the main SDK package.
### Before (v3)
```typescript theme={"system"}
// SDK v3
import { getTokenAllowance, setTokenAllowance } from '@lifi/sdk';
const allowance = await getTokenAllowance(token, ownerAddress, spenderAddress);
const txHash = await setTokenAllowance(approvalRequest);
```
### After (v4)
```typescript theme={"system"}
// SDK v4
import { createClient } from '@lifi/sdk';
import { getTokenAllowance, setTokenAllowance } from '@lifi/sdk-provider-ethereum';
const client = createClient({
integrator: 'Your dApp/company name',
});
const allowance = await getTokenAllowance(client, token, ownerAddress, spenderAddress);
const txHash = await setTokenAllowance(client, approvalRequest);
```
## The `actions()` Helper
If you prefer calling SDK functions without passing the client as the first parameter each time, you can use the `actions()` helper. It returns an object with all SDK action functions pre-bound to the client.
```typescript theme={"system"}
// SDK v4
import { createClient, actions } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const api = actions(client);
// No need to pass client as the first parameter
const quote = await api.getQuote({
fromChain: 42161,
toChain: 10,
fromToken: '0x...',
toToken: '0x...',
fromAmount: '10000000',
fromAddress: '0x...',
});
```
## New Utilities
v4 exports common utilities from `@lifi/sdk` so you no longer need `viem` or other libraries for basic formatting:
```typescript theme={"system"}
import { formatUnits, parseUnits } from '@lifi/sdk';
// Format token amount for display
const formatted = formatUnits(1000000n, 6); // "1"
// Parse user input to token amount
const amount = parseUnits("1", 6); // 1000000n
```
## Removed Parameters
Some configuration parameters have been removed or changed:
* `chains` - No longer passed to `createClient`. Chains are fetched automatically from the API.
## Client Instance Pattern
The client instance pattern allows you to:
1. **Create multiple clients** with different configurations
2. **Better testability** - easier to mock and test
3. **Type safety** - better TypeScript support
4. **Explicit dependencies** - clear what functions depend on
### Example: Multiple Clients
```typescript theme={"system"}
// SDK v4 - Multiple clients
import { createClient } from '@lifi/sdk';
const mainnetClient = createClient({
integrator: 'MyApp',
apiUrl: 'https://li.quest/v1',
});
const testnetClient = createClient({
integrator: 'MyApp',
apiUrl: 'https://staging.li.quest/v1',
});
// Use different clients for different environments
const mainnetRoutes = await getRoutes(mainnetClient, {...});
const testnetRoutes = await getRoutes(testnetClient, {...});
```
## Migration Checklist
* Update package installation to include provider packages
* Replace `createConfig()` with `createClient()` and store the client instance
* Update all function calls to include `client` as the first parameter (or use the `actions()` helper)
* Update provider imports to use separate provider packages
* Update provider factory names (`EVM` → `EthereumProvider`, etc.)
* For Solana: replace `getWalletAdapter` with `getWallet` (returns wallet-standard `Wallet`)
* For Sui: replace `getWallet` with `getClient` + `getSigner`
* Move `switchChainHook` (renamed to `switchChain`) and `disableMessageSigning` from `ExecutionOptions` to `EthereumProviderOptions`
* Update execution monitoring code: `execution.process` → `execution.actions`
* Replace `getProcessMessage()` with `getActionMessage()` and `getSubstatusMessage()`
* Update token allowance function imports to `@lifi/sdk-provider-ethereum`
* Update token allowance function calls to include `client` parameter
* Replace `config.set()` and `config.getChains()` with client methods
* Remove any references to global `config` object
* Replace `viem`'s `formatUnits`/`parseUnits` with `@lifi/sdk` exports if desired
## Examples
Check out our complete examples in the [SDK repository](https://github.com/lifinance/sdk/tree/main/examples), and feel free to [file an issue](https://github.com/lifinance/sdk/issues) if you encounter any problems.
## Changelog
For a detailed view of all the changes, please see the [CHANGELOG](https://github.com/lifinance/sdk/blob/main/CHANGELOG.md).
# Monetize SDK
Source: https://docs.li.fi/sdk/monetize-sdk
Learn how to configure fees and monetize your LI.FI SDK integration.
For more details about how fees work, fee collection on different chains, and
setting up fee wallets, see the [Monetizing the
integration](/introduction/integrating-lifi/monetizing-integration) guide.
When using the LI.FI SDK, you can monetize your integration by collecting fees from the transactions processed through your application. The SDK supports fee configuration in two ways: globally when creating the SDK client, or per-request through route options.
## Global fee configuration
The recommended approach is to configure fees globally when creating your SDK client using `createClient`. This ensures all requests use the same fee configuration automatically:
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
routeOptions: {
fee: 0.01, // 1% fee applied to all requests
},
// other options...
});
```
With this configuration, all route requests, quotes, and contract calls will automatically include the specified fee without needing to add it to each individual request.
## Per-request fee configuration
Alternatively, you can configure fees on a per-request basis. This is useful when you need different fee rates for different types of transactions or users. The `fee` parameter can be added to the `options` object when requesting routes or quotes.
### Basic route request with fees
```typescript theme={"system"}
import { getRoutes } from '@lifi/sdk';
const routesRequest = {
fromChainId: 42161, // Arbitrum
toChainId: 10, // Optimism
fromTokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toTokenAddress: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
fromAmount: '10000000', // 10 USDC
options: {
integrator: 'Your dApp/company name',
fee: 0.01, // 1% fee
},
};
const result = await getRoutes(client, routesRequest);
const routes = result.routes;
```
### Quote request with fees
```typescript theme={"system"}
import { getQuote } from '@lifi/sdk';
const quoteRequest = {
fromChain: 42161, // Arbitrum
toChain: 10, // Optimism
fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
fromAmount: '10000000', // 10 USDC
fromAddress: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0',
integrator: 'Your dApp/company name',
fee: 0.01, // 1% fee
};
const quote = await getQuote(client, quoteRequest);
```
# LI.FI SDK Overview
Source: https://docs.li.fi/sdk/overview
Cross-chain and on-chain swap and bridging toolkit
**Building an AI agent?** For AI integrations, we recommend using the [REST API](/api-reference/introduction) directly instead of the SDK. See our [Agent Integration Guide](/agents/overview) for the minimal endpoint set.
## Introduction
LI.FI SDK provides a powerful toolkit for developers to enable seamless cross-chain and on-chain swaps and bridging within their applications. Our JavaScript/TypeScript SDK can be implemented in front-end or back-end environments, allowing you to build robust UX/UI around our advanced bridge and swap functionalities. LI.FI SDK efficiently manages all communications between our smart routing API and smart contracts and ensures optimal performance, security, and scalability for your cross-chain and on-chain needs.
**LI.FI SDK features include:**
* All ecosystems, chains, bridges, exchanges, and solvers that LI.FI supports
* Complete functionality covering full-cycle from obtaining routes/quotes to executing transactions
* Easy tracking of the route and quote execution through the robust event and hooks handling
* Highly customizable settings to tailor the SDK to your specific needs including configuration of RPCs and options to allow or deny certain chains, tokens, bridges, exchanges, solvers
* Supports widely adopted industry standards, including [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702), [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792), [ERC-2612](https://eips.ethereum.org/EIPS/eip-2612), [EIP-712](https://eips.ethereum.org/EIPS/eip-712), and [Permit2](https://github.com/Uniswap/permit2)
* SDK ecosystem providers are based on industry-standard libraries ([Viem](https://viem.sh/), [Wallet Standard](https://github.com/wallet-standard/wallet-standard), [Bigmi](https://github.com/lifinance/bigmi))
* Support for arbitrary contract calls on the destination chain
* Designed for optimal performance with tree-shaking and dead-code elimination, ensuring minimal bundle sizes and faster page load times in front-end environments
* Compatibility tested with Node.js and popular front-end tools like Vite
**Looking for one-click DeFi operations?** The SDK fully supports [Composer](/composer/overview) — deposit into vaults, stake, and lend across chains with a single transaction. See the [SDK Composer Integration Guide](/composer/guides/sdk-integration).
## How to integrate the SDK
```typescript yarn theme={"system"}
yarn add @lifi/sdk
```
```typescript pnpm theme={"system"}
pnpm add @lifi/sdk
```
```typescript bun theme={"system"}
bun add @lifi/sdk
```
```typescript npm theme={"system"}
npm install @lifi/sdk
```
Learn more in the [installation guide](/sdk/installing-the-sdk).
```typescript theme={"system"}
import { createClient } from "@lifi/sdk";
const client = createClient({
integrator: "YourCompanyName",
});
```
Find all [config parameters](/sdk/configure-sdk).
Setup [EVM](/sdk/configure-sdk-providers#setup-evm-provider), [Solana](/sdk/configure-sdk-providers#setup-solana-provider), [Bitcoin](/sdk/configure-sdk-providers#setup-utxo-bitcoin-provider), [Sui](/sdk/configure-sdk-providers#setup-sui-provider), [Tron](/sdk/configure-sdk-providers#setup-tron-provider), and [Stellar](/sdk/configure-sdk-providers#setup-stellar-provider) providers.
Use the client to request [swap and bridge routes](/sdk/request-routes).
Load lists of available [chains and tools](/sdk/chains-tools).
Find [supported tokens](/sdk/token-management) with all metadata you need.
# Request Routes/Quotes
Source: https://docs.li.fi/sdk/request-routes
Prior to executing any swap or bridging, you need to request the best route from our smart routing API.
The LI.FI SDK provides functionality to request routes and quotes, as well as to execute them. This guide will walk you through the process of making a request using `getRoutes` and `getQuote` functions.
## How to request Routes
To get started, here is a simple example of how to request routes to bridge and swap 10 USDC on Arbitrum to the maximum amount of DAI on Optimism.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { getRoutes } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const routesRequest = {
fromChainId: 42161, // Arbitrum
toChainId: 10, // Optimism
fromTokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toTokenAddress: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
fromAmount: '10000000', // 10 USDC
};
const result = await getRoutes(client, routesRequest);
const routes = result.routes;
```
When you request routes, you receive an array of route objects containing the essential information to determine which route to take for a swap or bridging transfer. At this stage, transaction data is not included and must be requested separately. Read more [Execute Routes/Quotes](/sdk/execute-routes).
Additionally, if you would like to receive just one best option that our smart routing API can offer, it might be better to request a quote using `getQuote`.
## Routes request parameters
The `getRoutes` function expects a `RoutesRequest` object, which specifies a desired *any-to-any* transfer and includes all the information needed to calculate the most efficient routes.
### Parameters
Below are the parameters for the `RoutesRequest` interface along with their descriptions:
| Parameter | Type | Required | Description |
| ------------------ | ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `fromChainId` | number | yes | The ID of the source chain (e.g., Ethereum mainnet is 1). |
| `fromTokenAddress` | string | yes | The contract address of the token on the source chain. Ensure this address corresponds to the specified `fromChainId`. |
| `fromAmount` | string | yes | The amount to be transferred from the source chain, specified in the smallest unit of the token (e.g., wei for ETH). |
| `fromAddress` | string | no | The address from which the tokens are being transferred. |
| `toChainId` | number | yes | The ID of the destination chain (e.g., Optimism is 10). |
| `toTokenAddress` | string | yes | The contract address of the token on the destination chain. Ensure this address corresponds to the specified `toChainId`. |
| `toAddress` | string | no | The address to which the tokens will be sent on the destination chain once the transaction is completed. |
| `fromAmountForGas` | string | no | Part of the LI.Fuel. Allows receiving a part of the bridged tokens as gas on the destination chain. Specified in the smallest unit of the token. |
| `options` | RouteOptions | no | Additional options for customizing the route. This is defined by the RouteOptions interface (detailed below, see Route Options). |
## Route Options
The `RouteOptions` interface allows for further customization of the route request. Below are the parameters for the `RouteOptions` interface along with their descriptions:
| Parameter | Type | Required | Description |
| ---------------------- | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `integrator` | string | no | The identifier of the integrator, usually the dApp or company name. Ideally, this should be specified when configuring the SDK, but it can also be modified during a request |
| `fee` | number | no | The integrator fee percentage (e.g., 0.03 represents a 3% fee). This requires the integrator to be verified. |
| `maxPriceImpact` | number | no | Hides routes with a price impact greater than or equal to this value. (e.g., 0.3 represents 30%) |
| `order` | string | no | `CHEAPEST` - This sorting option prioritises routes with the highest estimated return amount. Users who value capital efficiency at the expense of speed and route complexity should choose the cheapest routes. `FASTEST` - This sorting option prioritizes routes with the shortest estimated execution time. Users who value speed and want their transactions to be completed as quickly as possible should choose the fastest routes. |
| `slippage` | number | no | The slippage tolerance, expressed as a decimal proportion (e.g., 0.005 represents 0.5%). |
| `referrer` | string | no | The wallet address of the referrer, for tracking purposes. |
| `allowSwitchChain` | boolean | no | Specifies whether to return routes that require chain switches (2-step routes). |
| `allowDestinationCall` | boolean | no | Specifies whether destination calls are enabled. |
| `bridges` | AllowDenyPrefer | no | An `AllowDenyPrefer` object to specify preferences for bridges. |
| `exchanges` | AllowDenyPrefer | no | An `AllowDenyPrefer` object to specify preferences for exchanges. |
| `protocols` | AllowDenyPrefer | no | An `AllowDenyPrefer` object to specify preferences for protocols (bridges and exchanges combined). |
| `executionType` | ExecutionType | no | Whether to include routes that require a transaction, a message, or both. Possible values: `'transaction'`, `'message'`, `'all'`. Default: `'transaction'`. |
| `timing` | Timing | no | A Timing object to specify preferences for Timing Strategies. |
| `jitoBundle` | boolean | no | SVM-specific. Enables Jito bundle routes for implicit source swaps. Without it, these routes are discarded. |
| `svmSponsor` | string | no | SVM-specific. Wallet address to sponsor transaction costs. |
| `svmPriorityFeeLevel` | SVMPriorityFeeLevel | no | SVM-specific. Priority fee level for Solana transactions. Possible values: `'NORMAL'`, `'FAST'`, `'ULTRA'`. |
| `preset` | string | no | Preset configuration for stablecoin routing optimization. When provided, overrides other route options with optimized settings. |
## Allow/Deny/Prefer
The `AllowDenyPrefer` interface is used to specify preferences for bridges or exchanges. Using the `allow` option, you can allow tools, and only those tools will be used to find the best routes. Tools specified in `deny` will be blocklisted.
You can find all available keys in [List: Chains, Bridges, DEX Aggregators, Solvers](/introduction/tools) or get the available option from the API. See [Chains and Tools](/sdk/chains-tools).
Below are the parameters for the `AllowDenyPrefer` interface:
| Parameter | Type | Required | Description |
| --------- | --------- | -------- | ----------------------------------------------------------------------------------------- |
| `allow` | string\[] | no | A list of allowed bridges or exchanges (default: all). |
| `deny` | string\[] | no | A list of denied bridges or exchanges (default: none). |
| `prefer` | string\[] | no | A list of preferred bridges or exchanges (e.g., \['1inch'] to prefer 1inch if available). |
## Timing
The `Timing` interface allows you to specify preferences for the timing of route execution. This can help optimize the performance of your requests based on timing strategies.
Parameters for the `Timing` interface:
| Parameter | Type | Required | Description |
| -------------------------- | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `swapStepTimingStrategies` | TimingStrategy\[] | no | An array of timing strategies specifically for each swap step in the route. This allows you to define custom strategies for timing control during the execution of individual swap steps. |
| `routeTimingStrategies` | TimingStrategy\[] | no | An array of timing strategies that apply to the entire route. This enables you to set preferences for how routes are timed overall, potentially improving execution efficiency and reliability. |
## Timing Strategy
This can help optimize the timing of requests based on specific conditions.
| Parameter | Type | Required | Description |
| ------------------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `strategy` | string | | The strategy type, which must be set to 'minWaitTime'. This indicates that the timing strategy being applied is based on a minimum wait time. |
| `minWaitTimeMs` | number | | The minimum wait time in milliseconds before any results are returned. This value ensures that the request waits for a specified duration to allow for more accurate results. |
| `startingExpectedResults` | number | | The initial number of expected results that should be returned after the minimum wait time has elapsed. This helps in managing user expectations regarding the outcomes of the request. |
| `reduceEveryMs` | number | | The interval in milliseconds at which the expected results are reduced as the wait time progresses. This parameter allows for dynamic adjustments to the expected results based on the elapsed time. |
You can implement [custom timing strategies](/guides/integration-tips/latency#optimizing-response-timing) to improve the user experience and optimize the performance of your application by controlling the timing of route execution.
## Request a Quote
When you request a quote, our smart routing API provides the best available option. The quote includes all necessary information and transaction data required to initiate a swap or bridging transfer.
You can request a quote by specifying either `fromAmount` (the amount to send) or `toAmount` (the desired amount to receive). Use `fromAmount` when the user knows how much they want to spend, and `toAmount` when they know how much they want to receive.
Here is a simple example of how to request a quote to bridge and swap 10 USDC on Arbitrum to the maximum amount of DAI on Optimism.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { getQuote } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
// Request by source amount
const quoteRequest = {
fromChain: 42161, // Arbitrum
toChain: 10, // Optimism
fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
fromAmount: '10000000', // 10 USDC
fromAddress: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0',
};
const quote = await getQuote(client, quoteRequest);
// Or request by destination amount
const toAmountQuoteRequest = {
fromChain: 42161, // Arbitrum
toChain: 10, // Optimism
fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
toToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', // DAI on Optimism
toAmount: '10000000000000000000', // 10 DAI
fromAddress: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0',
};
const toAmountQuote = await getQuote(client, toAmountQuoteRequest);
```
## Quote request parameters
The `getQuote` function expects a `QuoteRequest` object. You must provide either `fromAmount` or `toAmount`, but not both. Below are the parameters for the `QuoteRequest` interface.
| Parameter | Type | Required | Description |
| ------------------ | ------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fromChain` | number | yes | The ID of the source chain (e.g., Ethereum mainnet is 1). |
| `fromToken` | string | yes | The contract address of the token on the source chain. Ensure this address corresponds to the specified `fromChain`. |
| `fromAmount` | string | conditional | The amount to send from the source chain, specified in the smallest unit of the token (e.g., wei for ETH). Required if `toAmount` is not provided. |
| `toAmount` | string | conditional | The desired amount to receive on the destination chain, specified in the smallest unit of the token. Required if `fromAmount` is not provided. |
| `fromAddress` | string | yes | The address from which the tokens are being transferred. |
| `toChain` | number | yes | The ID of the destination chain (e.g., Optimism is 10). |
| `toToken` | string | yes | The contract address of the token on the destination chain. Ensure this address corresponds to the specified `toChain`. |
| `toAddress` | string | no | The address to which the tokens will be sent on the destination chain once the transaction is completed. |
| `fromAmountForGas` | string | no | Part of the LI.Fuel. Allows receiving a part of the bridged tokens as gas on the destination chain. Specified in the smallest unit of the token. Only available with `fromAmount` requests. |
### Other Quote parameters
In addition to the parameters mentioned above, all parameters listed in the [Route Options](##route-options) section are also available when using `getQuote`, except for `allowSwitchChain`, which is used exclusively to control chain switching in route requests.
Also, parameters to specify options for allowing, denying, or preferring certain bridges, exchanges, and protocols have slightly different names:
* `allowBridges` (string\[], optional)
* `denyBridges` (string\[], optional)
* `preferBridges` (string\[], optional)
* `allowExchanges` (string\[], optional)
* `denyExchanges` (string\[], optional)
* `preferExchanges` (string\[], optional)
* `allowProtocols` (string\[], optional)
* `denyProtocols` (string\[], optional)
Additionally, you can specify [timing strategies](/guides/integration-tips/latency#optimizing-response-timing) for the swap steps using the `swapStepTimingStrategies` parameter:
* **`swapStepTimingStrategies`** (string\[], optional) Specifies the timing strategy for swap steps. This parameter allows you to define how long the request should wait for results and manage expected outcomes. The format is:
```typescript theme={"system"}
minWaitTime-${minWaitTimeMs}-${startingExpectedResults}-${reduceEveryMs}
```
## Request contract call Quote
Besides requesting general quotes, the LI.FI SDK also provides functionality to request quotes for destination contract calls.
Read more in the [Composer documentation](/composer/overview). For SDK-specific Composer integration, see [SDK Composer Integration Guide](/composer/guides/sdk-integration).
Here is a simple example of how to request a quote to bridge and purchase an NFT on the OpenSea marketplace costing 0.0000085 ETH on the Base chain using ETH from Optimism. The call data for this example was obtained using the OpenSea Seaport SDK.
```typescript theme={"system"}
import { createClient } from '@lifi/sdk';
import { getContractCallsQuote } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const contractCallsQuoteRequest = {
fromAddress: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0',
fromChain: 10,
fromToken: '0x0000000000000000000000000000000000000000',
toAmount: '8500000000000',
toChain: 8453,
toToken: '0x0000000000000000000000000000000000000000',
contractCalls: [
{
fromAmount: '8500000000000',
fromTokenAddress: '0x0000000000000000000000000000000000000000',
toContractAddress: '0x0000000000000068F116a894984e2DB1123eB395',
toContractCallData:
'0xe7acab24000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000006e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029dacdf7ccadf4ee67c923b4c22255a4b2494ed700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000520000000000000000000000000000000000000000000000000000000000000064000000000000000000000000090884b5bd9f774ed96f941be2fb95d56a029c99c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066757dd300000000000000000000000000000000000000000000000000000000669d0a580000000000000000000000000000000000000000000000000000000000000000360c6ebe0000000000000000000000000000000000000000ad0303de3e1093e50000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000029f25e8a71e52e795e5016edf7c9e02a08c519b40000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ff0cbadd00000000000000000000000000000000000000000000000000000006ff0cbadd0000000000000000000000000090884b5bd9f774ed96f941be2fb95d56a029c99c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003179fcad000000000000000000000000000000000000000000000000000000003179fcad000000000000000000000000000000a26b00c1f0df003000390027140000faa7190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a88c37e000000000000000000000000000000000000000000000000000000008a88c37e000000000000000000000000009323bb21a4c6122f60713e4a1e38e7b94a40ce2900000000000000000000000000000000000000000000000000000000000000e3b5b41791fe051471fa3c2da1325a8147c833ad9a6609ffc07a37e2603de3111b262911aaf25ed6d131dd531574cf54d4ea61b479f2b5aaa2dff7c210a3d4e203000000f37ec094486e9092b82287d7ae66fbf8cd6148233c70813583e3264383afbd0484b80500070135f54edd2918ddd4260c840f8a6957160766a4e4ef941517f2a0ab3077a2ac6478f0ad7fad9b821766df11ca3fdb16a8e95782faaed6e0395df2f416651ac87a5c1edec0a36ad42555083e57cff59f4ad98617a48a3664b2f19d46f4db85e95271c747d03194b5cfdcfc86bb0b08fb2bc4936d6f75be03ab498d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
toContractGasLimit: '210000',
},
],
};
const contractCallQuote = await getContractCallsQuote(client, contractCallsQuoteRequest);
```
## Contract call Quote request parameters
The `getContractCallsQuote` function expects a `ContractCallsQuoteRequest` object, which includes all the information needed to request a quote for a destination contract call.
Contract call quote request can be treated as an extension to the quote request and in addition to the parameters mentioned below, all parameters listed in the [Other Quote parameters](/sdk/request-routes#other-quote-parameters) section (such as `integrator`, `fee`, `slippage`, etc.) are also available when using `getContractCallsQuote`.
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
Array of contract call objects.
| Parameter | Type | Required | Description |
| ---------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| `fromAmount` | string | yes | The amount of tokens to be sent to the contract. This amount is independent of any previously bridged or deposited tokens. |
| `fromTokenAddress` | string | yes | The address of the token to be sent to the contract. For example, an ETH staking transaction would require ETH. |
| `toContractAddress` | string | yes | The address of the contract to interact with on the destination chain. |
| `toContractCallData` | string | yes | The call data to be sent to the contract for the interaction on the destination chain. |
| `toContractGasLimit` | string | yes | The estimated gas required for the contract call. Incorrect values may cause the interaction to fail. |
| `toApprovalAddress` | string | no | The address to approve the token transfer if it is different from the contract address. |
| `contractOutputsToken` | string | no | The address of the token that will be output by the contract, if applicable (e.g., staking ETH produces stETH). |
## Difference between `Route` and `Quote`
Even though `Route` and `Quote` terms lie in the same field of providing you with the best option to make a swap or bridging transfer, there are some differences you need to be aware of.
A `Route` in LI.FI represents a detailed transfer plan that may include *multiple steps*. Each step corresponds to an individual transaction, such as swapping tokens or bridging funds between chains. These steps must be executed in a specific sequence, as each one depends on the output of the previous step. A `Route` provides a detailed pathway for complex transfers involving multiple actions.
In contrast, a `Quote` is a *single-step* transaction. It contains all the necessary information to perform a transfer in one go, without requiring any additional steps. `Quotes` are used for simpler transactions where a single action, such as a token swap or a cross-chain transfer, is sufficient. Thus, while `Routes` can involve multiple steps to complete a transfer, a `Quote` always represents just one step.
# Testing Integration
Source: https://docs.li.fi/sdk/testing-integration
Run test transactions on mainnets.
Testing your integration is a crucial step to ensure everything functions correctly before going live and we understand that.
We no longer support testnets and advise running your test transactions on mainnets. This is because bridges and exchanges have limited support for testnets and there is almost no liquidity on those networks.
Running test transactions on mainnets allows you to validate your setup in a real-world environment.
To minimize costs, we recommend testing on chains with low gas fees. Optimism and other Layer 2 (L2) chains are excellent choices for cost-effective testing.
Before testing, make sure you have followed the previous documentation to [Configure SDK](/sdk/configure-sdk), [Configure SDK Providers](/sdk/configure-sdk-providers) and [Request Routes/Quotes](/sdk/request-routes) for your integration tests. Proper setup ensures that your integration is configured correctly for interaction with the supported ecosystems.
# Token Management
Source: https://docs.li.fi/sdk/token-management
Request all available tokens and their balances, manage token approvals and more.
## Get available tokens
### `getTokens`
Retrieves a list of all available tokens on specified chains.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `params` (`TokensRequest`, optional): Configuration for the requested tokens.
* `chains` (`(ChainId | ChainKey)[]`, optional): List of chain IDs or keys. If not specified, returns tokens on all available chains.
* `chainTypes` (`ChainType[]`, optional): List of chain types.
* `extended` (`boolean`, optional): When `true`, returns `TokensExtendedResponse` with additional token data. Default: `false`.
* `minPriceUSD` (`number`, optional): Minimum USD price filter for returned tokens.
* `orderBy` (`TokensSortOrder`, optional): Sort order for the token list.
* `limit` (`number`, optional): Maximum number of tokens to return.
* `search` (`string`, optional): Search query to filter tokens by name or symbol.
* `tags` (`TokenTag[]`, optional): Filter tokens by tags.
* `options` (`RequestOptions`, optional): Additional request options.
**Returns**
A Promise that resolves to `TokensResponse`, or `TokensExtendedResponse` when `extended: true` is set.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { ChainType, getTokens } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
try {
const tokens = await getTokens(client, {
chainTypes: [ChainType.EVM, ChainType.SVM],
});
console.log(tokens);
} catch (error) {
console.error(error);
}
```
### `getToken`
Fetches details about a specific token on a specified chain.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `chain` (`ChainKey | ChainId`): ID or key of the chain that contains the token.
* `token` (`string`): Address or symbol of the token on the requested chain.
* `options` (`RequestOptions`, optional): Additional request options.
**Returns**
A Promise that resolves to a `TokenExtended` object.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getToken } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const chainId = 1;
const tokenAddress = '0x0000000000000000000000000000000000000000';
try {
const token = await getToken(client, chainId, tokenAddress);
console.log(token);
} catch (error) {
console.error(error);
}
```
## Get token balance
Please ensure that you configure the SDK with EVM/Solana providers first. They are required to use this functionality. Additionally, it is recommended to provide your private RPC URLs, as public ones are used by default and may rate limit you for multiple requests, such as getting the balance of multiple tokens at once.
Read more [Configure SDK Providers](/sdk/configure-sdk-providers).
### `getTokenBalance`
Returns the balance of a specific token a wallet holds.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `walletAddress` (`string`): A wallet address.
* `token` (`Token`): A Token object.
**Returns**
A Promise that resolves to a `TokenAmount` or `null`.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getToken, getTokenBalance } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const chainId = 1;
const tokenAddress = '0x0000000000000000000000000000000000000000';
const walletAddress = '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0';
try {
const token = await getToken(client, chainId, tokenAddress);
const tokenBalance = await getTokenBalance(client, walletAddress, token);
console.log(tokenBalance);
} catch (error) {
console.error(error);
}
```
### `getTokenBalances`
Returns the balances for a list of tokens a wallet holds.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `walletAddress` (`string`): A wallet address.
* `tokens` (`Token[]`): A list of Token objects.
**Returns**
A Promise that resolves to a list of `TokenAmount` objects.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { ChainId, getTokenBalances, getTokens } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const walletAddress = '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0';
try {
const tokensResponse = await getTokens(client);
const optimismTokens = tokensResponse.tokens[ChainId.OPT];
const tokenBalances = await getTokenBalances(client, walletAddress, optimismTokens);
console.log(tokenBalances);
} catch (error) {
console.error(error);
}
```
### `getTokenBalancesByChain`
Queries the balances of tokens for a specific list of chains for a given wallet.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `walletAddress` (`string`): A wallet address.
* `tokensByChain` (`[chainId: number]: Token[]`): A list of Token objects organized by chain IDs.
**Returns**
A Promise that resolves to an object containing the tokens and their amounts on different chains.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getTokenBalancesByChain } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const walletAddress = '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0';
const tokensByChain = {
1: [
{
chainId: 1,
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
symbol: 'DAI',
name: 'DAI Stablecoin',
decimals: 18,
priceUSD: '0.9999',
},
],
10: [
{
chainId: 10,
address: '0x4200000000000000000000000000000000000042',
symbol: 'OP',
name: 'Optimism',
decimals: 18,
priceUSD: '1.9644',
},
],
};
try {
const balances = await getTokenBalancesByChain(client, walletAddress, tokensByChain);
console.log(balances);
} catch (error) {
console.error(error);
}
```
### `getWalletBalances`
Returns the balances of tokens a wallet holds across EVM chains using the LI.FI API.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `walletAddress` (`string`): A wallet address.
* `options` (`RequestOptions`, optional): Additional request options.
**Returns**
A Promise that resolves to `Record` — token balances organized by chain ID.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getWalletBalances } from '@lifi/sdk';
const client = createClient({
integrator: 'Your dApp/company name',
});
const walletAddress = '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0';
try {
const balances = await getWalletBalances(client, walletAddress);
console.log(balances);
} catch (error) {
console.error(error);
}
```
## Managing token allowance
Token allowance and approval functionalities are specific to EVM (Ethereum Virtual Machine) chains. It allows smart contracts to interact with ERC-20 tokens by approving a certain amount of tokens that a contract can spend from the user's wallet.
Please ensure that you configure the SDK with the EVM provider. It is required to use this functionality.
Read more [Configure SDK Providers](/sdk/configure-sdk-providers).
Token allowance functions are exported from `@lifi/sdk-provider-ethereum` package. Make sure to install and import from the correct package.
### `getTokenAllowance`
Fetches the current allowance for a specific token.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `token` (`BaseToken`): The token for which to check the allowance.
* `ownerAddress` (`string`): The owner of the token.
* `spenderAddress` (`string`): The spender address that was approved.
**Returns**
A Promise that resolves to a `bigint` representing the allowance or undefined if the token is a native token.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getTokenAllowance } from '@lifi/sdk-provider-ethereum';
const client = createClient({
integrator: 'Your dApp/company name',
});
const token = {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
chainId: 1,
};
const ownerAddress = '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0';
const spenderAddress = '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE';
try {
const allowance = await getTokenAllowance(client, token, ownerAddress, spenderAddress);
console.log('Allowance:', allowance);
} catch (error) {
console.error('Error:', error);
}
```
### `getTokenAllowanceMulticall`
Fetches the current allowance for a list of token/spender address pairs.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `ownerAddress` (`string`): The owner of the tokens.
* `tokens` (`TokenSpender[]`): A list of token and spender address pairs.
**Returns**
A Promise that resolves to an array of `TokenAllowance` objects.
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { getTokenAllowanceMulticall } from '@lifi/sdk-provider-ethereum';
const client = createClient({
integrator: 'Your dApp/company name',
});
const ownerAddress = '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0';
const tokens = [
{
token: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
chainId: 1,
},
spenderAddress: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE',
},
{
token: {
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
chainId: 1,
},
spenderAddress: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE',
},
];
try {
const allowances = await getTokenAllowanceMulticall(client, ownerAddress, tokens);
console.log('Allowances:', allowances);
} catch (error) {
console.error('Error:', error);
}
```
### `setTokenAllowance`
Sets the token allowance for a specific token and spender address.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `request` (`ApproveTokenRequest`): The approval request.
* `walletClient` (`Client`): The Viem client used to send the transaction.
* `token` (`BaseToken`): The token for which to set the allowance.
* `spenderAddress` (`string`): The address of the spender.
* `amount` (`bigint`): The amount of tokens to approve.
**Returns**
A Promise that resolves to a `Hash` representing the transaction hash or `void` if no transaction is needed (e.g., for native tokens).
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { setTokenAllowance } from '@lifi/sdk-provider-ethereum';
const client = createClient({
integrator: 'Your dApp/company name',
});
const approvalRequest = {
walletClient: walletClient, // Viem wallet client
token: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
chainId: 1,
},
spenderAddress: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE',
amount: 100000000n,
};
try {
const txHash = await setTokenAllowance(client, approvalRequest);
console.log('Transaction Hash:', txHash);
} catch (error) {
console.error('Error:', error);
}
```
### `revokeTokenApproval`
Revokes the token approval for a specific token and spender address.
**Parameters**
* `client` (`SDKClient`): The SDK client instance.
* `request` (`RevokeApprovalRequest`): The revoke request.
* `walletClient` (`Client`): The Viem client used to send the transaction.
* `token` (`BaseToken`): The token for which to revoke the allowance.
* `spenderAddress` (`string`): The address of the spender.
**Returns**
A Promise that resolves to a `Hash` representing the transaction hash or `void` if no transaction is needed (e.g., for native tokens).
```typescript Example theme={"system"}
import { createClient } from '@lifi/sdk';
import { revokeTokenApproval } from '@lifi/sdk-provider-ethereum';
const client = createClient({
integrator: 'Your dApp/company name',
});
const revokeRequest = {
walletClient: walletClient, // Viem wallet client
token: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
chainId: 1,
},
spenderAddress: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE',
};
try {
const txHash = await revokeTokenApproval(client, revokeRequest);
console.log('Transaction Hash:', txHash);
} catch (error) {
console.error('Error:', error);
}
```
# Configure Widget
Source: https://docs.li.fi/widget/configure-widget
Flexibility at your fingertips
The LI.FI Widget supports a range of configuration options, allowing you to:
* Allow or deny specific chains, tokens, bridges, and exchanges.
* Filter chains by ecosystem type (EVM, SVM, UTXO, MVM, TVM).
* Preselect default source and destination chains.
* Choose default tokens for both source and destination.
* Set the amount of the source or destination token.
* Specify a destination address.
* Configure integrator fees with static or dynamic fee calculation.
* Enable gasless/relayer routes for eligible transactions.
* Configure blockchain providers for Ethereum, Solana, Bitcoin, Sui, Tron, and Stellar ecosystems.
* Customize various LI.FI SDK settings through the `sdkConfig` configuration.
These options enable precise control over the widget's behavior and improve the user experience by adjusting it to specific needs and preferences.
## LI.FI SDK configuration
The LI.FI Widget is built on top of the LI.FI SDK, leveraging its robust functionality for cross-chain swaps and bridging. The sdkConfig option allows you to configure various aspects of the SDK directly within the widget.
Let's look at the example of configuring private RPC endpoints using the sdkConfig option.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig, ChainId } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
sdkConfig: {
rpcUrls: {
[ChainId.ARB]: ["https://arbitrum-example.node.com/"],
[ChainId.SOL]: ["https://solana-example.node.com/"],
},
},
};
export const WidgetPage = () => {
return (
);
};
```
In a production app, it is recommended to pass through your authenticated RPC provider URL (Alchemy, Infura, Ankr, etc).
If no RPC URLs are provided, LI.FI Widget will default to public RPC providers.
Public RPC endpoints (especially Solana) can sometimes rate-limit users depending on location or during periods of heavy load, leading to issues such as incorrectly displaying balances or errors with transaction simulation.
Please see other SDK configuration options in the [Configure SDK](/sdk/configure-sdk) section.
## Blockchain providers configuration
The LI.FI Widget supports multiple blockchain ecosystems through dedicated provider packages. You can configure providers for Ethereum (EVM), Solana (SVM), Bitcoin (UTXO), Sui (MVM), Tron (TVM), and Stellar (STL) chains.
```typescript theme={"system"}
import { LiFiWidget } from "@lifi/widget";
import type { WidgetConfig } from "@lifi/widget";
import { EthereumProvider } from "@lifi/widget-provider-ethereum";
import { SolanaProvider } from "@lifi/widget-provider-solana";
import { BitcoinProvider } from "@lifi/widget-provider-bitcoin";
import { SuiProvider } from "@lifi/widget-provider-sui";
import { TronProvider } from "@lifi/widget-provider-tron";
import { StellarProvider } from "@lifi/widget-provider-stellar";
const widgetConfig: WidgetConfig = {
providers: [
EthereumProvider(),
SolanaProvider(),
BitcoinProvider(),
SuiProvider(),
TronProvider(),
StellarProvider(),
],
};
export const WidgetPage = () => {
return (
);
};
```
Each provider can be customized with specific configurations. For example, the Ethereum provider supports configuration for WalletConnect, Coinbase, MetaMask, Porto, and Base Account connectors. See the [Wallet Management](/widget/wallet-management) page for details.
## Initialize form values
The LI.FI Widget uses a number of form values that are used to fetch and execute routes.
These values are `fromAmount`, `fromChain`, `fromToken`, `toChain`, `toToken`, `toAmount`, and `toAddress`.
They are most often set by using the Widget UI but they can also be initialized and updated programmatically.
By configuring these options, you can streamline the user experience, ensuring that the widget is preloaded with the desired chains, tokens, amount and address for a swap or bridge. This reduces the need for manual input and helps guide users through the intended flow.
You can initialize these values by either:
* Widget config - by adding `fromAmount`, `fromChain`, `fromToken`, `toChain`, `toToken`, `toAmount`, or `toAddress` values to the widget config.
* URL search params - when `buildUrl` in the widget config is set to `true`, by adding them to the URL search params in the url of the page the widget is featured on.
When setting form values via config or URL search params you will see any corresponding form field UI updated to reflect those values.
## Initializing by widget config
The LI.FI Widget allows you to preconfigure default chains and tokens, making it easy to set up your desired swap or bridging parameters right from the start. Below is an example of how to configure the widget with specific default chains, tokens, amount, and send to address values.
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
import { ChainType } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
// set source chain to Polygon
fromChain: 137,
// set destination chain to Optimism
toChain: 10,
// set source token to USDC (Polygon)
fromToken: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
// set source token to USDC (Optimism)
toToken: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
// set source token amount to 10 USDC (Polygon)
fromAmount: 10,
// set the destination wallet address
toAddress: {
address: "0x29DaCdF7cCaDf4eE67c923b4C22255A4B2494eD7",
chainType: ChainType.EVM,
},
};
export const WidgetPage = () => {
return (
);
};
```
You can also set a minimum amount in USD equivalent using the `minFromAmountUSD` parameter (number) to ensure users meet minimum transaction requirements.
## Initializing by URL search params
To initialize form values in the widget using URL search params you will need to ensure that `buildUrl` is set to `true` in the widget config.
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
// instruct the widget to use and build url search params
buildUrl: true,
};
```
You can then feature the URL search params in the URL when navigating to the page that features the widget.
```
https://playground.li.fi/?fromAmount=20&fromChain=42161&fromToken=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9&toAddress=0x29DaCdF7cCaDf4eE67c923b4C22255A4B2494eD7&toChain=42161&toToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831
```
Its important to understand this will only work for the widgets initialization - dynamically changing the search params in the URL without a page load will not cause an update of the form values in the widget.
Config values override URL search params
If you want to use URL search params to populate the widget's form values on initialization (or page load) its important that those form values are NOT featured in the config object used to initialize the widget. fromAmount, fromChain, fromToken, toAddress, toChain, and toToken should NOT be set on the widget config in order to allow the URL to perform the initial set up of the widgets state.
On first page load if you have form values in both the config and the URL then the URL search params will be rewritten to match the config values and the widget form will be populated with the values presented in the config.
## Update form values
After the widget has initialized there are two ways you can update the form values in the widget
* Using the widget config - this uses reactive values in the config and requires some management of those values for updates
* Using the formRef - this provides an function call that you can use to update values in the widgets form store.
Note that when `buildUrl` is set to `true` in the widget config both methods
should also update the URL search params as well as the value displayed in the
widget itself.
## Updating by widget config
Once the widget has initialized you can update the form values in the widget by updating the widget config.
To perform an update you should only include the form values in the config that you want to change and ensure these changes are passed to the Widget.
For example, if you want to change the fromChain and fromToken and nothing else you should include only include those values
In addition to the form values you want to change you should also set a formUpdateKey. This needs to be a unique, randomly generated string and is used to ensure that the form values are updated in the widget - essentially forcing an update. This can avoid some edge case issues that might arise when setting values in the widget via a mix of config and user actions via the widgets UI.
Here is an example of what your config would look like.
```typescript theme={"system"}
import type { WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
fromChain: 10,
fromToken: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
// use the date object to generate a unique value
formUpdateKey: new Date().valueOf().toString()
// config may still feature other config values but
// should not include other form values…
}
```
You can also reset the form values and their fields to an empty state using `undefined`. This example resets only the fromChain and fromToken form values.
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
fromChain: undefined,
fromToken: undefined,
// use the date object to generate a unique value
formUpdateKey: new Date().valueOf().toString(),
// config may still feature other config values but
// should not include other form values…
};
```
Here `undefined` used to reset a the widgets form value to an empty state. The absence of a property from the widget config object means that property will remain unchanged.
### State management with widget config
When using config to update widgets form values it is often a good choice to consider using an application state management library to store your widget config. There are many options to choose from such as Zustand, MobX, Redux or even React context.
For example, if you were to use Zustand as your state management tool you could use Zustand's API to access and set values on your config from any part of your application. In addition you would also be able to use Zustand's equality functionality, such as the built-in `shallow` function, to ensure that your widget config is only used to update the instance of the LiFi Widget when necessary. This should be beneficial for optimizing re-renders.
You can find an example that uses [Zustand to manage widget config](https://github.com/lifinance/widget/tree/main/examples/zustand-widget-config) in the widget repository.
## Updating by form ref
This method provides developers a way to set the form values directly in the widget without making changes to the widget config. By passing a ref object to the widget you can access a function to set values directly on the widgets form state. See the example below.
```typescript theme={"system"}
import type { FormState } from '@lifi/widget';
import { LiFiWidget } from '@lifi/widget';
export const WidgetPage = () => {
const widgetConfig: WidgetConfig = {
buildUrl: true,
};
const formRef = useRef(null);
const handleClick = () => {
formRef.current?.setFieldValue( 'fromChain', 10, { setUrlSearchParam: true });
};
return (
<>
>
)
}
```
Notice the use of `setFieldValue` function.
```typescript theme={"system"}
formRef.current?.setFieldValue( 'fromChain', 10, { setUrlSearchParam: true });
```
Once initialized the `setFieldValue` function can be called to set the form value, note that `setUrlSearchParam` will ensure the url is updated if you have `buildUrl` set to `true` in your widget config.
Here are some examples of usage.
```typescript fromChain & fromToken theme={"system"}
// fromChain and fromToken can be set independently but you might also find that you want to set them at the same time
formRef.current?.setFieldValue(
'fromChain',
10,
{ setUrlSearchParam: true }
);
formRef.current?.setFieldValue(
'fromToken',
'0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
{ setUrlSearchParam: true }
);
// To reset fromChain and fromToken
formRef.current?.setFieldValue(
'fromChain',
undefined,
{ setUrlSearchParam: true }
);
formRef.current?.setFieldValue(
'fromToken',
undefined,
{ setUrlSearchParam: true }
);
```
```typescript fromAmount theme={"system"}
formRef.current?.setFieldValue(
'fromAmount',
'10',
{ setUrlSearchParam: true }
);
// To reset fromAmount
formRef.current?.setFieldValue(
'fromAmount',
undefined,
{ setUrlSearchParam: true }
);
```
```typescript toAddress theme={"system"}
import { ChainType } from '@lifi/widget';
formRef.current?.setFieldValue(
'toAddress',
{
name: 'Lenny',
address: '0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea9',
chainType: ChainType.EVM,
},
{ setUrlSearchParam: true }
);
// To reset toAddress
formRef.current?.setFieldValue(
'toAddress',
undefined,
{ setUrlSearchParam: true }
);
```
## Configure route options
The widget provides several options to control route fetching and selection behavior.
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
// Set default route priority (RECOMMENDED, FASTEST, CHEAPEST, SAFEST)
routePriority: "RECOMMENDED",
// Set default slippage (0.03 = 3%)
slippage: 0.03,
// Show only the recommended route and hide the route selector
showSingleRoute: true,
// Enable gasless/relayer routes for eligible transactions
useRelayerRoutes: true,
};
export const WidgetPage = () => {
return (
);
};
```
## Configure integrator fees
You can configure fees that will be collected on each transaction. There are two approaches:
### Static fee
Set a fixed fee percentage for all transactions using `feeConfig`:
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
feeConfig: {
fee: 0.01, // 1% fee
name: "DApp fee",
showFeePercentage: true,
showFeeTooltip: true,
},
};
```
### Dynamic fee calculation
Use `calculateFee` function for dynamic fee calculation based on route parameters:
```typescript theme={"system"}
import type { WidgetConfig, CalculateFeeParams } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
feeConfig: {
name: "Platform fee",
showFeePercentage: true,
calculateFee: async (params: CalculateFeeParams) => {
// params includes: fromChain, toChain, fromToken, toToken,
// fromAddress, toAddress, fromAmount, toAmount, slippage
// Return fee as a number (e.g., 0.03 for 3%)
if (params.fromChain.id === params.toChain.id) {
return 0.01; // 1% for same-chain swaps
}
return 0.03; // 3% for cross-chain transfers
},
},
};
```
Only use one of `fee` or `calculateFee`, not both. If `calculateFee` is provided, it takes precedence over the static `fee` value.
## Configure allow and deny options
We provide `allow` and `deny` configuration options to control which chains, tokens, bridges, and exchanges can be used within your application. Here's how you can set up and use these options:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
// disable BSC from being shown in the chains list
chains: {
deny: [56],
},
// allow bridging through Stargate bridge only
bridges: {
allow: ["stargateV2"],
},
};
export const WidgetPage = () => {
return (
);
};
```
### Filter chains by ecosystem type
You can also filter chains by their ecosystem type (EVM, SVM, UTXO, MVM, TVM):
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig, ChainType } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
chains: {
// Only show EVM and Solana chains
types: {
allow: [ChainType.EVM, ChainType.SVM],
},
},
};
export const WidgetPage = () => {
return (
);
};
```
### Configure from/to chain filters separately
You can apply different filters to source and destination chain lists:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
chains: {
// Apply to both from and to lists
deny: [56],
// Only for source chain selection
from: {
allow: [1, 137, 10],
},
// Only for destination chain selection
to: {
allow: [42161, 8453],
},
},
};
```
### Token filtering
To control which tokens appear in the **from** and **to** lists, use the `allow` and `deny` options:
* If defined at the top level of the `tokens` object, they apply to **both** lists.
* If defined inside the `from` or `to` objects, they apply **only** to that specific list.
* If an `allow` list is defined, only tokens included in it are allowed. If no `allow` list is defined, all tokens are allowed unless they are explicitly included in `deny`. If a token appears in both `allow` and `deny`, the `allow` list takes precedence.
* A token must pass both the top level `allow`/`deny` check and the check for the current list (`from` or `to`) to be considered allowed.
* Token filters are applied per chain. When tokens are allowed/denied for a specific chain, only that chain's tokens are affected. Other chains remain unfiltered and show all their available tokens.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
tokens: {
// Top-level allow/deny apply to BOTH 'from' and 'to' lists
allow: [
{
address: "0x0000000000000000000000000000000000000000",
chainId: 1,
},
],
deny: [
{
address: "0x0000000000000000000000000000000000000000",
chainId: 137,
},
],
// 'from' list-specific allow/deny complements top-level settings
from: {
allow: [
{
address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
chainId: 1,
},
],
deny: [
{
address: "0x0000000000000000000000000000000000000000",
chainId: 1,
},
],
},
// 'to' list-specific allow/deny
to: {
allow: [
{
address: "0x0000000000000000000000000000000000000000",
chainId: 137,
},
],
deny: [
{
address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
chainId: 1,
},
],
},
},
};
export const WidgetPage = () => {
return (
);
};
```
### Featured, popular, and included tokens
Apart from the `allow` and `deny` options, the `tokens` option can be configured to include other tokens, featured tokens, or popular tokens that will appear at the top of the corresponding list of tokens.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
tokens: {
// Featured tokens will appear on top of the list
featured: [
{
address: "0x2fd6c9b869dea106730269e13113361b684f843a",
symbol: "CHH",
decimals: 9,
chainId: 56,
name: "Chihuahua",
logoURI:
"https://s2.coinmarketcap.com/static/img/coins/64x64/21334.png",
},
],
// Popular tokens will appear in a separate "Popular tokens" section
popular: [
{
address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
symbol: "USDT",
decimals: 6,
chainId: 1,
name: "Tether USD",
},
],
// Include any token to the list
include: [
{
address: "0xba98c0fbebc892f5b07a42b0febd606913ebc981",
symbol: "MEH",
decimals: 18,
chainId: 1,
name: "meh",
logoURI:
"https://s2.coinmarketcap.com/static/img/coins/64x64/22158.png",
},
],
},
};
export const WidgetPage = () => {
return (
);
};
```
## Destination address
There are use cases where users need to have a different destination address. Usually, they can enter the destination address independently.
Still, the widget also has configuration options to pre-configure the destination address or create a curated list of wallet addresses to choose from.
## Configure single destination address
Developers can use the `toAddress` option to configure a single destination address. The `address` and `chainType` properties are required, while the `name` and `logoURI` properties are optional.
```typescript theme={"system"}
import { ChainType, LiFiWidget, WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
toAddress: {
name: "Vault Deposit",
address: "0x0000000000000000000000000000000000000000",
chainType: ChainType.EVM,
logoURI: "https://example.com/image.svg",
},
};
export const WidgetPage = () => {
return (
);
};
```
## Configure a curated list of wallet addresses
Developers can use `toAddresses` option to configure a curated list of wallet addresses.
```typescript theme={"system"}
import { ChainType, LiFiWidget, WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
toAddresses: [
{
name: "Lenny",
address: "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea9",
chainType: ChainType.EVM,
logoURI: "https://example.com/image.svg",
},
{
address: "0x4577a46A3eCf44E0ed44410B7793977ffbe22CE0",
chainType: ChainType.EVM,
},
{
name: "My sweet solami",
address: "6AUWsSCRFSCbrHKH9s84wfzJXtD6mNzAHs11x6pGEcmJ",
chainType: ChainType.SVM,
},
],
};
export const WidgetPage = () => {
return (
);
};
```
Using this configuration, when users click on the `Send to wallet` button, they will open a pre-configured list of addresses from which to choose, skipping the step where they can manually enter the address.
Together with configuring the wallet list, developers can make the destination address required to be filled out. Please see Required destination address for more details.
## Explorer URLs
In the widget there are numerous points where a user can click to open an explorer in a separate browser tab in order to find out more information about a transaction or an address. Any buttons or links in the widget that present this icon will direct the user to an explorer.
We have default behaviors in relation to opening explorers and we can also use widget config to override and change these behaviors.
### Default behavior for chains
Often when trying to direct a user to an explorer the widget will know which chain relates to a transaction or address and it will present an explorer that matches that chain.
For example, after the user has executed a transaction, on the transaction details page they can click on the "Token allowance approved" explorer button to see more detail about that approval. If the approval was done using the Optimism chain then a new tab would open taking the user to optimistic.etherscan.io to show them more information about that approval.
If no explorer can be found in relation to a chain then the user will be directed to LiFi's explorer.
### Default behavior for internal explorers
An internal explorer is an explorer that is the preferred choice of an organization that is building an app using the widget. In some parts of the widget we use an internal explorer rather than attempting to find an explorer for a specific chain.
For example, once the user has completed a transaction and is on the transaction details page they are presented with a transfer ID (see below). This is accompanied by a link which allows the user to open an explorer in order to find more information about that transaction. There is no attempt to find a chain specific explorer. The default explorer used is LI.FI own internal explorer and users will be directed to [https://scan.li.fi](https://scan.li.fi/)
### Overriding the explorer URLs
It's possible to override the explorer URLs that widget uses via the widget config. We can do this for specific chains and for the internal explorer. You can use your own explorer urls for multiple chains and at the same time state your own alternative for the internal explorer.
#### Overriding explorers for a chain
In the widget config you can override chains by adding an entry to the explorerUrls object: you provide the chain id as a key and the base url of the explorer as the value.
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
explorerUrls: {
42161: ["https://scan.li.fi"],
},
};
```
The explorer specified above will be used only for that chain, in the above example this would be Arbitrum. For other chains that aren't specified in the explorerUrls object the widget will still present the default behavior (as stated above).
#### Custom explorer paths
For explorers that don't follow the standard `/address/:address` and `/tx/:hash` convention, you can specify custom paths:
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
explorerUrls: {
42161: [
{
url: "https://custom-explorer.com",
txPath: "/transaction/", // Custom transaction path
addressPath: "/account/", // Custom address path
},
],
},
};
```
#### Overriding explorers for the internal explorer
In the widget config you can override the internal explorer by adding an entry to the explorerUrls object: you provide `internal` as a key and the base url of the explorer as the value.
```typescript theme={"system"}
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
explorerUrls: {
internal: ["https://jumper.exchange/scan"],
},
};
```
Any places within the widget that use the internal explorer will now use the url stated in the config rather than the default.
### Address and transaction pages
The widget assumes that the explorer will provide pages for addresses at `/address/:address` and for transactions at `/tx/:hash` and will attempt to navigate the user to those pages when the users clicks the related buttons.
A link to a wallet address would look like:
```
https://scan.li.fi/address/0xb9c0dE368BECE5e76B52545a8E377a4C118f597B
```
And a link to a transaction would look like:
```
https://scan.li.fi/tx/0x05dbd8d3be79ad466e7d2898f719cc47b1b3b545cf4782aece16e11849ddd24b
```
The widget assumes that any explorer used with the widget will follow this convention, unless custom paths are specified.
## Adding route labels
The Widget allows you to visually enhance specific routes by adding route labels — styled badges with customizable text and appearance.
To display route labels dynamically, configure the `routeLabels: RouteLabelRule[]` array in your `WidgetConfig`.
```typescript theme={"system"}
interface RouteLabelRule {
label: RouteLabel; // The label to display if conditions match
bridges?: AllowDeny; // Optional: Filter by bridge(s)
exchanges?: AllowDeny; // Optional: Filter by exchange(s)
fromChainId?: number[]; // Optional: Filter by source chain ID(s)
toChainId?: number[]; // Optional: Filter by destination chain ID(s)
fromTokenAddress?: string[]; // Optional: Filter by source token address(es)
toTokenAddress?: string[]; // Optional: Filter by destination token address(es)
match?: (route: Route) => boolean; // Optional: Custom predicate evaluated against the route
}
interface RouteLabel {
text: string; // Text to show on the label
sx?: SxProps; // Optional: Style object (MUI-style)
}
```
Each label rule defines matching conditions and a label configuration that will be applied if the conditions are met.
The label configuration includes `text` and `sx` styling of the badge in the MUI-style CSS-in-JS way.
The rest of the fields determine when and where a label should be applied based on route conditions.
You can combine multiple criteria such as `fromChainId`, `exchanges`, `tokens`, and more.
For bridges and exchanges, use the `allow` and `deny` fields for fine-grained control, similarly to how it is described in [Configure allow and deny options](#configure-allow-and-deny-options).
For matching logic that the built-in fields cannot express — gas costs, step count, route tags, or any other property on the `Route` object — provide a custom `match` predicate.
Its result is AND'd with the other criteria on the rule, so all specified conditions must pass for the label to apply.
A rule can also use `match` on its own without any other fields.
The `match` predicate is not supported in `@lifi/widget-light`, since functions cannot be transferred across the iframe boundary via `postMessage`.
Example configuration:
```typescript theme={"system"}
import { ChainId } from "@lifi/sdk";
import type { WidgetConfig } from "@lifi/widget";
const widgetConfig: WidgetConfig = {
routeLabels: [
{
label: {
text: "OP Reward",
sx: {
background: "linear-gradient(90deg, #ff0404, #ff04c8)",
"@keyframes gradient": {
"0%": { backgroundPosition: "0% 50%" },
"50%": { backgroundPosition: "100% 50%" },
"100%": { backgroundPosition: "0% 50%" },
},
animation: "gradient 3s ease infinite",
backgroundSize: "200% 200%",
color: "#ffffff",
},
},
fromChainId: [ChainId.OPT], // Applies to routes from Optimism
},
{
label: {
text: "LI.FI Bonus",
sx: {
display: "flex",
alignItems: "center",
position: "relative",
overflow: "hidden",
marginLeft: "auto",
order: 1,
backgroundImage:
"url(https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/exchanges/lifidexaggregator.svg)",
backgroundPosition: "left center",
backgroundRepeat: "no-repeat",
backgroundSize: "24px",
paddingLeft: "12px",
backgroundColor: "#f5b5ff",
},
},
fromChainId: [ChainId.OPT], // Applies to routes from Optimism
exchanges: {
allow: ["relay"], // Only show for Relay routes
},
},
{
label: { text: "Single Tx" },
// Highlight routes that complete in one transaction — no extra
// approvals or intermediate steps, so they're faster and less
// likely to fail mid-execution.
match: (route) => route.steps.length === 1,
},
],
};
```
Rendered example of the configured route labels:
Labels only appear when *all* specified criteria are satisfied by a route.
# Customize Widget
Source: https://docs.li.fi/widget/customize-widget
Customize the look and feel of the widget to match the design of your dApp and suit your needs
**LI.FI Widget** supports visual customization, allowing you to match your web app's design. The widget's layout stays consistent, but you can modify colors, fonts, border radius, container styles, disable or hide parts of the UI, and more.
Start customizing the widget by tweaking some of the following options:
```typescript theme={"system"}
interface WidgetConfig {
// sets default appearance - light, dark, or system
appearance?: Appearance;
// disables parts of the UI
disabledUI?: DisabledUIConfig;
// hides parts of the UI
hiddenUI?: HiddenUIConfig;
// makes parts of the UI required
requiredUI?: RequiredUIConfig;
// sets default UI behaviors
defaultUI?: DefaultUI;
// tweaks container, components, colors, fonts, border-radius
theme?: WidgetTheme;
}
```
## Theme
By customizing the theme, you can ensure the LI.FI Widget matches the look and feel of your application, providing a seamless user experience.
Preview theme changes live in the [Widget Playground](https://playground.li.fi/) — open **Theme** in the sidebar and click **Edit theme**.
The `theme` configuration option allows you to customize various aspects of the widget's appearance, including colors, typography, shapes, and component styles.
### Containers
The `container` option customizes the main container of the widget.
The `routesContainer` and `chainSidebarContainer` options apply custom styles to routes and chain sidebar expansions respectively (available in `wide` variant).
In the example below, we adjust the boxShadow and border-radius properties of all the containers.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';
export const WidgetPage = () => {
const widgetConfig: WidgetConfig = useMemo(
() => ({
theme: {
container: {
boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
borderRadius: '16px',
},
chainSidebarContainer: {
boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
borderRadius: '16px',
},
routesContainer: {
boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
borderRadius: '16px',
},
},
}),
[]
);
return ;
};
```
### Color Schemes
The `colorSchemes` option defines separate color palettes for light and dark modes. This allows you to customize colors independently for each appearance mode.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';
export const WidgetPage = () => {
const widgetConfig: WidgetConfig = useMemo(
() => ({
theme: {
colorSchemes: {
light: {
palette: {
primary: { main: '#7B3FE4' },
secondary: { main: '#F5B5FF' },
background: {
default: '#ffffff',
paper: '#f8f8fa',
},
text: {
primary: '#000000',
secondary: '#747474',
},
},
},
dark: {
palette: {
primary: { main: '#9B6FE4' },
secondary: { main: '#D59FFF' },
background: {
default: '#121212',
paper: '#212121',
},
text: {
primary: '#ffffff',
secondary: '#bbbbbb',
},
},
},
},
},
}),
[]
);
return ;
};
```
### Shape and Typography
The `shape` option defines border-radius overrides for all elements in the widget:
* `borderRadius` - Primary border radius for cards and containers
* `borderRadiusSecondary` - Secondary border radius for buttons and inputs
* `borderRadiusTertiary` - Tertiary border radius for specific elements
The `typography` option customizes the font settings like font families.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';
export const WidgetPage = () => {
const widgetConfig: WidgetConfig = useMemo(
() => ({
theme: {
colorSchemes: {
light: {
palette: {
primary: { main: '#7B3FE4' },
secondary: { main: '#F5B5FF' },
},
},
},
shape: {
borderRadius: 12,
borderRadiusSecondary: 12,
borderRadiusTertiary: 24,
},
typography: {
fontFamily: 'Inter, sans-serif',
},
container: {
boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
borderRadius: '16px',
},
},
}),
[]
);
return ;
};
```
### Components
The `components` option allows you to customize the styles of specific components within the widget.
The current list of available components:
* **MuiAppBar** - Used as a header/navigation component at the top of the widget.
* **MuiAvatar** - Used to display token/chain avatars.
* **MuiButton** - Used for various buttons in the widget.
* **MuiCard** - Used for card elements within the widget. There are three default card variants:
* **outlined** - Default variant where the card has thin borders.
* **elevation** - Variant where the card has a shadow.
* **filled** - Variant where the card is filled with color (`palette.background.paper` property).
* **MuiDrawer** - Used for the drawer container when `variant` is set to `'drawer'`.
* **MuiIconButton** - Used for icon buttons within the widget.
* **MuiInputCard** - Used for input cards within the widget.
* **MuiNavigationTabs** - Used for navigation tabs in the split mode.
* **MuiNavigationTab** - Used for individual navigation tab items.
* **MuiTabs** - Used for tab navigation within the widget (available in `split` mode).
* **MuiCheckbox** - Used for checkbox elements within the widget.
With the `components` option, each component can be customized using the MUI's `styleOverrides` property, allowing for granular control over its styling.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { tabsClasses } from '@mui/material';
import { useMemo } from 'react';
export const WidgetPage = () => {
const widgetConfig: WidgetConfig = useMemo(
() => ({
theme: {
colorSchemes: {
light: {
palette: {
primary: { main: '#006Eff' },
secondary: { main: '#FFC800' },
background: {
default: '#ffffff',
paper: '#f8f8fa',
},
text: {
primary: '#00070F',
secondary: '#6A7481',
},
grey: {
200: '#EEEFF2',
300: '#D5DAE1',
700: '#555B62',
800: '#373F48',
},
},
},
},
shape: {
borderRadius: 12,
borderRadiusSecondary: 12,
borderRadiusTertiary: 24,
},
container: {
boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
borderRadius: '16px',
},
components: {
MuiCard: {
defaultProps: { variant: 'filled' },
},
// Used only for 'split' mode and can be safely removed if not used
MuiNavigationTabs: {
styleOverrides: {
root: {
backgroundColor: '#f8f8fa',
[`.${tabsClasses.indicator}`]: {
backgroundColor: '#ffffff',
},
},
},
},
},
},
}),
[]
);
return ;
};
```
## Pre-configured Themes
The LI.FI Widget includes several pre-configured themes that provide a starting point for customization. These themes demonstrate various configurations of colors, shapes, and component styles, giving you an idea of how the widget can be styled to fit different design requirements.
Besides the default theme, there are pre-configured themes available:
```typescript theme={"system"}
import {
azureLightTheme,
jumperTheme,
watermelonLightTheme,
windows95Theme
} from '@lifi/widget';
```
### Azure Light
A clean, professional theme with blue primary colors.
### Watermelon Light
A playful theme with pink/coral primary colors.
### Windows 95
A retro theme mimicking the classic Windows 95 aesthetic with sharp corners and distinctive shadows.
### Customizing Pre-configured Themes
You can further customize these pre-configured themes by spreading them and overriding specific properties:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig, azureLightTheme } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
theme: {
...azureLightTheme,
container: {
...azureLightTheme.container,
borderRadius: '24px',
},
},
};
```
## Appearance
The widget has complete dark mode support out of the box. The `appearance` option can be set to:
* `'light'` - Always use light mode
* `'dark'` - Always use dark mode
* `'system'` - Match the user's system settings (default)
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';
export const WidgetPage = () => {
const widgetConfig: WidgetConfig = useMemo(
() => ({
appearance: 'dark',
}),
[]
);
return ;
};
```
## Disabled UI Elements
The `disabledUI` property allows you to specify which UI elements should be disabled in the widget. Disabling UI elements can be useful to prevent user interaction with certain parts of the widget that might not be necessary or desirable for your specific implementation.
```typescript theme={"system"}
interface DisabledUIConfig {
// Disables the input field for the token amount
fromAmount?: boolean;
// Disables the button for the source token selection
fromToken?: boolean;
// Disables the button for specifying the destination address
toAddress?: boolean;
// Disables the button for the destination token selection
toToken?: boolean;
}
```
## Hidden UI Elements
The `hiddenUI` property allows you to specify which UI elements should be hidden in the widget. This is useful for tailoring the user interface to fit your specific needs by removing elements that might not be relevant for your use case.
```typescript theme={"system"}
interface HiddenUIConfig {
// Hides the connected wallets section in address book
addressBookConnectedWallets?: boolean;
// Hides the "All Networks" option in chain selectors
allNetworks?: boolean;
// Hides the appearance settings UI (light/dark mode switch)
appearance?: boolean;
// Hides the bridges settings UI
bridgesSettings?: boolean;
// Hides the chain selection UI
chainSelect?: boolean;
// Hides the chain sidebar in the wide variant
chainSidebar?: boolean;
// Hides the contact support button
contactSupport?: boolean;
// Hides the close button in the drawer variant
drawerCloseButton?: boolean;
// Hides the button for the source token selection
fromToken?: boolean;
// Hides the gas refuel message UI
gasRefuelMessage?: boolean;
// Hides the toggle to hide tokens with small balances
hideSmallBalances?: boolean;
// Hides the transaction history UI
history?: boolean;
// Hides the insufficient gas message UI
insufficientGasMessage?: boolean;
// Hides the integrator-specific step details UI
integratorStepDetails?: boolean;
// Hides the language selection UI
language?: boolean;
// Hides the low address activity confirmation dialog
lowAddressActivityConfirmation?: boolean;
// Hides the 'Powered by LI.FI' branding - not recommended :)
poweredBy?: boolean;
// Hides the button to reverse/swap the from and to tokens
reverseTokensButton?: boolean;
// Hides the price impact indicator on route cards
routeCardPriceImpact?: boolean;
// Hides the token description in routes
routeTokenDescription?: boolean;
// Hides the search bar in the tokens list
searchTokenInput?: boolean;
// Hides the button for specifying the destination address
toAddress?: boolean;
// Hides the button for the destination token selection
toToken?: boolean;
// Hides the wallet menu UI
walletMenu?: boolean;
}
```
The following example shows how to hide appearance and language settings in the UI:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';
export const WidgetPage = () => {
const widgetConfig: WidgetConfig = useMemo(
() => ({
hiddenUI: { language: true, appearance: true },
}),
[]
);
return ;
};
```
## Required UI Elements
The `requiredUI` property allows you to specify which UI elements should be required in the widget. This means that the user must interact with these elements for the widget to proceed with swapping/bridging.
```typescript theme={"system"}
interface RequiredUIConfig {
// Shows a message when the account is a smart account that may have
// different addresses on other chains
accountDeployedMessage?: boolean;
// Makes the destination address required for interaction
toAddress?: boolean;
}
```
### Required destination address
Making the destination address required can come in handy when developers want to build a flow where only a pre-configured list of wallet addresses can be set as the destination. See [Configure a curated list of wallet addresses](/widget/configure-widget#configure-a-curated-list-of-wallet-addresses) for more details.
If you are interested in additional customization options for your service, reach out via our [Partnership](https://docs.li.fi/overview/partnership) page.
As you can see, widget customization is pretty straightforward. We are eager to see what combinations you will come up with as we continue to add new customization options.
# Install Widget
Source: https://docs.li.fi/widget/install-widget
Easy installation to go multi-chain
To get started, install the latest version of LI.FI Widget along with the required peer dependencies.
## Installation
### Core Widget Package
```bash yarn theme={"system"}
yarn add @lifi/widget @tanstack/react-query
```
```bash pnpm theme={"system"}
pnpm add @lifi/widget @tanstack/react-query
```
```bash npm theme={"system"}
npm install @lifi/widget @tanstack/react-query
```
```bash bun theme={"system"}
bun add @lifi/widget @tanstack/react-query
```
### With Blockchain Providers
For full multi-chain support including Ethereum, Solana, Bitcoin, Sui, Tron, and Stellar, install the widget with all provider packages and their peer dependencies:
```bash yarn theme={"system"}
yarn add @lifi/widget @lifi/widget-provider-ethereum @lifi/widget-provider-solana @lifi/widget-provider-bitcoin @lifi/widget-provider-sui @lifi/widget-provider-tron @lifi/widget-provider-stellar wagmi @wagmi/core @bigmi/react bs58 @mysten/dapp-kit-react @tronweb3/tronwallet-adapter-react-hooks @tanstack/react-query
```
```bash pnpm theme={"system"}
pnpm add @lifi/widget @lifi/widget-provider-ethereum @lifi/widget-provider-solana @lifi/widget-provider-bitcoin @lifi/widget-provider-sui @lifi/widget-provider-tron @lifi/widget-provider-stellar wagmi @wagmi/core @bigmi/react bs58 @mysten/dapp-kit-react @tronweb3/tronwallet-adapter-react-hooks @tanstack/react-query
```
```bash npm theme={"system"}
npm install @lifi/widget @lifi/widget-provider-ethereum @lifi/widget-provider-solana @lifi/widget-provider-bitcoin @lifi/widget-provider-sui @lifi/widget-provider-tron @lifi/widget-provider-stellar wagmi @wagmi/core @bigmi/react bs58 @mysten/dapp-kit-react @tronweb3/tronwallet-adapter-react-hooks @tanstack/react-query
```
```bash bun theme={"system"}
bun add @lifi/widget @lifi/widget-provider-ethereum @lifi/widget-provider-solana @lifi/widget-provider-bitcoin @lifi/widget-provider-sui @lifi/widget-provider-tron @lifi/widget-provider-stellar wagmi @wagmi/core @bigmi/react bs58 @mysten/dapp-kit-react @tronweb3/tronwallet-adapter-react-hooks @tanstack/react-query
```
## Dependencies Explained
### Core Dependencies
* [TanStack Query](https://tanstack.com/query/v5) - Async state manager that handles requests, caching, and more. Required peer dependency.
### Blockchain Provider Packages
These are optional packages that enable support for different blockchain ecosystems:
* **@lifi/widget-provider-ethereum** - Ethereum/EVM chain support
* Peer dependencies: [wagmi](https://wagmi.sh/) ^3 and [@wagmi/core](https://wagmi.sh/) ^3
* **@lifi/widget-provider-solana** - Solana chain support
* Peer dependency: [bs58](https://github.com/cryptocoinjs/bs58) >=4.0.1
* **@lifi/widget-provider-bitcoin** - Bitcoin chain support
* Peer dependency: [@bigmi/react](https://github.com/lifinance/bigmi) ^0.8.0
* **@lifi/widget-provider-sui** - Sui chain support
* Peer dependency: [@mysten/dapp-kit-react](https://sdk.mystenlabs.com/dapp-kit) ^2.0.0
* **@lifi/widget-provider-tron** - Tron chain support
* Peer dependency: [@tronweb3/tronwallet-adapter-react-hooks](https://github.com/tronweb3/tronwallet-adapter) ^1.1.11
* **@lifi/widget-provider-stellar** - Stellar chain support
* No peer dependency beyond React 19. It bundles the [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit) as a direct dependency.
**Polyfill Requirements:** If you need to support older browsers, you'll need to install and configure polyfills. See the [Polyfill Requirements](/widget/polyfill-requirements) documentation for details.
## Compatibility
The widget is compatible with React 19+ and works with various frameworks and wallet libraries including Next.js, Remix, Vue, Nuxt.js, Svelte, RainbowKit, ConnectKit, Privy, and more.
See the [Compatibility](/widget/compatibility) page for framework-specific setup guides and code examples.
Check out our complete examples in the [widget repository](https://github.com/lifinance/widget/tree/main/examples) or [file an issue](https://github.com/lifinance/widget/issues) if you have any incompatibilities.
## Basic Example
The widget is written in TypeScript and all configuration options are fully typed.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
theme: {
container: {
border: '1px solid rgb(234, 234, 234)',
borderRadius: '16px',
},
},
};
export const WidgetPage = () => {
return ;
};
```
## Example with Blockchain Providers
For multi-chain support, configure the widget with blockchain providers:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { EthereumProvider } from '@lifi/widget-provider-ethereum';
import { SolanaProvider } from '@lifi/widget-provider-solana';
import { BitcoinProvider } from '@lifi/widget-provider-bitcoin';
import { SuiProvider } from '@lifi/widget-provider-sui';
import { TronProvider } from '@lifi/widget-provider-tron';
import { StellarProvider } from '@lifi/widget-provider-stellar';
const widgetConfig: WidgetConfig = {
providers: [
EthereumProvider(),
SolanaProvider(),
BitcoinProvider(),
SuiProvider(),
TronProvider(),
StellarProvider(),
],
theme: {
container: {
border: '1px solid rgb(234, 234, 234)',
borderRadius: '16px',
},
},
};
export const WidgetPage = () => {
return ;
};
```
# Internationalization
Source: https://docs.li.fi/widget/internationalization
Unlock global communities with effortless internationalization support
**LI.FI Widget** supports internationalization (i18n) and, with the help of our community, is translated into multiple languages to provide a localized user experience to your users, making it easier for them to understand and interact with the widget.
See supported languages and help us translate by [**joining**](https://crowdin.com/project/lifi-widget) our translation projects on [**Crowdin**](https://crowdin.com/project/lifi-widget):

## Configure languages
By default, the widget is in English. You can configure the default language, which languages you want to show inside the widget, or in which language your users can see the widget if you hide the in-built language selection.
There are `allow`, `deny`, and `default` options for language configuration.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
// hide the language selection part of the UI
// hiddenUI: { language: true },
languages: {
// default to German
default: 'de',
// allow German and Spanish languages only
allow: ['de', 'es'],
// disable Chinese from being shown in the languages list
// deny: ['zh'],
},
};
export const WidgetPage = () => {
return (
);
};
```
## Language Resources
You can customize the widget to support any language that your dApp needs by providing language resources.
Rather than trying to add a language via config, it's best to first consider helping us to translate the language you need by joining our [Crowdin translation project](https://crowdin.com/project/lifi-widget). 🙂
```typescript widget.tsx theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import es from './i18n/es.json';
const widgetConfig: WidgetConfig = {
languageResources: {
es,
},
};
export const WidgetPage = () => {
return (
);
};
```
```json i18n/es.json theme={"system"}
{
"language": {
"name": "Español",
"title": "Idioma"
}
}
```
Also, you can customize the existing language resources if you want to adjust some text. Find the complete list of key-value pairs in the reference `en.json` in our repository [here](https://github.com/lifinance/widget/blob/main/packages/widget/src/i18n/en.json).
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
languageResources: {
en: {
button: { swap: 'Test swap' },
},
},
};
export const WidgetPage = () => {
return (
);
};
```
# Monetize Widget
Source: https://docs.li.fi/widget/monetize-widget
Learn how to configure fees and monetize your LI.FI Widget integration.
For more details about how fees work, fee collection on different chains, and
setting up fee wallets, see the [Monetizing the
integration](/introduction/integrating-lifi/monetizing-integration) guide.
Fees are configured via the `feeConfig` option, which supports both static and dynamic fee calculation.
### Fee configuration
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig, WidgetFeeConfig } from '@lifi/widget';
// Basic advanced configuration
const basicFeeConfig: WidgetFeeConfig = {
name: "DApp fee",
logoURI: "https://yourdapp.com/logo.png",
fee: 0.01, // 1% fee
showFeePercentage: true,
showFeeTooltip: true
};
// Dynamic fee calculation
const dynamicFeeConfig: WidgetFeeConfig = {
name: "DApp fee",
logoURI: "https://yourdapp.com/logo.png",
showFeePercentage: true,
showFeeTooltip: true,
calculateFee: async (params) => {
// params: { fromChain, toChain, fromToken, toToken,
// fromAddress, toAddress, fromAmount, toAmount, slippage }
const { fromChain, toChain, fromToken, toToken, fromAmount } = params;
// Example: Different fees for same-chain vs cross-chain
if (fromChain.id === toChain.id) {
return 0.01; // 1% for same-chain swaps
}
// Example: Volume-based fee structure (fromAmount is bigint)
if (fromAmount && fromAmount > 1000000000000000000n) {
return 0.015; // 1.5% for large volumes
}
return 0.03; // Default 3% fee
}
};
const widgetConfig: WidgetConfig = {
feeConfig: basicFeeConfig, // or dynamicFeeConfig
// Other options...
};
export const WidgetPage = () => {
return (
);
};
```
### WidgetFeeConfig interface
The `WidgetFeeConfig` interface provides the following options:
* **`name`** (optional): Display name for your integration shown in fee details
* **`logoURI`** (optional): URL to your logo displayed alongside fee information
* **`fee`** (optional): Fixed fee percentage (e.g., 0.03 for 3%)
* **`showFeePercentage`** (default: false): Whether to display the fee percentage in the UI
* **`showFeeTooltip`** (default: false): Whether to show a tooltip with fee details (requires `name` or `feeTooltipComponent`)
* **`feeTooltipComponent`** (optional): Custom React component for the fee tooltip
* **`calculateFee`** (optional): Function for dynamic fee calculation based on transaction parameters
Only use either `fee` or `calculateFee` - not both. The `calculateFee`
function allows for dynamic fee calculation based on factors like token pairs,
transaction amounts, user tiers, or any other custom logic.
# LI.FI Widget Overview
Source: https://docs.li.fi/widget/overview
Cross-chain and on-chain swap and bridging UI toolkit
LI.FI Widget is a set of prebuilt UI components for integrating cross-chain bridging and swapping into your web app. It supports all ecosystems, chains, bridges, exchanges, and solvers that LI.FI offers — styled to match your design.
Explore the Widget interactively — configure and preview changes in real time
**LI.FI Widget features include:**
* Embeddable variants (compact, wide, drawer) and modes (split, custom, refuel)
* Modular provider architecture — install only the ecosystems you need (Ethereum, Solana, Bitcoin, Sui, Tron, Stellar) built on industry-standard libraries ([Viem](https://viem.sh/), [Wagmi](https://wagmi.sh/), [Bigmi](https://github.com/lifinance/bigmi), [Wallet Standard](https://github.com/wallet-standard/wallet-standard), [TronWallet Adapters](https://github.com/tronweb3/tronwallet-adapter), [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit))
* Theming, dark mode, and full CSS customization
* Built-in wallet management with opt-out support for your own wallet solution
* Allow/deny filtering for chains, tokens, bridges, and exchanges
* Gasless/relayer routes, route priority settings, and slippage controls
* Static and dynamic fee configuration for monetization
* Complete UI translations (17 languages)
* Industry standards support: [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702), [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792), [ERC-2612](https://eips.ethereum.org/EIPS/eip-2612), [EIP-712](https://eips.ethereum.org/EIPS/eip-712), [Permit2](https://github.com/Uniswap/permit2)
* Compatibility tested with React, Next.js, Vue, Nuxt.js, Svelte, Remix, Vite, RainbowKit, Reown AppKit, Privy, Dynamic, ConnectKit
**Composer works automatically in the Widget.** When users select a vault or staking token as their destination, Composer handles the deposit seamlessly. See the [Widget Composer Integration Guide](/composer/guides/widget-integration).
## How to integrate the Widget
Follow the [installation guide](/widget/install-widget).
Configure your widget [layout](/widget/select-widget-layout).
Set chains, tokens, fees, and route options in your [widget config](/widget/configure-widget).
Install [provider packages](/widget/install-widget#blockchain-provider-packages) for the ecosystems you need.
Match your app's look and feel with [theme customization](/widget/customize-widget).
# Select Widget Layout
Source: https://docs.li.fi/widget/select-widget-layout
Choose a mode, variant, and height for your widget
## Mode
Modes allow you to present different workflows for your users.
Try different modes in the [Widget Playground](https://playground.li.fi/) — open **Mode** in the sidebar.
```typescript theme={"system"}
type WidgetMode = 'default' | 'split' | 'custom' | 'refuel';
```
### Default Mode
The **default** mode provides the standard functionality to bridge and swap in a unified view.
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
mode: 'default', // This is the default
};
```
### Split Mode
The **split** mode separates mental models and provides different views for bridging and swapping experiences with tabs on the main page.
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
mode: 'split',
};
```
#### Split Mode Options
For `mode: 'split'`, the `modeOptions` configuration controls whether to show both "Swap" and "Bridge" tabs or a single interface:
* **Default (no options)**: Shows both "Bridge" and "Swap" tabs
* **`split: 'bridge'`**: Shows only bridge interface (no tabs)
* **`split: 'swap'`**: Shows only swap interface (no tabs)
* **`split: { defaultTab: 'bridge' }` or `split: { defaultTab: 'swap' }`**: Shows both tabs with the specified default tab selected
```typescript theme={"system"}
// Default - shows both tabs
const tabsConfig: WidgetConfig = {
mode: 'split',
};
// Pure bridge interface
const bridgeConfig: WidgetConfig = {
mode: 'split',
modeOptions: {
split: 'bridge',
},
};
// Pure swap interface
const swapConfig: WidgetConfig = {
mode: 'split',
modeOptions: {
split: 'swap',
},
};
// Both tabs with swap as default
const tabsSwapDefaultConfig: WidgetConfig = {
mode: 'split',
modeOptions: {
split: { defaultTab: 'swap' },
},
};
```
### Custom Mode
The **custom** mode offers a different look, allowing you to show custom components and build complete new flows including NFT Checkout and Deposit.
```typescript theme={"system"}
type CustomMode = 'checkout' | 'deposit';
```
#### Checkout Flow
For NFT or product checkout flows:
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
mode: 'custom',
modeOptions: {
custom: { type: 'checkout' },
},
contractCalls: [...], // Your contract calls
contractComponent: ,
contractTool: {
name: 'Your Protocol',
logoURI: 'https://your-protocol.com/logo.png',
},
};
```
#### Deposit Flow
For protocol deposit flows:
```typescript theme={"system"}
import { ChainType, LiFiWidget, WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
mode: 'custom',
modeOptions: {
custom: { type: 'deposit' },
},
toAddress: {
name: 'Protocol Vault',
address: '0x...',
chainType: ChainType.EVM,
logoURI: 'https://your-protocol.com/logo.png',
},
disabledUI: { toAddress: true },
hiddenUI: { appearance: true, language: true },
showSingleRoute: true,
contractComponent: ,
contractTool: {
name: 'Your Protocol',
logoURI: 'https://your-protocol.com/logo.png',
},
};
```
See the complete [Deposit Flow example](https://github.com/lifinance/widget/tree/main/examples/deposit-flow).
### Refuel Mode
The **refuel** mode is optimized for gas refueling operations, helping users get native tokens on destination chains.
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
mode: 'refuel',
};
```
### ModeOptions Interface
```typescript theme={"system"}
type SplitMode = 'bridge' | 'swap'
type SplitModeOptions = {
defaultTab: SplitMode
}
type CustomMode = 'checkout' | 'deposit'
interface ModeOptions {
// Options for 'split' mode
// Pass a string for single-mode (no tabs), or an object for tabs with a default
split?: SplitMode | SplitModeOptions
// Options for 'custom' mode
custom?: { type: CustomMode }
}
```
***
## Variant
Variants provide a way to optimize the presentational style of the Widget for the space available in your application.
Try different variants in the [Widget Playground](https://playground.li.fi/) — open **Variant** in the sidebar.
```typescript theme={"system"}
type WidgetVariant = 'compact' | 'wide' | 'drawer';
```
### Compact Variant
The compact variant is a great choice when you have limited space on a page or are dealing with smaller screen sizes. It has everything you need to bridge and swap in a compact view and allows you to integrate the widget wherever you want on your web app's page.
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
variant: 'compact', // This is the default
};
```
### Wide Variant
The wide variant allows you to take advantage of bigger page and screen sizes where you might have more available screen real estate. It provides a more comprehensive overview of available routes, displayed in a sidebar with slick animation.
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
variant: 'wide',
};
```
#### Chain Sidebar
The `wide` variant shows a chain sidebar by default. To hide it, set `chainSidebar` to `true` in `hiddenUI`:
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
variant: 'wide',
hiddenUI: {
chainSidebar: true,
},
};
```
### Drawer Variant
The drawer variant allows you to show or hide the Widget based on user interaction. It can fit nicely on the page's side and has the same layout as the compact variant.
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
variant: 'drawer',
};
```
#### Controlling the Drawer
The drawer doesn't have a pre-built button to open and close it. To control the drawer, create and assign a `ref` to the widget:
```typescript theme={"system"}
import { useRef } from 'react';
import { LiFiWidget, WidgetDrawer, WidgetConfig } from '@lifi/widget';
export const WidgetPage = () => {
const drawerRef = useRef(null);
const toggleWidget = () => {
drawerRef.current?.toggleDrawer();
};
const openWidget = () => {
drawerRef.current?.openDrawer();
};
const closeWidget = () => {
drawerRef.current?.closeDrawer();
};
const isWidgetOpen = () => {
return drawerRef.current?.isOpen();
};
return (
);
};
```
#### WidgetDrawer Interface
```typescript theme={"system"}
interface WidgetDrawer {
isOpen(): boolean // Check if drawer is open
toggleDrawer(): void // Toggle drawer open/closed
openDrawer(): void // Open the drawer
closeDrawer(): void // Close the drawer
}
```
#### Controlled Drawer
You can also control the drawer state externally using the `open` and `onClose` props:
```typescript theme={"system"}
import { useState } from 'react';
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
export const WidgetPage = () => {
const [isOpen, setIsOpen] = useState(false);
return (
);
};
```
***
## Height
Here are several recommended ways to configure the widget's height: **default, restricted max height, restricted height, and full height.**
Try different height configurations in the [Widget Playground](https://playground.li.fi/) — open **Height** in the sidebar.
We recommend default, restricted max height, or restricted height for the compact and wide variants. Full height is recommended only for compact. The drawer variant uses full container/viewport height.
### Default
By default the widget fits its content on smaller pages and caps at `686` pixels (`maxHeight`) on pages with long lists. This requires no config change.
### Restricted Max Height
The widget expands and contracts to fit content but won't exceed the stated max height. Overflow pages become scrollable.
Set `maxHeight` on `theme.container` as a number (pixels). Values above `686` (the default) are required.
```typescript theme={"system"}
import { WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
theme: {
container: {
maxHeight: 820,
},
},
};
```
### Restricted Height
All pages occupy the exact height specified — the widget stays a consistent size. Overflow pages become scrollable.
Set `height` on `theme.container` as a number (pixels). Values above `686` (the default) are required.
```typescript theme={"system"}
import { WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
theme: {
container: {
height: 900,
},
},
};
```
Don't use `height` and `maxHeight` together — they represent different layout approaches in the widget.
### Full Height
Recommended for mobile and limited screen space where the widget is the primary content. The widget fills the full height of its containing HTML element and delegates scrolling to the page.
```typescript theme={"system"}
import { WidgetConfig } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
variant: 'compact',
theme: {
container: {
display: 'flex',
height: '100%',
},
header: {
position: 'fixed',
top: 0,
},
},
};
```
Configuration breakdown:
* **variant: 'compact'** — required; compact is built for smaller screens.
* **theme.container** — `display: 'flex'` and `height: '100%'` make the widget fill its container.
* **theme.header** (optional) — set `position: 'fixed'` and a `top` value to make the header sticky. Adjust `top` to account for elements above the widget (e.g. a 60px nav bar → `top: 60`). Without this, the header scrolls with page content.
Full height delegates sizing to the parent container, so your page's HTML and CSS must handle this correctly.
* Viewport meta may need to be updated for the page to present correctly
* e.g. ``
* The Widget Playground uses `min-height: 100dvh` — pages taller than the viewport remain scrollable, while shorter pages use flex to fill the screen.
* Add `overscroll-behavior: none;` to the root/body of your page to prevent undesired scrolling behavior.
Consider placement in relation to your site's navigation, headers, and footers. Preview header and/or footer placement in the [Widget Playground](https://playground.li.fi/) — open **Developer Controls** in the sidebar and toggle 'Show mock header' and/or 'Show mock footer'.
# Wallet Management
Source: https://docs.li.fi/widget/wallet-management
Configure your widget for seamless wallet management
The widget has a built-in wallet management UI, so you can connect wallets and use the widget as a standalone dApp out of the box. However, when embedding the widget into an existing dApp, reusing the existing wallet management UI of that dApp often makes the most sense.
See wallet management modes in the [Widget Playground](https://playground.li.fi/) — open **Wallet management** in the sidebar.
There are several ecosystems and types of chains supported by the widget:
* **EVM** (Ethereum Virtual Machine) - Ethereum, Polygon, Arbitrum, etc.
* **SVM** (Solana Virtual Machine) - Solana
* **UTXO** - Bitcoin
* **MVM** (Move Virtual Machine) - Sui
* **TVM** (Tron Virtual Machine) - Tron
* **STL** (Stellar) - Stellar
Each ecosystem uses different libraries to manage wallet connections.
## Using Widget Providers
The simplest way to set up wallet management is using the built-in provider packages:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { EthereumProvider } from '@lifi/widget-provider-ethereum';
import { SolanaProvider } from '@lifi/widget-provider-solana';
import { BitcoinProvider } from '@lifi/widget-provider-bitcoin';
import { SuiProvider } from '@lifi/widget-provider-sui';
import { TronProvider } from '@lifi/widget-provider-tron';
import { StellarProvider } from '@lifi/widget-provider-stellar';
const widgetConfig: WidgetConfig = {
providers: [
EthereumProvider(),
SolanaProvider(),
BitcoinProvider(),
SuiProvider(),
TronProvider(),
StellarProvider(),
],
};
export const WidgetPage = () => {
return ;
};
```
## EVM Wallet Connection
To manage wallet connections to EVM chains, the widget uses the [Wagmi](https://wagmi.sh/) library internally and provides first-class support for Wagmi-based libraries such as:
* [RainbowKit](https://www.rainbowkit.com/)
* [ConnectKit](https://docs.family.co/connectkit)
* [Reown AppKit](https://docs.reown.com/appkit/overview) (formerly WalletConnect)
* [Privy](https://www.privy.io/)
* [Dynamic](https://www.dynamic.xyz/)
### Automatic Detection
If you already manage wallets using Wagmi or a Wagmi-based library in your dApp and the Widget detects that it is wrapped in [WagmiProvider](https://wagmi.sh/react/api/WagmiProvider), it will automatically reuse your wallet management without any additional configuration.
### Basic Wagmi Setup
```typescript theme={"system"}
import { LiFiWidget } from '@lifi/widget';
import { createClient } from 'viem';
import { WagmiProvider, createConfig, http } from 'wagmi';
import { mainnet, arbitrum, optimism, scroll } from 'wagmi/chains';
import { injected } from 'wagmi/connectors';
const wagmiConfig = createConfig({
// Provide the full list of chains you want to support
chains: [mainnet, arbitrum, optimism, scroll],
connectors: [injected()],
client({ chain }) {
return createClient({ chain, transport: http() });
},
});
export const WidgetPage = () => {
return (
);
};
```
### Keep Chains in Sync
It's important to keep the Wagmi chains configuration in sync with the Widget chain list so all functionality, like switching chains, works correctly. There are two approaches:
1. **Manual**: Update both Widget and Wagmi chains configuration to specify all supported chains.
2. **Dynamic**: Get available chains from LI.FI API and dynamically update Wagmi configuration.
#### Dynamic Chain Sync
Use the `useSyncWagmiConfig` hook from `@lifi/widget-provider-ethereum` and `useWidgetChains` from `@lifi/widget`:
```typescript WalletProvider.tsx theme={"system"}
import { useSyncWagmiConfig } from '@lifi/widget-provider-ethereum';
import { useWidgetChains } from '@lifi/widget';
import type { WidgetConfig } from '@lifi/widget';
import { injected } from '@wagmi/connectors';
import { useRef, type FC, type PropsWithChildren } from 'react';
import { createClient, http } from 'viem';
import { mainnet } from 'viem/chains';
import type { Config } from 'wagmi';
import { createConfig, WagmiProvider } from 'wagmi';
const connectors = [injected()];
// useWidgetChains requires a WidgetConfig to fetch chains from the LI.FI API
const widgetConfig: WidgetConfig = {
integrator: 'your-dapp-name',
};
export const WalletProvider: FC = ({ children }) => {
const { chains } = useWidgetChains(widgetConfig);
const wagmi = useRef(null);
if (!wagmi.current) {
wagmi.current = createConfig({
chains: [mainnet],
client({ chain }) {
return createClient({ chain, transport: http() });
},
ssr: true,
});
}
useSyncWagmiConfig(wagmi.current, connectors, chains);
return (
{children}
);
};
```
```typescript WidgetPage.tsx theme={"system"}
import { LiFiWidget } from '@lifi/widget';
import { WalletProvider } from '../providers/WalletProvider';
export const WidgetPage = () => {
return (
);
};
```
Please check out our complete examples in the widget repository:
* [RainbowKit](https://github.com/lifinance/widget/tree/main/examples/rainbowkit)
* [ConnectKit](https://github.com/lifinance/widget/tree/main/examples/connectkit)
* [Reown AppKit](https://github.com/lifinance/widget/tree/main/examples/reown)
* [Privy](https://github.com/lifinance/widget/tree/main/examples/privy)
* [Dynamic](https://github.com/lifinance/widget/tree/main/examples/dynamic)
## SVM (Solana) Wallet Connection
To manage wallet connections to Solana, the widget uses the [Solana Wallet Standard](https://github.com/wallet-standard/wallet-standard) via the `@lifi/widget-provider-solana` package.
The Solana provider automatically discovers wallets that implement the Wallet Standard. To use it, simply include `SolanaProvider()` in your widget's `providers` array:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { SolanaProvider } from '@lifi/widget-provider-solana';
const widgetConfig: WidgetConfig = {
providers: [SolanaProvider()],
};
export const WidgetPage = () => {
return ;
};
```
The `@lifi/widget-provider-solana` package requires `bs58` (>=4.0.1) as a peer dependency.
## MVM (Sui) Wallet Connection
To manage wallet connections to Sui, the widget uses [@mysten/dapp-kit-react](https://sdk.mystenlabs.com/dapp-kit) (^2.0.0).
In Widget v4, the Sui peer dependency changed from `@mysten/dapp-kit` to `@mysten/dapp-kit-react`.
If the Widget detects it's wrapped in a Sui `DAppKitContext`, it will reuse your wallet management automatically.
To use the built-in Sui wallet management, include `SuiProvider()` in the `providers` array:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { SuiProvider } from '@lifi/widget-provider-sui';
const widgetConfig: WidgetConfig = {
providers: [SuiProvider()],
};
export const WidgetPage = () => {
return ;
};
```
If you already have a Sui wallet context in your app using `@mysten/dapp-kit-react`, the widget will detect it and reuse your existing connection:
```typescript SuiWalletProvider.tsx theme={"system"}
import type { FC, PropsWithChildren } from 'react';
import { createDAppKit, DAppKitProvider } from '@mysten/dapp-kit-react';
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { getJsonRpcFullnodeUrl } from '@mysten/sui/jsonRpc';
const dAppKit = createDAppKit({
networks: ['mainnet'],
createClient: (network) =>
new SuiGrpcClient({
network,
baseUrl: getJsonRpcFullnodeUrl(network),
}),
autoConnect: true,
storage: localStorage,
storageKey: 'my-dapp',
});
export const SuiWalletProvider: FC = ({ children }) => {
return {children};
};
```
```typescript WidgetPage.tsx theme={"system"}
import { LiFiWidget } from '@lifi/widget';
import { SuiProvider } from '@lifi/widget-provider-sui';
import { SuiWalletProvider } from '../providers/SuiWalletProvider';
export const WidgetPage = () => {
return (
);
};
```
## UTXO (Bitcoin) Wallet Connection
To manage wallet connections to Bitcoin, the widget uses [Bigmi](https://github.com/lifinance/bigmi).
To use the built-in Bitcoin wallet management, include `BitcoinProvider()` in the `providers` array:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { BitcoinProvider } from '@lifi/widget-provider-bitcoin';
const widgetConfig: WidgetConfig = {
providers: [BitcoinProvider()],
};
export const WidgetPage = () => {
return ;
};
```
The `@lifi/widget-provider-bitcoin` package requires `@bigmi/react` (^0.8.0) as a peer dependency.
If the Widget detects it's wrapped in `BigmiProvider`, it will reuse your wallet management automatically:
```typescript WidgetPage.tsx theme={"system"}
import type { Config, CreateConnectorFn } from '@bigmi/client';
import { createConfig, phantom, unisat, xverse } from '@bigmi/client';
import { bitcoin, createClient, http } from '@bigmi/core';
import { BigmiProvider } from '@bigmi/react';
import { LiFiWidget } from '@lifi/widget';
const connectors: CreateConnectorFn[] = [phantom(), unisat(), xverse()];
const config = createConfig({
chains: [bitcoin],
connectors,
client({ chain }) {
return createClient({ chain, transport: http() });
},
}) as Config;
export const WidgetPage = () => {
return (
);
};
```
## TVM (Tron) Wallet Connection
To manage wallet connections to Tron, the widget uses [@tronweb3/tronwallet-adapter-react-hooks](https://github.com/tronweb3/tronwallet-adapter) (^1.1.11).
To use the built-in Tron wallet management, include `TronProvider()` in the `providers` array:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { TronProvider } from '@lifi/widget-provider-tron';
const widgetConfig: WidgetConfig = {
providers: [TronProvider()],
};
export const WidgetPage = () => {
return ;
};
```
The `@lifi/widget-provider-tron` package requires `@tronweb3/tronwallet-adapter-react-hooks` (^1.1.11) as a peer dependency.
The `TronProvider` accepts an optional configuration object with a `walletConnect` option to enable WalletConnect support for Tron wallets:
```typescript theme={"system"}
import { TronProvider } from '@lifi/widget-provider-tron';
const widgetConfig: WidgetConfig = {
providers: [
TronProvider({
walletConnect: {
network: 'Mainnet',
options: {
projectId: 'your-walletconnect-project-id',
},
},
}),
],
};
```
The `walletConnect` option accepts a `WalletConnectAdapterConfig` from `@tronweb3/tronwallet-adapters` with required `network` (`'Mainnet'`, `'Shasta'`, `'Nile'`, or a chain ID) and `options` (WalletConnect `SignClientTypes.Options` including your `projectId`) fields. Set `walletConnect` to `true` to use default settings.
## STL (Stellar) Wallet Connection
To manage wallet connections to Stellar, the widget uses the [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit), bundled as a direct dependency. There is no peer dependency to install.
To use the built-in Stellar wallet management, include `StellarProvider()` in the `providers` array:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { StellarProvider } from '@lifi/widget-provider-stellar';
const widgetConfig: WidgetConfig = {
providers: [StellarProvider()],
};
export const WidgetPage = () => {
return ;
};
```
The provider connects Freighter, xBull, Lobstr, Rabet, Hana, Klever, OneKey, and Bitget, and renders them in the widget's own wallet menu.
Stellar differs from the other ecosystems in three ways:
* The provider runs on the public Stellar network only. There is no testnet option.
* It detects no external Stellar wallet context. Unlike Wagmi, Sui `DAppKitContext`, or `BigmiProvider`, there is nothing to reuse, so the widget always manages the Stellar connection itself.
* Its only configuration option is `sdkProvider`.
A Stellar-to-Stellar route always settles to the account that signs it. The widget therefore hides the destination address field for those routes, and a `toAddress` or a required receiver is not honoured. Cross-chain routes out of Stellar take a normal destination address.
## Configuration
### WidgetWalletConfig Interface
```typescript theme={"system"}
interface WidgetWalletConfig {
// Callback when "Connect wallet" button is clicked
onConnect?(args?: WalletMenuOpenArgs): void;
// Define ecosystem order for multichain wallets
walletEcosystemsOrder?: Record;
// Enable hybrid external/internal wallet management
// @default false
usePartialWalletManagement?: boolean;
// Force internal wallet management, ignoring external contexts
// @default false
forceInternalWalletManagement?: boolean;
}
```
### Connect Wallet Button
When using external wallet management, use the `onConnect` callback to open your wallet modal:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useConnectModal } from '@rainbow-me/rainbowkit';
export const WidgetPage = () => {
const { openConnectModal } = useConnectModal();
return (
);
};
```
### Ethereum Provider Configuration
When using the built-in wallet management via `EthereumProvider`, you can configure wallet connectors:
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { EthereumProvider } from '@lifi/widget-provider-ethereum';
const widgetConfig: WidgetConfig = {
providers: [
EthereumProvider({
walletConnect: {
projectId: 'your-walletconnect-project-id',
},
coinbase: {
appName: 'Your App Name',
},
metaMask: {
// MetaMask SDK options
},
porto: {
// Porto connector options (EIP-7702)
},
baseAccount: {
// Base Account options
},
}),
],
};
export const WidgetPage = () => {
return ;
};
```
Each connector option can also be set to `true` to use default settings, or omitted to disable that connector:
```typescript theme={"system"}
EthereumProvider({
walletConnect: true, // Use defaults (no projectId required for basic discovery)
coinbase: true, // Use defaults
metaMask: false, // Explicitly disabled (same as omitting)
})
```
#### EthereumProviderConfig Interface
```typescript theme={"system"}
interface EthereumProviderConfig {
walletConnect?: WalletConnectParameters | boolean
coinbase?: CoinbaseWalletParameters | boolean
metaMask?: MetaMaskParameters | boolean
baseAccount?: BaseAccountParameters | boolean
porto?: Partial | boolean
disableMessageSigning?: boolean
sdkProvider?: SDKProvider | SDKProviderFactory
}
```
The `disableMessageSigning` option disables permit-based (EIP-2612) gasless approvals, falling back to standard token approval transactions. This is useful for smart account compatibility. See [Smart Accounts Compatibility](#smart-accounts-compatibility) below.
The `sdkProvider` option lets you supply a custom SDK provider. See [Custom SDK Providers](#custom-sdk-providers) for details.
### Partial Wallet Management
If your external wallet management doesn't support all ecosystems, enable partial wallet management to use a hybrid approach:
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
walletConfig: {
usePartialWalletManagement: true,
},
};
```
In partial mode:
* External wallet management handles "opt-out" ecosystems
* Internal wallet management handles remaining ecosystems
* Both wallet menus can operate together
This is useful when migrating to a new setup or when your wallet library only supports certain chains (e.g., RainbowKit for EVM, while internal handles Solana and Bitcoin).
### Force Internal Wallet Management
The widget automatically detects existing wallet contexts (e.g., WagmiContext for EVM). To override this and force internal management for all ecosystems:
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
walletConfig: {
forceInternalWalletManagement: true,
},
};
```
### Ecosystem Order for Wallets
Define the preferred ecosystem order for multichain wallets:
```typescript theme={"system"}
import { ChainType } from '@lifi/widget';
const widgetConfig: WidgetConfig = {
walletConfig: {
walletEcosystemsOrder: {
MetaMask: [ChainType.EVM, ChainType.SVM],
Phantom: [ChainType.SVM, ChainType.EVM],
},
},
};
```
The keys must match wallet names as labeled in the Widget UI. This only affects display order, not actual ecosystem support.
### Custom SDK Providers
Every provider config accepts an `sdkProvider` option that lets you replace the built-in SDK provider with a custom implementation for signing, chain switching, or other low-level operations.
You can pass either an `SDKProvider` object directly or a factory function that receives ecosystem-specific dependencies and returns an `SDKProvider`.
Each ecosystem exposes different dependencies to the factory:
```typescript theme={"system"}
// EVM
interface EthereumProviderDeps {
getWalletClient: () => Promise
switchChain: (chainId: number) => Promise
disableMessageSigning?: boolean
}
// Solana
interface SolanaProviderDeps {
getWallet: () => Promise
}
// Bitcoin
interface BitcoinProviderDeps {
getWalletClient: () => Promise
}
// Sui
interface SuiProviderDeps {
getClient: () => Promise
getSigner: () => Promise
}
// Tron
interface TronProviderDeps {
getWallet: () => Promise
}
// Stellar
interface StellarProviderDeps {
getWallet: () => Promise
// The network the Wallets Kit was initialized with. Balance reads resolve
// their network from the provider options, so a custom sdkProvider has to
// forward this to stay on the network it signs on.
networkPassphrase: string
}
```
Example using a factory function with the Ethereum provider:
```typescript theme={"system"}
import { EthereumProvider } from '@lifi/widget-provider-ethereum';
import type { EthereumProviderDeps } from '@lifi/widget-provider-ethereum';
import type { SDKProvider } from '@lifi/sdk';
const widgetConfig: WidgetConfig = {
providers: [
EthereumProvider({
sdkProvider: (deps: EthereumProviderDeps): SDKProvider => {
return {
// Custom SDKProvider implementation using deps.getWalletClient,
// deps.switchChain, etc.
};
},
}),
],
};
```
When omitted, each provider uses its built-in SDK provider implementation.
## Smart Accounts Compatibility
When using the Widget with smart accounts (Privy, Dynamic, ZeroDev, etc.), you may encounter signature compatibility issues.
### The Problem
* **EOAs** use ECDSA signatures for standard transactions
* **Smart Accounts** may use ERC-1271 or other signature validation methods
This can cause incompatibility with native permit functionality (EIP-2612) used for gasless token approvals.
EIP-7702 delegated smart wallets, such as delegated MetaMask accounts,
currently require source-chain native gas because gasless or relayer routes
are not offered for this wallet type. See [EIP-7702 delegated wallet troubleshooting](/faqs/troubleshooting#eip-7702-delegated-smart-wallets).
#### EIP-5792 Transaction Batching Support
If your smart account provider supports [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) (Wallet Function Call API), there should be no compatibility issues. The widget will automatically use batch transactions instead of individual permit signatures.
### Disabling Message Signing
For smart accounts without EIP-5792 support, disable message signing to use standard approval transactions. In v4, this option is configured on the `EthereumProvider`:
```typescript theme={"system"}
import { EthereumProvider } from '@lifi/widget-provider-ethereum';
const widgetConfig: WidgetConfig = {
providers: [
EthereumProvider({
disableMessageSigning: true,
}),
],
};
```
Disabling message signing will fallback to standard token approval
transactions, which may require additional gas fees but ensures compatibility
with all smart account implementations.
### updateTransactionRequestHook
For advanced use cases, you can modify transaction requests before they're sent:
```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
sdkConfig: {
executionOptions: {
updateTransactionRequestHook: async (txRequest) => {
// Modify the transaction request
return {
...txRequest,
// Your modifications
};
},
},
},
};
```
## Wallet Management Events
The `@lifi/wallet-management` package provides its own event emitter for wallet connection and disconnection events. These are separate from the [Widget Events](/widget/widget-events).
```typescript theme={"system"}
import {
useWalletManagementEvents,
WalletManagementEvent,
} from '@lifi/wallet-management';
import type {
WalletConnected,
WalletDisconnected,
} from '@lifi/wallet-management';
import { useEffect } from 'react';
export const WalletEventsExample = () => {
const walletEvents = useWalletManagementEvents();
useEffect(() => {
const onWalletConnected = (data: WalletConnected) => {
console.log('Wallet connected:', data.address, data.connectorName);
};
const onWalletDisconnected = (data: WalletDisconnected) => {
console.log('Wallet disconnected:', data.chainType);
};
walletEvents.on(
WalletManagementEvent.WalletConnected,
onWalletConnected
);
walletEvents.on(
WalletManagementEvent.WalletDisconnected,
onWalletDisconnected
);
return () => walletEvents.removeAllListeners();
}, [walletEvents]);
return null;
};
```
### WalletConnected
Fires when a wallet is connected via the widget's internal wallet management UI.
```typescript theme={"system"}
interface WalletConnected {
address: string
chainId: number
chainType: ChainType
connectorId: string
connectorName: string
}
```
### WalletDisconnected
Fires when a wallet is disconnected.
```typescript theme={"system"}
interface WalletDisconnected {
address?: string
chainId?: number
chainType: ChainType
connectorId?: string
connectorName?: string
}
```
# Widget API Reference
Source: https://docs.li.fi/widget/widget-api-reference
API documentation for the widget components and hooks.
## Widget Component
Properties and types of the `LiFiWidget` component configuration.
```typescript theme={"system"}
import { LiFiWidget, WidgetConfig } from '@lifi/widget'
const config: WidgetConfig = {
integrator: 'your-dapp-name',
// ... other options
}
```
## Core Configuration
| Name | Type | Default | Description |
| ------------ | ------------------ | ------------ | ------------------------------------------------ |
| `integrator` | `string` | **Required** | Identifier of the integrator (dApp/company name) |
| `apiKey` | `string` | – | API authentication key |
| `referrer` | `string` | – | Identifier of the referrer |
| `feeConfig` | `WidgetFeeConfig` | – | Fee configuration |
| `providers` | `WidgetProvider[]` | – | Array of blockchain provider components |
***
## Form Values (Swap Details)
| Name | Type | Default | Description |
| ------------------ | ------------------ | ------- | ------------------------------------- |
| `fromChain` | `number` | – | Source chain ID |
| `toChain` | `number` | – | Destination chain ID |
| `fromToken` | `string` | – | Source token contract address |
| `toToken` | `string` | – | Destination token contract address |
| `fromAmount` | `number \| string` | – | Amount to swap |
| `toAmount` | `number \| string` | – | Expected destination amount |
| `toAddress` | `ToAddress` | – | Single destination wallet address |
| `toAddresses` | `ToAddress[]` | – | Curated list of destination addresses |
| `minFromAmountUSD` | `number` | – | Minimum USD value for fromAmount |
| `formUpdateKey` | `string` | – | Unique key to force form updates |
### ToAddress Type
```typescript theme={"system"}
interface ToAddress {
name?: string // Display name
address: string // Wallet address (required)
chainType: ChainType // EVM, SVM, UTXO, etc. (required)
logoURI?: string // Optional logo
}
```
***
## Route Configuration
| Name | Type | Default | Description |
| ------------------ | --------- | --------------- | --------------------------------------------------------------------------------- |
| `routePriority` | `Order` | `'RECOMMENDED'` | Route selection priority (`'RECOMMENDED'`, `'CHEAPEST'`, `'FASTEST'`, `'SAFEST'`) |
| `slippage` | `number` | `0.005` | Default slippage (0.005 = 0.5%) |
| `showSingleRoute` | `boolean` | `false` | Show only recommended route and hide route selector |
| `useRelayerRoutes` | `boolean` | `false` | Enable gasless/relayer routes |
***
## Filtering Options
### Chains
| Name | Type | Description |
| -------------------- | ------------- | ---------------------------------- |
| `chains.allow` | `number[]` | Only allow these chain IDs |
| `chains.deny` | `number[]` | Deny these chain IDs |
| `chains.from.allow` | `number[]` | Allow only for source chain |
| `chains.from.deny` | `number[]` | Deny for source chain |
| `chains.to.allow` | `number[]` | Allow only for destination chain |
| `chains.to.deny` | `number[]` | Deny for destination chain |
| `chains.types.allow` | `ChainType[]` | Allow chain types (EVM, SVM, etc.) |
| `chains.types.deny` | `ChainType[]` | Deny chain types |
```typescript theme={"system"}
interface WidgetChains {
types?: AllowDeny
allow?: number[]
deny?: number[]
from?: AllowDeny
to?: AllowDeny
}
```
### Tokens
| Name | Type | Description |
| ------------------- | --------------- | --------------------------------- |
| `tokens.allow` | `BaseToken[]` | Only allow these tokens |
| `tokens.deny` | `BaseToken[]` | Deny these tokens |
| `tokens.from.allow` | `BaseToken[]` | Allow only for source tokens |
| `tokens.from.deny` | `BaseToken[]` | Deny for source tokens |
| `tokens.to.allow` | `BaseToken[]` | Allow only for destination tokens |
| `tokens.to.deny` | `BaseToken[]` | Deny for destination tokens |
| `tokens.featured` | `StaticToken[]` | Featured tokens (shown at top) |
| `tokens.include` | `Token[]` | Additional tokens to include |
| `tokens.popular` | `StaticToken[]` | Popular tokens section |
```typescript theme={"system"}
interface WidgetTokens {
featured?: StaticToken[]
include?: Token[]
popular?: StaticToken[]
allow?: BaseToken[]
deny?: BaseToken[]
from?: AllowDeny
to?: AllowDeny
}
```
### Bridges & Exchanges
| Name | Type | Description |
| ----------------- | ---------- | -------------------------- |
| `bridges.allow` | `string[]` | Only allow these bridges |
| `bridges.deny` | `string[]` | Deny these bridges |
| `exchanges.allow` | `string[]` | Only allow these exchanges |
| `exchanges.deny` | `string[]` | Deny these exchanges |
```typescript theme={"system"}
interface AllowDeny {
allow?: T[]
deny?: T[]
}
```
***
## UI Variants
| Name | Type | Default | Description |
| ------------- | ---------------------------------------------- | ----------- | --------------------- |
| `variant` | `'compact' \| 'wide' \| 'drawer'` | `'compact'` | Widget layout style |
| `mode` | `'default' \| 'split' \| 'custom' \| 'refuel'` | `'default'` | Widget mode |
| `modeOptions` | `ModeOptions` | – | Mode-specific options |
### ModeOptions
```typescript theme={"system"}
type SplitMode = 'bridge' | 'swap'
type SplitModeOptions = { defaultTab: SplitMode }
type CustomMode = 'checkout' | 'deposit'
interface ModeOptions {
split?: SplitMode | SplitModeOptions // For split mode
custom?: { type: CustomMode } // For custom mode
}
```
***
## Appearance & Theme
| Name | Type | Default | Description |
| ------------ | ------------------------------- | ---------- | ------------------- |
| `appearance` | `'light' \| 'dark' \| 'system'` | `'system'` | Theme mode |
| `theme` | `WidgetTheme` | – | Theme customization |
### WidgetTheme
```typescript theme={"system"}
interface WidgetTheme {
colorSchemes?: {
light?: { palette: PaletteOptions }
dark?: { palette: PaletteOptions }
}
shape?: Partial
typography?: TypographyVariantsOptions
components?: WidgetThemeComponents
container?: CSSProperties
routesContainer?: CSSProperties
chainSidebarContainer?: CSSProperties
header?: CSSProperties
navigation?: {
edge?: boolean // @default true
}
}
```
### Theme Components
```typescript theme={"system"}
type WidgetThemeComponents = Partial,
| 'MuiAppBar'
| 'MuiAvatar'
| 'MuiButton'
| 'MuiCard'
| 'MuiDrawer'
| 'MuiIconButton'
| 'MuiInputCard'
| 'MuiNavigationTabs'
| 'MuiNavigationTab'
| 'MuiTabs'
| 'MuiCheckbox'
>>
```
***
## UI Control
UI control options use object configs where each key is a UI element and the value is a boolean.
### DisabledUIConfig
Disable specific UI elements (still visible but not interactive):
```typescript theme={"system"}
interface DisabledUIConfig {
fromAmount?: boolean
fromToken?: boolean
toAddress?: boolean
toToken?: boolean
}
```
### HiddenUIConfig
Hide specific UI elements completely:
```typescript theme={"system"}
interface HiddenUIConfig {
addressBookConnectedWallets?: boolean
allNetworks?: boolean
appearance?: boolean
bridgesSettings?: boolean
chainSelect?: boolean
chainSidebar?: boolean // Hide chain sidebar in wide variant
contactSupport?: boolean
drawerCloseButton?: boolean
fromToken?: boolean
gasRefuelMessage?: boolean
hideSmallBalances?: boolean
history?: boolean
insufficientGasMessage?: boolean
integratorStepDetails?: boolean
language?: boolean
lowAddressActivityConfirmation?: boolean
poweredBy?: boolean
reverseTokensButton?: boolean
routeCardPriceImpact?: boolean
routeTokenDescription?: boolean
searchTokenInput?: boolean
toAddress?: boolean
toToken?: boolean
walletMenu?: boolean
}
```
### RequiredUIConfig
Make specific UI elements required:
```typescript theme={"system"}
interface RequiredUIConfig {
accountDeployedMessage?: boolean
toAddress?: boolean
}
```
### DefaultUI
Configure default UI states:
```typescript theme={"system"}
interface DefaultUI {
transactionDetailsExpanded?: boolean
navigationHeaderTitleNoWrap?: boolean
}
```
***
## Internationalization
| Name | Type | Description |
| ------------------- | ------------------- | ------------------------- |
| `languages.default` | `LanguageKey` | Default language |
| `languages.allow` | `LanguageKey[]` | Only show these languages |
| `languages.deny` | `LanguageKey[]` | Hide these languages |
| `languageResources` | `LanguageResources` | Custom translations |
```typescript theme={"system"}
type LanguageKey =
| 'bn' | 'de' | 'en' | 'es' | 'fr' | 'hi' | 'id'
| 'it' | 'ja' | 'ko' | 'pl' | 'pt' | 'th' | 'tr'
| 'uk' | 'vi' | 'zh'
```
***
## Wallet Configuration
| Name | Type | Description |
| -------------------------------------------- | ------------------------------------- | -------------------------------- |
| `walletConfig.onConnect` | `(args?: WalletMenuOpenArgs) => void` | Callback for connect button |
| `walletConfig.walletEcosystemsOrder` | `Record` | Ecosystem order per wallet |
| `walletConfig.usePartialWalletManagement` | `boolean` | Enable hybrid wallet management |
| `walletConfig.forceInternalWalletManagement` | `boolean` | Force internal wallet management |
```typescript theme={"system"}
interface WidgetWalletConfig {
onConnect?(args?: WalletMenuOpenArgs): void
walletEcosystemsOrder?: Record
usePartialWalletManagement?: boolean // @default false
forceInternalWalletManagement?: boolean // @default false
}
```
***
## SDK Configuration
| Name | Type | Description |
| ---------------------------- | -------------------------- | ------------------------- |
| `sdkConfig.rpcUrls` | `Record` | Custom RPC URLs per chain |
| `sdkConfig.routeOptions` | `RouteOptions` | Route fetching options |
| `sdkConfig.executionOptions` | `ExecutionOptions` | Route execution options |
```typescript theme={"system"}
interface WidgetSDKConfig {
rpcUrls?: Record
routeOptions?: Omit
executionOptions?: {
updateTransactionRequestHook?: (request: TransactionRequest) => Promise
}
}
```
In Widget v4, `disableMessageSigning` has moved from `sdkConfig.executionOptions` to the `EthereumProvider` configuration. See [Wallet Management](/widget/wallet-management) for details.
***
## Fee Configuration
| Name | Type | Description |
| ------------------------------- | ----------------------------- | ------------------------- |
| `feeConfig.name` | `string` | Display name for fee |
| `feeConfig.logoURI` | `string` | Logo URL |
| `feeConfig.fee` | `number` | Static fee (0-1) |
| `feeConfig.showFeePercentage` | `boolean` | Show fee percentage in UI |
| `feeConfig.showFeeTooltip` | `boolean` | Show fee tooltip |
| `feeConfig.feeTooltipComponent` | `ReactNode` | Custom tooltip component |
| `feeConfig.calculateFee` | `(params) => Promise` | Dynamic fee calculation |
```typescript theme={"system"}
interface WidgetFeeConfig {
name?: string
logoURI?: string
fee?: number
showFeePercentage?: boolean // @default false
showFeeTooltip?: boolean // @default false
feeTooltipComponent?: ReactNode
calculateFee?(params: CalculateFeeParams): Promise
}
interface CalculateFeeParams {
fromChain: ExtendedChain
toChain: ExtendedChain
fromToken: Token
toToken: Token
fromAddress?: string
toAddress?: string
fromAmount?: bigint
toAmount?: bigint
slippage?: number
}
```
***
## Explorer URLs
| Name | Type | Description |
| ----------------------- | --------------- | ------------------------------ |
| `explorerUrls[chainId]` | `ExplorerUrl[]` | Custom explorer URLs per chain |
| `explorerUrls.internal` | `ExplorerUrl[]` | Override internal explorer |
```typescript theme={"system"}
type ExplorerUrl = string | {
url: string
txPath?: string // Default: '/tx/'
addressPath?: string // Default: '/address/'
}
```
***
## Route Labels
Add custom labels to routes:
```typescript theme={"system"}
interface RouteLabelRule {
label: RouteLabel
bridges?: AllowDeny
exchanges?: AllowDeny
fromChainId?: number[]
toChainId?: number[]
fromTokenAddress?: string[]
toTokenAddress?: string[]
match?: (route: Route) => boolean
}
interface RouteLabel {
text: string
sx?: SxProps // MUI style object
}
```
***
## Contract Integration
For custom checkout/deposit flows:
| Name | Type | Description |
| ---------------------------- | -------------------- | ------------------------- |
| `contractCalls` | `ContractCall[]` | Contract calls to execute |
| `contractComponent` | `ReactNode` | Main custom component |
| `contractSecondaryComponent` | `ReactNode` | Secondary component |
| `contractCompactComponent` | `ReactNode` | Compact view component |
| `contractTool` | `WidgetContractTool` | Tool display info |
```typescript theme={"system"}
interface WidgetContractTool {
name: string
logoURI: string
}
```
***
## Form & URL State
| Name | Type | Default | Description |
| ----------- | ---------------------- | ------- | ------------------------------------ |
| `buildUrl` | `boolean` | `false` | Sync widget state to URL |
| `keyPrefix` | `string` | – | Prefix for multiple widget instances |
| `formRef` | `RefObject` | – | Ref for programmatic form control |
### FormState
```typescript theme={"system"}
interface FormState {
setFieldValue: (
key: K,
value: FieldValues[K],
options?: { setUrlSearchParam: boolean }
) => void
}
type FieldNames =
| 'fromChain' | 'toChain'
| 'fromToken' | 'toToken'
| 'fromAmount' | 'toAmount'
| 'toAddress'
```
***
## Drawer Props
For `variant: 'drawer'`:
| Name | Type | Description |
| ------------ | --------------------------- | --------------------- |
| `open` | `boolean` | Controlled open state |
| `onClose` | `() => void` | Close callback |
| `elementRef` | `RefObject` | Ref to drawer element |
### WidgetDrawer Ref
```typescript theme={"system"}
interface WidgetDrawer {
isOpen(): boolean
toggleDrawer(): void
openDrawer(): void
closeDrawer(): void
}
```
***
## Exported Hooks
### From `@lifi/widget`
| Hook | Description |
| ------------------------- | ------------------------------------------------------------ |
| `useWidgetEvents` | Subscribe to widget events (returns event emitter) |
| `useWidgetChains(config)` | Fetch available chains from LI.FI API using a `WidgetConfig` |
| `useFieldActions` | Access form field actions |
| `useFieldValues` | Access current form field values |
### From `@lifi/widget-provider`
| Hook | Description |
| -------------------- | --------------------------------------- |
| `useEthereumContext` | Access Ethereum wallet context |
| `useSolanaContext` | Access Solana wallet context |
| `useBitcoinContext` | Access Bitcoin wallet context |
| `useSuiContext` | Access Sui wallet context |
| `useTronContext` | Access Tron wallet context |
| `useStellarContext` | Access Stellar wallet context |
| `isWalletInstalled` | Check if a specific wallet is installed |
### From `@lifi/widget-provider-ethereum`
| Export | Description |
| -------------------------- | --------------------------------------------------------- |
| `EthereumProvider` | Factory function returning an Ethereum provider component |
| `createDefaultWagmiConfig` | Create a default Wagmi config with common connectors |
| `useSyncWagmiConfig` | Hook to sync Wagmi config with LI.FI chains |
### From `@lifi/widget-provider-solana`
| Export | Description |
| ------------------ | ------------------------------------------------------ |
| `SolanaProvider` | Factory function returning a Solana provider component |
| `useWalletAccount` | Hook to access Solana wallet account |
### From `@lifi/widget-provider-bitcoin`
| Export | Description |
| -------------------------- | ------------------------------------------------------- |
| `BitcoinProvider` | Factory function returning a Bitcoin provider component |
| `createDefaultBigmiConfig` | Create a default Bigmi config |
### From `@lifi/widget-provider-sui`
| Export | Description |
| ------------- | --------------------------------------------------- |
| `SuiProvider` | Factory function returning a Sui provider component |
### From `@lifi/widget-provider-tron`
| Export | Description |
| -------------------- | ---------------------------------------------------- |
| `TronProvider` | Factory function returning a Tron provider component |
| `createTronAdapters` | Create default Tron wallet adapters |
### From `@lifi/widget-provider-stellar`
| Export | Description |
| ---------------------- | -------------------------------------------------------- |
| `StellarProvider` | Factory function returning a Stellar provider component |
| `useWalletAccount` | Hook to access the connected Stellar account |
| `useStellarWalletsKit` | Hook to access the Stellar Wallets Kit state and actions |
***
## Other Options
| Name | Type | Default | Description |
| ------------- | ----------------------- | ----------- | -------------------------- |
| `poweredBy` | `'default' \| 'jumper'` | `'default'` | Powered by branding style |
| `routeLabels` | `RouteLabelRule[]` | – | Custom route labels/badges |
# Widget Events
Source: https://docs.li.fi/widget/widget-events
Stay up-to-date with widget events
**LI.FI Widget** provides a `useWidgetEvents` hook that lets you subscribe to a series of widget events and helps you retrieve helpful information about executing routes, track bridge and swap progress, track selection of chains and tokens, interactions with specific UI elements, and more.
We continue working on extending available events and if you are interested in a specific event, reach out via our [support](https://help.li.fi).
To minimize unnecessary re-renders and prevent potential glitches in the main Widget component, please integrate the `useWidgetEvents` hook outside of the component where the main `LiFiWidget` is integrated.
Example of how to subscribe to widget events:
```typescript theme={"system"}
import type { Route, ExecutionAction } from '@lifi/sdk';
import type { RouteExecutionUpdate, RouteHighValueLossUpdate } from '@lifi/widget';
import { useWidgetEvents, WidgetEvent } from '@lifi/widget';
import { useEffect } from 'react';
export const WidgetEventsExample = () => {
const widgetEvents = useWidgetEvents();
// ...
useEffect(() => {
const onRouteExecutionStarted = (route: Route) => {
// console.log('onRouteExecutionStarted fired.');
};
const onRouteExecutionUpdated = (update: RouteExecutionUpdate) => {
// console.log('onRouteExecutionUpdated fired.');
};
const onRouteExecutionCompleted = (route: Route) => {
// console.log('onRouteExecutionCompleted fired.');
};
const onRouteExecutionFailed = (update: RouteExecutionUpdate) => {
// console.log('onRouteExecutionFailed fired.');
};
const onRouteHighValueLoss = (update: RouteHighValueLossUpdate) => {
// console.log('onRouteHighValueLoss continued.');
};
widgetEvents.on(WidgetEvent.RouteExecutionStarted, onRouteExecutionStarted);
widgetEvents.on(WidgetEvent.RouteExecutionUpdated, onRouteExecutionUpdated);
widgetEvents.on(WidgetEvent.RouteExecutionCompleted, onRouteExecutionCompleted);
widgetEvents.on(WidgetEvent.RouteExecutionFailed, onRouteExecutionFailed);
widgetEvents.on(WidgetEvent.RouteHighValueLoss, onRouteHighValueLoss);
return () => widgetEvents.removeAllListeners();
}, [widgetEvents]);
// ...
// Return null because it's an example
return null;
};
```
## List of events
Here is the list of all available events:
**AvailableRoutes**
* Type: *Route\[]*
The event fires when available routes are returned after the user has selected source and destination tokens, entered an amount, and requested the routes.
**RouteSelected**
* Type: *RouteSelected*
The event fires when the user selects a specific route from the list of available routes.
**RouteExecutionStarted**
* Type: *Route*
The event fires when the user clicks on the Start swapping or Start bridging button.
**RouteExecutionUpdated**
* Type: *RouteExecutionUpdate*
The event fires when there is an update to the Route object during execution.
**RouteExecutionCompleted**
* Type: *Route*
The event fires when the execution is completed successfully.
**RouteExecutionFailed**
* Type: *RouteExecutionUpdate*
The event fires when the execution has failed.
**RouteHighValueLoss**
* Type: *RouteHighValueLossUpdate*
The event fires when the High Value Loss bottom sheet appears on the screen.
**ContactSupport**
* Type: *ContactSupport*
The event fires when the user clicks on the Contact support button on the Transaction Details page.
**SourceChainTokenSelected**
* Type: *ChainTokenSelected*
The event fires when the user selects the source chain and token.
**DestinationChainTokenSelected**
* Type: *ChainTokenSelected*
The event fires when the user selects the destination chain and token.
**SendToWalletToggled**
* Type: *boolean*
The event fires when the user clicks on the wallet icon next to the action button on the main page to show/hide the destination wallet selection UI.
**WidgetExpanded**
* Type: *boolean*
The event fires when the side panel with routes is shown to the user. Only available in the `wide` widget variant.
**PageEntered**
* Type: *NavigationRouteType*
The event fires when the user navigates to a page in the widget.
**FormFieldChanged**
* Type: *FormFieldChanged*
The event fires whenever a form value is changed in the widget.
**SettingUpdated**
* Type: *SettingUpdated*
The event fires whenever a setting is updated in the widget.
**TokenSearch**
* Type: *TokenSearch*
The event fires when the user searches for a token (includes the query value and the matched token results).
**LowAddressActivityConfirmed**
* Type: *LowAddressActivityConfirmed*
The event fires when the user confirms proceeding despite a low address activity warning for the specified address and chain.
**ChainPinned**
* Type: *ChainPinned*
The event fires when the user pins or unpins a chain in the UI.
**Routes**: Some of the events here present information about routes. A route is the LI.FI way of presenting a quote on an exchange/transfer. A route is a collection of steps, transactions and costs associated with that transfer. In the Widget we present a set of routes that the user can select from. Once selected the execution of that route can begin and the user will be guided through the steps required to complete that route. The route events above can help track a route status.
## Widget Events types
Properties and types of the `useWidgetEvents` hook.
```typescript theme={"system"}
enum WidgetEvent {
AvailableRoutes = 'availableRoutes',
ChainPinned = 'chainPinned',
ContactSupport = 'contactSupport',
DestinationChainTokenSelected = 'destinationChainTokenSelected',
FormFieldChanged = 'formFieldChanged',
LowAddressActivityConfirmed = 'lowAddressActivityConfirmed',
PageEntered = 'pageEntered',
RouteExecutionCompleted = 'routeExecutionCompleted',
RouteExecutionFailed = 'routeExecutionFailed',
RouteExecutionStarted = 'routeExecutionStarted',
RouteExecutionUpdated = 'routeExecutionUpdated',
RouteHighValueLoss = 'routeHighValueLoss',
RouteSelected = 'routeSelected',
SendToWalletToggled = 'sendToWalletToggled',
SettingUpdated = 'settingUpdated',
SourceChainTokenSelected = 'sourceChainTokenSelected',
TokenSearch = 'tokenSearch',
WidgetExpanded = 'widgetExpanded',
}
type WidgetEvents = {
availableRoutes: (data: Route[]) => void
chainPinned: (data: ChainPinned) => void
contactSupport: (data: ContactSupport) => void
destinationChainTokenSelected: (data: ChainTokenSelected) => void
formFieldChanged: (data: FormFieldChanged) => void
lowAddressActivityConfirmed: (data: LowAddressActivityConfirmed) => void
pageEntered: (data: NavigationRouteType) => void
routeExecutionCompleted: (data: Route) => void
routeExecutionFailed: (data: RouteExecutionUpdate) => void
routeExecutionStarted: (data: Route) => void
routeExecutionUpdated: (data: RouteExecutionUpdate) => void
routeHighValueLoss: (data: RouteHighValueLossUpdate) => void
routeSelected: (data: RouteSelected) => void
sendToWalletToggled: (data: boolean) => void
settingUpdated: (data: SettingUpdated) => void
sourceChainTokenSelected: (data: ChainTokenSelected) => void
tokenSearch: (data: TokenSearch) => void
widgetExpanded: (data: boolean) => void
}
type ContactSupport = {
supportId?: string
}
type RouteHighValueLossUpdate = {
fromAmountUSD: number
toAmountUSD: number
gasCostUSD?: number
feeCostUSD?: number
valueLoss: number
}
// `Route` and `ExecutionAction` are exported from `@lifi/sdk`.
type RouteExecutionUpdate = {
route: Route
action: ExecutionAction
}
type RouteSelected = {
route: Route
routes: Route[]
}
type TokenSearch = {
value: string
tokens: TokenAmount[]
}
type ChainTokenSelected = {
chainId: ChainId
tokenAddress: string
}
type FormFieldChanged = {
[K in keyof DefaultValues]: {
fieldName: K
newValue: DefaultValues[K]
oldValue: DefaultValues[K]
}
}[keyof DefaultValues]
type SettingUpdated<
K extends keyof SettingsProps = keyof SettingsProps,
> = {
setting: K
newValue: SettingsProps[K]
oldValue: SettingsProps[K]
newSettings: SettingsProps
oldSettings: SettingsProps
}
type ChainPinned = {
chainId: number
pinned: boolean
}
type LowAddressActivityConfirmed = {
address: string
chainId: number
}
```
# Widget Light API Reference
Source: https://docs.li.fi/widget/widget-light-api-reference
Component and hook API reference for @lifi/widget-light
Complete API reference for all exports from `@lifi/widget-light`.
## ``
The main component that renders the widget inside an iframe and manages the postMessage bridge.
```tsx theme={"system"}
import { LiFiWidgetLight } from '@lifi/widget-light'
```
### Props
| Prop | Type | Required | Default | Description |
| -------------- | ------------------------------------ | -------- | ------------------------ | ---------------------------------------------------------------------------------------- |
| `src` | `string` | No | `'https://widget.li.fi'` | URL of the hosted widget iframe |
| `config` | `WidgetLightConfig` | Yes | -- | JSON-serializable widget configuration ([reference](/widget/widget-light-configuration)) |
| `handlers` | `IframeEcosystemHandler[]` | No | `[]` | Ecosystem handlers for wallet/RPC bridging |
| `iframeOrigin` | `string` | No | Derived from `src` | Restrict `postMessage` to this origin |
| `autoResize` | `boolean` | No | `false` | When `true`, iframe height auto-adjusts to match content |
| `onConnect` | `(args?: ConnectWalletArgs) => void` | No | -- | Called when the widget requests a wallet connection |
| `style` | `CSSProperties` | No | -- | Inline styles for the iframe element |
| `className` | `string` | No | -- | CSS class for the iframe element |
| `title` | `string` | No | `'LI.FI Widget'` | Accessible title for the iframe |
### Example
```tsx theme={"system"}
```
Only one `` instance per page is supported. The event bus and guest bridge are module-level singletons.
## `useWidgetLightHost(options)`
Low-level hook that manages the host side of the postMessage bridge. Use this if you need to render your own `