feat: enable account address resolution to support dApps connectivity#564
feat: enable account address resolution to support dApps connectivity#564baptiste-marchand wants to merge 16 commits intomainfrom
Conversation
a041ba7 to
e0ea92e
Compare
There was a problem hiding this comment.
Bug: Account Field Mandate Breaks Tests
Existing unit tests create request params without the required account field, but the request struct changes now mandate this field. Tests for signPsbt, computeFee, fillPsbt, broadcastPsbt, sendTransfer, getUtxo, and signMessage will fail during assertion validation because their params lack the required account: { address: string } object.
packages/snap/src/handlers/KeyringRequestHandler.test.ts#L64-L431
105bfe9 to
62f251f
Compare
…rmation of sendFlow()
| default: { | ||
| throw new Error('Unsupported method'); | ||
| } | ||
| } |
There was a problem hiding this comment.
Redundant switch in resolveAccountAddress method
Low Severity
The switch statement in resolveAccountAddress is entirely redundant — every BtcMethod case performs the identical operation: extracting request.params.account.address. Since every variant of BtcWalletRequestStruct includes account: WalletAccountStruct in its params, TypeScript allows accessing request.params.account.address directly on the union type without narrowing. The whole switch (and unreachable default branch) can be replaced with a single line: const addressToValidate = request.params.account.address.
|
|
||
| const result = await response; | ||
|
|
||
| expect(result).toRespondWith({ |
There was a problem hiding this comment.
Promise used as response in test
High Severity
In the sendTransfer integration test, snap.onKeyringRequest(...) is no longer awaited, but the code calls response.getInterface() as if response were the resolved response object. If onKeyringRequest returns a Promise, this becomes a runtime failure and breaks the test flow/CI.
mikesposito
left a comment
There was a problem hiding this comment.
I left a small suggestion on the resolveAccountAddress function. Besides this, it seems to me that we are addressing several different things in this PR; they may be all related to dapp connectivity, but since we are adding 1790 lines in the same PR it seems warranted to split the different features and bugfixes we are addressing in separate PRs (or a PR train)
| resolveAccountAddress( | ||
| keyringAccounts: KeyringAccount[], | ||
| scope: CaipChainId, | ||
| request: Infer<typeof BtcWalletRequestStruct>, | ||
| ): CaipAccountId { |
There was a problem hiding this comment.
I'm not sure that we are expanding KeyringRequestHandler the way it was initially intended: it seems to me that this class is meant to be used via the route public function, that should handle the request and return a KeyringResponse via the this.#toKeyringResponse(..) function, at least this is how other request handlers are implemented on this class.
That being said, I don't think we have a pattern of always routing a request to KeyringRequestHandler from KeyringHandler. So an alternative approach would be to remove the KeyringRequestHandler.resolveAccountAddress function and move its logic to KeyringHandler.resolveAccountAddress directly, as we aren't using any class property or method from KeyringRequestHandler in that function.
| export class KeyringRequestHandler { | ||
| readonly #accountsUseCases: AccountUseCases; | ||
|
|
||
| constructor(accounts: AccountUseCases) { |
There was a problem hiding this comment.
Struct Allows Multiple Recipients But Logic Rejects Them
Medium Severity
SendTransferRequest uses array() for recipients, which permits zero or many recipients to pass struct validation. However, AccountUseCases.sendTransfer now throws a ValidationError when there are zero or more than one recipient. A caller sending multiple recipients (a perfectly valid request per the struct schema) will silently pass the API boundary check but then receive a runtime ValidationError from the use case layer — a different error type and message than what struct validation failures produce — making the API contract misleading and error handling inconsistent.
Additional Locations (1)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| psbt, | ||
| recipient, | ||
| origin, | ||
| ); |
There was a problem hiding this comment.
PSBT mutated by confirmation then signed
High Severity
In sendTransfer, the same psbt object is passed to insertSendTransfer (which calls psbt.toString() internally) and then immediately signed via account.sign(psbt). The developer explicitly acknowledged this mutation problem in KeyringRequestHandler.ts#signPsbt, where the comment at line 133 states "Creates a fresh PSBT from the original base64 because the original PSBT is mutated by the confirmation repository" and a fresh PSBT is re-parsed from the original base64. No equivalent fix is applied in sendTransfer, so the signing step operates on a potentially corrupted PSBT object.
Additional Locations (1)
| SignMessageKeyringRequestStruct, | ||
| ]); | ||
|
|
||
| export type BtcWalletRequest = Infer<typeof BtcWalletRequestStruct>; |
There was a problem hiding this comment.
@mikesposito This PR has been refactored and split into 3 PRs: |


Implements account address resolution for Bitcoin requests, allowing MetaMask to route non-EVM dapp signing requests correctly. This is a requirement for Bitcoin dApps connectivity
resolveAccountAddressmethod inKeyringHandlerandKeyringRequestHandlerto determine the account address for signing requests.Note
Medium Risk
Adds a new
keyring_resolveAccountAddressrouting path and expands request validation to require anaccount.address, which could break dApps/integrations that still send the old param shape. It also inserts new user-confirmation steps before signing PSBTs and sending transfers, changing transaction flow behavior and test expectations.Overview
Enables MetaMask to route incoming Bitcoin dapp requests by adding
KeyringHandler.resolveAccountAddress, which validates the request (BtcWalletRequestStruct) and maps the providedaccount.address+scopeto a CAIP-10 address (or returnsnull).Updates Bitcoin keyring request validation so signing/PSBT-related methods (
SignPsbt,FillPsbt,ComputeFee,BroadcastPsbt,GetUtxo,SendTransfer,SignMessage) now require anaccount: { address }parameter, and adjusts integration/unit tests accordingly (including a new “missing account” failure).Introduces new confirmation UX and repository wiring: a
SignPsbtConfirmationView(with new i18n strings) and newConfirmationRepositorymethods (insertSignPsbt,insertSendTransfer).KeyringRequestHandlernow shows a confirmation beforesignPsbt, andAccountUseCases.sendTransferis constrained to exactly one recipient, builds a PSBT with frozen UTXOs/fee rate, and shows a confirmation before signing/broadcasting; confirmation contexts now pass throughoriginand optional fiat exchange-rate display.Written by Cursor Bugbot for commit 68881d0. This will update automatically on new commits. Configure here.