# 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 contract ## 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). Diamond helper contracts # 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. *** LI.FI Overview ## 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: Permit2 Architecture ### 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): ![https://crowdin.com/project/lifi-widget](https://badges.crowdin.net/lifi-widget/localized.svg) ## 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 }; ``` Compact variant ### 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', }; ``` Wide variant #### 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', }; ``` Drawer variant #### 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 (
setIsOpen(false)} config={{ variant: 'drawer', }} integrator="controlled-drawer-example" />
); }; ``` *** ## 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 `