Skip to content

[LWDM] feat(perps): sign the deposit through the exchange swap flow - #20923

Draft
ooke-ledger wants to merge 5 commits into
feat/LIVE-35327from
feat/LIVE-35328
Draft

[LWDM] feat(perps): sign the deposit through the exchange swap flow#20923
ooke-ledger wants to merge 5 commits into
feat/LIVE-35327from
feat/LIVE-35328

Conversation

@ooke-ledger

Copy link
Copy Markdown
Contributor

📝 Description

Adds the signing step of the Perps deposit flow - the screen reached from the
review, where the user confirms the deposit on device.

The swap orchestration that already backs the custom.exchange.swap handler is
extracted out of server.ts into a shared executeSwap, so the perps screens
drive the exact same device flow (partner key, payload, payout/refund address
checks, sign, broadcast) instead of a second implementation of it. The perps UI
stays perps: only the orchestration is shared.

The deposit executes against the quote the review screen priced against, so the
amount signed on device is the amount the user was shown.

🔗 Context

https://ledgerhq.atlassian.net/browse/LIVE-35328

Stacked on #20441 (LIVE-35327, the review step).

✅ Checklist

  • Unit tests cover the device step sequence and the signed payload on both
    desktop and mobile.

Copilot AI lite review requested due to automatic review settings August 19, 2026 09:06
@ooke-ledger
ooke-ledger requested review from a team as code owners August 19, 2026 09:06
@live-github-bot live-github-bot Bot added desktop Has changes in LLD mobile Has changes in LLM labels Aug 19, 2026
@live-github-bot live-github-bot Bot added the common Has changes in live-common label Aug 19, 2026
@ledger-wiz-cspm-secret-detection

ledger-wiz-cspm-secret-detection Bot commented Aug 19, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Low 1 Info
Software Management Finding Software Management Findings -
Total 1 Low 1 Info

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a Perps deposit “signing” step on both mobile and desktop by reusing the swap exchange orchestration, which has been extracted into a shared executeSwap helper in live-common.

Changes:

  • Extracts the custom.exchange.swap end-to-end flow into libs/ledger-live-common/src/wallet-api/Exchange/executeSwap.ts and moves UI request types into uiRequests.ts.
  • Introduces Perps deposit signing screens/dialogs + orchestration hooks on mobile and desktop (device steps: start → confirm → sign → broadcast).
  • Updates Desktop DeviceAction swap confirmation rendering to support a Perps-specific “continue on device” confirmation screen.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
libs/ledger-live-common/src/wallet-api/Exchange/uiRequests.ts Centralizes wallet-api exchange UI request types used by handlers and perps flows.
libs/ledger-live-common/src/wallet-api/Exchange/server.ts Refactors swap handler to delegate to shared executeSwap and re-exports request types.
libs/ledger-live-common/src/wallet-api/Exchange/executeSwap.ts New shared swap execution pipeline (device nonce → payload → transaction → UI hooks).
libs/ledger-live-common/src/hw/actions/completeExchange.ts Exposes Result type for consumers (perps deposit execution).
apps/ledger-live-mobile/src/mvvm/features/Perps/... Adds Perps deposit signing screen + view model, routes to it from review, and adds execution hook + tests.
apps/ledger-live-mobile/src/const/navigation.ts Registers PerpsDepositSign screen name.
apps/ledger-live-mobile/src/components/RootNavigator/... Wires PerpsDepositSign into navigation types and stack navigator.
apps/ledger-live-mobile/src/components/DeviceAction/index.tsx Exports Status type so perps hook can type device-action rendering bindings.
apps/ledger-live-desktop/src/renderer/components/DeviceAction/... Adds perps confirmation override for swap confirmation UI and exports States type.
apps/ledger-live-desktop/src/mvvm/features/Perps/... Adds Perps deposit signing dialog + components + execution hook + tests, and opens it from review.
apps/ledger-live-desktop/src/mvvm/features/GlobalDialogs/index.tsx Mounts the new Perps deposit signing dialog globally.
.changeset/perps-deposit-signing.md Declares release notes for the new perps deposit signing step + refactor.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +48 to +54
export type SwapUiRequest = CompleteExchangeUiRequest & {
provider?: string;
fromAccountId?: string;
toAccountId?: string;
tokenCurrency?: string;
correlationId?: string;
};
Comment on lines +269 to +274
const magnitudeAwareRate = tx.amount && amountExpectedTo.dividedBy(tx.amount);
const refundAddress = decodePayload.refundAddress;
const payoutAddress = decodePayload.payoutAddress;

// tx.amount should be BigNumber
tx.amount = new BigNumber(tx.amount);
i18nKey="perpsDepositSign.terms"
components={{
termsLink: (
<span className="cursor-pointer underline" onClick={() => openURL(SWAPKIT_TERMS_URL)} />
Comment on lines +520 to +521
// Perps deposit is a FUND, but keeps its own "continue on your device" screen
// regardless of exchange type — handle it before the swap/sell/fund switch.
Comment on lines +8 to +13
const mockNavigate = jest.fn();

jest.mock("@react-navigation/native", () => ({
...jest.requireActual("@react-navigation/native"),
useNavigation: () => ({ navigate: mockNavigate }),
}));
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ E2E tests are required

Changes detected require e2e testing before merge (even before asking for any review).

🖥️ Desktop

-> Run Desktop E2E

  • Select "Run workflow"
  • Branch: feat/LIVE-35328
  • Device: nanoSP or stax

📱 Mobile

-> Run Mobile E2E

  • Select "Run workflow"
  • Branch: feat/LIVE-35328
  • Device: nanoX

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Web Tools Build Status

Build Status Deployment
Web Tools Build ✅ Deployed https://web-tools-k0qb54y7v-ledger-hq-prd.vercel.app
Native Storybook Build ⏭️ Skipped
React Storybook Build ⏭️ Skipped

Copilot AI review requested due to automatic review settings August 19, 2026 09:12
@live-github-bot

live-github-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Mobile Bundle Checks

Comparing 21e5b09 against 48674fc.

✅ Previous issues have all been fixed.

Desktop Bundle Checks

Comparing 21e5b09 against 2a8a767.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Suppressed comments (3)

libs/ledger-live-common/src/wallet-api/Exchange/uiRequests.ts:54

  • SwapUiRequest attempts to make provider optional, but intersecting with CompleteExchangeUiRequest (where provider is required) will keep provider required in TypeScript. If callers expect provider to be optional (as suggested by usages like exchangeParams.provider ?? ...), redefine SwapUiRequest as Omit<CompleteExchangeUiRequest, "provider"> & { provider?: string; ... } (or remove the optional override if it must be required everywhere) so the type matches runtime expectations.
export type SwapUiRequest = CompleteExchangeUiRequest & {
  provider?: string;
  fromAccountId?: string;
  toAccountId?: string;
  tokenCurrency?: string;
  correlationId?: string;
};

libs/ledger-live-common/src/wallet-api/Exchange/executeSwap.ts:235

  • Inside an async function, return Promise.reject(new Error(...)) is redundant and slightly harms readability/stack traces compared to throw new Error(...). Prefer throwing the error directly here to keep control flow consistent with the rest of the function’s error handling.
    if (transaction.family !== mainFromAccount.currency.family) {
      return Promise.reject(
        new Error(
          `Account and transaction must be from the same family. Account family: ${mainFromAccount.currency.family}, Transaction family: ${transaction.family}`,
        ),
      );
    }

apps/ledger-live-desktop/src/mvvm/features/Perps/screens/PerpsDepositSign/components/PerpsDepositSignTerms.tsx:13

  • This renders an interactive control as a <span> with onClick, which is not keyboard-accessible by default and lacks link semantics for assistive tech. Prefer rendering an <a> (or <button>) with proper semantics (and keyboard support) so users can activate the terms link using keyboard navigation and screen readers (e.g., by using an <a href=...> with onClick/preventDefault if needed).
      components={{
        termsLink: (
          <span className="cursor-pointer underline" onClick={() => openURL(SWAPKIT_TERMS_URL)} />
        ),
      }}

Comment on lines +418 to +429
if (params.exchangeType === "SWAP" && params.toAccountId) {
const realToAccountId = getAccountIdFromWalletAccountId(params.toAccountId);
if (!realToAccountId) {
throw new ExchangeError(createAccounIdNotFound(params.toAccountId));
}

toAccount = accounts.find(a => a.id === realToAccountId);

if (!toAccount) {
throw new ServerError(createAccountNotFound(params.toAccountId));
}
}
const currency = params.tokenCurrency
? await getCryptoAssetsStore().findTokenById(params.tokenCurrency)
: null;
const newTokenAccount = currency ? makeEmptyTokenAccount(toAccount, currency) : null;
fromCurrency: getCurrencyForAccount(fromAccount),
toAccount: newTokenAccount ? newTokenAccount : toAccount,
toParentAccount: toParentAccount,
toCurrency: getCurrencyForAccount(newTokenAccount ? newTokenAccount : toAccount),
@ooke-ledger
ooke-ledger marked this pull request as draft August 19, 2026 09:18
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
65.8% Coverage on New Code (required ≥ 80%)
13.8% Duplication on New Code (required ≤ 3%)
B Reliability Rating on New Code (required ≥ A)
7 New Code Smells (required ≤ 1)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

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

Labels

common Has changes in live-common desktop Has changes in LLD mobile Has changes in LLM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants