Skip to content

LI.Fi Swap Provider#798

Open
ahsan-javaiid wants to merge 1 commit intoenkryptcom:developfrom
ahsan-javaiid:feat/lifi
Open

LI.Fi Swap Provider#798
ahsan-javaiid wants to merge 1 commit intoenkryptcom:developfrom
ahsan-javaiid:feat/lifi

Conversation

@ahsan-javaiid
Copy link
Copy Markdown
Contributor

@ahsan-javaiid ahsan-javaiid commented Apr 29, 2026

Description

This pull request add https://li.fi/ swap provider support in enkrypt. li.fi offers swap and bridge functionality across 60+ chains overall along with liquidity and most of them are already supported by enkrypt wallet.

li.fi api docs: https://docs.li.fi/api-reference/introduction

This pull request integrates li.fi rest api without any other dependencies making it maintenance free.

Fee Model

Screenshot 2026-04-29 at 11 40 59 PM

Supported Chains

  • Rootstock, Ethereum, Arbitrum, BSC, Base, Polygon, OP Mainnet, Moonbeam, Gnosis, Avalanche, Kaia, zkSync, Blast, Telos
  • Solana

Demo

Rootstock -> ETH

rootstock-rif-to-eth-usdc.mp4

ETH -> Solana

li.fi-demo-01.mp4

Test

  • Tested following cases and it works fine.
  • Rootstock -> ETH
  • ETH -> Rootstock
  • ETH -> Solana
  • Solana -> ETH

Summary by CodeRabbit

  • New Features
    • LiFi is now integrated as a new swap provider option
    • Users can obtain swap quotes and execute swaps through LiFi
    • Support extends across multiple EVM and Solana blockchain networks
    • Transaction status tracking and monitoring enabled for LiFi swaps

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Warning

Rate limit exceeded

@ahsan-javaiid has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 53 minutes and 6 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9123f021-3998-42ee-99a1-8b35aa579e15

📥 Commits

Reviewing files that changed from the base of the PR and between effbc77 and 0ea6630.

