Skip to content

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

Merged
jinglescode merged 4 commits into
mainfrom
feature/bitcoin-wallet-sdk
Jun 9, 2026
Merged

feat(bitcoin): add headless + browser wallet SDK#8
jinglescode merged 4 commits into
mainfrom
feature/bitcoin-wallet-sdk

Conversation

@jinglescode

@jinglescode jinglescode commented May 16, 2026

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 4 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
@Temasar1

Temasar1 commented Jun 8, 2026

Copy link
Copy Markdown
Member

feat(bitcoin): harden wallet SDK — provider interfaces, address APIs, verification & cleanup

Provider interfaces (IBitcoinFetcher / IBitcoinSubmitter)

  • Added fetchUTxOs(txid, vout?) to IBitcoinFetcher — mirrors Cardano's IFetcher.fetchUTxOs(hash, index?), allowing callers to look up unspent outputs by transaction hash + optional output index. Implemented in both providers using Esplora /tx/{txid} + /tx/{txid}/outspends.

IBitcoinWallet interface & implementations

  • Added 5 address/xpub methods to IBitcoinWallet with clear UnsupportedOperation stubs in BitcoinBrowserWallet and XverseAdapter (browser extension wallets don't hold keys):
    • getAddressesByPurpose(purpose) — all derived addresses for a purpose
    • findManagedAddress(address) — reverse lookup of a managed address
    • getAccountXpub(account?) — BIP-84 account xpub (P2WPKH)
    • getTaprootXpub(account?) — BIP-86 account xpub (P2TR)
    • getAccountZpub(account?) — zpub encoding of the BIP-84 xpub

BitcoinHeadlessWallet

  • Added fetchUTXOs(purposes?) and getTransactionHistory(options?) for on-chain data access.
  • Wired all new IBitcoinWallet methods through to BitcoinAddressManager.
  • Updated all internal provider call sites to the renamed method names.
  • Removed stray scratch code left at end-of-file that caused Jest suite parse errors.

XverseAdapter

  • Added normalizePurpose guard — purpose values from the Xverse response are now validated against the AddressPurpose enum. Unknown values are filtered out silently rather than blindly cast.

Tests

  • All 198 tests pass (17 suites).
  • Updated verifyMessage assertions across verify-message.test.ts, xverse-adapter.test.ts, and bitcoin-browser-wallet.test.ts to assert on result.valid / result.reason.
  • Updated bitcoin-headless-wallet.test.ts mock to match renamed provider methods.
  • Added/expanded coverage for XverseAdapter and BitcoinBrowserWallet.

submitted with transaction to verify with bitcoin and headless wallet
https://mempool.space/testnet4/tx/2005d6691167973c76531820eb5867eaccdd494723c2829673ffeb2f3a00f856

@jinglescode jinglescode merged commit c224db6 into main Jun 9, 2026
@jinglescode jinglescode deleted the feature/bitcoin-wallet-sdk branch June 9, 2026 01:25
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.

2 participants