Aegis now integrates with the two dapp-wallet APIs the wider ecosystem
actually uses — MetaMask-style window.ethereum for Ethereum, Phantom-style
window.solana for Solana — plus BTC signet as a third Bitcoin network
alongside mainnet + testnet3.
- wallet-inject.js: adds a main-world bridge, installed via a one-shot
<script textContent=…> appended to <head> and immediately removed.
Electron's contextBridge shallow-copies args and strips methods, which
means BCH- and Tron-shaped params (plain data) work in the isolated
world but Solana's wallet-adapter dapps — which pass @solana/web3.js
Transaction objects and expect .serializeMessage()/.addSignature() to
fire on them — need code that lives in the same world as the dapp.
Bridge talks back to the isolated world via window.postMessage on a
namespaced envelope (aegisTag = "aegis-" + addonId), which forwards to
theseus.invoke. Same pattern MetaMask + Phantom use.
- window.ethereum (EIP-1193): request({method, params}), on(),
removeListener(), chainId, networkVersion, selectedAddress. Handles
eth_requestAccounts, eth_accounts, eth_chainId, net_version,
personal_sign, eth_sign, eth_sendTransaction, wallet_switchEthereumChain
(rejects with "use the Aegis picker"), wallet_addEthereumChain
(rejects, chains come from Settings), wallet_get/requestPermissions.
Every other eth_*/net_*/web3_* method passes through to the wallet's
configured RPC via a new eth.rpc handler. EIP-6963 announceProvider
event fires so wagmi / RainbowKit / any 6963-aware dapp discovers
Aegis alongside MetaMask instead of racing for window.ethereum.
- window.solana (wallet-adapter shape): connect(), disconnect(),
publicKey (with toString/toBase58/toBytes/equals — the PublicKey
interface dapps check), signMessage(u8) → {publicKey, signature: u8},
signTransaction(tx) → mutates + returns the same tx with the
signature added, signAndSendTransaction(tx) → returns {signature: txid},
signAllTransactions([tx]), request({method, params}). isPhantom flag
set true so dapps that gate on it pick us. on/off events for connect
/ disconnect / accountChanged.
- Handlers in index.js registerPageMessages: eth.requestAccounts,
eth.personalSign, eth.sendTransaction, eth.switchChain, eth.rpc,
sol.connect, sol.signMessage, sol.signAndSend. Every write path is
per-origin gated + goes through api.approvalModal with the wallet
label + network in the row list so the user always knows which
Aegis wallet is about to sign.
- Signet added to chain-btc.js — signet shares testnet3's address
format and SLIP-44 coin type (BIP-325 only changed consensus/signing),
so bitcoinjs-lib.networks.testnet handles derivation unchanged. Only
the electrum pool (aranguren + wakiyamap) + explorer (mempool.space
/signet) + faucet (signetfaucet.com) differ. Registered as
btc:signet in COINS with per-network coinType lookup.
Known limits (follow-ups in the same shape as existing chains):
- SOL signAndSendTransaction is single-signer only; dapps that combine
the wallet's sig with co-signer sigs need the wire assembled on the
dapp side.
- ETH eth_signTypedData_v4 (EIP-712) is not wired — the handler set
covers personal_sign only.
Aegis's Bitcoin adapter can now sign transactions from every BIP44/49/84/86
address it derives. Receive already worked on all four in the previous rev
— this closes the send side.
- BIP44 (legacy P2PKH, 1…): signAndBroadcast now fetches each spent UTXO's
parent transaction via blockchain.transaction.get(txid, false) and hands
the raw hex to PSBT as nonWitnessUtxo. Prev-tx calls fan out in parallel
with Promise.all so a multi-input legacy send doesn't serialize the wait.
- BIP86 (Taproot key-path, bc1p…): signInput now uses a tap-tweaked
signer — the internal ECPair, tweaked with sha256("TapTweak" ||
internalPubkey) via ECPair.tweak(). bitcoinjs-lib matches the tweaked
pubkey against the on-chain output key and signs with schnorr. The
input carries tapInternalKey so the PSBT layer knows it's a key-path
spend (no leaf script).
- The plan-time "not yet in this rev" refusal is gone. paymentFor()
returns send: "p2pkh" / "p2tr" for the two families; every path in
the picker signs today.
- Fee vsize model already covered p2pkh (148 vB per input) and p2tr
(58 vB per input) — unchanged.
- Verified in scratchpad/verify-btc-send.mjs: all four families
produce a fully-finalized wire tx (bitcoinjs-lib refuses to
finalize an invalid signature, so a valid extractTransaction()
result is proof the signing path is correct). Vsize per family:
BIP44 222 vB, BIP49 165 vB, BIP84 141 vB, BIP86 142 vB — all
match the input-count/vsize model in this file's fee estimator.
BTC now matches DGB's family selector: pick BIP44 (1…), BIP49 (3…),
BIP84 (bc1q…, default) or BIP86 Taproot (bc1p…) from Settings, on
mainnet or testnet3 (paths shift coin type 0 → 1 automatically).
- lib/chain-btc.js: paymentFor(purpose, node, network) returns the
right bitcoinjs-lib payment (p2pkh / p2sh(p2wpkh) / p2wpkh / p2tr)
keyed off the derivation path's purpose. WalletKeys.entry captures
the family, redeem script (BIP49) and internal x-only pubkey
(BIP86) alongside the standard script/address fields.
bitcoinjs.initEccLib(ecc) is called once at load so p2tr resolves.
- Registry: BTC + DGB address families are purpose-only now; a
helper (addressFamiliesFor / defaultAccountPathFor) computes the
concrete m/PURPOSE'/COIN'/0' per (chain, network) — coin type
{mainnet:0, testnet:1} for BTC, always 20 for DGB. chainMeta
expands the list so the panel doesn't need per-chain knowledge.
- Panel: #btcSettings block mirrors #dgbSettings (family select →
path input auto-fill → Apply). The family-select listener + the
fillFamilyPicker() helper are shared between DGB and BTC — the
DOM prefix is the only per-chain input.
- Send is wired for BIP84 (default) and BIP49 (adds redeemScript to
the PSBT input). BIP44 (needs nonWitnessUtxo prev-tx fetch) and
BIP86 (needs tap-tweaked signer) throw a clear "not yet in this
rev — sweep to BIP84" error so users hit it at plan time, not at
broadcast time. Receive works on all four families today.
- Verified all four families derive the canonical BIP44/49/84/86
spec test vectors for the standard abandon×11 mnemonic — see
scratchpad/verify-btc-families.mjs. Byte-identical to the BIPs.
Seven coins across twelve networks now — BTC joins the shipping roster.
- lib/chain-btc.js: BIP84 native SegWit — m/84'/0'/0'/0/x → bc1q…
(mainnet), m/84'/1'/0'/0/x → tb1q… (testnet3). Reuses the exact same
stack the DGB adapter already pulls in: bitcoinjs-lib for network
params + payments.p2wpkh + PSBT, bip32 for HD derivation, ecpair for
the Signer interface, ecc (@bitcoinerlab/secp256k1) for message-sign
recoverable sigs. No new npm deps.
- Backend: same lib/electrum.js Aegis uses for BCH and DGB — plugged
into a public Bitcoin ElectrumX pool (blockstream.info, lu.ke,
grey.pw) for mainnet and aranguren.org / blockstream.info:993 for
testnet3. Send flow: PSBT build + per-input signInput +
finalizeAllInputs + broadcast. BIP-137 recoverable message signing.
- Registered as btc:mainnet + btc:testnet in COINS with the orange
Bitcoin disc SVG logo. Mount case mirrors DGB (accountPath honored,
so switching to m/44'/0'/0' or m/49'/0'/0' via the setAccountPath
message gives legacy 1… or wrapped-segwit 3… — same one-line UI plumb
as the DGB address-family selector, deferred to a follow-up).
- Panel: sat as the small-unit label, bitcoin: BIP21 QR payload,
chain-aware send placeholder ("bc1q…" mainnet / "tb1q…" testnet).
- Verified: BIP84 spec test vector — abandon×11 mnemonic derives
bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu at m/84'/0'/0'/0/0
(byte-identical to the vector in the BIP text). Testnet variant
produces tb1q6rz28mcfaxtmd6v789l9rrlrusdprr9pqcpvkl at m/84'/1'/0'/0/0
(cross-checkable on iancoleman.io/bip39 with coin BTC Testnet).