⛔ Files ignored due to path filters (1)
  • packages/swap/src/common/icons/li.fi.png is excluded by !**/*.png
📒 Files selected for processing (6)
  • packages/swap/src/configs.ts
  • packages/swap/src/index.ts
  • packages/swap/src/providers/lifi/index.ts
  • packages/swap/src/providers/lifi/supported.ts
  • packages/swap/src/providers/lifi/types.ts
  • packages/swap/src/types/index.ts

Walkthrough

The PR integrates LiFi as a new swap provider by adding its configuration (icon, fee structure), provider enums, and implementing a complete LiFi provider class that handles quote retrieval, swap execution, and transaction status tracking across multiple blockchains including EVM and Solana networks.

Changes

Cohort / File(s) Summary
LiFi Provider Implementation
packages/swap/src/providers/lifi/index.ts, packages/swap/src/providers/lifi/supported.ts, packages/swap/src/providers/lifi/types.ts
Implements LiFi provider with token mapping, API integration (/quote, /status), transaction format conversion for EVM and Solana, gas estimation, and status code mapping. Defines supported networks with chain IDs and address validators; adds TypeScript types for LiFi API responses.
Type System Extensions
packages/swap/src/types/index.ts
Extends ProviderName and ProviderNameProper enums to include LiFi provider identifier and display name.
Integration & Configuration
packages/swap/src/configs.ts, packages/swap/src/index.ts
Registers LiFi icon and provider info; defines fee/referral configuration for enkrypt wallet; instantiates LiFi provider in both network branches of Swap constructor.

Sequence Diagram

sequenceDiagram
    actor User
    participant Swap System
    participant LiFi Provider
    participant LiFi API
    participant Blockchain

    User->>Swap System: Request quote
    Swap System->>LiFi Provider: getQuote()
    LiFi Provider->>LiFi API: POST /quote
    LiFi API-->>LiFi Provider: Quote response
    LiFi Provider->>LiFi Provider: Normalize tokens & amounts
    LiFi Provider-->>Swap System: ProviderQuoteResponse

    User->>Swap System: Approve & execute swap
    Swap System->>LiFi Provider: getSwap(quote)
    LiFi Provider->>LiFi Provider: Convert to SolanaTransaction/SwapTransaction
    LiFi Provider-->>Swap System: ProviderSwapResponse

    Blockchain->>Blockchain: Execute transaction
    User->>Swap System: Check status
    Swap System->>LiFi Provider: getStatus()
    LiFi Provider->>LiFi API: POST /status
    LiFi API-->>LiFi Provider: Status response
    LiFi Provider->>LiFi Provider: Map status/substatus
    LiFi Provider-->>Swap System: TransactionStatus
    Swap System-->>User: Status result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • add okx provider #726: Adds new swap provider with overlapping changes to ProviderName enums, fee configurations, and provider initialization patterns in the Swap system.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'LI.Fi Swap Provider' is concise and directly related to the main objective of the PR, which adds LI.Fi as a supported swap provider to the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 53 minutes and 6 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/swap/src/providers/lifi/index.ts`:
- Around line 406-408: In getStatus, the code currently reads transactions[1]
which fails for routes that only have one transaction; change it to use the last
transaction's hash (e.g., derive txHash from
options.transactions?.slice(-1)[0]?.hash or similar) so getStatus always polls
the submitted swap hash; update references in getStatus to use the last
transaction entry (transactions) rather than a hardcoded index.
- Around line 417-419: The getStatus() LiFi polling path should tolerate
transient network/timeouts: wrap the fetch(`${BASE_URL}/status?...`) call with
an AbortController-based timeout (e.g., setTimeout to call controller.abort
after X ms), and surround both the fetch and the response.json() with try/catch;
on any fetch/json error or abort return TransactionStatus.pending. Ensure you
clear the timeout once fetch completes and reference the existing symbols
BASE_URL, LiFiStatusResponse, and TransactionStatus.pending so the change is
localized to the getStatus() implementation.
- Around line 196-198: The code currently applies a fallback fee/referrer using
FEE_CONFIGS[this.name]?.[meta.walletIdentifier] with a default of 0.002 and ''
which causes unconfigured wallets to send a LiFi fee; change the logic in the
payload construction (in packages/swap/src/providers/lifi/index.ts where
fee/referrer are set) to first read const liFiConfig =
FEE_CONFIGS[this.name]?.[meta.walletIdentifier]; then only set fee =
String(liFiConfig.fee) and referrer = liFiConfig.referrer when both values are
present (non-null/defined); otherwise omit those properties or set them to
undefined so unconfigured wallets send no fee/referrer.
- Around line 180-184: The Solana native-token lookup is using the wrong key
(supportedNetworks.SOLANA) so options.fromToken.name and options.toToken.name
comparisons never match; replace those references with the correct enum/key
SupportedNetworkName.Solana when comparing token names (i.e., change the checks
that set NATIVE_TOKEN_ADDRESS_SOLANA) and ensure SupportedNetworkName is
imported where used so the native SOL rewriting for fromTokenAddress and
toTokenAddress works at runtime.

In `@packages/swap/src/providers/lifi/types.ts`:
- Around line 13-18: Make the transactionRequest.to field optional in the LiFi
response model so the type matches runtime behavior: update
packages/swap/src/providers/lifi/types.ts so transactionRequest has to?: string
instead of to: string, and then adjust any direct accesses (e.g., in
packages/swap/src/providers/lifi/index.ts where the code branches on the absence
of transactionRequest.to) to guard against undefined where needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c119eb61-d594-451a-9efa-fae67faef74f

📥 Commits

Reviewing files that changed from the base of the PR and between 519816a and effbc77.

⛔ Files ignored due to path filters (1)
  • packages/swap/src/common/icons/li.fi.png is excluded by !**/*.png
📒 Files selected for processing (6)
  • packages/swap/src/configs.ts
  • packages/swap/src/index.ts
  • packages/swap/src/providers/lifi/index.ts
  • packages/swap/src/providers/lifi/supported.ts
  • packages/swap/src/providers/lifi/types.ts
  • packages/swap/src/types/index.ts

Comment thread packages/swap/src/providers/lifi/index.ts
Comment on lines +196 to +198
integrator: LIFI_INTEGRATOR_ID,
fee: String(FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.fee ?? 0.002),
referrer: FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.referrer ?? '',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t apply fallback LiFi fees to unconfigured wallets.

packages/swap/src/configs.ts only defines LiFi config for WalletIdentifier.enkrypt, but this code still sends fee=0.002 for every other wallet and pairs it with an empty referrer. That can skew quotes or make the LiFi request invalid for MEW. Default this to no fee unless both values are configured.

🛠 Suggested fix
+    const feeConfig = FEE_CONFIGS[this.name]?.[meta.walletIdentifier];
     const params = new URLSearchParams({
       fromChain: String(supportedNetworks[this.network].chainId),
       toChain: String(supportedNetworks[options.toToken.networkInfo.name].chainId),
       fromToken: this.normalizeEvmAddress(fromTokenAddress),
       toToken: this.normalizeEvmAddress(toTokenAddress),
       fromAmount: options.amount.toString(),
       fromAddress: this.normalizeEvmAddress(options.fromAddress),
       toAddress: this.normalizeEvmAddress(options.toAddress),
       slippage: (parseFloat(DEFAULT_SLIPPAGE) / 100).toString(),
       integrator: LIFI_INTEGRATOR_ID,
-      fee: String(FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.fee ?? 0.002),
-      referrer: FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.referrer ?? '',
     });
+    if (feeConfig?.referrer) {
+      params.set("fee", String(feeConfig.fee));
+      params.set("referrer", feeConfig.referrer);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
integrator: LIFI_INTEGRATOR_ID,
fee: String(FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.fee ?? 0.002),
referrer: FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.referrer ?? '',
const feeConfig = FEE_CONFIGS[this.name]?.[meta.walletIdentifier];
const params = new URLSearchParams({
fromChain: String(supportedNetworks[this.network].chainId),
toChain: String(supportedNetworks[options.toToken.networkInfo.name].chainId),
fromToken: this.normalizeEvmAddress(fromTokenAddress),
toToken: this.normalizeEvmAddress(toTokenAddress),
fromAmount: options.amount.toString(),
fromAddress: this.normalizeEvmAddress(options.fromAddress),
toAddress: this.normalizeEvmAddress(options.toAddress),
slippage: (parseFloat(DEFAULT_SLIPPAGE) / 100).toString(),
integrator: LIFI_INTEGRATOR_ID,
});
if (feeConfig?.referrer) {
params.set("fee", String(feeConfig.fee));
params.set("referrer", feeConfig.referrer);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/swap/src/providers/lifi/index.ts` around lines 196 - 198, The code
currently applies a fallback fee/referrer using
FEE_CONFIGS[this.name]?.[meta.walletIdentifier] with a default of 0.002 and ''
which causes unconfigured wallets to send a LiFi fee; change the logic in the
payload construction (in packages/swap/src/providers/lifi/index.ts where
fee/referrer are set) to first read const liFiConfig =
FEE_CONFIGS[this.name]?.[meta.walletIdentifier]; then only set fee =
String(liFiConfig.fee) and referrer = liFiConfig.referrer when both values are
present (non-null/defined); otherwise omit those properties or set them to
undefined so unconfigured wallets send no fee/referrer.

Comment on lines +406 to +408
async getStatus(options: StatusOptions): Promise<TransactionStatus> {
const txHash = options.transactions?.[1]?.hash;
if (!txHash) return TransactionStatus.pending;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Track the submitted swap hash, not transactions[1].

Routes without an approval only produce one transaction, so Line 407 leaves txHash undefined and status polling stays pending forever. Use the last transaction hash instead.

🛠 Suggested fix
-    const txHash = options.transactions?.[1]?.hash;
+    const txHash =
+      options.transactions?.[options.transactions.length - 1]?.hash;
     if (!txHash) return TransactionStatus.pending;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/swap/src/providers/lifi/index.ts` around lines 406 - 408, In
getStatus, the code currently reads transactions[1] which fails for routes that
only have one transaction; change it to use the last transaction's hash (e.g.,
derive txHash from options.transactions?.slice(-1)[0]?.hash or similar) so
getStatus always polls the submitted swap hash; update references in getStatus
to use the last transaction entry (transactions) rather than a hardcoded index.

Comment on lines +417 to +419
const response = await fetch(`${BASE_URL}/status?${params.toString()}`);
if (!response.ok) return TransactionStatus.pending;
const json = (await response.json()) as LiFiStatusResponse;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden LiFi status polling against transient failures.

A network error or hung status request currently rejects getStatus() and can break the polling loop. Mirror the quote path here: add a timeout and return pending on fetch/JSON failures.

🛠 Suggested fix
-    const response = await fetch(`${BASE_URL}/status?${params.toString()}`);
-    if (!response.ok) return TransactionStatus.pending;
-    const json = (await response.json()) as LiFiStatusResponse;
-
-    if (json.status === "FAILED") return TransactionStatus.failed;
-    if (json.status === "DONE") {
-      if (json.substatus === "REFUNDED") return TransactionStatus.failed;
-      return TransactionStatus.success;
-    }
-    return TransactionStatus.pending;
+    const withTimeout = this.withTimeoutSignal(undefined, 30_000);
+    try {
+      const response = await fetch(`${BASE_URL}/status?${params.toString()}`, {
+        signal: withTimeout.signal,
+      });
+      if (!response.ok) return TransactionStatus.pending;
+      const json = (await response.json()) as LiFiStatusResponse;
+
+      if (json.status === "FAILED") return TransactionStatus.failed;
+      if (json.status === "DONE") {
+        if (json.substatus === "REFUNDED") return TransactionStatus.failed;
+        return TransactionStatus.success;
+      }
+      return TransactionStatus.pending;
+    } catch {
+      return TransactionStatus.pending;
+    } finally {
+      withTimeout.cleanup();
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await fetch(`${BASE_URL}/status?${params.toString()}`);
if (!response.ok) return TransactionStatus.pending;
const json = (await response.json()) as LiFiStatusResponse;
const withTimeout = this.withTimeoutSignal(undefined, 30_000);
try {
const response = await fetch(`${BASE_URL}/status?${params.toString()}`, {
signal: withTimeout.signal,
});
if (!response.ok) return TransactionStatus.pending;
const json = (await response.json()) as LiFiStatusResponse;
if (json.status === "FAILED") return TransactionStatus.failed;
if (json.status === "DONE") {
if (json.substatus === "REFUNDED") return TransactionStatus.failed;
return TransactionStatus.success;
}
return TransactionStatus.pending;
} catch {
return TransactionStatus.pending;
} finally {
withTimeout.cleanup();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/swap/src/providers/lifi/index.ts` around lines 417 - 419, The
getStatus() LiFi polling path should tolerate transient network/timeouts: wrap
the fetch(`${BASE_URL}/status?...`) call with an AbortController-based timeout
(e.g., setTimeout to call controller.abort after X ms), and surround both the
fetch and the response.json() with try/catch; on any fetch/json error or abort
return TransactionStatus.pending. Ensure you clear the timeout once fetch
completes and reference the existing symbols BASE_URL, LiFiStatusResponse, and
TransactionStatus.pending so the change is localized to the getStatus()
implementation.

Comment on lines +13 to +18
transactionRequest?: {
to: string;
data: string;
value?: string;
gasLimit?: string;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Model Solana transactionRequest.to as optional.

packages/swap/src/providers/lifi/index.ts uses the absence of transactionRequest.to to detect the Solana path, so keeping to required here makes the type lie about a valid response shape and hides future unsafe accesses.

🛠 Suggested fix
   transactionRequest?: {
-    to: string;
+    to?: string;
     data: string;
     value?: string;
     gasLimit?: string;
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
transactionRequest?: {
to: string;
data: string;
value?: string;
gasLimit?: string;
};
transactionRequest?: {
to?: string;
data: string;
value?: string;
gasLimit?: string;
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/swap/src/providers/lifi/types.ts` around lines 13 - 18, Make the
transactionRequest.to field optional in the LiFi response model so the type
matches runtime behavior: update packages/swap/src/providers/lifi/types.ts so
transactionRequest has to?: string instead of to: string, and then adjust any
direct accesses (e.g., in packages/swap/src/providers/lifi/index.ts where the
code branches on the absence of transactionRequest.to) to guard against
undefined where needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant