Skip to content

Add wallet Sign Message flow for CEX ownership verification#6065

Open
j0ntz wants to merge 4 commits into
developfrom
jon/sign-message-cex
Open

Add wallet Sign Message flow for CEX ownership verification#6065
j0ntz wants to merge 4 commits into
developfrom
jon/sign-message-cex

Conversation

@j0ntz

@j0ntz j0ntz commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

Description

Adds an in-app Sign Message flow so a user can prove ownership of a self-hosted wallet by signing an exchange-provided message. This addresses EU Travel Rule / TFR withdrawal checks where CEXs/CASPs (Kraken, Coinbase, Bitvavo, Bitpanda, OKX, Bringin, NiceHash, etc.) require a signed message from the destination wallet instead of a manual ID/selfie proof.

BTC-first, per the task scope.

What it does

  • Adds a Sign Message option to the wallet list menu, restricted to Bitcoin-family (UTXO) wallets whose plugin implements message signing.
  • Opens a new SignMessageScene that shows the wallet's receive address (copyable, so the user can hand it to the exchange), a multiline message input, and a Sign Message button.
  • Signs with wallet.signMessage(message, { otherParams: { publicAddress } }) and displays the resulting base64 signature in a copyable card.
  • Includes a short phishing-safety note ("only sign messages from a service you trust; a signature never reveals your private keys").

Implementation notes

  • signMessage is used rather than signBytes because for Bitcoin signBytes base64-re-encodes the bytes before signing, which would produce a signature over the wrong data. signMessage signs the literal UTF-8 message the exchange verifies (same pattern already used by bityProvider and EdgeProviderServer).
  • The signing address is one the wallet owns (from wallet.getAddresses), because the UTXO plugin requires the address to be in its data layer.

Files

  • SignMessageScene.tsx (new scene)
  • routerTypes.tsx, Main.tsx (route + scene registration)
  • WalletListMenuActions.tsx, WalletListMenuModal.tsx (menu entry, UTXO-scoped)
  • en_US.ts (strings)

Asana: https://app.asana.com/0/1215088146871429/1209296431612665

Testing

Verified end-to-end on the iOS simulator (edge-funds, My Bitcoin wallet): opened the wallet menu, tapped Sign Message, entered a message, and confirmed a valid base64 signature was produced and displayed. Proof screenshots attached below.


Note

Medium Risk
Touches wallet signing and key-derived signatures for user-supplied messages; logic is scoped to owned addresses and includes BIP-137 edge-case handling, but phishing/mis-signing risk remains user-dependent.

Overview
Adds Sign Message to the wallet list menu for Bitcoin-family UTXO wallets, opening a new scene where users paste an exchange message, choose a signing address (default receive address or a specific owned address), and copy the resulting signature.

On SegWit chains (Bitcoin, Litecoin, DigiByte), users can pick Standard (Electrum) or BIP-137; new bitcoinMessageSignature helpers classify addresses, remap signature header bytes for native/nested SegWit, and block Taproot/P2WSH when BIP-137 is selected. Signing uses wallet.signMessage with publicAddress in otherParams, with UX guards (stale signature clearing, address-not-owned errors, safety copy).

Navigation, menu actions, locales, and unit tests for the BIP-137 utilities are included.

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

@j0ntz

j0ntz commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (iOS sim)

agent proof 1209296431612665 01 wallet menu sign message

agent proof 1209296431612665 01 wallet menu sign message

agent proof 1209296431612665 02 sign message scene

agent proof 1209296431612665 02 sign message scene

agent proof 1209296431612665 03 signature success

agent proof 1209296431612665 03 signature success

Captured by the agent's in-app test run (build-and-test).

Comment thread src/components/scenes/SignMessageScene.tsx
Comment thread src/components/scenes/SignMessageScene.tsx
Comment thread src/locales/en_US.ts Outdated
@j0ntz j0ntz force-pushed the jon/sign-message-cex branch from 97138ed to 7624da7 Compare July 1, 2026 22:18
@j0ntz

j0ntz commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (after bugbot fixes)

agent proof 1209296431612665 04 segwit signature success

agent proof 1209296431612665 04 segwit signature success

agent proof 1209296431612665 05 signature cleared on edit

agent proof 1209296431612665 05 signature cleared on edit

Captured by the agent's in-app test run (build-and-test).

Comment thread src/components/scenes/SignMessageScene.tsx
@j0ntz j0ntz force-pushed the jon/sign-message-cex branch from 7624da7 to d59ad8d Compare July 1, 2026 22:23
@j0ntz

j0ntz commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (followup: user-selectable signing address, Ravencoin UTXO wallet on iOS sim)

agent proof 1209296431612665 01 signmsg default address

agent proof 1209296431612665 01 signmsg default address

agent proof 1209296431612665 02 signature default address

agent proof 1209296431612665 02 signature default address

agent proof 1209296431612665 03 address edited signature cleared

agent proof 1209296431612665 03 address edited signature cleared

agent proof 1209296431612665 04 signature after reset

agent proof 1209296431612665 04 signature after reset

Captured by the agent's in-app test run (build-and-test).

@j0ntz

j0ntz commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence: BIP-137 signature format

agent proof 1209296431612665 01 format selector default

agent proof 1209296431612665 01 format selector default

agent proof 1209296431612665 02 standard signature

agent proof 1209296431612665 02 standard signature

agent proof 1209296431612665 03 bip137 signature

agent proof 1209296431612665 03 bip137 signature

agent proof 1209296431612665 04 non segwit hidden

agent proof 1209296431612665 04 non segwit hidden

Captured by the agent's in-app test run (build-and-test).

Comment thread src/util/bitcoinMessageSignature.ts Outdated
Comment thread src/components/scenes/SignMessageScene.tsx
@j0ntz j0ntz force-pushed the jon/sign-message-cex branch from bb08a13 to 8870e5a Compare July 9, 2026 22:36

@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 using default effort and found 1 potential issue.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 8870e5a. Configure here.

Comment thread src/util/bitcoinMessageSignature.ts
@j0ntz

j0ntz commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (after Bugbot fixes): BIP-137 on HEAD + Taproot guard

agent proof 1209296431612665 01 format selector default

agent proof 1209296431612665 01 format selector default

agent proof 1209296431612665 03 bip137 signature

agent proof 1209296431612665 03 bip137 signature

agent proof 1209296431612665 04 non segwit hidden

agent proof 1209296431612665 04 non segwit hidden

agent proof 1209296431612665 05 taproot rejected

agent proof 1209296431612665 05 taproot rejected

Captured by the agent's in-app test run (build-and-test).

@j0ntz j0ntz force-pushed the jon/sign-message-cex branch from 8870e5a to 4b7a06e Compare July 9, 2026 22:44
j0ntz added 4 commits July 9, 2026 15:49
Adds a Sign Message option to the wallet list menu for Bitcoin-family
(UTXO) wallets. The new scene shows the wallet's receive address, lets
the user paste an exchange-provided message, signs it with the wallet
key, and returns a copyable signature. This lets users prove control of
a self-hosted wallet for CEX/CASP withdrawal checks (EU Travel Rule)
without manual ID/selfie verification.
Add signMessageInput and signMessageButton testIDs to the Sign Message
scene so UI automation can drive the message field and sign action by a
stable selector.
The Sign Message scene now shows the wallet's receive address in an editable
field instead of a read-only row. Exchanges typically ask a user to prove
control of the specific address they already provided (often a previously-used
one), so the user can replace the default with that address. The wallet must
control whichever address is entered; the plugin signs with the key derived
from that address's stored derivation path and rejects any address it does not
own, surfaced as a clear error. Editing the address clears any prior signature,
and a Use default address link restores the auto-detected receive address.
Let users on SegWit chains (Bitcoin, Litecoin, DigiByte) choose between the
Standard (Electrum) and BIP-137 signature formats. BIP-137 re-encodes the
signature header byte by address script type (native SegWit 39-42, nested
SegWit 35-38) so strict external verifiers recognize the address type. The
option is hidden on non-SegWit UTXO chains, and legacy addresses are never
remapped.
@j0ntz j0ntz force-pushed the jon/sign-message-cex branch from 4b7a06e to 752e288 Compare July 9, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant