Skip to content

fix: revert ledger clearSignTransaction back to signTransaction. #41899

Closed
montelaidev wants to merge 4 commits into
mainfrom
fix/ledger-revert-clear-sign
Closed

fix: revert ledger clearSignTransaction back to signTransaction. #41899
montelaidev wants to merge 4 commits into
mainfrom
fix/ledger-revert-clear-sign

Conversation

@montelaidev

@montelaidev montelaidev commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Description

This PR reverts the usage of clearSignTransaction back to signTransaction for ledger because of an edge case where erc20 transactions do not work on the monad network.

The root cause is that ERC20 resolution doesn't work for Monad because it's not in the list of supported networks for clear signing.

When erc20: true:

The library fetches ERC20 token signatures from Ledger's CDN — which does have 60 tokens for Monad. If the transaction targets one of those tokens via transfer or approve, a token descriptor blob (containing chainId=143) is sent to the device. The device can't process the descriptor for an unrecognized chain, enters an inconsistent state, and rejects the signing APDU with 0x6a80.

Custom networks work otherwise because without resolution data, the device blind-signs the raw transaction hash. It doesn't need to know the chain — ECDSA works on any chain. The failure only happens when ERC20/NFT/plugin resolution data is injected for an unsupported chain.

Only approve (0x095ea7b3) and transfer (0xa9059cbb) trigger the ERC20 path — all other selectors skip it.

Changelog

CHANGELOG entry: Fix issue where monad transactions do not work on ledger.

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/MUL-1466?atlOrigin=eyJpIjoiZjNjMDNmZWFiYzY5NGE2MTliOWVhM2ZlNWE0NmQyMDkiLCJwIjoiaiJ9

#41602

Manual testing steps

  1. Using a ledger account
  2. Go to relay.link
  3. Select a erc20 token on the monad network.
  4. Approve and swap

Screenshots/Recordings

Before

After

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Medium Risk
Changes the Ledger transaction-signing path for all networks, which can affect signing UX/compatibility and potentially what users see/confirm on-device. While the change is small, it touches hardware-wallet signing behavior and should be validated on key networks/devices.

Overview
Reverts Ledger transaction signing in the offscreen handler from clearSignTransaction (selector-based ERC20/NFT/plugin resolution) back to signTransaction(hdPath, tx, null), removing the selector parsing/fallback logic and related dependencies.

Updates the Ledger offscreen Jest tests accordingly by dropping transaction-selector/serialization cases and asserting the simpler signTransaction call signature.

Reviewed by Cursor Bugbot for commit 587c616. Bugbot is set up for automated code reviews on this repo. Configure here.

@montelaidev montelaidev self-assigned this Apr 17, 2026
@montelaidev montelaidev requested a review from a team as a code owner April 17, 2026 16:43
@montelaidev montelaidev added the team-accounts-framework Accounts team label Apr 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@montelaidev montelaidev changed the title Fix/ledger revert clear sign fix: revert ledger clearSignTransaction back to signTransaction. Apr 17, 2026
@metamaskbotv2

metamaskbotv2 Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

✨ Files requiring CODEOWNER review ✨

🔑 @MetaMask/accounts-engineers (2 files, +9 -277)
  • 📁 app/
    • 📁 offscreen/
      • 📁 hardware-wallets/
        • 📄 ledger.test.ts +6 -224
        • 📄 ledger.ts +3 -53

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1b59543. Configure here.

Comment thread app/offscreen/hardware-wallets/ledger.ts
@metamaskbotv2

metamaskbotv2 Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor
Builds ready [0c8eabf]
⚡ Performance Benchmarks (Total: 🟢 7 pass · 🟡 8 warn · 🔴 0 fail)

Baseline (latest main): 71bd826 | Date: 10/14/58243 | Pipeline: 24577024445 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-browserify
loadNewAccount🟡 [Show logs]
confirmTx🟡 [Show logs]
bridgeUserActions🟡 [Show logs]

📈 Results compared to the previous 5 runs on main

  • bridgeUserActions/bridge_load_asset_picker: -45%
  • bridgeUserActions/total: -15%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/FCP: p75 2.4s
  • 🟡 confirmTx/FCP: p75 2.5s
  • 🟡 bridgeUserActions/FCP: p75 2.5s
Startup Benchmarks · Samples: 100
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/uiStartup: -26%
  • startupStandardHome/load: -16%
  • startupStandardHome/domContentLoaded: -17%
  • startupStandardHome/firstPaint: -17%
  • startupStandardHome/firstReactRender: -10%
  • startupStandardHome/initialActions: -33%
  • startupStandardHome/loadScripts: -20%
  • startupStandardHome/numNetworkReqs: -34%
  • startupStandardHome/uiStartup: -25%
  • startupStandardHome/load: -20%
  • startupStandardHome/domContentLoaded: -19%
  • startupStandardHome/backgroundConnect: -41%
  • startupStandardHome/firstReactRender: -27%
  • startupStandardHome/loadScripts: -20%
  • startupStandardHome/setupStore: -20%
  • startupStandardHome/numNetworkReqs: -44%
  • startupStandardHome/uiStartup: -12%
  • startupStandardHome/domInteractive: -55%
  • startupStandardHome/initialActions: -33%
  • startupStandardHome/numNetworkReqs: -34%
  • startupStandardHome/uiStartup: -13%
  • startupStandardHome/domInteractive: -36%
  • startupStandardHome/initialActions: +14%
  • startupStandardHome/setupStore: -50%
  • startupStandardHome/numNetworkReqs: -29%
