Skip to content

feat(widget-checkout): checkout mode — on-ramp, deposit & wallet funding#817

Open
chybisov wants to merge 17 commits into
mainfrom
feat/widget-checkout
Open

feat(widget-checkout): checkout mode — on-ramp, deposit & wallet funding#817
chybisov wants to merge 17 commits into
mainfrom
feat/widget-checkout

Conversation

@chybisov

@chybisov chybisov commented Jul 8, 2026

Copy link
Copy Markdown
Member

Overview

Integration PR for widget checkout mode — the full on-ramp / deposit / wallet funding feature, assembled from the 11-PR checkout/* stack (all already merged into feat/widget-checkout). This brings ~235 files: a new @lifi/widget-checkout package, Transak & Mesh on-ramp provider packages, checkout session core in @lifi/widget-provider, and playground wiring.

Draft while the assembled feature completes CI against main, the deferred follow-ups below are addressed, and QA runs. Not ready to merge yet.

Linear

Project: Checkout V1 (team EMB) — "Checkout flow with fiat on-ramp (Transak), CEX funding (Mesh), and deposit addresses (Intent Factory) on Widget V4."

Directly linked in the stack: EMB-464 (quote-aware Transak funding, via #803).

Covered by feature area:

Area Tickets
Architecture & scaffolding EMB-306, EMB-307, EMB-308, EMB-382
Use cash (Transak) EMB-301, EMB-302, EMB-303, EMB-304, EMB-377, EMB-387, EMB-407, EMB-464 · docs EMB-305
Connect CEX (Mesh) EMB-326, EMB-327, EMB-328, EMB-376, EMB-388, EMB-406, EMB-433 · docs EMB-329
Pay from wallet EMB-361, EMB-368, EMB-369, EMB-383, EMB-405 · docs EMB-371
Transfer crypto EMB-372, EMB-373, EMB-374, EMB-408 · docs EMB-375
Cross-cutting docs EMB-367

Backend/QA counterparts (context): CORE-371, CORE-398, QAC-6.

Stack (merged into feat/widget-checkout, in order)

Changesets for each already landed with their sub-PR.

Behavior changes for integrators (intentional)

  • Wallet menu now honors chains.types. WalletProvider filters enabledChainTypes by the chains.types allow/deny config, so a widget configured e.g. chains.types.allow: [EVM] no longer offers Solana/Bitcoin/Sui/Tron wallets in the connect menu (previously chains.types only filtered chains). Intentional per [9] feat(checkout): use any route for wallet payments, EVM-only #794 ("…and the wallet menu (which now honors chains.types)"). Only affects integrators that set chains.types.

Deferred follow-ups — address in this PR before merging to main

These findings were surfaced during review of the stack and intentionally deferred to a single pass on the assembled branch (rather than per-PR). Checked items were fixed in dd8893ae (verified by biome/typecheck/vitest + adversarial per-group re-review).

Correctness / safety

  • Status polling (packages/widget-checkout/src/hooks/useCheckoutTransactionStatus.ts): un-mask isError (drop && !resolvedStatus) so the already-wired error/retry screen appears (bounded retry comes from the shared query client's default). Resolves both the "swallow-forever spinner" (from [5] feat(checkout): transaction status and details pages #786) and the "poll-stall on a mid-poll error" (from [9] feat(checkout): use any route for wallet payments, EVM-only #794).
  • Transak native-ETH hack (packages/widget-provider-transak/src/TransakHost.tsx): remove the chainId-1 5xx retry that silently re-requests a session for native ETH — it can fund the wrong asset. (Self-described temporary test hardcode.)
  • Transak empty dialog on pre-flight failure (TransakHost.tsx): call setOpen(true) only on the success path (mirror MeshHost) so failures show the inline error on the status page instead of a blank hosted dialog.
  • Mesh overlay teardown (packages/widget-provider-mesh/src/MeshHost.tsx): store the createLink handle and call closeLink() in close()/cancel(); clear failure/depositTxHash in close(); MMT-aware expiresAt for reconnect accounts.
  • Exchange-override restore (useCheckoutExchangesOverride.ts + SelectSourcePage.tsx + EnterAmountPage.tsx): snapshot-based restore of the integrator's exchanges when switching to the wallet flow, so a wallet quote never forwards a stale enabledExchanges=['smartDeposits'].
  • Eager-reconnect guard (SelectSourcePage.tsx): don't auto-forward to token selection when a wallet eagerly reconnects — require explicit "Pay from wallet" intent.
  • Routes-error surfacing (useRoutes.ts additive isError + useCheckoutFlowQuote.ts): show Try-again when the routes fetch itself fails (not only the step query).
  • Checkout userId crash guard (useCheckoutUserId.ts): don't crash on insecure contexts where crypto.randomUUID is unavailable.

Backend / QA (blocking)

  • Verify the on-ramp session field contract. [9] feat(checkout): use any route for wallet payments, EVM-only #794 renamed the request body walletAddress → depositAddress for POST /v1/checkout/onramp/session. Confirm the backend accepts depositAddress — if it still expects walletAddress, Transak session creation is broken. Note the asymmetry: CexSessionRequest (POST /v1/checkout/cex/session) still sends walletAddress.
  • QA the substantive merges. The 11 branches were merged into feat/widget-checkout via 3-way conflict resolution; exercise the checkout flows (Transak/Mesh on-ramp, transfer deposit, wallet-any-route, cash fiat quote, resume/abandon) end-to-end.

Low-severity nits

  • CheckoutTransferIdCard window.open(..., '_blank') without noopener.
  • Expired CEX reconnect account selectable on an idle page (consumption-time expiry check).
  • TransferDepositPage warning copy vs rounded-up amount (reword to "at least"-style).
  • Playground: env.d.ts missing VITE_CHECKOUT_TO_ADDRESS; .env.staging comment points at wrong .local file.

Accepted as-is (reviewed, intentional/negligible)

  • resolveStatusVariant only honors walletDisconnected when fundingSource === 'wallet' (commented as intentional).
  • useIsCheckoutBusy memo can go stale across the 24h TTL boundary only with zero store writes (self-heals on any write).

Known follow-ups (post-merge, not in this PR)

  1. Settings-store cross-widget pollution: checkout's overrideExchanges writes smartDeposits into the persisted li.fi-widget-settings localStorage shared with standard widgets on the same origin. The snapshot restore shrinks the window but can't eliminate it — follow-up: exclude checkout-injected keys from persistence or namespace checkout's settings store.
  2. Auto-deposit re-arm: the reset effect in CheckoutTransactionPage.tsx (CheckoutDepositAutoStarter) cannot run as written (component unmounts before observing a non-Idle status) — auto-deposit is effectively once-per-tab-session-per-routeId. Left untouched to avoid changing payment auto-trigger semantics; clarify intent and rewire (or remove) deliberately.
  3. Wallet-flow routes-error UX: the wallet funding source early-returns before the Try-again branch in CheckoutFlowCtaButton, so routes errors show a disabled Pay button.
  4. Test infra for host packages: widget-provider-transak / widget-provider-mesh have no vitest setup; host fixes rely on typecheck + review + CI.

Testing

  • Full CI (typecheck + E2E shards + Dev smoke) against main via this PR.
  • Unit: error-path tests added for useCheckoutTransactionStatus.
  • Manual QA in the playground per the checklist above.

tomiiide and others added 11 commits July 7, 2026 12:07
Co-authored-by: Eugene Chybisov <18644653+chybisov@users.noreply.github.com>
… flow (#784)

Co-authored-by: eugene <eugene@li.finance>
Co-authored-by: eugene <eugene@li.finance>
Co-authored-by: eugene <eugene@li.finance>
Co-authored-by: eugene <eugene@li.finance>
…ess (#788)

Co-authored-by: eugene <eugene@li.finance>
@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea4d3cc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@lifi/widget-provider Minor
@lifi/widget-provider-transak Major
@lifi/widget-provider-mesh Major
@lifi/widget-checkout Minor
@lifi/widget Minor
@lifi/wallet-management Patch
@lifi/widget-provider-bitcoin Patch
@lifi/widget-provider-ethereum Patch
@lifi/widget-provider-solana Patch
@lifi/widget-provider-sui Patch
@lifi/widget-provider-tron Patch
vite-iframe Patch
nft-checkout Patch
tanstack-router-example Patch

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

# Conflicts:
#	CLAUDE.md
#	packages/widget-playground/src/components/Widget/WidgetView.tsx
#	packages/widget-provider/package.json
#	packages/widget/src/hooks/useRoutes.ts
#	packages/widget/src/i18n/en.json
#	pnpm-lock.yaml
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

✅ E2E Dev Smoke — passing

Check Result
Dev server start (pnpm dev) ✅ started
Smoke tests ✅ passed

4 passed · 0 failed · 0 skipped · 34s

View run

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

E2E Examples — all passed

All examples passed in the latest run.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

E2E Playground results

passed  158 passed

Details

stats  158 tests across 10 suites
duration  2 minutes, 30 seconds
commit  ea4d3cc

📥 Download full HTML report (open the run → Artifacts → playwright-report)

@chybisov chybisov marked this pull request as ready for review July 8, 2026 10:23
chybisov added 3 commits July 8, 2026 13:40
- status polling: expose raw isError so the status page's retry screen
  can render after a mid-poll failure (stop-on-error + manual retry);
  add error-path tests
- Transak: remove the temporary native-ETH 5xx retry fallback; open the
  hosted dialog only once a session succeeds (pre-flight errors surface
  inline on the status page)
- Mesh: tear down the Link overlay on close/cancel via closeLink();
  close() clears failure/depositTxHash; MMT accounts use the default
  reconnect TTL instead of the raw token expiry
- checkout: restore the integrator exchange list (snapshot-based) when
  switching to the wallet flow; require explicit pay-from-wallet intent
  before auto-navigating on wallet connect; drop expired CEX reconnect
  accounts at selection time
- surface routes-level fetch errors through useCheckoutFlowQuote
  (useRoutes now exposes isError)
- harden checkout userId generation for insecure contexts; guard empty
  route steps; transfer deposit copy; noopener on explorer link;
  playground env fixes

Note: useRoutes.ts diff includes a biome reformat (destructure went
multiline); substantive changes are additive only (see diff -w).
Web Crypto's getRandomValues remains the real fallback; the last-resort
tier (no Web Crypto at all) now uses a time-based ephemeral id.
@chybisov chybisov added the Agent Review Request triggers QA Agent Zeus label Jul 8, 2026
@github-actions github-actions Bot added QA AI Reviewing and removed Agent Review Request triggers QA Agent Zeus labels Jul 8, 2026
@lifi-qa-agent

lifi-qa-agent Bot commented Jul 8, 2026

Copy link
Copy Markdown

🔍 QA Review — EMB-464

PR: #817 feat(widget-checkout): checkout mode — on-ramp, deposit & wallet funding
Scope: Quote-aware Transak funding: live quote + dynamic fiat currencies
Review type: 🆕 First review
Verdict: ⚠️ Needs Work


What this ticket does

EMB-464 replaces the widget's fake Transak quote (a static cryptoAmount × priceUSD calculation) with real backend API consumption. The three new endpoints (/v1/checkout/onramp/fiat-currencies, /v1/checkout/onramp/quote, /v1/checkout/onramp/session) drive: (1) a live fiat→crypto quote shown as the user types, (2) a dynamic currency list seeded from backend defaults, (3) payment-method routing, and (4) a fundingSessionId captured for reconciliation. Implemented in sub-PR #803 and merged into the feat/widget-checkout stack.


Acceptance Criteria

# AC Status Evidence
1 Fiat-first cash flow: typing fiat shows a live funding estimate + fees + warnings, updating on amount/currency change ⚠️ Partial Estimate + fees confirmed (CheckoutReceiveCard); warnings[] field exists in OnrampQuoteResponse type but no UI renders it
2 Deposit-address route resolves from the quote's funding estimate ✅ Pass CheckoutReceiveCard writes estimatedFundingAmountfromAmount field via useEffect; useCheckoutFlowQuoteextractDepositAddress(route) chain confirmed
3 Currency list + default come from the backend; arbitrary currency codes render without a broken flag/name ✅ Pass useOnRampFiatCurrencies + SelectCashCurrencyPage fallback to ['USD','EUR','GBP']; currencyToFlag returns 💱 for unknown codes; getCurrencyName uses Intl.DisplayNames with catch fallback
4 paymentMethod (when applicable) feeds both quote and session ✅ Pass useFiatCurrencyStore.paymentMethoduseOnRampQuote body → useOnRampSessionByCategoryTransakHost.openDepositFlowOnrampSessionRequest
5 fundingSessionId captured and persisted on the pending record ✅ Pass TransakHost captures res.data.fundingSessionId ?? null; PendingCheckoutPersistenceBridge passes result.fundingSessionIdwriteCashSuccessPendingRecord.fundingSessionId
6 Wallet / Mesh / transfer flows unaffected ✅ Pass Cash-specific logic gated by fundingSource === 'cash'; CI shows 158 E2E playground tests passing
7 CTA gated while the quote is loading/stale/errored ✅ Pass cashNotReady flag in CheckoutFlowCtaButton combines !hasFiatAmount || !onRampQuote.isReady || onRampQuote.isFetching || onRampQuote.isDebouncePending || !cashRouteMatchesQuote

Blocking Open Item: Backend Contract — walletAddress → depositAddress

The PR body explicitly marks this as unresolved (☐ unchecked):

Sub-PR #794 renamed the OnrampSessionRequest field walletAddress → depositAddress. The current TransakHost.tsx sends:

const body: OnrampSessionRequest = {
  depositAddress: args.depositAddress,
  ...
}

Risk: If the backend (POST /v1/checkout/onramp/session) still expects walletAddress, every Transak session creation will fail silently with a 400/422 from the backend, and the !res.ok branch will surface a generic error to the user. This cannot be caught by frontend tests or E2E smoke tests — it requires a live backend call against the dev environment.

This must be resolved before the integration PR merges. The PR description states: "Confirm the backend accepts depositAddress — if it still expects walletAddress, Transak session creation is broken." No confirmation comment exists on the PR.


Findings

1. [High] warnings[] from the onramp quote is typed but never rendered

The OnrampQuoteResponse type includes warnings?: string[]. The ticket AC explicitly requires: "typing fiat shows a live funding estimate + fees + warnings". The CheckoutReceiveCard component reads onRampQuote.data?.fees and renders fee rows (including breakdown), but there is no component in the PR that iterates over onRampQuote.data?.warnings and displays them to the user.

The warnings field is defined in the response type and noted in the ticket description ("Whether any warnings[] entry is blocking vs advisory"), but the frontend consumes it as a no-op — the array arrives in the response and is discarded.

Required action: Either render warnings[] as advisory chips/alerts in CheckoutReceiveCard (or a sibling component), or document explicitly that warnings are deferred (and update the AC to remove the word "warnings" from AC-1). As written, AC-1 is not fully satisfied.

Files: packages/widget-checkout/src/components/CheckoutReceiveCard.tsx, packages/widget-provider/src/checkout/api.ts


2. [High] Aikido XSS flag — CheckoutTransferIdCard.tsx (window.open(txLink, ...))

The Aikido bot flagged CheckoutTransferIdCard.tsx for "Potential XSS via window.location.href". Reading the actual file, the code is:

const openTransferIdInExplorer = () => {
  window.open(txLink, '_blank', 'noopener')
}

Assessment: This is a false positive on the specific XSS vector Aikido identified. window.open with noopener is not window.location.href — it opens a new tab without passing a reference to the opener, which is the correct safe pattern. The noopener flag was noted as one of the ✅ resolved items in the PR body (window.open noopener added).

However, there is a residual risk the Aikido bot is pointing at: txLink is an unvalidated string received from props (txLink?: string). If txLink were ever set to a javascript: URI, window.open would execute it. The component renders a button conditionally: {txLink ? <CardIconButton ... onClick={openTransferIdInExplorer}>. There is no URL protocol validation before passing txLink to window.open.

Required action: Add a guard before window.open:

const openTransferIdInExplorer = () => {
  if (txLink && /^https?:\/\//i.test(txLink)) {
    window.open(txLink, '_blank', 'noopener')
  }
}

This closes the javascript: URI injection vector. The fix is a one-liner and should be applied before merge.

File: packages/widget-checkout/src/pages/CheckoutTransactionDetailsPage/CheckoutTransferIdCard.tsx


3. [Medium] staleTime: 0 on the quote query makes every navigation re-fetch

useOnRampQuote sets staleTime: 0 with placeholderData: keepPreviousData. This means every component mount (e.g. returning to EnterAmountPage from SelectCashCurrencyPage) triggers a fresh network request for the quote, even if the fiat amount and currency have not changed.

The fiat-currencies hook correctly uses staleTime: 24 * 60 * 60 * 1000 since currencies are slow-changing. The quote hook uses staleTime: 0 deliberately (quotes are rate-sensitive), but combined with the 350ms debounce the UX impact is: on every page re-mount while the debounce is settled, a refetch fires immediately. The keepPreviousData placeholder means the user sees the prior value while the refetch runs, so this is not a data-correctness problem — but it is unnecessary backend load.

Recommendation: Set staleTime to a short window (e.g. 15_000 ms — 15 seconds) to tolerate back-navigation. The 350ms debounce already prevents rapid-fire requests on typing. This is advisory; the current behaviour is not incorrect.

File: packages/widget-checkout/src/hooks/useOnRampQuote.ts


4. [Medium] seedCurrency resets paymentMethod: null — single-option currencies lose their auto-selected method on navigation

In useFiatCurrencyStore, seedCurrency sets paymentMethod: null unconditionally:

seedCurrency: (currency) =>
  set((state) =>
    state.currencyTouched ? state : { currency, paymentMethod: null }
  ),

SelectCashCurrencyPage runs a useEffect that calls seedCurrency(data.defaultCurrency) on load. If a user opens the currency page, the seed runs and clears paymentMethod. The page then has a second useEffect that auto-selects selectedCurrency?.paymentOptions[0]?.id. But if the selectedCurrency is the same currency that was already active (i.e., the user opened the page without changing anything), setCurrency is not called, and the second effect may not fire to restore the payment method.

This creates a scenario where opening and closing the currency page (without selecting a different currency) can transiently clear paymentMethod, causing the quote to be sent without it until the page's second effect fires. Given the 24h staleTime on fiat-currencies, the useEffect for auto-select fires on mount — so the window is narrow, but it exists for users with fast networks who navigate away before the effect resolves.

File: packages/widget-checkout/src/stores/useFiatCurrencyStore.tsx, SelectCashCurrencyPage.tsx


Test Coverage

File Has Test Notes
useOnRampQuote.ts ❌ No New hook with debounce + enabled-guard logic — no unit test
useOnRampFiatCurrencies.ts ❌ No New hook with normalization logic — no unit test
useFiatCurrencyStore.tsx ❌ No New store with seedCurrency/setCurrency/reset state machine — no unit test
CheckoutFlowCtaButton.tsx ❌ No cashNotReady + cashRouteMatchesQuote logic untested
SelectCashCurrencyPage.tsx ❌ No No component test; fallback to static list untested
currencyToFlag.ts ❌ No New utility — no test for arbitrary/unknown codes
fiatFormat.ts ❌ No New utilities — no test for formatFiat, getCurrencyName, getCurrencySymbol
usePendingCheckoutWriter.ts ✅ Yes (250 lines) Comprehensive; covers writeCashSuccess with fundingSessionId
usePendingCheckoutStore.ts ✅ Yes Covers TTL, sweep, versioning
useOnRampPreconnect.ts ✅ Yes
CheckoutModal.tsx ✅ Yes Covers close-guard + pending record scenarios
sessionClient.ts ✅ Yes

Test gap summary: 7 new implementation files with no test coverage. The most critical missing test is useOnRampQuote.ts — the debounce + isDebouncePending + isReady state machine drives the CTA gate and is high-impact if it regresses.

Note: widget-provider-transak has no vitest setup (acknowledged in PR description). TransakHost.tsx is not independently testable without a vitest environment — this is a known limitation. The OnRampHostedModals.test.tsx provides integration coverage for the modal lifecycle.


Ticket Quality


Downstream Impact

  • OnrampSessionRequest field rename (walletAddress → depositAddress) is a contract break — if the backend hasn't updated, this is a production outage for all Transak users.
  • FiatCurrency widened from enum to string — consumers of this type in downstream packages may need updates (none found in PR scope).
  • OnRampSession.fundingSessionId is a new field — makeSession in CheckoutModal.test.tsx omits it (as unknown as OnRampSession), which is fine given the cast but confirms the mock will need updating if the interface evolves.

Ticket Coverage: Partial (6/7 AC fully met; AC-1 has an open warnings display gap)


Generated by lifi-qa-agent[bot] on 2026-07-08

@lifi-qa-agent lifi-qa-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requesting changes on 4 items — each requires either a code fix or an explicit acceptance comment with justification before this review is considered complete.

# Severity Type Issue / File
1 🔴 High Code warnings[] from onramp quote is typed but never rendered — AC-1 partially unmet
2 🔴 High Security CheckoutTransferIdCard.tsxtxLink passed to window.open without protocol validation
3 🟠 Medium Code staleTime: 0 on quote query causes unnecessary refetch on every page re-mount
4 🟠 Medium Code seedCurrency clears paymentMethod: null — opening/closing currency page may transiently drop payment method

1. [High] warnings[] from onramp quote is typed but never rendered

OnrampQuoteResponse.warnings?: string[] exists in the type and is returned by the backend. AC-1 explicitly requires: "typing fiat shows a live funding estimate + fees + warnings". CheckoutReceiveCard renders fees.breakdown and fees.total.amount but does not read onRampQuote.data?.warnings anywhere. The field is silently discarded. Add rendering of warnings[] as advisory alerts/chips in CheckoutReceiveCard, or explicitly mark warnings as deferred scope and update AC-1 to remove "warnings" from its text.

Files: packages/widget-checkout/src/components/CheckoutReceiveCard.tsx, packages/widget-provider/src/checkout/api.ts

2. [High] Security — txLink in CheckoutTransferIdCard.tsx passed to window.open without protocol guard

The Aikido bot flagged CheckoutTransferIdCard.tsx. The window.open(txLink, '_blank', 'noopener') call (noted as fixed with noopener) still passes txLink without validating the URL protocol. If txLink is set to a javascript: URI (e.g. from a malicious backend response or a compromised txLink source), window.open will execute it. Add a protocol guard before the call:

const openTransferIdInExplorer = () => {
  if (txLink && /^https?:\/\//i.test(txLink)) {
    window.open(txLink, '_blank', 'noopener')
  }
}

File: packages/widget-checkout/src/pages/CheckoutTransactionDetailsPage/CheckoutTransferIdCard.tsx

3. [Medium] staleTime: 0 on quote query causes unnecessary network requests on navigation

useOnRampQuote uses staleTime: 0 with placeholderData: keepPreviousData. On every re-mount of EnterAmountPage (e.g. returning from SelectCashCurrencyPage), a fresh quote is fetched even if the fiat amount and currency are unchanged. Consider staleTime: 15_000 to tolerate back-navigation without unnecessary backend load.

File: packages/widget-checkout/src/hooks/useOnRampQuote.ts

4. [Medium] seedCurrency sets paymentMethod: null — opening currency page transiently drops payment method

useFiatCurrencyStore.seedCurrency resets paymentMethod: null. SelectCashCurrencyPage calls seedCurrency(data.defaultCurrency) on mount. If the user opens and closes the page without selecting a new currency, the payment method is cleared by seedCurrency and only restored when the auto-select useEffect fires on the unchanged selectedCurrency.paymentOptions[0]. This creates a narrow window where the quote is sent without paymentMethod. Fix seedCurrency to preserve paymentMethod when the currency is unchanged:

seedCurrency: (currency) =>
  set((state) =>
    state.currencyTouched
      ? state
      : state.currency === currency
        ? state  // no-op if currency unchanged — preserve paymentMethod
        : { currency, paymentMethod: null }
  ),

Files: packages/widget-checkout/src/stores/useFiatCurrencyStore.tsx, packages/widget-checkout/src/pages/SelectCashCurrencyPage/SelectCashCurrencyPage.tsx


Additionally, the blocking open item in the PR description must be resolved before merge:

Backend contract verification: confirm the backend accepts depositAddress (not walletAddress) on POST /v1/checkout/onramp/session. If the backend still expects walletAddress, every Transak session creation is broken.

This cannot be verified by frontend tests — it requires a live backend call against the dev environment or a written confirmation from the CORE team.


💡 Once you've addressed the items above, re-apply the "Agent Review Request" label to trigger an automated re-review.

Keeps the on-hold checkout packages out of npm publishing while the
changesets pipeline continues to version them; main-widget releases
are unaffected. Remove the flags to un-hold.

Also removes three dead files left behind by the stack merges
(knip-flagged, zero references): CheckoutDepositContractCallsInit,
CheckoutRecipientCard, playground CheckoutWalletProvider.
}

const openTransferIdInExplorer = () => {
window.open(txLink, '_blank', 'noopener')

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 Cross Site Scripting (XSS) via window.location.href - high severity
Using window.location.href or similar redirection methods with unvalidated user input can lead to cross-site scripting (XSS). If an attacker controls part of the URL, they may inject malicious scripts that execute in the victim’s browser, potentially allowing session hijacking, data theft, or other unauthorized actions. Proper validation and encoding of user-controlled values are required before using them in redirects or client-side navigation.

Show fix

Remediation: Avoid using window.location.href or similar redirection methods with unvalidated user input. Only allow redirects to trusted, predefined destinations or validate the input against a strict allowlist. When possible, use fixed URLs or server-side redirection logic, and ensure any user-controlled values are properly validated and encoded before use.

Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants