feat(stellar): add Stellar (STL) SDK provider - #427
Conversation
Introduces the @lifi/sdk-provider-stellar package (address validation, Federation resolution, and SAC-based balance reads) and registers ChainType.STL in the SDK client's chain fetching. Transaction execution is not yet implemented.
🦋 Changeset detectedLatest commit: c328023 The changes in this PR will be included in the next version bump. This PR includes changesets to release 7 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Resolve pnpm-lock.yaml conflict by regenerating from merged package.json files (base: main's lockfile + Stellar provider deps). Align sdk-provider-stellar with main's TypeScript 7 migration (#428): bump typescript devDep ^6.0.3 -> ^7.0.2. Verified: check:types, unit tests, and build pass under tsc 7.0.2; pnpm install --frozen-lockfile is consistent.
getClientStorage registers STL, so getChains is now called with STL in chainTypes. Update the stale unit assertion to match (was failing on the branch independently of the main merge).
|
📦 Preview published under the Install the exact version(s) — npm i @lifi/sdk-provider-bitcoin@0.0.0-preview-5e098f3
npm i @lifi/sdk-provider-ethereum@0.0.0-preview-5e098f3
npm i @lifi/sdk-provider-solana@0.0.0-preview-5e098f3
npm i @lifi/sdk-provider-stellar@0.0.0-preview-5e098f3
npm i @lifi/sdk-provider-sui@0.0.0-preview-5e098f3
npm i @lifi/sdk-provider-tron@0.0.0-preview-5e098f3
npm i @lifi/sdk@0.0.0-preview-5e098f3 |
Signs the backend-generated Soroban envelope with the connected wallet, submits it over Stellar RPC, waits for on-chain confirmation, then hands off to the shared LI.FI status wait. Two Stellar-specific constraints shape the pipeline: - A Stellar envelope embeds the sender's account sequence number and timebounds [0, now+300s], so it cannot be reused. Unlike the base PrepareTransactionTask, StellarPrepareTransactionTask always re-fetches the step transaction, since the quote path carries a transactionRequest into the route and would otherwise sign a stale envelope. - Granting a SAC allowance consumes the sender's sequence number, so the allowance tasks run before the transaction is prepared and the approval is confirmed on-chain before the route envelope is requested. The approval spender is taken from step.estimate.approvalAddress only when it is a valid Soroban C-address; the backend currently sends placeholders for Stellar steps (a G-address fallback wallet, or the EVM diamond), which must not be approved. A provider-level override is available until the backend emits the router address. Adds getStellarBalance.int.spec.ts, skipped for now: the LI.FI API does not yet serve Stellar chains, so no RPC URLs resolve for ChainId.XLM.
# Conflicts: # pnpm-lock.yaml
…tion Two defects found in review of the executor: - createPipeline offered only two resume entry points (copied from Sui and Solana), so a step whose hash was persisted but never confirmed restarted at CheckBalanceTask, re-prepared, and signed a SECOND envelope — executing the swap twice. Adopt Bitcoin's three-way entry and resume at the confirmation poll instead, re-submitting the persisted envelope first since Soroban submission is idempotent by hash (DUPLICATE = success). This makes the previously write-only txHex load-bearing and closes the crash-between-persist-and-submit window too. - submitStellarTransaction and waitForStellarTransaction threw their classified TransactionErrors from inside the callStellarRpcsWithRetry callback, which collapses everything into an AggregateError. Every mapping in parseStellarErrors was therefore unreachable for submit and confirm failures. Only the transport call now goes through the failover wrapper; classification happens outside it. Also: derive the approval hash locally, as the route transaction does, so it is recorded even if the submit response is lost; set pollingIntervalMs on the context so it is not permanently the module default; classify readAllowance's simulation failure (its message contained "allowance" and mis-mapped to AllowanceRequired); split submit/wait into separate helper modules; and trim the barrel to the provider surface the siblings expose. Adds specs for the unconditional-refetch invariant, the resume entry points, and the rebroadcast-on-resume path.
|
📦 Preview published under the Install the exact version(s) — npm i @lifi/sdk-provider-bitcoin@0.0.0-preview-207d898
npm i @lifi/sdk-provider-ethereum@0.0.0-preview-207d898
npm i @lifi/sdk-provider-solana@0.0.0-preview-207d898
npm i @lifi/sdk-provider-stellar@0.0.0-preview-207d898
npm i @lifi/sdk-provider-sui@0.0.0-preview-207d898
npm i @lifi/sdk-provider-tron@0.0.0-preview-207d898
npm i @lifi/sdk@0.0.0-preview-207d898 |
…started The cross-task invariant that matters is that SetAllowance does not hand control back until the approval has settled — the next task fetches the route envelope from a live getAccount read. Task ordering alone would still pass if the confirmation were ever made fire-and-forget. Also corrects the readAllowance comment: the classification matters because parseStellarErrors passes a BaseError through untouched, not because of the word 'allowance' in the message.
| import { ChainId, LruMap, type SDKClient } from '@lifi/sdk' | ||
| import { rpc } from '@stellar/stellar-sdk' | ||
|
|
||
| const stellarRpcs = new LruMap<rpc.Server>(12) |
There was a problem hiding this comment.
Module-level 'stellarRpcs' LruMap caches rpc.Server instances across requests; consider scoping cache to client/context or clearly documenting intentional shared cache.
Details
✨ AI Reasoning
A new module-level variable 'stellarRpcs' (an LruMap of rpc.Server) is created and populated with RPC Server instances resolved per client. In a long-running Node.js process this object persists across requests and users. While caching RPC clients can be intentional, it may unintentionally retain request-specific or tenant-specific state, leading to cross-request data leakage or stale/incorrect server instances if different clients expect different RPC URL sets. The variable is declared at module scope and mutated by ensureStellarRpcs, making it a shared mutable global cache.
🔧 How do I fix it?
Avoid storing request-specific data in module-level variables. Use request-scoped variables or explicitly mark shared caches as intentional.
Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
Every other provider's declared range matches its resolved version exactly; sdk-provider-stellar was the only one trailing (^16.0.1 already resolved to 16.1.0). No installed version changes. No new changeset: the existing .changeset/stellar-provider.md minor already covers this package's introduction in the same release.
|
📦 Preview published under the Install the exact version(s) — npm i @lifi/sdk-provider-bitcoin@0.0.0-preview-3a18dab
npm i @lifi/sdk-provider-ethereum@0.0.0-preview-3a18dab
npm i @lifi/sdk-provider-solana@0.0.0-preview-3a18dab
npm i @lifi/sdk-provider-stellar@0.0.0-preview-3a18dab
npm i @lifi/sdk-provider-sui@0.0.0-preview-3a18dab
npm i @lifi/sdk-provider-tron@0.0.0-preview-3a18dab
npm i @lifi/sdk@0.0.0-preview-3a18dab |
|
📦 Preview published under the Install the exact version(s) — npm i @lifi/sdk-provider-bitcoin@0.0.0-preview-c328023
npm i @lifi/sdk-provider-ethereum@0.0.0-preview-c328023
npm i @lifi/sdk-provider-solana@0.0.0-preview-c328023
npm i @lifi/sdk-provider-stellar@0.0.0-preview-c328023
npm i @lifi/sdk-provider-sui@0.0.0-preview-c328023
npm i @lifi/sdk-provider-tron@0.0.0-preview-c328023
npm i @lifi/sdk@0.0.0-preview-c328023 |
Matches the SDK branch head (lifinance/sdk#427, c3280231), which refines the Stellar approval handling. The previous pin (3a18dab) predated that commit. All seven @lifi/sdk* pins move together, as they must: the provider tarballs hard-pin @lifi/sdk to their own preview version, and a second copy of @lifi/sdk breaks route execution because executionState is a module-level singleton. Verified one copy resolves across widget, widget-provider, widget-provider-stellar and wallet-management. Example pins stay on published versions.
🔗 Linear: EMB-463 · Stellar: Add support on widget/SDK
Part of EMB-463 (Stellar on widget/SDK). Phase 1 — address validation + balances; transaction execution follows in Phase 2.
What
@lifi/sdk-provider-stellarpackage:isAddress— valid G-address; rejects muxedM/ contractC(backend requires G-address senders)resolveAddress— SEP-2 FederationgetBalance— reads each token's SACbalance()over Soroban RPC (matches the SAC C-address token model, EXBE-245)getStepExecutor— throws for now; execution is Phase 2, gated on backend tx-generation (EXBE-227)ChainType.STLingetClientStorageso STL chains + RPC URLs load.@stellar/stellar-sdk(no third-party wallet libs).Verified
✅ type-check · build · unit tests (
isAddressG/M/C cases +getClientStorage) pass.Note
Draft. This must be published to npm before the widget PR (lifinance/widget) can install / pass CI.