fix(ledger): fall back to blind signing when clear-sign resolution fails#41948
Conversation
- Use ensureApp() for getAppConfiguration; avoid undefined arbitraryDataEnabled - Reset transport on partial makeApp failure and transport-without-ethApp state - Log full app configuration when METAMASK_DEBUG (UI + offscreen) - Document investigation notes and add getAppConfiguration unit test
…ution fails Ledger's `clearSignTransaction` requires a plugin / contract resolver for the target chain and contract. On networks / contracts without one (e.g. Monad gas-sponsored swaps routed through the delegation contract), the call throws and the user sees a generic "Something went wrong" / "blind signing is not enabled" error even though blind signing is enabled on the device. Wrap `clearSignTransaction` in a try/catch and, on non-user-rejection failures, fall back to `signTransaction(hdPath, tx, null)` so the device performs a raw blind sign. Clear signing is still attempted first, so ERC20 / NFT flows on supported chains are unaffected. User rejections on device (status 0x6985 / CONDITIONS_OF_USE_NOT_SATISFIED) are detected and re-thrown so we never silently re-prompt after an explicit rejection. Fixes #41602. Made-with: Cursor
…b.com/MetaMask/metamask-extension into fix/41602-ledger-monad-double-confirm
…ests The two fallback tests added for GH 41602 were triggering unmocked console.warn / console.error, which polluted the global unit-test console baseline. Align them with the convention used by the rest of this file: spy on the specific channel with a local mockImplementation, assert the expected log shape, and restore at the end. Also tightens the rejection-path assertion to verify that statusCode 0x6985 survives serialization back to the UI rather than only checking success: false. Made-with: Cursor
…g logging - Removed unnecessary debug logging and related imports from the Ledger offscreen handler. - Simplified the `makeApp` method to ensure transport is only opened when necessary. - Updated `getAppConfiguration` to throw an error if no transport is available, improving error handling. - Cleaned up comments and documentation for clarity. This refactor enhances code readability and maintainability while addressing issues related to transport management and error handling.
|
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. |
✨ Files requiring CODEOWNER review ✨🔑 @MetaMask/accounts-engineers (2 files, +129 -10)
|
|
Builds ready [ff1ed6a]
⚡ Performance Benchmarks (Total: 🟢 7 pass · 🟡 8 warn · 🔴 0 fail)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
|
Close as duplicated to #41899 |
|
Reopen for QA to test before we make final decisions on #41899 |
|
Tested on the latest build with Mon, Sei and other networks that don't support gas sponsorship. Tested swap and bridge with 1 and 2 approvals, with and without dapp, works as expected. No regression issues have been found. Adding "qa passed" label. |
|
Looks good. Did not test |



Description
Ledger hardware wallet users on Monad (and other chains / contracts without a matching Ledger plugin) were hitting a misleading "blind signing is not enabled" error when trying to complete gas-sponsored swaps, even though blind signing was enabled on the device.
Root cause:
LedgerOffscreenHandler.signTransactioncalledapp.clearSignTransaction, which requires a Ledger plugin / contract resolver for the target chain+contract. For Monad's delegation / swap contracts no plugin exists, soclearSignTransactionthrows. That throw was surfaced to the UI as a generic hardware-wallet error that the confirmation flow rendered as the "blind signing" copy.Fix: Wrap
clearSignTransactionin atry/catch. On non-user-rejection failures, fall back tosignTransaction(hdPath, tx, null)so the device performs a raw blind sign (which works because the user already has blind signing enabled). Clear signing is still attempted first, so ERC20 / NFT flows on supported chains are unchanged.User rejections on device (status
0x6985/CONDITIONS_OF_USE_NOT_SATISFIED) are detected and re-thrown instead of triggering a fallback, so we never silently re-prompt after an explicit on-device rejection.The change is scoped entirely to the offscreen Ledger handler — no UI, keyring, or controller changes were needed.
Changelog
CHANGELOG entry: Fixed a bug where Ledger users could not complete swaps on networks without Ledger plugin support (e.g. Monad), which previously surfaced as a misleading "blind signing is not enabled" error.
Related issues
Fixes: #41602
Manual testing steps
Screenshots/Recordings
Before
Users received a "Something went wrong — blind signing is not enabled" modal on the second confirmation of the Monad swap, even with blind signing enabled on device.
After
Both confirmations complete; Ledger shows the blind-sign payload on unsupported contracts and the clear-signed payload on supported contracts.
Pre-merge author checklist
app/offscreen/hardware-wallets/ledger.test.tsfor both the fallback path and the on-device rejection path)Pre-merge reviewer checklist
Made with Cursor
Note
Medium Risk
Changes the Ledger transaction-signing flow to retry with blind signing when clear-sign resolution fails, which could affect how transactions are presented/signed on-device for some contracts/chains. Scoped to the offscreen Ledger handler and covered by new unit tests, but it touches a critical signing path.
Overview
Improves Ledger
signTransactionreliability on chains/contracts without Ledger plugin support by catchingclearSignTransactionfailures and retrying viasignTransaction(hdPath, tx, null)(blind sign), with a warning log.Adds explicit detection of on-device user rejection (
0x6985/CONDITIONS_OF_USE_NOT_SATISFIED) to avoid the fallback and instead surface the rejection error. Updatesledger.test.tsto covergetAppConfiguration, the new fallback behavior, and the no-fallback rejection case.Reviewed by Cursor Bugbot for commit 78e9e99. Bugbot is set up for automated code reviews on this repo. Configure here.