feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
// Imported BCH wallet — single-address, key material lives in Theseus's
// wallet-imports.enc (design §3.2). This adapter mirrors chain-bch.js's
// public shape (snapshot, refresh, plan, signAndBroadcast, dispose) but
// does NOT go through vault.derive + HKDF: derivation is direct from the
// seed+path or WIF that the user imported.
//
// M.1a scope: read-only (balance + history over Electrum). planSend/send
// throw with a clear message until M.1b lands the sign path.
module . exports = function makeImportedBchAdapter ( { sha256 , ripemd160 , cashaddr , electrum , WebSocket , tx } ) {
// Same electrum scripthash convention chain-bch uses: sha256(script), byte-
// reversed, hex. P2PKH-only for imports today — that's what every entry in
// Deviant's keystore is.
const toHex = ( b ) => Array . from ( b , ( x ) => x . toString ( 16 ) . padStart ( 2 , "0" ) ) . join ( "" ) ;
const p2pkhScript = ( h160 ) => Uint8Array . from ( [ 0x76 , 0xa9 , 0x14 , ... h160 , 0x88 , 0xac ] ) ;
const scripthashOf = ( script ) => toHex ( sha256 ( script ) . slice ( ) . reverse ( ) ) ;
const hash160 = ( b ) => ripemd160 ( sha256 ( b ) ) ;
const IMPORTED _BCH _NETWORKS = {
mainnet : {
id : "mainnet" , label : "Mainnet" , prefix : "bitcoincash" ,
explorerTx : "https://blockchair.com/bitcoin-cash/transaction/" ,
explorerAddr : "https://blockchair.com/bitcoin-cash/address/" ,
defaultServers : [
"wss://bch.imaginary.cash:50004" ,
"wss://cashnode.bch.ninja:50004" ,
"wss://electroncash.dk:50004" ,
"wss://fulcrum.jettscythe.xyz:50004" ,
] ,
} ,
chipnet : {
id : "chipnet" , label : "Chipnet testnet" , prefix : "bchtest" ,
explorerTx : "https://chipnet.imaginary.cash/tx/" ,
explorerAddr : "https://chipnet.imaginary.cash/address/" ,
defaultServers : [
"wss://chipnet.imaginary.cash:50004" ,
"wss://chipnet.bch.ninja:50004" ,
] ,
faucet : "https://tbch.googol.cash/" ,
} ,
} ;
// Decode a cashaddr → 20-byte hash160 payload. We stored cashaddr at import
// time and use it here to compute the scripthash for Electrum without ever
// asking main for the signer material — that only happens at sign time.
function h160OfCashaddr ( addr ) {
const clean = String ( addr || "" ) . replace ( /^bitcoincash:|^bchtest:/ , "" ) ;
const { type , hash } = cashaddr . decode ( addr . includes ( ":" ) ? addr : "bitcoincash:" + clean ) ;
if ( type !== 0 ) throw new Error ( ` imported wallet must be P2PKH (got type ${ type } ) ` ) ;
return hash ;
}
class ImportedBchWallet {
constructor ( { walletId , storage , log = ( ) => { } , onChange = ( ) => { } , network = "mainnet" , cashaddr : address , servers } = { } ) {
const net = IMPORTED _BCH _NETWORKS [ network ] ;
if ( ! net ) throw new Error ( ` chain-bch-imported: unknown network ${ network } ` ) ;
if ( ! address ) throw new Error ( "chain-bch-imported: cashaddr required" ) ;
this . walletId = walletId ;
this . chain = "bch" ;
this . network = net . id ;
this . _net = net ;
this . log = log ;
this . onChange = onChange ;
this . _address = address ;
this . _h160 = h160OfCashaddr ( address ) ;
this . _script = p2pkhScript ( this . _h160 ) ;
this . _scripthash = scripthashOf ( this . _script ) ;
this . _scriptHex = toHex ( this . _script ) ;
this . _servers = Array . isArray ( servers ) && servers . length ? servers : net . defaultServers . slice ( ) ;
this . _client = new electrum . Client ( this . _servers ) ;
this . _client . onServer = ( ) => this . _emit ( ) ;
this . _state = {
balance : { confirmed : 0 , unconfirmed : 0 } ,
history : [ ] ,
height : 0 ,
scanning : false ,
error : null ,
} ;
}
setServers ( list ) {
this . _servers = Array . isArray ( list ) && list . length ? list : this . _net . defaultServers . slice ( ) ;
this . _client . setServers ( this . _servers ) ;
}
2026-09-14 02:30:51 +02:00
schedulePoll ( ms ) {
clearTimeout ( this . _pollTimer ) ;
this . _pollTimer = setTimeout ( ( ) => { this . refresh ( false ) . catch ( ( ) => { } ) ; this . schedulePoll ( ms ) ; } , ms ) ;
}
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
_emit ( ) { try { this . onChange ( ) ; } catch { } }
snapshot ( ) {
return {
chain : "bch" ,
network : this . _net . id ,
ticker : "BCH" ,
decimals : 8 ,
address : this . _address ,
addressIndex : 0 ,
addressPath : null ,
balance : this . _state . balance ,
height : this . _state . height ,
history : this . _state . history ,
scanning : this . _state . scanning ,
error : this . _state . error ,
server : this . _client . url || null ,
servers : this . _servers ,
imported : true ,
explorerTx : this . _net . explorerTx ,
explorerAddr : this . _net . explorerAddr ,
faucet : this . _net . faucet ,
} ;
}
async refresh ( full ) {
this . _state . scanning = true ; this . _emit ( ) ;
try {
// Balance for this single scripthash.
const bal = await this . _client . request ( "blockchain.scripthash.get_balance" , [ this . _scripthash ] ) ;
this . _state . balance = { confirmed : Number ( bal ? . confirmed || 0 ) , unconfirmed : Number ( bal ? . unconfirmed || 0 ) } ;
if ( full ) {
const hist = await this . _client . request ( "blockchain.scripthash.get_history" , [ this . _scripthash ] ) ;
this . _state . history = ( hist || [ ] ) . slice ( - 50 ) . map ( ( h ) => ( {
txid : h . tx _hash , time : 0 , delta : 0 , confirmations : h . height > 0 ? 1 : 0 ,
} ) ) ;
}
this . _state . error = null ;
} catch ( e ) {
this . _state . error = e ? . message || String ( e ) ;
} finally {
this . _state . scanning = false ;
this . _emit ( ) ;
}
}
nextAddress ( ) { return { address : this . _address , index : 0 } ; }
current ( ) { return { address : this . _address , index : 0 , branch : 0 , path : null , h160 : this . _h160 , script : this . _script , scripthash : this . _scripthash , scriptHex : this . _scriptHex } ; }
plan ( ) { throw new Error ( "Imported wallets are read-only in this build. Spending support ships in the next Aegis update." ) ; }
signAndBroadcast ( ) { throw new Error ( "Imported wallets are read-only in this build." ) ; }
signMessage ( ) { throw new Error ( "Imported wallets are read-only in this build." ) ; }
recovery ( ) { return { accountPath : null , xpub : null , xprv : null , note : "Recovery lives in the source of the import (Deviant keystore or wherever you got the seed/WIF from)." } ; }
2026-09-14 02:30:51 +02:00
dispose ( ) { clearTimeout ( this . _pollTimer ) ; try { this . _client . disconnect ( ) ; } catch { } }
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
}
return { ImportedBchWallet , IMPORTED _BCH _NETWORKS } ;
} ;