LI.Fi Swap Provider#798
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (6)
WalkthroughThe 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Review rate limit: 0/1 reviews remaining, refill in 53 minutes and 6 seconds.Comment |
effbc77 to
0ea6630
Compare
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
packages/swap/src/common/icons/li.fi.pngis excluded by!**/*.png
📒 Files selected for processing (6)
packages/swap/src/configs.tspackages/swap/src/index.tspackages/swap/src/providers/lifi/index.tspackages/swap/src/providers/lifi/supported.tspackages/swap/src/providers/lifi/types.tspackages/swap/src/types/index.ts
| integrator: LIFI_INTEGRATOR_ID, | ||
| fee: String(FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.fee ?? 0.002), | ||
| referrer: FEE_CONFIGS[this.name]?.[meta.walletIdentifier]?.referrer ?? '', |
There was a problem hiding this comment.
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.
| 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.
| async getStatus(options: StatusOptions): Promise<TransactionStatus> { | ||
| const txHash = options.transactions?.[1]?.hash; | ||
| if (!txHash) return TransactionStatus.pending; |
There was a problem hiding this comment.
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.
| const response = await fetch(`${BASE_URL}/status?${params.toString()}`); | ||
| if (!response.ok) return TransactionStatus.pending; | ||
| const json = (await response.json()) as LiFiStatusResponse; |
There was a problem hiding this comment.
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.
| 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.
| transactionRequest?: { | ||
| to: string; | ||
| data: string; | ||
| value?: string; | ||
| gasLimit?: string; | ||
| }; |
There was a problem hiding this comment.
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.
| 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.
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
Supported Chains
Demo
Rootstock -> ETH
rootstock-rif-to-eth-usdc.mp4
ETH -> Solana
li.fi-demo-01.mp4
Test
Summary by CodeRabbit