User Journey Benchmarks · Samples: 5 · mock API
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟡 [Show logs]
solanaAssetDetails🟡 [Show logs]
importSrpHome🟡 [Show logs]
sendTransactions🟡 [Show logs]
swap🟡 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: -84%
  • onboardingImportWallet/metricsToWalletReadyScreen: -26%
  • onboardingImportWallet/doneButtonToHomeScreen: -77%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +27%
  • onboardingImportWallet/total: -43%
  • onboardingNewWallet/srpButtonToPwForm: -77%
  • onboardingNewWallet/skipBackupToMetricsScreen: -65%
  • onboardingNewWallet/doneButtonToAssetList: -28%
  • onboardingNewWallet/total: -28%
  • assetDetails/assetClickToPriceChart: -81%
  • assetDetails/total: -81%
  • solanaAssetDetails/assetClickToPriceChart: -71%
  • solanaAssetDetails/total: -71%
  • importSrpHome/loginToHomeScreen: -12%
  • importSrpHome/openAccountMenuAfterLogin: -56%
  • importSrpHome/homeAfterImportWithNewWallet: -68%
  • importSrpHome/total: -60%
  • sendTransactions/selectTokenToSendFormLoaded: -10%
  • sendTransactions/reviewTransactionToConfirmationPage: +37%
  • sendTransactions/total: +34%
  • swap/openSwapPageFromHome: -96%
  • swap/fetchAndDisplaySwapQuotes: +32%
  • swap/total: +11%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 assetDetails/FCP: p75 2.5s
  • 🟡 assetDetails/LCP: p75 2.5s
  • 🟡 solanaAssetDetails/FCP: p75 2.6s
  • 🟡 solanaAssetDetails/LCP: p75 2.6s
  • 🟡 importSrpHome/FCP: p75 2.6s
  • 🟡 sendTransactions/FCP: p75 2.5s
  • 🟡 swap/FCP: p75 2.5s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-browserify
dappPageLoad🟢 [Show logs]
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 191.62 KiB (3.12%)
  • ui: 9.24 KiB (0.11%)
  • common: -452.71 KiB (-3.37%)

@sonarqubecloud

Copy link
Copy Markdown

@metamaskbotv2

metamaskbotv2 Bot commented Apr 20, 2026

Copy link
Copy Markdown
Contributor
Builds ready [587c616]
⚡ Performance Benchmarks (Total: 🟢 7 pass · 🟡 8 warn · 🔴 0 fail)

Baseline (latest main): 71bd826 | Date: 10/14/58243 | Pipeline: 24656192702 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-browserify
loadNewAccount🟡 [Show logs]
confirmTx🟡 [Show logs]
bridgeUserActions🟡 [Show logs]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: -19%
  • loadNewAccount/total: -19%
  • bridgeUserActions/bridge_load_asset_picker: -59%
  • bridgeUserActions/total: -14%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/FCP: p75 2.6s
  • 🟡 confirmTx/FCP: p75 2.6s
  • 🟡 bridgeUserActions/INP: p75 208ms
  • 🟡 bridgeUserActions/FCP: p75 2.5s
Startup Benchmarks · Samples: 100
Benchmarkchrome-browserifychrome-webpackfirefox-browserifyfirefox-webpack
startupStandardHome🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]🟢 [Show logs]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/uiStartup: -19%
  • startupStandardHome/domInteractive: +16%
  • startupStandardHome/backgroundConnect: +13%
  • startupStandardHome/initialActions: -33%
  • startupStandardHome/loadScripts: -13%
  • startupStandardHome/setupStore: +14%
  • startupStandardHome/numNetworkReqs: -24%
  • startupStandardHome/uiStartup: -18%
  • startupStandardHome/load: -13%
  • startupStandardHome/domContentLoaded: -13%
  • startupStandardHome/firstPaint: +18%
  • startupStandardHome/backgroundConnect: -33%
  • startupStandardHome/firstReactRender: -27%
  • startupStandardHome/loadScripts: -13%
  • startupStandardHome/setupStore: -13%
  • startupStandardHome/numNetworkReqs: -44%
  • startupStandardHome/uiStartup: -15%
  • startupStandardHome/domInteractive: -59%
  • startupStandardHome/initialActions: -33%
  • startupStandardHome/numNetworkReqs: -34%
  • startupStandardHome/uiStartup: -11%
  • startupStandardHome/domInteractive: -24%
  • startupStandardHome/initialActions: -43%
  • startupStandardHome/setupStore: -57%
  • startupStandardHome/numNetworkReqs: -29%
User Journey Benchmarks · Samples: 5 · mock API
Benchmarkchrome-browserify
onboardingImportWallet🟢 [Show logs]
onboardingNewWallet🟢 [Show logs]
assetDetails🟡 [Show logs]
solanaAssetDetails🟡 [Show logs]
importSrpHome🟡 [Show logs]
sendTransactions🟡 [Show logs]
swap🟡 [Show logs]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: -84%
  • onboardingImportWallet/metricsToWalletReadyScreen: -17%
  • onboardingImportWallet/doneButtonToHomeScreen: -77%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: +27%
  • onboardingImportWallet/total: -44%
  • onboardingNewWallet/srpButtonToPwForm: -78%
  • onboardingNewWallet/skipBackupToMetricsScreen: -67%
  • onboardingNewWallet/doneButtonToAssetList: -26%
  • onboardingNewWallet/total: -27%
  • assetDetails/assetClickToPriceChart: -52%
  • assetDetails/total: -52%
  • solanaAssetDetails/assetClickToPriceChart: -71%
  • solanaAssetDetails/total: -71%
  • importSrpHome/loginToHomeScreen: -13%
  • importSrpHome/openAccountMenuAfterLogin: -79%
  • importSrpHome/homeAfterImportWithNewWallet: -67%
  • importSrpHome/total: -60%
  • sendTransactions/openSendPageFromHome: -23%
  • sendTransactions/selectTokenToSendFormLoaded: -15%
  • sendTransactions/reviewTransactionToConfirmationPage: +38%
  • sendTransactions/total: +35%
  • swap/openSwapPageFromHome: -96%
  • swap/fetchAndDisplaySwapQuotes: +32%
  • swap/total: +11%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 assetDetails/FCP: p75 2.5s
  • 🟡 solanaAssetDetails/FCP: p75 2.5s
  • 🟡 importSrpHome/FCP: p75 2.5s
  • 🟡 sendTransactions/FCP: p75 2.5s
  • 🟡 swap/FCP: p75 2.5s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-browserify
dappPageLoad🟢 [Show logs]
Bundle size diffs [🚀 Bundle size reduced!]
  • background: 180.27 KiB (2.93%)
  • ui: 5 Bytes (0%)
  • common: -180.18 KiB (-1.37%)

erc20: isERC20Tx,
nft: isNftTx,
});
const result = await app.signTransaction(hdPath, tx, null);

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.

Sorry, I saw your change directly to use signTransaction() which means ledger clear signing has completely disabled for all network which is not right in my opionion. previously Ledger team suggest us to just use try catch block to try clear signing first and then fallback to use transitional signing if clear sign not supported.
So i think using try catch block to try clear sign fist and then fall back to tranditional signing is better solution which means metamask can still take advantage of ledger clear signing for supported tokens and chains.

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.

@dawnseeker8 in my testing I did not notice any differences in what was displayed on the device between prod on this branch. That being said I did not complete all my testing and I do think NFTs might be effected. @montelaidev can confirm but I believe a try catch solution would result in the the user being prompted to sign the transaction that will fail and then be immediately prompted to sign another "Legacy" transaction.

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.

@montelaidev I did a little more digging and am now even more confused. The source code for clearSignTransaction already fallsback to a normal sign. If this is the case, why is this not working already then?

/**
   * Helper to get resolution and signature of a transaction in a single method
   *
   * @param path: the BIP32 path to sign the transaction on
   * @param rawTxHex: the raw ethereum transaction in hexadecimal to sign
   * @param resolutionConfig: configuration about what should be clear signed in the transaction
   * @param throwOnError: optional parameter to determine if a failing resolution of the transaction should throw an error or not
   * @example
   const tx = "e8018504e3b292008252089428ee52a8f3d6e5d15f8b131996950d7f296c7952872bd72a2487400080"; // raw tx to sign
   const result = eth.clearSignTransaction("44'/60'/0'/0/0", tx, { erc20: true, externalPlugins: true, nft: true});
   console.log(result);
   */
  async clearSignTransaction(
    path: string,
    rawTxHex: string,
    resolutionConfig: ResolutionConfig,
    throwOnError = false,
  ): Promise<{ r: string; s: string; v: string }> {
    const resolution = await ledgerService
      .resolveTransaction(rawTxHex, this.loadConfig, resolutionConfig)
      .catch(e => {
        console.warn(
          "an error occurred in resolveTransaction => fallback to blind signing: " + String(e),
        );

        if (throwOnError) {
          throw e;
        }
        return null;
      });

    return this.signTransaction(path, rawTxHex, resolution);
  }

@owencraston

Copy link
Copy Markdown
Contributor

@montelaidev lets not merge this yet until we have done extensive QA and I have given the sign off.

@github-actions github-actions Bot locked and limited conversation to collaborators May 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants