Skip to content

feat(bitcoin): add headless + browser wallet SDK#8

Open
jinglescode wants to merge 2 commits into
mainfrom
feature/bitcoin-wallet-sdk
Open

feat(bitcoin): add headless + browser wallet SDK#8
jinglescode wants to merge 2 commits into
mainfrom
feature/bitcoin-wallet-sdk

Conversation

@jinglescode
Copy link
Copy Markdown
Member

Summary

  • BitcoinHeadlessWallet — in-process signer: BIP-39 → BIP-32 root → BIP-84 (P2WPKH payment) + BIP-86 (P2TR ordinals). Implements all 7 IBitcoinWallet methods (getNetwork, getAddresses, getAccounts, getBalance, signMessage, signTransfer, signPsbt).
  • BitcoinBrowserWallet — registry + Xverse adapter speaking Sats Connect over provider.request(method, params). Handles both the sats-connect-core-normalised {status, result} envelope and the raw JSON-RPC 2.0 {jsonrpc, result} envelope so it works against any Xverse build.
  • 93 bitcoin tests across 5 suites — BIP-84/86 mainnet vectors, recoverable-signature round-trip verification (recover pubkey → derive P2WPKH → match address), dust-aware change handling, Taproot tweaked-key signing (BIP-341), Xverse wire-format compatibility, address-type validation.

Crypto correctness

  • signMessage produces a 65-byte ECDSA recoverable signature with the "Bitcoin Signed Message:\n" magic prefix and computed recovery id — verifiable by Electrum / Sparrow / bitcoinjs-message.
  • signTransfer opts into BIP-125 RBF (sequence 0xfffffffd), uses realistic P2WPKH vbyte math (overhead 11, input 68, output 31), and absorbs sub-dust change (< 546 sats) into the miner fee instead of producing a non-standard output.
  • signPsbt for Taproot uses BIP-86 single-key spend with the tweaked private key; signer's publicKey exposes the tweaked output key (matching bitcoinjs-lib's getTaprootHashesForSig check) and tapInternalKey is set on the PSBT input so finalisers can round-trip.

Dependencies (3 added)

  • bitcoinjs-lib ^6.1.7
  • bip39 ^3.1.0
  • ecpair ^2.1.0

(reuses existing bip32 ^5.0.0 + tiny-secp256k1 ^2.2.4)

Test plan

  • npx tsc --noEmit — no errors in src/ or test/
  • npx jest test/bitcoin/ — 5 suites, 93 tests pass
  • npx jest (full repo) — 16 suites, 171 tests pass
  • npx tsup — ESM 187 KB + CJS 173 KB + dts 39 KB builds clean
  • Manual: connect Xverse on testnet4, fetch balance, send small transfer
  • Manual: sign+broadcast PSBT from a third-party builder against testnet4

🤖 Generated with Claude Code

jinglescode and others added 2 commits May 16, 2026 09:38
Implement IBitcoinWallet across two surfaces:

- BitcoinHeadlessWallet: in-process signer (BIP-39 → BIP-32 → BIP-84/86).
  - getNetwork, getAddresses, getAccounts, getBalance
  - signMessage (ECDSA with magic-prefix + 65-byte recoverable sig)
  - signTransfer (RBF opt-in, dust-aware change, P2WPKH vbyte estimates)
  - signPsbt (P2WPKH ECDSA + P2TR BIP-86 key-path Schnorr w/ tweaked key)
- BitcoinBrowserWallet: registry + Xverse adapter speaking Sats Connect
  with dual-envelope support (sats-connect-core normalised + raw JSON-RPC 2.0).

Test coverage: 93 bitcoin tests across 5 suites — BIP-84/86 vectors,
recoverable-sig verification, dust-handling, Taproot tweaked-key signing,
Xverse wire-format compatibility, and address-type validation.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Mirrors existing signMessage (BIP-137, 65-byte compact recoverable
ECDSA, base64). Cross-type acceptance: recovered pubkey matched
against P2PKH / P2SH-P2WPKH / P2WPKH / P2TR (BIP-86). Adds
IBitcoinWallet.verifyMessage, instance methods on Headless + Browser
wallets, and local trustless verify in the Xverse adapter (throws
clear "not yet supported" for non-65-byte BIP-322 sigs).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
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