theseus/bundled-addons/aegis/index.js

1658 lines
74 KiB
JavaScript
Raw Normal View History

feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// Aegis — multi-chain wallet bundled with Theseus. activate() runs in the
// main process; every wallet's key material lives here, in memory, and is
// re-derived from the password vault on every launch. Nothing secret is ever
// written to disk or logged.
//
// A single addon can hold many wallets — one per {chain, network}, or several
// sub-accounts of the same chain — and each wallet is backed by its own
// vault-derived 32-byte HKDF root. Chains today: BCH, Tron mainnet, Tron
// Nile testnet. Adding a fourth chain is a new adapter file under lib/ and
// an entry in the CHAIN_REGISTRY below.
//
// Back-compat: the addon id stays "bchwallet" (the manifest label became
// Aegis, but the id gates the vault-derive namespace and older vaults have
// funds against it). The legacy BCH default wallet uses purpose
// "bchwallet/mainnet/0" — byte-identical to the pre-multi-wallet build —
// so on-disk funds are untouched. See memory bchwallet-vault-root-derivation.
const path = require("node:path");
const fs = require("node:fs");
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const LEGACY_BCH_PURPOSE = "bchwallet/mainnet/0";
const LEGACY_BCH_WALLET_ID = "bch-default";
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
let ctx = null;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// ---- deps -------------------------------------------------------------------
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
async function loadDeps(api) {
const { secp256k1 } = await api.import("@noble/curves/secp256k1.js");
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
const { ed25519 } = await api.import("@noble/curves/ed25519.js");
const { sha256 } = await api.import("@noble/hashes/sha2.js");
const { ripemd160 } = await api.import("@noble/hashes/legacy.js");
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const { keccak_256 } = await api.import("@noble/hashes/sha3.js");
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
const { blake2b } = await api.import("@noble/hashes/blake2.js");
const { HDKey } = await api.import("@scure/bip32");
const WebSocket = api.require("ws");
const cashaddr = require("./lib/cashaddr.js");
const keysLib = require("./lib/keys.js")({ HDKey, secp256k1, sha256, ripemd160, cashaddr });
const tx = require("./lib/tx.js")({ sha256 });
const electrum = require("./lib/electrum.js")({ WebSocket, log: (...a) => api.log("electrum", ...a) });
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const base58check = require("./lib/base58check.js")({ sha256 });
const bchAdapter = require("./lib/chain-bch.js")({
HDKey, secp256k1, sha256, ripemd160, cashaddr, keysLib, tx, electrum, WebSocket,
});
const tronAdapter = require("./lib/chain-tron.js")({
HDKey, secp256k1, sha256, keccak_256, base58check,
});
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
const siaAdapter = require("./lib/chain-sia.js")({ ed25519, blake2b });
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
const ethAdapter = require("./lib/chain-eth.js")({ HDKey, secp256k1, keccak_256 });
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
const eip712 = require("./lib/eip712.js")({ keccak_256 });
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
// Solana uses the raw base58 alphabet (no checksum), which lives inside
// base58check as encodeBase58 / decodeBase58 — expose them under a
// `{encode, decode}` shape the SOL adapter reads from.
const solBase58 = { encode: base58check.encodeBase58, decode: base58check.decodeBase58 };
feat(theseus/aegis): SPL token support (view balances + send) SPL tokens now show up in the Solana wallet — balances on the Receive card, an asset picker on Send that flips the amount input into the token's own units. Sends build a TransferChecked + auto-create the recipient's Associated Token Account (idempotently) in the same transaction, so the user never has to fund an ATA by hand. - lib/sol-spl.js: SPL primitives that don't need @solana/web3.js. TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, findProgramAddress (PDA loop backed by an ed25519 is-on-curve check via @noble Point.fromBytes), associatedTokenAddress (matches the spl-token JS seed layout: [owner, tokenProgram, mint]), transferCheckedInstruction (discriminator 12, u64 amount, decimals byte), createATAIdempotentInstruction (associated-token program discriminator 1). A small known-mint registry ships inline for USDC / USDT / wSOL on mainnet + USDC on devnet — everything else falls back to a truncated mint address in the UI. - Message assembler classifies every unique pubkey into writable-signed / readonly-signed / writable-unsigned / readonly-unsigned, sorts the fee payer first, and serializes header + accountKeys + blockhash + instructions using Solana's compact-u16 short-vec encoding. Same wire shape @solana/web3.js produces from Transaction.serializeMessage. - lib/chain-sol.js: snapshot() now carries a tokens[] array of {mint, symbol, name, decimals, balance, tokenAccount, tokenProgram, isKnown, isToken2022}. Fetched via getTokenAccountsByOwner against both the classic Token program and Token-2022. New planTokenTransfer + signAndBroadcastToken handle a full send (TransferChecked + optional CreateATAIdempotent) in one wire. - Panel: Send tab gained an Asset dropdown (SOL / <each token>) that only shows for SOL wallets with tokens. Picking a token flips the unit picker's big-unit to the token symbol, amount goes in the token's own decimals, planTokenSend + sendToken take over from planSend/send. Receive tab gained a Tokens card listing each SPL balance with a per-row Send button that pre-fills the asset picker. - Verified in scratchpad: ATA derivation runs the PDA loop correctly (owner pubkey passes isOnCurve, derived ATA does not — the definitional property of a Program-Derived Address). Cross-check the ATA for any (owner, mint) on Phantom / Solscan / spl-token JS and the value matches. Known limits: - No token metadata lookup on-chain — mints outside the built-in registry show up with a truncated mint address as symbol. Wiring Metaplex Metadata program reads would let unknown tokens show their real names. - Send is single-signer only (the wallet is the fee payer, sender and sole required signer). Multi-sig SPL transfers work via the dapp bridge (window.solana.signAndSendTransaction, which already handles partial signatures).
2026-09-07 23:55:09 +02:00
const solAdapter = require("./lib/chain-sol.js")({ ed25519, base58: solBase58, sha256 });
feat(theseus/aegis): DGB adapter on @dgb-wallet/{core,psbt} vendored packages Aegis now shares its DGB code with the standalone DigiByte web-wallet at D:\Dev\SilentCode\Digibyte. Address derivation and PSBT construction come from that project's @dgb-wallet/core and @dgb-wallet/psbt packages instead of Aegis-local reimplementations. Any bugfix upstream flows in via a re-vendor of dist/*. - lib/dgb/{core,psbt}/ — vendored dist/ output of the two packages plus a tiny package.json shim marking them as ESM. @dgb-wallet/core's own import specifier "@dgb-wallet/core" inside psbt/*.js is rewritten to "../core/index.js" so the sibling module resolves without a workspace. - New Theseus deps: bitcoinjs-lib, bip32, bip39, @bitcoinerlab/secp256k1, ecpair — the peer deps the vendored packages need. Loaded via api.require in index.js's loadDeps(). - chain-dgb.js is a thin adapter now: BIP32 tree via bip32 + DGB Network object, addresses via core.p2wpkhAddress, tx via psbt.buildPsbt + PSBT.signInput (per-input, since each UTXO's key differs) + psbt.finalizeAndExtract. Runtime backend stays the same — Theseus's lib/electrum.js against the DGB ElectrumX pool. - Verified end-to-end in scratchpad: abandon×11 mnemonic derives dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8 (matches iancoleman.io/bip39 and the previous inline implementation, so no on-chain address change for anyone who was already using Aegis's DGB slot). PSBT build+sign+ finalize on a mock UTXO produces a valid 223-byte witness tx. BIP44 (D…) and BIP49 (S…) address families are implemented in the vendored core but not yet exposed in Aegis's picker — the panel needs an "address family" selector inside the DGB settings block first. Left for a follow-up; today's DGB pick uses BIP84 native SegWit only.
2026-09-07 02:19:44 +02:00
// DGB delegates address derivation + PSBT to the vendored @dgb-wallet/*
// packages under lib/dgb/. Those are ESM; the peer deps (bitcoinjs-lib,
// bip32, ecpair, @bitcoinerlab/secp256k1) are CommonJS and reachable via
// api.require from the Theseus dependency tree.
const { pathToFileURL } = require("node:url");
const dgbCore = await import(pathToFileURL(path.join(api.folder, "lib/dgb/core/index.js")).href);
const dgbPsbt = await import(pathToFileURL(path.join(api.folder, "lib/dgb/psbt/index.js")).href);
const bitcoinjs = api.require("bitcoinjs-lib");
const { BIP32Factory } = api.require("bip32");
const { ECPairFactory } = api.require("ecpair");
const ecc = api.require("@bitcoinerlab/secp256k1");
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
const dgbAdapter = require("./lib/chain-dgb.js")({
feat(theseus/aegis): DGB adapter on @dgb-wallet/{core,psbt} vendored packages Aegis now shares its DGB code with the standalone DigiByte web-wallet at D:\Dev\SilentCode\Digibyte. Address derivation and PSBT construction come from that project's @dgb-wallet/core and @dgb-wallet/psbt packages instead of Aegis-local reimplementations. Any bugfix upstream flows in via a re-vendor of dist/*. - lib/dgb/{core,psbt}/ — vendored dist/ output of the two packages plus a tiny package.json shim marking them as ESM. @dgb-wallet/core's own import specifier "@dgb-wallet/core" inside psbt/*.js is rewritten to "../core/index.js" so the sibling module resolves without a workspace. - New Theseus deps: bitcoinjs-lib, bip32, bip39, @bitcoinerlab/secp256k1, ecpair — the peer deps the vendored packages need. Loaded via api.require in index.js's loadDeps(). - chain-dgb.js is a thin adapter now: BIP32 tree via bip32 + DGB Network object, addresses via core.p2wpkhAddress, tx via psbt.buildPsbt + PSBT.signInput (per-input, since each UTXO's key differs) + psbt.finalizeAndExtract. Runtime backend stays the same — Theseus's lib/electrum.js against the DGB ElectrumX pool. - Verified end-to-end in scratchpad: abandon×11 mnemonic derives dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8 (matches iancoleman.io/bip39 and the previous inline implementation, so no on-chain address change for anyone who was already using Aegis's DGB slot). PSBT build+sign+ finalize on a mock UTXO produces a valid 223-byte witness tx. BIP44 (D…) and BIP49 (S…) address families are implemented in the vendored core but not yet exposed in Aegis's picker — the panel needs an "address family" selector inside the DGB settings block first. Left for a follow-up; today's DGB pick uses BIP84 native SegWit only.
2026-09-07 02:19:44 +02:00
dgbCore, dgbPsbt, bitcoinjs,
bip32Factory: BIP32Factory, ecpairFactory: ECPairFactory, ecc,
sha256, electrum,
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
});
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
const btcAdapter = require("./lib/chain-btc.js")({
bitcoinjs, bip32Factory: BIP32Factory, ecpairFactory: ECPairFactory, ecc,
sha256, electrum,
});
feat(theseus/aegis): DGB adapter on @dgb-wallet/{core,psbt} vendored packages Aegis now shares its DGB code with the standalone DigiByte web-wallet at D:\Dev\SilentCode\Digibyte. Address derivation and PSBT construction come from that project's @dgb-wallet/core and @dgb-wallet/psbt packages instead of Aegis-local reimplementations. Any bugfix upstream flows in via a re-vendor of dist/*. - lib/dgb/{core,psbt}/ — vendored dist/ output of the two packages plus a tiny package.json shim marking them as ESM. @dgb-wallet/core's own import specifier "@dgb-wallet/core" inside psbt/*.js is rewritten to "../core/index.js" so the sibling module resolves without a workspace. - New Theseus deps: bitcoinjs-lib, bip32, bip39, @bitcoinerlab/secp256k1, ecpair — the peer deps the vendored packages need. Loaded via api.require in index.js's loadDeps(). - chain-dgb.js is a thin adapter now: BIP32 tree via bip32 + DGB Network object, addresses via core.p2wpkhAddress, tx via psbt.buildPsbt + PSBT.signInput (per-input, since each UTXO's key differs) + psbt.finalizeAndExtract. Runtime backend stays the same — Theseus's lib/electrum.js against the DGB ElectrumX pool. - Verified end-to-end in scratchpad: abandon×11 mnemonic derives dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8 (matches iancoleman.io/bip39 and the previous inline implementation, so no on-chain address change for anyone who was already using Aegis's DGB slot). PSBT build+sign+ finalize on a mock UTXO produces a valid 223-byte witness tx. BIP44 (D…) and BIP49 (S…) address families are implemented in the vendored core but not yet exposed in Aegis's picker — the panel needs an "address family" selector inside the DGB settings block first. Left for a follow-up; today's DGB pick uses BIP84 native SegWit only.
2026-09-07 02:19:44 +02:00
return { HDKey, secp256k1, ed25519, sha256, ripemd160, keccak_256, blake2b,
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
cashaddr, keysLib, tx, electrum, base58check,
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
bchAdapter, tronAdapter, siaAdapter, dgbAdapter, ethAdapter, solAdapter, btcAdapter,
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
dgbCore, dgbPsbt, bitcoinjs, ecc, eip712 };
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// ---- servers ---------------------------------------------------------------
function bchDefaultServers(api) {
try { return JSON.parse(fs.readFileSync(path.join(api.folder, "electrum-servers.json"), "utf8")); }
catch { return []; }
}
function bchServerList(api) {
const custom = api.storage.get("servers", null);
return Array.isArray(custom) && custom.length ? custom : bchDefaultServers(api);
}
// ---- chain registry --------------------------------------------------------
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
// Two-level structure so the panel can present coin-then-network as separate
// picks. `coin` fields are chain-wide; `networks[<id>]` fields override or
// add to them per-network. `logo` is the SVG key panel.js draws from.
const COINS = {
bch: {
chain: "bch",
label: "Bitcoin Cash",
short: "BCH",
ticker: "BCH",
decimals: 8,
color: "#0ac18e",
logo: "bch",
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
supportsMessageSign: true,
supportsPageInject: true, // window.bitcoincash on *.x pages
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
networks: {
mainnet: {
id: "mainnet", label: "Mainnet", testnet: false,
// Legacy default wallet uses the flat "bchwallet/mainnet/0" purpose;
// new BCH mainnet sub-accounts start at index 1 under the /bch/ prefix.
purposePrefix: "bchwallet/bch/",
startIndex: 1,
},
chipnet: {
id: "chipnet", label: "Chipnet testnet", testnet: true,
purposePrefix: "bchwallet/bch/chipnet/",
startIndex: 0,
},
},
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
},
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
trx: {
chain: "trx",
label: "Tron",
short: "TRX",
ticker: "TRX",
decimals: 6,
color: "#ff060a",
logo: "trx",
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
supportsMessageSign: true,
supportsPageInject: true, // window.tronWeb everywhere
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
networks: {
mainnet: {
id: "mainnet", label: "Mainnet", testnet: false,
purposePrefix: "bchwallet/trx/mainnet/", startIndex: 0,
},
nile: {
id: "nile", label: "Nile testnet", testnet: true,
purposePrefix: "bchwallet/trx/nile/", startIndex: 0,
},
},
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
},
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
sc: {
chain: "sc",
label: "Siacoin",
short: "SC",
ticker: "SC",
decimals: 24,
color: "#20be82",
logo: "sc",
supportsMessageSign: true,
supportsPageInject: false,
// First SC wallet in Aegis reuses the legacy standalone-siawallet purpose
// so pre-Aegis funds carry over automatically (addon.json declares
// `absorbs: ["siawallet"]` to allow the derivation). Second+ use the new
// Aegis-namespaced prefix.
networks: {
mainnet: {
id: "mainnet", label: "Mainnet", testnet: false,
purposePrefix: "bchwallet/sc/mainnet/", startIndex: 1,
legacyFirstPurpose: "siawallet/mainnet/0",
},
},
},
dgb: {
chain: "dgb",
label: "DigiByte",
short: "DGB",
ticker: "DGB",
decimals: 8,
color: "#0066cc",
logo: "dgb",
supportsMessageSign: true,
supportsPageInject: false,
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
// BIP44/49/84/86 address families — the picker lives in the DGB
// settings block. Default is BIP84 (dgb1q…), which matches modern
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
// DGB Core, DigiByte-Go, and the SilentCode web-wallet. `coinType`
// per chain feeds the path builder below.
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
addressFamilies: [
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
{ id: "bip84", purpose: 84, label: "Native SegWit (dgb1q…)" },
{ id: "bip86", purpose: 86, label: "Taproot (dgb1p…)" },
{ id: "bip49", purpose: 49, label: "Wrapped SegWit (S…)" },
{ id: "bip44", purpose: 44, label: "Legacy P2PKH (D…)" },
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
],
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
coinType: 20,
defaultPurpose: 84,
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
networks: {
mainnet: {
id: "mainnet", label: "Mainnet", testnet: false,
purposePrefix: "bchwallet/dgb/mainnet/", startIndex: 0,
},
},
},
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
eth: {
chain: "eth",
label: "Ethereum",
short: "ETH",
ticker: "ETH",
decimals: 18,
color: "#627eea",
logo: "eth",
supportsMessageSign: true,
supportsPageInject: false, // EIP-1193 provider is a follow-up
networks: {
mainnet: {
id: "mainnet", label: "Mainnet", testnet: false,
purposePrefix: "bchwallet/eth/mainnet/", startIndex: 0,
},
sepolia: {
id: "sepolia", label: "Sepolia testnet", testnet: true,
purposePrefix: "bchwallet/eth/sepolia/", startIndex: 0,
},
},
},
sol: {
chain: "sol",
label: "Solana",
short: "SOL",
ticker: "SOL",
decimals: 9,
color: "#9945ff",
logo: "sol",
supportsMessageSign: true,
supportsPageInject: false,
networks: {
mainnet: {
id: "mainnet", label: "Mainnet-beta", testnet: false,
purposePrefix: "bchwallet/sol/mainnet/", startIndex: 0,
},
devnet: {
id: "devnet", label: "Devnet", testnet: true,
purposePrefix: "bchwallet/sol/devnet/", startIndex: 0,
},
},
},
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
btc: {
chain: "btc",
label: "Bitcoin",
short: "BTC",
ticker: "BTC",
decimals: 8,
color: "#f7931a",
logo: "btc",
supportsMessageSign: true,
supportsPageInject: false,
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
// BIP44/49/84/86 across bc1q… / bc1p… / 3… / 1… on mainnet and
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
// tb1q… / tb1p… / 2… / m/n… on testnet3 + signet. Coin type shifts
// per network (0 for mainnet, 1 for both testnet3 and signet — SLIP-44
// treats every Bitcoin testnet as coin type 1).
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
addressFamilies: [
{ id: "bip84", purpose: 84, label: "Native SegWit (bc1q… / tb1q…)" },
{ id: "bip86", purpose: 86, label: "Taproot (bc1p… / tb1p…)" },
{ id: "bip49", purpose: 49, label: "Wrapped SegWit (3… / 2…)" },
{ id: "bip44", purpose: 44, label: "Legacy P2PKH (1… / m…, n…)" },
],
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
coinType: { mainnet: 0, testnet: 1, signet: 1 },
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
defaultPurpose: 84,
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
networks: {
mainnet: {
id: "mainnet", label: "Mainnet", testnet: false,
purposePrefix: "bchwallet/btc/mainnet/", startIndex: 0,
},
testnet: {
id: "testnet", label: "Testnet3", testnet: true,
purposePrefix: "bchwallet/btc/testnet/", startIndex: 0,
},
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
signet: {
id: "signet", label: "Signet", testnet: true,
purposePrefix: "bchwallet/btc/signet/", startIndex: 0,
},
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
},
},
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
};
function chainKey(chain, network) { return `${chain}:${network}`; }
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
// Coin type per (chain, network). A number literal on the COINS entry
// (DGB uses a single 20) or a per-network object ({mainnet: 0, testnet: 1}
// for BTC). Returns null when the chain doesn't declare a family picker.
function coinTypeFor(c, network) {
if (!c || c.coinType == null) return null;
return typeof c.coinType === "number" ? c.coinType : (c.coinType[network] ?? null);
}
// Full derivation paths per family for a given (chain, network) — expands
// the family list on the fly so each picker knows exactly which path a
// pick would produce.
function addressFamiliesFor(c, network) {
if (!c || !c.addressFamilies) return null;
const ct = coinTypeFor(c, network);
if (ct == null) return c.addressFamilies;
return c.addressFamilies.map((f) => ({
...f,
defaultAccountPath: `m/${f.purpose}'/${ct}'/0'`,
}));
}
function defaultAccountPathFor(c, network) {
const ct = coinTypeFor(c, network);
if (ct == null || c.defaultPurpose == null) return null;
return `m/${c.defaultPurpose}'/${ct}'/0'`;
}
feat(theseus/aegis): EIP-3085 wallet_addEthereumChain + EIP-3326 switchChain Aegis now handles the standard MetaMask try-switch-then-add flow. A dapp that wants to route through Polygon (or Base, or Arbitrum, or any other EVM the Silent Mode user hasn't added yet) calls the pair the industry already wrote for it — Aegis registers the chain, provisions a wallet on it under the same vault seed, auto-connects the origin, fires chainChanged, and hands the dapp back a provider pointed at the new chain. No sidebar detour, no Custom RPC copy-paste. Users still see every chain in the picker post-add and can revoke sites in Settings. - lib/chain-eth.js: EthWallet accepts a customNetwork override ({id, label, chainId, defaultRpc, explorerTx, explorerAddr, ticker}). When present it replaces the NETWORKS lookup so mainnet+Sepolia ship built-in and every EIP-3085 chain is a runtime override the addon persists. The ticker flows into snapshot() so the send approval reads MATIC / BNB / whatever the chain's native currency is, not a hardcoded ETH. - index.js customEthChains storage: `{[chainId]: {chainName, rpcUrl, explorerTx, explorerAddr, ticker, addedAt, addedByOrigin}}`. Persisted under api.storage.customEthChains, so an added chain survives Theseus restarts. chainMeta("eth", "custom-<chainId>") synthesizes the meta from storage so the panel renders custom chains without needing them in COINS at module-load time. - eth.addChain handler (EIP-3085): approval overlay shows chain name, decimal + hex chain id, native ticker, RPC and explorer URLs (the phishing-signal quartet). On approval, persist config + create wallet with a custom-<chainId> network + auto-grant the origin readAddress on this chain. No-op success if the chain is already added. - eth.switchChain rewritten to be EIP-3326 correct: look up any ready ETH wallet whose adapter reports the requested chainId, make it the selected wallet, fire chainChanged. When no wallet matches, throw with .code = 4902 (the standard 'chain not added' code) so wagmi / RainbowKit / any 3326-aware dapp does the fallback wallet_addEthereumChain call in the same click. - eth.state handler: cheap {address, chainIdHex, networkVersion} peek for the origin's currently-connected wallet (no approval, no key access). The main-world bridge calls it after every switch/add to emit chainChanged + accountsChanged locally — the events MetaMask fires and RainbowKit listens for. - wallet-inject.js: routes wallet_addEthereumChain via eth.addChain, preserves the 4902 code across the postMessage boundary on switch failures, calls pullEthStateAndEmit() to fire the post-switch/add events.
2026-09-07 22:26:27 +02:00
// Custom EVM chains (EIP-3085) live in api.storage under `customEthChains`.
// Structured as { [chainId]: {chainId, chainName, rpcUrl, explorerTx,
// explorerAddr, ticker, addedAt, addedByOrigin} }. They're not in COINS at
// module-load time — we synthesize a chainMeta / mount entry from storage
// so wallet_addEthereumChain can register new networks at runtime without
// a Theseus restart.
const CUSTOM_ETH_PREFIX = "custom-";
function customEthChains(api) {
const raw = api.storage.get("customEthChains", {});
return raw && typeof raw === "object" ? raw : {};
}
function customEthNetworkEntry(api, network) {
if (!network || !network.startsWith(CUSTOM_ETH_PREFIX)) return null;
const chainId = Number(network.slice(CUSTOM_ETH_PREFIX.length));
if (!Number.isFinite(chainId)) return null;
const all = customEthChains(api);
const cfg = all[String(chainId)];
if (!cfg) return null;
return {
id: network,
label: cfg.chainName || `EVM #${chainId}`,
chainId,
defaultRpc: cfg.rpcUrl,
explorerTx: cfg.explorerTx,
explorerAddr: cfg.explorerAddr,
ticker: cfg.ticker || "ETH",
faucet: null,
};
}
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
function chainMeta(chain, network) {
feat(theseus/aegis): EIP-3085 wallet_addEthereumChain + EIP-3326 switchChain Aegis now handles the standard MetaMask try-switch-then-add flow. A dapp that wants to route through Polygon (or Base, or Arbitrum, or any other EVM the Silent Mode user hasn't added yet) calls the pair the industry already wrote for it — Aegis registers the chain, provisions a wallet on it under the same vault seed, auto-connects the origin, fires chainChanged, and hands the dapp back a provider pointed at the new chain. No sidebar detour, no Custom RPC copy-paste. Users still see every chain in the picker post-add and can revoke sites in Settings. - lib/chain-eth.js: EthWallet accepts a customNetwork override ({id, label, chainId, defaultRpc, explorerTx, explorerAddr, ticker}). When present it replaces the NETWORKS lookup so mainnet+Sepolia ship built-in and every EIP-3085 chain is a runtime override the addon persists. The ticker flows into snapshot() so the send approval reads MATIC / BNB / whatever the chain's native currency is, not a hardcoded ETH. - index.js customEthChains storage: `{[chainId]: {chainName, rpcUrl, explorerTx, explorerAddr, ticker, addedAt, addedByOrigin}}`. Persisted under api.storage.customEthChains, so an added chain survives Theseus restarts. chainMeta("eth", "custom-<chainId>") synthesizes the meta from storage so the panel renders custom chains without needing them in COINS at module-load time. - eth.addChain handler (EIP-3085): approval overlay shows chain name, decimal + hex chain id, native ticker, RPC and explorer URLs (the phishing-signal quartet). On approval, persist config + create wallet with a custom-<chainId> network + auto-grant the origin readAddress on this chain. No-op success if the chain is already added. - eth.switchChain rewritten to be EIP-3326 correct: look up any ready ETH wallet whose adapter reports the requested chainId, make it the selected wallet, fire chainChanged. When no wallet matches, throw with .code = 4902 (the standard 'chain not added' code) so wagmi / RainbowKit / any 3326-aware dapp does the fallback wallet_addEthereumChain call in the same click. - eth.state handler: cheap {address, chainIdHex, networkVersion} peek for the origin's currently-connected wallet (no approval, no key access). The main-world bridge calls it after every switch/add to emit chainChanged + accountsChanged locally — the events MetaMask fires and RainbowKit listens for. - wallet-inject.js: routes wallet_addEthereumChain via eth.addChain, preserves the 4902 code across the postMessage boundary on switch failures, calls pullEthStateAndEmit() to fire the post-switch/add events.
2026-09-07 22:26:27 +02:00
// ETH custom-network fallback for EIP-3085 chains.
if (chain === "eth" && String(network || "").startsWith(CUSTOM_ETH_PREFIX)) {
const cfg = customEthNetworkEntry(ctx?.api, network);
if (!cfg) return null;
return {
chain: "eth", network: cfg.id,
label: "Ethereum · " + cfg.label,
short: cfg.ticker, ticker: cfg.ticker, decimals: 18,
color: COINS.eth.color, logo: COINS.eth.logo,
coinLabel: cfg.label, networkLabel: `chainId ${cfg.chainId}`,
testnet: false,
purposePrefix: `bchwallet/eth/${cfg.id}/`, startIndex: 0,
supportsMessageSign: true, supportsPageInject: false,
addressFamilies: null, defaultAccountPath: null,
isCustom: true, chainId: cfg.chainId,
};
}
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
const c = COINS[chain]; const n = c && c.networks[network];
if (!c || !n) return null;
return {
chain: c.chain, network: n.id,
label: c.label + " · " + n.label, short: c.short, ticker: c.ticker, decimals: c.decimals,
color: c.color, logo: c.logo, coinLabel: c.label, networkLabel: n.label, testnet: !!n.testnet,
purposePrefix: n.purposePrefix, startIndex: n.startIndex,
supportsMessageSign: !!c.supportsMessageSign, supportsPageInject: !!c.supportsPageInject,
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
addressFamilies: addressFamiliesFor(c, network),
defaultAccountPath: defaultAccountPathFor(c, network),
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
};
}
function coinsForPanel() {
return Object.values(COINS).map((c) => ({
chain: c.chain, label: c.label, short: c.short, ticker: c.ticker, color: c.color, logo: c.logo, decimals: c.decimals,
networks: Object.values(c.networks).map((n) => ({ id: n.id, label: n.label, testnet: !!n.testnet })),
}));
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// ---- wallet list ------------------------------------------------------------
function readWallets(api) {
const raw = api.storage.get("wallets", null);
return Array.isArray(raw) ? raw : null;
}
function writeWallets(api, list) { api.storage.set("wallets", list); }
// Bring pre-multi-wallet storage forward: create the legacy BCH default entry
// and rehome its receiveCursor / txCache under the new per-wallet subkey.
function migrateLegacyStorage(api) {
if (readWallets(api)) return; // already multi-wallet
const legacyAccountPath = String(api.storage.get("accountPath", "") || "").trim() || "m/44'/145'/0'";
const wallets = [{
id: LEGACY_BCH_WALLET_ID,
label: "BCH — main",
chain: "bch",
network: "mainnet",
purpose: LEGACY_BCH_PURPOSE,
accountPath: legacyAccountPath,
isDefault: true,
isLegacy: true,
createdAt: 0,
}];
writeWallets(api, wallets);
api.storage.set("selectedWalletId", LEGACY_BCH_WALLET_ID);
// Move per-wallet state under the scoped prefix used by chain-bch.js.
const prefix = `wallets/${LEGACY_BCH_WALLET_ID}/`;
for (const legacyKey of ["receiveCursor", "txCache"]) {
const v = api.storage.get(legacyKey, null);
if (v !== null && api.storage.get(prefix + legacyKey, null) === null) {
api.storage.set(prefix + legacyKey, v);
}
}
api.log("migrated legacy BCH wallet into multi-wallet layout");
}
function nextIndex(wallets, meta) {
let max = meta.startIndex - 1;
for (const w of wallets) {
if (chainKey(w.chain, w.network) !== chainKey(meta.chain, meta.network)) continue;
if (w.isLegacy) continue;
const m = /\/(\d+)$/.exec(w.purpose || "");
const n = m ? Number(m[1]) : NaN;
if (Number.isFinite(n) && n > max) max = n;
}
return max + 1;
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
function makeWalletId(meta, index) {
const n = String(meta.network).replace(/[^a-z0-9]/gi, "");
return `${meta.chain}-${n}-${index}`;
}
function autoLabel(meta, wallets) {
const same = wallets.filter((w) => chainKey(w.chain, w.network) === chainKey(meta.chain, meta.network));
if (!same.length) return meta.short;
return `${meta.short} #${same.length + 1}`;
}
// ---- runtime wallet map -----------------------------------------------------
// A "runtime" is a mounted wallet: its adapter instance plus phase + error.
// activate() derives all of them in parallel once the vault unlocks.
async function mountAllWallets() {
const c = ctx;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const walletList = readWallets(c.api) || [];
for (const w of walletList) {
if (!c.runtimes.has(w.id)) c.runtimes.set(w.id, { entry: w, phase: "locked", error: null, adapter: null });
}
emitState();
await Promise.all(walletList.map((w) => mountWallet(w)));
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
async function mountWallet(entry) {
const c = ctx;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const rt = c.runtimes.get(entry.id) || { entry, phase: "locked", error: null, adapter: null };
rt.entry = entry;
rt.phase = "locked"; rt.error = null;
c.runtimes.set(entry.id, rt);
emitState();
let root;
try {
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
root = await c.api.vault.derive(entry.purpose);
} catch (e) {
const msg = e?.message || String(e);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
rt.phase = /not set up/i.test(msg) ? "nosetup" : "error";
rt.error = msg;
emitState();
return;
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (ctx !== c) return;
try {
let adapter;
if (entry.chain === "bch") {
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
// Mainnet still honors the user-set custom electrum list; chipnet uses
// adapter-embedded defaults (no per-network custom list in this rev).
const servers = entry.network === "mainnet" ? bchServerList(c.api) : undefined;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
adapter = new c.d.bchAdapter.BchWallet(root, {
walletId: entry.id,
storage: c.api.storage,
log: (...a) => c.api.log(`[${entry.id}]`, ...a),
onChange: () => emitStateForWallet(entry.id),
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
network: entry.network,
servers,
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
accountPath: entry.accountPath,
});
} else if (entry.chain === "trx") {
adapter = new c.d.tronAdapter.TronWallet(root, entry.network, {
storage: c.api.storage,
log: (...a) => c.api.log(`[${entry.id}]`, ...a),
onChange: () => emitStateForWallet(entry.id),
});
adapter.schedulePoll(20_000);
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
} else if (entry.chain === "sc") {
// walletdUrl is per-Sia-wallet (each sub-account may point at a
// different node) and stored under the scoped wallets/<id>/walletdUrl
// key. Empty = the panel shows a "point me at walletd" gate.
const walletdUrl = String(c.api.storage.get(`wallets/${entry.id}/walletdUrl`, "") || "");
adapter = new c.d.siaAdapter.SiaWallet(root, {
walletId: entry.id,
storage: c.api.storage,
log: (...a) => c.api.log(`[${entry.id}]`, ...a),
onChange: () => emitStateForWallet(entry.id),
walletdUrl,
});
if (walletdUrl) adapter.startPolling();
} else if (entry.chain === "dgb") {
adapter = new c.d.dgbAdapter.DgbWallet(root, {
walletId: entry.id,
storage: c.api.storage,
log: (...a) => c.api.log(`[${entry.id}]`, ...a),
onChange: () => emitStateForWallet(entry.id),
accountPath: entry.accountPath,
});
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
} else if (entry.chain === "eth") {
const rpcUrl = String(c.api.storage.get(`wallets/${entry.id}/rpcUrl`, "") || "");
feat(theseus/aegis): EIP-3085 wallet_addEthereumChain + EIP-3326 switchChain Aegis now handles the standard MetaMask try-switch-then-add flow. A dapp that wants to route through Polygon (or Base, or Arbitrum, or any other EVM the Silent Mode user hasn't added yet) calls the pair the industry already wrote for it — Aegis registers the chain, provisions a wallet on it under the same vault seed, auto-connects the origin, fires chainChanged, and hands the dapp back a provider pointed at the new chain. No sidebar detour, no Custom RPC copy-paste. Users still see every chain in the picker post-add and can revoke sites in Settings. - lib/chain-eth.js: EthWallet accepts a customNetwork override ({id, label, chainId, defaultRpc, explorerTx, explorerAddr, ticker}). When present it replaces the NETWORKS lookup so mainnet+Sepolia ship built-in and every EIP-3085 chain is a runtime override the addon persists. The ticker flows into snapshot() so the send approval reads MATIC / BNB / whatever the chain's native currency is, not a hardcoded ETH. - index.js customEthChains storage: `{[chainId]: {chainName, rpcUrl, explorerTx, explorerAddr, ticker, addedAt, addedByOrigin}}`. Persisted under api.storage.customEthChains, so an added chain survives Theseus restarts. chainMeta("eth", "custom-<chainId>") synthesizes the meta from storage so the panel renders custom chains without needing them in COINS at module-load time. - eth.addChain handler (EIP-3085): approval overlay shows chain name, decimal + hex chain id, native ticker, RPC and explorer URLs (the phishing-signal quartet). On approval, persist config + create wallet with a custom-<chainId> network + auto-grant the origin readAddress on this chain. No-op success if the chain is already added. - eth.switchChain rewritten to be EIP-3326 correct: look up any ready ETH wallet whose adapter reports the requested chainId, make it the selected wallet, fire chainChanged. When no wallet matches, throw with .code = 4902 (the standard 'chain not added' code) so wagmi / RainbowKit / any 3326-aware dapp does the fallback wallet_addEthereumChain call in the same click. - eth.state handler: cheap {address, chainIdHex, networkVersion} peek for the origin's currently-connected wallet (no approval, no key access). The main-world bridge calls it after every switch/add to emit chainChanged + accountsChanged locally — the events MetaMask fires and RainbowKit listens for. - wallet-inject.js: routes wallet_addEthereumChain via eth.addChain, preserves the 4902 code across the postMessage boundary on switch failures, calls pullEthStateAndEmit() to fire the post-switch/add events.
2026-09-07 22:26:27 +02:00
// Custom EIP-3085 chains resolve their config from storage rather than
// the built-in NETWORKS map.
const customNetwork = customEthNetworkEntry(c.api, entry.network);
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
adapter = new c.d.ethAdapter.EthWallet(root, entry.network, {
walletId: entry.id,
storage: c.api.storage,
log: (...a) => c.api.log(`[${entry.id}]`, ...a),
onChange: () => emitStateForWallet(entry.id),
rpcUrl,
feat(theseus/aegis): EIP-3085 wallet_addEthereumChain + EIP-3326 switchChain Aegis now handles the standard MetaMask try-switch-then-add flow. A dapp that wants to route through Polygon (or Base, or Arbitrum, or any other EVM the Silent Mode user hasn't added yet) calls the pair the industry already wrote for it — Aegis registers the chain, provisions a wallet on it under the same vault seed, auto-connects the origin, fires chainChanged, and hands the dapp back a provider pointed at the new chain. No sidebar detour, no Custom RPC copy-paste. Users still see every chain in the picker post-add and can revoke sites in Settings. - lib/chain-eth.js: EthWallet accepts a customNetwork override ({id, label, chainId, defaultRpc, explorerTx, explorerAddr, ticker}). When present it replaces the NETWORKS lookup so mainnet+Sepolia ship built-in and every EIP-3085 chain is a runtime override the addon persists. The ticker flows into snapshot() so the send approval reads MATIC / BNB / whatever the chain's native currency is, not a hardcoded ETH. - index.js customEthChains storage: `{[chainId]: {chainName, rpcUrl, explorerTx, explorerAddr, ticker, addedAt, addedByOrigin}}`. Persisted under api.storage.customEthChains, so an added chain survives Theseus restarts. chainMeta("eth", "custom-<chainId>") synthesizes the meta from storage so the panel renders custom chains without needing them in COINS at module-load time. - eth.addChain handler (EIP-3085): approval overlay shows chain name, decimal + hex chain id, native ticker, RPC and explorer URLs (the phishing-signal quartet). On approval, persist config + create wallet with a custom-<chainId> network + auto-grant the origin readAddress on this chain. No-op success if the chain is already added. - eth.switchChain rewritten to be EIP-3326 correct: look up any ready ETH wallet whose adapter reports the requested chainId, make it the selected wallet, fire chainChanged. When no wallet matches, throw with .code = 4902 (the standard 'chain not added' code) so wagmi / RainbowKit / any 3326-aware dapp does the fallback wallet_addEthereumChain call in the same click. - eth.state handler: cheap {address, chainIdHex, networkVersion} peek for the origin's currently-connected wallet (no approval, no key access). The main-world bridge calls it after every switch/add to emit chainChanged + accountsChanged locally — the events MetaMask fires and RainbowKit listens for. - wallet-inject.js: routes wallet_addEthereumChain via eth.addChain, preserves the 4902 code across the postMessage boundary on switch failures, calls pullEthStateAndEmit() to fire the post-switch/add events.
2026-09-07 22:26:27 +02:00
customNetwork,
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
});
adapter.schedulePoll(20_000);
} else if (entry.chain === "sol") {
const rpcUrl = String(c.api.storage.get(`wallets/${entry.id}/rpcUrl`, "") || "");
adapter = new c.d.solAdapter.SolWallet(root, entry.network, {
walletId: entry.id,
storage: c.api.storage,
log: (...a) => c.api.log(`[${entry.id}]`, ...a),
onChange: () => emitStateForWallet(entry.id),
rpcUrl,
});
adapter.schedulePoll(20_000);
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
} else if (entry.chain === "btc") {
adapter = new c.d.btcAdapter.BtcWallet(root, entry.network, {
walletId: entry.id,
storage: c.api.storage,
log: (...a) => c.api.log(`[${entry.id}]`, ...a),
onChange: () => emitStateForWallet(entry.id),
accountPath: entry.accountPath,
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
} else {
throw new Error(`unknown chain ${entry.chain}`);
}
rt.adapter = adapter;
rt.phase = "ready";
// Kick a first fetch. Errors here don't fail the mount — the panel shows
// them per-wallet via snapshot.error.
adapter.refresh(true).catch((e) => c.api.log(`[${entry.id}] initial refresh:`, e?.message || e));
emitStateForWallet(entry.id);
} catch (e) {
rt.phase = "error";
rt.error = e?.message || String(e);
emitState();
} finally {
// Wipe the root buffer — the adapter has already turned it into keys.
if (root) try { root.fill(0); } catch {}
}
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
function unmountWallet(walletId) {
const rt = ctx.runtimes.get(walletId);
if (rt && rt.adapter) { try { rt.adapter.dispose(); } catch {} }
ctx.runtimes.delete(walletId);
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// ---- state / snapshot -------------------------------------------------------
function selectedWalletId() {
const list = readWallets(ctx.api) || [];
if (!list.length) return null;
const saved = String(ctx.api.storage.get("selectedWalletId", "") || "");
if (saved && list.some((w) => w.id === saved)) return saved;
const dflt = list.find((w) => w.isDefault) || list[0];
return dflt.id;
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
function walletEntries() { return readWallets(ctx.api) || []; }
function overallPhase() {
// If ANY wallet is nosetup, treat the whole addon as nosetup — the user
// hasn't unlocked / set up the vault, so no wallet can work.
const runtimes = [...ctx.runtimes.values()];
if (!runtimes.length) return "locked";
if (runtimes.some((r) => r.phase === "nosetup")) return "nosetup";
if (runtimes.some((r) => r.phase === "locked")) return "locked";
return "ready";
}
function walletSummary(w) {
const meta = chainMeta(w.chain, w.network);
const rt = ctx.runtimes.get(w.id);
const snap = rt && rt.adapter ? rt.adapter.snapshot() : null;
return {
id: w.id, label: w.label, chain: w.chain, network: w.network, isDefault: !!w.isDefault, isLegacy: !!w.isLegacy,
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
logo: meta?.logo || null, color: meta?.color || "#888",
coinLabel: meta?.coinLabel || w.chain, networkLabel: meta?.networkLabel || w.network, testnet: !!meta?.testnet,
ticker: meta?.ticker || "?", short: meta?.short || w.chain, decimals: meta?.decimals || 8,
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
address: snap?.address || null,
balance: snap?.balance || { confirmed: 0, unconfirmed: 0 },
phase: rt?.phase || "locked",
error: rt?.error || null,
};
}
function snapshotForSelected() {
const id = selectedWalletId();
if (!id) return { phase: "empty" };
const rt = ctx.runtimes.get(id);
const entry = walletEntries().find((w) => w.id === id);
const meta = entry ? chainMeta(entry.chain, entry.network) : null;
const base = {
walletId: id,
label: entry?.label,
chain: entry?.chain,
network: entry?.network,
isLegacy: !!entry?.isLegacy,
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
meta: meta ? {
logo: meta.logo, color: meta.color, short: meta.short, ticker: meta.ticker, decimals: meta.decimals,
coinLabel: meta.coinLabel, networkLabel: meta.networkLabel, testnet: meta.testnet,
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
addressFamilies: meta.addressFamilies, defaultAccountPath: meta.defaultAccountPath,
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
} : null,
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
supportsMessageSign: !!meta?.supportsMessageSign,
phase: rt?.phase || "locked",
error: rt?.error || null,
};
if (rt && rt.adapter) Object.assign(base, rt.adapter.snapshot());
return base;
}
function fullState() {
return {
overallPhase: overallPhase(),
selectedWalletId: selectedWalletId(),
wallets: walletEntries().map(walletSummary),
selected: snapshotForSelected(),
bchServers: {
list: bchServerList(ctx.api),
custom: Array.isArray(ctx.api.storage.get("servers", null)),
},
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
coins: coinsForPanel(),
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
};
}
function emitState() { try { ctx.api.emit("state", fullState()); } catch {} }
function emitStateForWallet(id) {
// Any wallet change fans out to the panel with the full state so the
// wallet list balances update in the header too.
if (!ctx || !ctx.runtimes.has(id)) return;
emitState();
}
// ---- panel messages ---------------------------------------------------------
function requireWallet(id) {
const rt = ctx.runtimes.get(id);
if (!rt || rt.phase !== "ready" || !rt.adapter) throw new Error("wallet is not ready (vault locked?)");
return rt;
}
function requireSelected() { return requireWallet(selectedWalletId()); }
function fromPanel(m) { if (!m || m.from !== "panel") throw new Error("panel-only message"); }
function fromPage(m) {
if (!m || m.from !== "page" || !m.origin) throw new Error("page-only message");
return m.origin;
}
const fmtBch = (sats) => (Number(sats) / 1e8).toFixed(8).replace(/(\.\d*?[1-9])0+$|\.0+$/, "$1");
const fmtTrx = (sun) => (Number(sun) / 1e6).toFixed(6).replace(/(\.\d*?[1-9])0+$|\.0+$/, "$1");
function fmtValue(units, decimals) {
const n = Number(units) / Math.pow(10, decimals);
return n.toFixed(decimals).replace(/(\.\d*?[1-9])0+$|\.0+$/, "$1");
}
feat(theseus/aegis): SPL token support (view balances + send) SPL tokens now show up in the Solana wallet — balances on the Receive card, an asset picker on Send that flips the amount input into the token's own units. Sends build a TransferChecked + auto-create the recipient's Associated Token Account (idempotently) in the same transaction, so the user never has to fund an ATA by hand. - lib/sol-spl.js: SPL primitives that don't need @solana/web3.js. TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, findProgramAddress (PDA loop backed by an ed25519 is-on-curve check via @noble Point.fromBytes), associatedTokenAddress (matches the spl-token JS seed layout: [owner, tokenProgram, mint]), transferCheckedInstruction (discriminator 12, u64 amount, decimals byte), createATAIdempotentInstruction (associated-token program discriminator 1). A small known-mint registry ships inline for USDC / USDT / wSOL on mainnet + USDC on devnet — everything else falls back to a truncated mint address in the UI. - Message assembler classifies every unique pubkey into writable-signed / readonly-signed / writable-unsigned / readonly-unsigned, sorts the fee payer first, and serializes header + accountKeys + blockhash + instructions using Solana's compact-u16 short-vec encoding. Same wire shape @solana/web3.js produces from Transaction.serializeMessage. - lib/chain-sol.js: snapshot() now carries a tokens[] array of {mint, symbol, name, decimals, balance, tokenAccount, tokenProgram, isKnown, isToken2022}. Fetched via getTokenAccountsByOwner against both the classic Token program and Token-2022. New planTokenTransfer + signAndBroadcastToken handle a full send (TransferChecked + optional CreateATAIdempotent) in one wire. - Panel: Send tab gained an Asset dropdown (SOL / <each token>) that only shows for SOL wallets with tokens. Picking a token flips the unit picker's big-unit to the token symbol, amount goes in the token's own decimals, planTokenSend + sendToken take over from planSend/send. Receive tab gained a Tokens card listing each SPL balance with a per-row Send button that pre-fills the asset picker. - Verified in scratchpad: ATA derivation runs the PDA loop correctly (owner pubkey passes isOnCurve, derived ATA does not — the definitional property of a Program-Derived Address). Cross-check the ATA for any (owner, mint) on Phantom / Solscan / spl-token JS and the value matches. Known limits: - No token metadata lookup on-chain — mints outside the built-in registry show up with a truncated mint address as symbol. Wiring Metaplex Metadata program reads would let unknown tokens show their real names. - Send is single-signer only (the wallet is the fee payer, sender and sole required signer). Multi-sig SPL transfers work via the dapp bridge (window.solana.signAndSendTransaction, which already handles partial signatures).
2026-09-07 23:55:09 +02:00
// BigInt-safe display for SPL token amounts (raw units in u64 strings).
function fmtTokenAmount(rawStr, decimals) {
const s = String(rawStr || "0");
const neg = s.startsWith("-");
const abs = neg ? s.slice(1) : s;
const d = Number(decimals) || 0;
if (d === 0) return (neg ? "-" : "") + abs;
const pad = abs.padStart(d + 1, "0");
const whole = pad.slice(0, pad.length - d);
const frac = pad.slice(pad.length - d).replace(/0+$/, "");
return (neg ? "-" : "") + whole + (frac ? "." + frac : "");
}
function registerPanelMessages(api) {
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
api.onMessage("state", (_p, m) => { fromPanel(m); return fullState(); });
api.onMessage("selectWallet", (p, m) => {
fromPanel(m);
const id = String(p && p.id || "");
if (!walletEntries().some((w) => w.id === id)) throw new Error("unknown wallet");
api.storage.set("selectedWalletId", id);
emitState();
return fullState();
});
api.onMessage("addWallet", async (p, m) => {
fromPanel(m);
const chain = String(p && p.chain || "");
const network = String(p && p.network || "");
const meta = chainMeta(chain, network);
if (!meta) throw new Error("unknown chain/network");
const list = walletEntries().slice();
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
// If this coin+network declares a `legacyFirstPurpose` (SC does, to
// recover funds from the standalone siawallet addon) and no wallet of
// this coin+network exists yet, use that purpose verbatim. The bump
// to the /aegis-namespaced/ prefix only starts on the second sub-account.
const netMeta = COINS[chain]?.networks?.[network] || {};
const existingForCoin = list.filter((w) => w.chain === chain && w.network === network && !w.isLegacy);
let purpose, isLegacy = false;
if (netMeta.legacyFirstPurpose && existingForCoin.length === 0
&& !list.some((w) => w.purpose === netMeta.legacyFirstPurpose)) {
purpose = netMeta.legacyFirstPurpose;
isLegacy = true;
} else {
const index = nextIndex(list, meta);
purpose = meta.purposePrefix + index;
}
const idIndex = nextIndex(list, meta);
const id = makeWalletId(meta, idIndex);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (list.some((w) => w.id === id || w.purpose === purpose)) throw new Error("duplicate wallet");
const label = String(p && p.label || "").trim() || autoLabel(meta, list);
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
const entry = { id, label, chain, network, purpose, isLegacy, createdAt: Date.now() };
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
list.push(entry);
writeWallets(api, list);
api.storage.set("selectedWalletId", id);
ctx.runtimes.set(id, { entry, phase: "locked", error: null, adapter: null });
emitState();
await mountWallet(entry);
return fullState();
});
api.onMessage("removeWallet", (p, m) => {
fromPanel(m);
const id = String(p && p.id || "");
const list = walletEntries();
const entry = list.find((w) => w.id === id);
if (!entry) throw new Error("unknown wallet");
if (entry.isDefault) throw new Error("the default wallet cannot be removed");
const next = list.filter((w) => w.id !== id);
writeWallets(api, next);
if (selectedWalletId() === id) api.storage.set("selectedWalletId", next[0]?.id || "");
unmountWallet(id);
// Drop per-wallet storage subtree.
const all = api.storage.all ? api.storage.all() : {};
const prefix = `wallets/${id}/`;
for (const k of Object.keys(all)) if (k.startsWith(prefix)) api.storage.set(k, null);
emitState();
return fullState();
});
api.onMessage("renameWallet", (p, m) => {
fromPanel(m);
const id = String(p && p.id || "");
const label = String(p && p.label || "").trim().slice(0, 60);
if (!label) throw new Error("label required");
const list = walletEntries();
const entry = list.find((w) => w.id === id);
if (!entry) throw new Error("unknown wallet");
entry.label = label;
writeWallets(api, list);
emitState();
return fullState();
});
api.onMessage("refresh", async (_p, m) => { fromPanel(m); const rt = requireSelected(); await rt.adapter.refresh(true); return snapshotForSelected(); });
api.onMessage("nextAddress", (_p, m) => {
fromPanel(m);
const rt = requireSelected();
if (rt.entry.chain !== "bch") throw new Error("only BCH wallets have multiple receive addresses");
rt.adapter.nextAddress();
return snapshotForSelected();
});
api.onMessage("openUrl", (p, m) => { fromPanel(m); api.openTab(String(p && p.url || "")); return true; });
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
api.onMessage("setBchServers", (p, m) => {
fromPanel(m);
const patch = p || {};
if ("servers" in patch) {
const list = Array.isArray(patch.servers) ? patch.servers.map((s) => String(s).trim()).filter(Boolean) : [];
for (const s of list) if (!/^wss?:\/\/[^/\s]+$/i.test(s)) throw new Error(`server must be ws(s)://host:port — got ${s}`);
api.storage.set("servers", list.length ? list : null);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// Push the new server list into every mounted BCH wallet.
for (const rt of ctx.runtimes.values()) {
if (rt.entry.chain === "bch" && rt.adapter) rt.adapter.setServers(bchServerList(api));
}
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
return fullState();
});
api.onMessage("setAccountPath", (p, m) => {
fromPanel(m);
const patch = p || {};
const id = String(patch.id || selectedWalletId());
const entry = walletEntries().find((w) => w.id === id);
if (!entry) throw new Error("unknown wallet");
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
if (!["bch", "dgb", "btc"].includes(entry.chain)) throw new Error("account path is a BCH/BTC/DGB-only setting");
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const v = String(patch.accountPath || "").trim();
feat(theseus/aegis): Bitcoin adapter (mainnet + testnet3, BIP84 native SegWit) 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).
2026-09-07 21:15:45 +02:00
if (v && !/^m(\/\d+'?)+$/.test(v)) throw new Error("derivation path must look like m/84'/0'/0'");
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const list = walletEntries();
const idx = list.findIndex((w) => w.id === id);
feat(theseus/aegis): BTC address-family picker (BIP44/49/84/86 + Taproot) 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.
2026-09-07 21:23:15 +02:00
// Per-network default: BTC/DGB come from the registry helper, BCH keeps
// its historical m/44'/145'/0'.
const dflt = entry.chain === "bch"
? "m/44'/145'/0'"
: (defaultAccountPathFor(COINS[entry.chain], entry.network) || "m/84'/0'/0'");
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
list[idx] = { ...list[idx], accountPath: v || dflt };
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
writeWallets(api, list);
const rt = ctx.runtimes.get(id);
if (rt && rt.adapter) { try { rt.adapter.dispose(); } catch {} rt.adapter = null; rt.phase = "locked"; }
mountWallet(list[idx]);
return fullState();
});
feat(theseus/aegis): Ethereum + Solana adapters, DGB address-family picker, Aegis-branded shield Multi-currency coverage matches what aegis.x has been advertising: BCH, TRX, SC, DGB, ETH, SOL — six coins, two-step coin/network picker for each. Panel logos, favicon and fallback all read as Aegis. - Ethereum (lib/chain-eth.js): mainnet + Sepolia. BIP44 m/44'/60'/0'/0/0 → secp256k1 → EIP-55 checksummed hex address (verified against MetaMask's canonical abandon×11 vector 0x9858EfFD23…4EcaEda94). JSON-RPC backend (Cloudflare mainnet, PublicNode Sepolia by default; per-wallet override). EIP-1559 send with an inline RLP encoder + secp256k1 recoverable sign; broadcast via eth_sendRawTransaction. personal_sign message signing follows the \x19Ethereum Signed Message:\n prefix. - Solana (lib/chain-sol.js): mainnet-beta + devnet. SLIP-0010 ed25519 derivation at m/44'/501'/0'/0' (all-hardened), base58 address (@noble ed25519). SLIP-0010 layer verified against spec Test Vector 1 in scratchpad/verify-slip10.mjs. Native SOL transfer via the system program with compact-u16 message serialization + ed25519 sign + sendTransaction. Devnet gets a faucet.solana.com link in Receive; the panel appends ?cluster=devnet when opening the explorer. - DGB address family selector (lib/chain-dgb.js already carried the paths): the Settings block now shows a Native SegWit / Taproot / Wrapped SegWit / Legacy P2PKH picker. Selecting a family auto-fills the derivation-path input with that family's default; Apply rebuilds the wallet against the new path. Address families exposed via chainMeta.addressFamilies so the panel can render them from data. - Panel branding: inline SVG shield (hexagonal aspis, same silhouette as the aegis.x hero) replaces the "?" fallback in logoSvg() and is what the header shows before a wallet is selected. Data-URI favicon wired into panel.html so the Theseus sidebar tab icon reads as Aegis rather than a chain-specific coin mark. - QR payloads now follow each chain's own URI scheme (BIP21 for BCH/DGB, EIP-681 for ETH, Solana Pay for SOL) so external scanners route the scan to the right wallet. Not shipped: EIP-1193 provider (window.ethereum) and wallet-adapter protocol (window.solana). The signing paths exist; only the page-inject bridge glue is missing. History for ETH/SOL is also empty in this rev — both need indexer plumbing (Etherscan V2 for ETH, getSignaturesForAddress + getTransaction pagination for SOL).
2026-09-07 20:31:27 +02:00
// ETH/SOL: per-wallet RPC URL, live-swap without key rebuild.
api.onMessage("setRpcUrl", (p, m) => {
fromPanel(m);
const patch = p || {};
const id = String(patch.id || selectedWalletId());
const entry = walletEntries().find((w) => w.id === id);
if (!entry) throw new Error("unknown wallet");
if (entry.chain !== "eth" && entry.chain !== "sol") throw new Error("RPC URL is an ETH/SOL setting");
const v = String(patch.rpcUrl || "").trim();
if (v && !/^https?:\/\/[^\s]+$/i.test(v)) throw new Error("RPC URL must start with http:// or https://");
api.storage.set(`wallets/${id}/rpcUrl`, v);
const rt = ctx.runtimes.get(id);
if (rt && rt.adapter && typeof rt.adapter.setRpcUrl === "function") {
rt.adapter.setRpcUrl(v);
rt.adapter.refresh().catch((e) => api.log(`[${id}] refresh:`, e?.message || e));
}
emitState();
return fullState();
});
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
// Sia-only: per-wallet walletd URL. Live: pushes the URL into the adapter
// without rebuilding the keys (the seed stays derived from the same
// vault path — only the node the wallet talks to changes).
api.onMessage("setWalletdUrl", (p, m) => {
fromPanel(m);
const patch = p || {};
const id = String(patch.id || selectedWalletId());
const entry = walletEntries().find((w) => w.id === id);
if (!entry) throw new Error("unknown wallet");
if (entry.chain !== "sc") throw new Error("walletd URL is a Sia-only setting");
const v = String(patch.walletdUrl || "").trim();
if (v && !/^https?:\/\/[^\s]+$/i.test(v)) throw new Error("walletd URL must start with http:// or https://");
api.storage.set(`wallets/${id}/walletdUrl`, v);
const rt = ctx.runtimes.get(id);
if (rt && rt.adapter) {
rt.adapter.setWalletdUrl(v);
if (v) rt.adapter.startPolling();
rt.adapter.refresh(true).catch((e) => api.log(`[${id}] refresh:`, e?.message || e));
}
emitState();
return fullState();
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// Live plan preview for the selected wallet.
api.onMessage("planSend", async (p, m) => {
fromPanel(m);
const rt = requireSelected();
const plan = await Promise.resolve(rt.adapter.plan(p || {}));
return describePlan(plan, rt.entry.chain, rt.entry.network);
});
feat(theseus/aegis): SPL token support (view balances + send) SPL tokens now show up in the Solana wallet — balances on the Receive card, an asset picker on Send that flips the amount input into the token's own units. Sends build a TransferChecked + auto-create the recipient's Associated Token Account (idempotently) in the same transaction, so the user never has to fund an ATA by hand. - lib/sol-spl.js: SPL primitives that don't need @solana/web3.js. TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, findProgramAddress (PDA loop backed by an ed25519 is-on-curve check via @noble Point.fromBytes), associatedTokenAddress (matches the spl-token JS seed layout: [owner, tokenProgram, mint]), transferCheckedInstruction (discriminator 12, u64 amount, decimals byte), createATAIdempotentInstruction (associated-token program discriminator 1). A small known-mint registry ships inline for USDC / USDT / wSOL on mainnet + USDC on devnet — everything else falls back to a truncated mint address in the UI. - Message assembler classifies every unique pubkey into writable-signed / readonly-signed / writable-unsigned / readonly-unsigned, sorts the fee payer first, and serializes header + accountKeys + blockhash + instructions using Solana's compact-u16 short-vec encoding. Same wire shape @solana/web3.js produces from Transaction.serializeMessage. - lib/chain-sol.js: snapshot() now carries a tokens[] array of {mint, symbol, name, decimals, balance, tokenAccount, tokenProgram, isKnown, isToken2022}. Fetched via getTokenAccountsByOwner against both the classic Token program and Token-2022. New planTokenTransfer + signAndBroadcastToken handle a full send (TransferChecked + optional CreateATAIdempotent) in one wire. - Panel: Send tab gained an Asset dropdown (SOL / <each token>) that only shows for SOL wallets with tokens. Picking a token flips the unit picker's big-unit to the token symbol, amount goes in the token's own decimals, planTokenSend + sendToken take over from planSend/send. Receive tab gained a Tokens card listing each SPL balance with a per-row Send button that pre-fills the asset picker. - Verified in scratchpad: ATA derivation runs the PDA loop correctly (owner pubkey passes isOnCurve, derived ATA does not — the definitional property of a Program-Derived Address). Cross-check the ATA for any (owner, mint) on Phantom / Solscan / spl-token JS and the value matches. Known limits: - No token metadata lookup on-chain — mints outside the built-in registry show up with a truncated mint address as symbol. Wiring Metaplex Metadata program reads would let unknown tokens show their real names. - Send is single-signer only (the wallet is the fee payer, sender and sole required signer). Multi-sig SPL transfers work via the dapp bridge (window.solana.signAndSendTransaction, which already handles partial signatures).
2026-09-07 23:55:09 +02:00
// SPL token plan/send — only meaningful when the selected wallet is SOL.
api.onMessage("planTokenSend", async (p, m) => {
fromPanel(m);
const rt = requireSelected();
if (rt.entry.chain !== "sol" || typeof rt.adapter.planTokenTransfer !== "function") {
throw new Error("token send is a Solana-only flow");
}
const plan = await rt.adapter.planTokenTransfer(p || {});
return {
recipients: plan.recipients, fee: String(plan.fee), feeRate: String(plan.feeRate),
inputs: 0, change: "0", total: plan.total, mint: plan.mint, decimals: plan.decimals,
};
});
api.onMessage("sendToken", async (p, m) => {
fromPanel(m);
const rt = requireSelected();
if (rt.entry.chain !== "sol") throw new Error("token send is Solana-only");
const plan = await rt.adapter.planTokenTransfer(p || {});
const meta = chainMeta("sol", rt.entry.network);
const tokenInfo = (rt.adapter.snapshot().tokens || []).find((t) => t.mint === plan.mint) || {};
const rows = [
{ label: "To", value: plan.recipients[0].to, mono: true },
{ label: "Amount", value: `${fmtTokenAmount(plan.recipients[0].value, plan.decimals)} ${tokenInfo.symbol || "token"}`, strong: true },
{ label: "Mint", value: plan.mint, mono: true },
{ label: "Network fee", value: `~${fmtValue(Number(plan.fee), meta.decimals)} SOL${(plan._spl && plan._spl.destExists === false) ? " (includes new token account rent)" : ""}` },
{ label: "Wallet", value: `${rt.entry.label} — Solana · ${rt.entry.network}` },
];
const pick = await api.approvalModal({
title: `Send ${tokenInfo.symbol || "SPL token"}?`,
origin: "Aegis wallet panel",
rows,
actions: [{ id: "send", label: "Send", primary: true }],
});
if (pick !== "send") throw new Error("cancelled");
return rt.adapter.signAndBroadcastToken(plan);
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// Execute a send with approval overlay.
api.onMessage("send", async (p, m) => {
fromPanel(m);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const rt = requireSelected();
const plan = await Promise.resolve(rt.adapter.plan(p || {}));
const d = describePlan(plan, rt.entry.chain, rt.entry.network);
const meta = chainMeta(rt.entry.chain, rt.entry.network);
const rows = [
{ label: "To", value: d.recipients[0].to, mono: true },
{ label: "Amount", value: `${fmtValue(d.recipients[0].value, meta.decimals)} ${meta.ticker}`, strong: true },
{ label: "Fee", value: rt.entry.chain === "bch" ? `${plan.fee} sat (${plan.feeRate} sat/B)` : `${fmtValue(plan.fee, meta.decimals)} ${meta.ticker}` },
{ label: "Total", value: `${fmtValue(d.total, meta.decimals)} ${meta.ticker}` },
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
{ label: "Wallet", value: `${rt.entry.label}${meta.coinLabel} · ${meta.networkLabel}` },
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
];
const pick = await api.approvalModal({
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
title: `Send ${meta.ticker}?`,
origin: "Aegis wallet panel",
rows,
actions: [{ id: "send", label: "Send", primary: true }],
});
if (pick !== "send") throw new Error("cancelled");
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
return rt.adapter.signAndBroadcast(plan);
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
api.onMessage("recovery", async (p, m) => {
fromPanel(m);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const id = String(p && p.id || selectedWalletId());
const rt = requireWallet(id);
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
if (typeof rt.adapter.recovery !== "function") throw new Error("this chain does not expose recovery details");
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const r = rt.adapter.recovery();
const out = { accountPath: r.accountPath, xpub: r.xpub, purpose: rt.entry.purpose };
if (p && p.reveal) {
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
// Sia's "xprv" is really the 32-byte wallet seed (walletd KeyFromSeed);
// BCH/DGB's is the account xprv. Warning copy fits both.
const pick = await api.approvalModal({
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
title: rt.entry.chain === "sc" ? "Reveal the wallet seed?" : "Reveal the account private key?",
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
origin: "Aegis wallet panel",
feat(theseus/aegis): fold Sia into the addon; add DGB (BIP84 native SegWit) Aegis now covers four coins across two-step coin+network picks: BCH (mainnet + chipnet), TRX (mainnet + Nile), SC (mainnet), DGB (mainnet). - Sia (SC): pulled the standalone siawallet's lib into bundled-addons/bchwallet/lib/sia/ and wrote lib/chain-sia.js exposing the common adapter shape. The very first SC wallet the user adds in Aegis reuses purpose "siawallet/mainnet/0" so pre-Aegis funds carry over automatically; subsequent SC sub-accounts start at "bchwallet/sc/mainnet/1". Per-wallet walletd URL setting; empty URL shows a "Point Aegis at a walletd node" gate in the panel. - Vault-derive gate now honors a manifest-declared `absorbs` list, so Aegis's addon.json can list `absorbs: ["siawallet"]` and the derive() guard accepts paths under either the current id or the absorbed one — the mechanism a superseding add-on uses to inherit an older add-on's keyspace without orphaning funds. - DigiByte (DGB): lib/chain-dgb.js ports the relevant bits of the SilentCode Digibyte design — SLIP-44 coin type 20, BIP84 native SegWit (m/84'/20'/0'/0/x → dgb1q…) via ripemd160(sha256(pubkey)) + bech32. ElectrumX-DGB backend reuses lib/electrum.js (public wss:50022 pool). BIP143 P2WPKH sighash + witness-tx serialize implemented inline (no FORKID — DGB uses standard Bitcoin sighash). Derivation cross-checked against a known BIP39 vector in scratchpad/verify-dgb.mjs — the address for "abandon×11 about, m/84'/20'/0'/0/0" is dgb1q9gmf0pv8jdymcly6lz6fl7lf6mhslsd72e2jq8, matching iancoleman.io/bip39. - Panel: SVG coin logos for SC (green disc with S) and DGB (blue octagon with D) alongside the BCH/TRX marks. Chain-specific settings block per coin (walletd URL for SC; derivation path for DGB). Balance render uses BigInt-safe arithmetic so 24-decimal SC amounts don't lose precision on the way through the panel; amount input on SC returns a hastings string. - Every chain adapter's snapshot fits the panel's shared shape (address/balance/history/etc.), so future chains only need a new chain-<x>.js file, a COINS registry entry, a matching case in mountWallet, and an SVG logo. Standalone siawallet addon stays as-is on disk; users can delete it once they've confirmed Aegis shows the same balance. Nothing here disables it.
2026-09-07 01:56:25 +02:00
body: "Anyone holding this can spend every coin in this wallet. It stays on screen until you close the Settings tab.",
actions: [{ id: "reveal", label: "Reveal", danger: true }],
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (pick === "reveal") out.xprv = r.xprv;
}
return out;
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
api.onMessage("permissions", (_p, m) => { fromPanel(m); return permissions(api); });
api.onMessage("revoke", (p, m) => {
fromPanel(m);
const perms = permissions(api);
delete perms[String(p && p.origin || "")];
api.storage.set("permissions", perms);
return perms;
});
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// One "describePlan" is enough for both chains because plan() returns a
// common shape: {recipients:[{to,value}], fee, feeRate, total, inputs:[]…}.
function describePlan(plan) {
const sent = plan.recipients.reduce((a, r) => a + r.value, 0);
return {
recipients: plan.recipients, fee: plan.fee, feeRate: plan.feeRate,
inputs: (plan.inputs || []).length, change: plan.change || null,
total: plan.total != null ? plan.total : (sent + plan.fee),
};
}
// ---- dapp bridges (page → activate()) --------------------------------------
// Permission model stays the same as the single-wallet build for BCH:
// { [origin]: { readAddress:true, sendTx:{capSats,usedSats,grantedAt},
// trx: { readAddress:true, network } } }
// The BCH bridge always talks to the LEGACY default BCH wallet (the .x pages
// pre-date multi-wallet and cannot pick between them). The Tron bridge talks
// to the currently-SELECTED Tron wallet; if none is selected, requests fail.
const BCH_ALLOWANCES = [100000, 1000000, 10000000]; // 0.001, 0.01, 0.1 BCH
const pendingByOrigin = new Set();
function permissions(api) { const p = api.storage.get("permissions", {}); return p && typeof p === "object" ? p : {}; }
async function withOriginLock(origin, fn) {
if (pendingByOrigin.has(origin)) throw new Error("a wallet request from this site is already waiting for approval");
pendingByOrigin.add(origin);
try { return await fn(); } finally { pendingByOrigin.delete(origin); }
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// Only .x sites (BCNR-native TLD) get the BCH bridge, matching the pre-
// multi-wallet gate. Widening the manifest to https://*/* makes the Tron
// bridge available everywhere; the BCH side enforces its narrower rule
// inside the handlers.
function isBchOrigin(origin) {
try { const h = new URL(origin).hostname; return /\.x$/.test(h); }
catch { return false; }
}
function legacyBchRuntime() {
const rt = ctx.runtimes.get(LEGACY_BCH_WALLET_ID);
if (!rt || rt.phase !== "ready" || !rt.adapter) throw new Error("wallet is not ready (vault locked?)");
return rt;
}
// The Tron bridge routes to the currently-selected wallet if it is Tron;
// otherwise it looks for the first ready Tron wallet on the selected network
// hint; else rejects with "no tron wallet".
function activeTronRuntime() {
const selId = selectedWalletId();
const selRt = selId && ctx.runtimes.get(selId);
if (selRt && selRt.entry.chain === "trx" && selRt.phase === "ready") return selRt;
for (const rt of ctx.runtimes.values()) if (rt.entry.chain === "trx" && rt.phase === "ready") return rt;
throw new Error("no Tron wallet available — add one in the Aegis sidebar");
}
function registerPageMessages(api) {
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// ---- BCH bridge (unchanged behavior; wallet source is legacy default) ----
api.onMessage("getAddress", async (_p, m) => {
const origin = fromPage(m);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (!isBchOrigin(origin)) throw new Error("this site is not on a Bitcoin Cash origin");
const rt = legacyBchRuntime();
const perms = permissions(api);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (perms[origin] && perms[origin].readAddress) return rt.adapter.current().address;
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Share your Bitcoin Cash address?",
origin,
body: "The site will see your current receiving address and can look up its balance and history on the public chain.",
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
rows: [{ label: "Address", value: rt.adapter.current().address, mono: true }],
actions: [{ id: "allow", label: "Share", primary: true }],
checkbox: { id: "always", label: "Always allow this site to see my address" },
});
if (!pick.startsWith("allow")) throw new Error("user rejected");
if (pick === "allow+always") { perms[origin] = { ...(perms[origin] || {}), readAddress: true }; api.storage.set("permissions", perms); emitState(); }
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
return rt.adapter.current().address;
});
});
api.onMessage("signAndSend", async (p, m) => {
const origin = fromPage(m);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (!isBchOrigin(origin)) throw new Error("this site is not on a Bitcoin Cash origin");
const rt = legacyBchRuntime();
return withOriginLock(origin, async () => {
let plan;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
try { plan = rt.adapter.plan(p || {}); }
catch (e) { throw new Error(/insufficient funds|too small/i.test(e?.message) ? "insufficient funds" : e?.message || String(e)); }
const d = describePlan(plan);
if (d.recipients.length > 8) throw new Error("too many outputs");
const perms = permissions(api);
const budget = perms[origin] && perms[origin].sendTx;
const remaining = budget ? Math.max(0, (budget.capSats | 0) - (budget.usedSats | 0)) : 0;
if (budget && d.total <= remaining) {
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const r = await rt.adapter.signAndBroadcast(plan);
budget.usedSats = (budget.usedSats | 0) + d.total;
api.storage.set("permissions", perms);
emitState();
api.log(`silent send ${d.total} sat for ${origin}, ${remaining - d.total} sat of allowance left`);
return { txid: r.txid };
}
const rows = d.recipients.map((r, i) => ({ label: d.recipients.length > 1 ? `To #${i + 1}` : "To", value: r.to, mono: true }));
rows.push({ label: "Amount", value: fmtBch(d.recipients.reduce((a, r) => a + r.value, 0)) + " BCH", strong: true });
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
rows.push({ label: "Fee", value: `${plan.fee} sat (${plan.feeRate} sat/B)` });
rows.push({ label: "Total", value: fmtBch(d.total) + " BCH" });
const pick = await api.approvalModal({
title: "Send Bitcoin Cash?",
origin,
body: budget
? `This payment is over what is left of the site's allowance (${fmtBch(remaining)} BCH). Check the address and amount.`
: "This site is asking your wallet to pay. Check the address and amount.",
rows,
actions: [{ id: "send", label: "Send", primary: true }],
select: {
id: "cap", label: "Afterwards",
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
options: [{ value: "", label: "ask every time" }, ...BCH_ALLOWANCES.map((s) => ({ value: String(s), label: `allow up to ${fmtBch(s)} BCH more without asking` }))],
},
});
const [action, ...flags] = pick.split("+");
if (action !== "send") throw new Error("user rejected");
const cap = flags.find((f) => f.startsWith("cap="));
const capSats = cap ? Number(cap.slice(4)) : 0;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (BCH_ALLOWANCES.includes(capSats)) {
perms[origin] = { ...(perms[origin] || {}), sendTx: { capSats, usedSats: 0, grantedAt: Date.now() } };
api.storage.set("permissions", perms);
} else if (budget) {
delete perms[origin].sendTx;
api.storage.set("permissions", perms);
}
emitState();
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const r = await rt.adapter.signAndBroadcast(plan);
return { txid: r.txid };
});
});
api.onMessage("signMessage", async (p, m) => {
const origin = fromPage(m);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
if (!isBchOrigin(origin)) throw new Error("this site is not on a Bitcoin Cash origin");
const rt = legacyBchRuntime();
const message = String(p && p.message != null ? p.message : "");
if (message.length > 4096) throw new Error("message too long");
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Sign a message?",
origin,
body: "Signing proves you control the address below. It moves no coins.",
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
rows: [
{ label: "Message", value: message.length > 400 ? message.slice(0, 400) + "…" : message, mono: true },
{ label: "Address", value: rt.adapter.current().address, mono: true },
],
actions: [{ id: "sign", label: "Sign", primary: true }],
});
if (pick !== "sign") throw new Error("user rejected");
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
return rt.adapter.signMessage(message);
});
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// ---- Tron bridge (tronWeb / tronLink) -----------------------------------
api.onMessage("trx.requestAccounts", async (_p, m) => {
const origin = fromPage(m);
const rt = activeTronRuntime();
const perms = permissions(api);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const alreadyOK = perms[origin] && perms[origin].trx && perms[origin].trx.readAddress;
const snap = rt.adapter.snapshot();
if (alreadyOK) return { code: 200, address: snap.address, network: snap.network };
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Connect this site to your Tron wallet?",
origin,
body: "The site will see this address and can build transactions for you to sign.",
rows: [
{ label: "Address", value: snap.address, mono: true },
{ label: "Network", value: snap.network === "nile" ? "Nile testnet" : "Tron mainnet" },
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
{ label: "Wallet", value: `${rt.entry.label} — Tron · ${snap.network === "nile" ? "Nile testnet" : "Mainnet"}` },
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
],
actions: [{ id: "allow", label: "Connect", primary: true }],
checkbox: { id: "always", label: "Always allow this site to see this address" },
});
if (!pick.startsWith("allow")) throw new Error("user rejected");
if (pick === "allow+always") {
perms[origin] = { ...(perms[origin] || {}), trx: { readAddress: true, network: snap.network } };
api.storage.set("permissions", perms);
emitState();
}
return { code: 200, address: snap.address, network: snap.network };
});
});
api.onMessage("trx.getAccount", (_p, m) => {
const origin = fromPage(m);
const perms = permissions(api);
if (!(perms[origin] && perms[origin].trx && perms[origin].trx.readAddress)) throw new Error("not connected — call tron_requestAccounts first");
const rt = activeTronRuntime();
const snap = rt.adapter.snapshot();
return { address: snap.address, network: snap.network };
});
// Sign an arbitrary raw_data_hex the dapp built (with its own tronWeb).
// The wallet never guesses the intent — the approval overlay shows the
// decoded contract type and destination when it can, and always the txID.
api.onMessage("trx.signTransaction", async (p, m) => {
const origin = fromPage(m);
const rt = activeTronRuntime();
const perms = permissions(api);
if (!(perms[origin] && perms[origin].trx && perms[origin].trx.readAddress)) throw new Error("not connected — call tron_requestAccounts first");
const tx = p && p.transaction;
if (!tx || typeof tx !== "object" || !tx.raw_data_hex || !tx.raw_data) throw new Error("bad transaction");
return withOriginLock(origin, async () => {
const contract = (tx.raw_data.contract || [])[0];
const type = contract?.type || "Contract";
const rows = [{ label: "Type", value: type }, { label: "Tx ID", value: tx.txID || "(unset)", mono: true }];
if (type === "TransferContract") {
const v = contract.parameter?.value || {};
try {
const to = v.to_address ? ctx.d.tronAdapter.hexToAddress(v.to_address) : (v.to_address || "");
const amount = Number(v.amount || 0);
rows.splice(1, 0, { label: "To", value: to, mono: true }, { label: "Amount", value: `${fmtTrx(amount)} TRX`, strong: true });
} catch {}
}
feat(theseus/aegis): SVG coin logos, two-step coin/network picker, BCH Chipnet - Inline SVG logos for BCH (green disc + ₿) and TRX (red disc + geometric T) replace the 🟨/🔴/🔵 emoji in the sidebar header and wallet picker rows. The approval overlay stays text-only ("Wallet: <name> — BCH · Mainnet") because that surface renders plain rows, not HTML. - Wallet picker's "Add wallet" is now two-step: click a coin to expand its networks, then click a network to create the wallet. The flat list is gone. - BCH Chipnet is a real chain option now: bchtest cashaddr prefix, m/44'/1'/0' derivation (BIP44 testnet coin type), bundled Chipnet electrum defaults, chipnet.imaginary.cash explorer, tbch.googol.cash faucet link in Receive. The shared electrum-servers setting stays mainnet-only in this rev; Chipnet uses adapter-embedded defaults. - lib/chain-bch.js gained a BCH_NETWORKS table so mainnet vs chipnet differences (prefix, path, servers, explorer, faucet) live in one place. - Registry is grouped by coin ({networks:{…}}) instead of a flat chain:network map — snapshot exposes coins[] for the panel and adds coinLabel/networkLabel/testnet fields per wallet. - Testnet wallets get a small "TEST" tag next to the network name so the user can never mistake a chipnet or Nile balance for real money. Legacy BCH mainnet index 0 derivation unchanged (bchwallet/mainnet/0 → m/44'/145'/0' → bitcoincash prefix); the network parameter defaults to "mainnet" and BCH_NETWORKS.mainnet reproduces the pre-change constants.
2026-09-07 01:07:31 +02:00
rows.push({ label: "Wallet", value: `${rt.entry.label} — Tron · ${rt.adapter.snapshot().network === "nile" ? "Nile testnet" : "Mainnet"}` });
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const pick = await api.approvalModal({
title: "Sign a Tron transaction?",
origin,
body: "The site built this transaction. Check the type, amount, and destination before signing.",
rows,
actions: [{ id: "sign", label: "Sign", primary: true }],
});
if (pick !== "sign") throw new Error("user rejected");
const sig = rt.adapter.signRawData(tx.raw_data_hex);
const signed = { ...tx, signature: [sig] };
return signed;
});
});
api.onMessage("trx.sendRawTransaction", async (p, m) => {
const origin = fromPage(m);
const rt = activeTronRuntime();
const perms = permissions(api);
if (!(perms[origin] && perms[origin].trx && perms[origin].trx.readAddress)) throw new Error("not connected — call tron_requestAccounts first");
const signedTx = p && p.transaction;
if (!signedTx || !signedTx.raw_data_hex || !Array.isArray(signedTx.signature)) throw new Error("bad signed tx");
// No approval here — broadcasting a *signed* tx does not add any risk
// the sign step didn't already carry. Sites that don't want an extra
// network round-trip pass {broadcast:true} to sign; we support both.
return rt.adapter.broadcastSignedTx(signedTx);
});
api.onMessage("trx.signMessageV2", async (p, m) => {
const origin = fromPage(m);
const rt = activeTronRuntime();
const perms = permissions(api);
if (!(perms[origin] && perms[origin].trx && perms[origin].trx.readAddress)) throw new Error("not connected — call tron_requestAccounts first");
const message = String(p && p.message != null ? p.message : "");
if (message.length > 4096) throw new Error("message too long");
return withOriginLock(origin, async () => {
const snap = rt.adapter.snapshot();
const pick = await api.approvalModal({
title: "Sign a Tron message?",
origin,
body: "Signing proves you control this address. It moves no coins.",
rows: [
{ label: "Message", value: message.length > 400 ? message.slice(0, 400) + "…" : message, mono: true },
{ label: "Address", value: snap.address, mono: true },
],
actions: [{ id: "sign", label: "Sign", primary: true }],
});
if (pick !== "sign") throw new Error("user rejected");
return rt.adapter.signMessageV2(message);
});
});
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
// ---- Ethereum (EIP-1193) ---------------------------------------------
// Routes the same way as Tron: pick the currently-selected ETH wallet if
// any; else the first ready ETH wallet. Chain switches happen at the
// wallet-picker level, not here — dapps that call wallet_switchEthereumChain
// get a friendly "switch wallet in the Aegis sidebar" error.
function activeEthRuntime() {
const selId = selectedWalletId();
const selRt = selId && ctx.runtimes.get(selId);
if (selRt && selRt.entry.chain === "eth" && selRt.phase === "ready") return selRt;
for (const rt of ctx.runtimes.values()) if (rt.entry.chain === "eth" && rt.phase === "ready") return rt;
throw new Error("no Ethereum wallet available — add one in the Aegis sidebar");
}
function ethConnectedFor(origin) {
const p = permissions(api)[origin];
return !!(p && p.eth && p.eth.readAddress);
}
api.onMessage("eth.requestAccounts", async (_p, m) => {
const origin = fromPage(m);
const rt = activeEthRuntime();
const snap = rt.adapter.snapshot();
const chainIdHex = "0x" + Number(snap.chainId).toString(16);
const networkVersion = String(snap.chainId);
const perms = permissions(api);
if (perms[origin] && perms[origin].eth && perms[origin].eth.readAddress) {
return { address: snap.address, chainIdHex, networkVersion };
}
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Connect this site to your Ethereum wallet?",
origin,
body: "The site will see this address and can build transactions for you to sign.",
rows: [
{ label: "Address", value: snap.address, mono: true },
{ label: "Network", value: snap.network === "mainnet" ? "Ethereum mainnet" : "Sepolia testnet" },
{ label: "Wallet", value: `${rt.entry.label} — Ethereum · ${snap.network}` },
],
actions: [{ id: "allow", label: "Connect", primary: true }],
checkbox: { id: "always", label: "Always allow this site to see this address" },
});
if (!pick.startsWith("allow")) throw new Error("user rejected");
if (pick === "allow+always") {
perms[origin] = { ...(perms[origin] || {}), eth: { readAddress: true, chainId: snap.chainId } };
api.storage.set("permissions", perms);
emitState();
}
return { address: snap.address, chainIdHex, networkVersion };
});
});
api.onMessage("eth.personalSign", async (p, m) => {
const origin = fromPage(m);
if (!ethConnectedFor(origin)) throw new Error("not connected — call eth_requestAccounts first");
const rt = activeEthRuntime();
const message = String(p && p.message != null ? p.message : "");
if (message.length > 4096) throw new Error("message too long");
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Sign an Ethereum message?",
origin,
body: "Signing proves you control this address. It moves no ETH.",
rows: [
{ label: "Message", value: message.length > 400 ? message.slice(0, 400) + "…" : message, mono: true },
{ label: "Address", value: rt.adapter.snapshot().address, mono: true },
],
actions: [{ id: "sign", label: "Sign", primary: true }],
});
if (pick !== "sign") throw new Error("user rejected");
return rt.adapter.signMessage(message);
});
});
api.onMessage("eth.sendTransaction", async (p, m) => {
const origin = fromPage(m);
if (!ethConnectedFor(origin)) throw new Error("not connected — call eth_requestAccounts first");
const rt = activeEthRuntime();
const tx = (p && p.tx) || {};
if (!tx.to) throw new Error("tx.to required");
// MetaMask semantics: `value` and `gas`/`gasLimit` are hex-encoded wei;
// convert to numbers/bigints Aegis's own plan() understands.
const valueWei = tx.value ? BigInt(tx.value).toString() : "0";
return withOriginLock(origin, async () => {
const snap = rt.adapter.snapshot();
const plan = await rt.adapter.plan({ to: tx.to, amount: valueWei, sendMax: false });
const meta = chainMeta("eth", rt.entry.network);
const pick = await api.approvalModal({
title: "Send Ethereum transaction?",
origin,
body: tx.data && tx.data !== "0x" ? "This transaction carries call data (a contract call). Check the destination + value carefully." : "This site is asking your wallet to send ETH.",
rows: [
{ label: "To", value: plan.recipients[0].to, mono: true },
{ label: "Amount", value: `${fmtValue(plan.recipients[0].value, meta.decimals)} ETH`, strong: true },
{ label: "Fee (est.)", value: `${fmtValue(plan.fee, meta.decimals)} ETH` },
{ label: "Wallet", value: `${rt.entry.label} — Ethereum · ${snap.network}` },
],
actions: [{ id: "send", label: "Send", primary: true }],
});
if (pick !== "send") throw new Error("user rejected");
const r = await rt.adapter.signAndBroadcast(plan);
return { txid: r.txid };
});
});
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
api.onMessage("eth.signTypedData", async (p, m) => {
const origin = fromPage(m);
if (!ethConnectedFor(origin)) throw new Error("not connected — call eth_requestAccounts first");
const rt = activeEthRuntime();
// Dapps send typedData as either a JSON string (older MetaMask spec) or
// an object (v4). Accept both; the encoder wants an object.
let td = p && p.typedData;
if (typeof td === "string") { try { td = JSON.parse(td); } catch (e) { throw new Error("typedData: JSON parse failed: " + e.message); } }
if (!td || typeof td !== "object") throw new Error("typedData required");
// Compute the digest first — if the encoder rejects the input the user
// never sees an approval overlay for a broken payload.
let digest;
try { digest = ctx.d.eip712.digest(td); }
catch (e) { throw new Error("EIP-712 encode failed: " + e.message); }
// Approval overlay: show domain (name + chain), primary type, and a
// truncated JSON preview of the message so the user has a fighting
// chance to spot phishing.
const dom = td.domain || {};
const domainSummary = [dom.name, dom.version && `v${dom.version}`, dom.chainId && `chain ${dom.chainId}`].filter(Boolean).join(" · ") || "(no domain)";
const messagePreview = JSON.stringify(td.message, null, 2);
const preview = messagePreview.length > 600 ? messagePreview.slice(0, 600) + "…" : messagePreview;
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Sign typed data (EIP-712)?",
origin,
body: "The site is asking you to sign a structured message. Verify the domain matches the site you're on — a mismatched domain is the classic phishing tell.",
rows: [
{ label: "Domain", value: domainSummary },
{ label: "Primary type", value: String(td.primaryType || "") },
{ label: "Message", value: preview, mono: true },
{ label: "Address", value: rt.adapter.snapshot().address, mono: true },
],
actions: [{ id: "sign", label: "Sign", primary: true }],
});
if (pick !== "sign") throw new Error("user rejected");
return rt.adapter.signTypedDataDigest(digest);
});
});
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
api.onMessage("eth.switchChain", async (p, m) => {
fromPage(m);
feat(theseus/aegis): EIP-3085 wallet_addEthereumChain + EIP-3326 switchChain Aegis now handles the standard MetaMask try-switch-then-add flow. A dapp that wants to route through Polygon (or Base, or Arbitrum, or any other EVM the Silent Mode user hasn't added yet) calls the pair the industry already wrote for it — Aegis registers the chain, provisions a wallet on it under the same vault seed, auto-connects the origin, fires chainChanged, and hands the dapp back a provider pointed at the new chain. No sidebar detour, no Custom RPC copy-paste. Users still see every chain in the picker post-add and can revoke sites in Settings. - lib/chain-eth.js: EthWallet accepts a customNetwork override ({id, label, chainId, defaultRpc, explorerTx, explorerAddr, ticker}). When present it replaces the NETWORKS lookup so mainnet+Sepolia ship built-in and every EIP-3085 chain is a runtime override the addon persists. The ticker flows into snapshot() so the send approval reads MATIC / BNB / whatever the chain's native currency is, not a hardcoded ETH. - index.js customEthChains storage: `{[chainId]: {chainName, rpcUrl, explorerTx, explorerAddr, ticker, addedAt, addedByOrigin}}`. Persisted under api.storage.customEthChains, so an added chain survives Theseus restarts. chainMeta("eth", "custom-<chainId>") synthesizes the meta from storage so the panel renders custom chains without needing them in COINS at module-load time. - eth.addChain handler (EIP-3085): approval overlay shows chain name, decimal + hex chain id, native ticker, RPC and explorer URLs (the phishing-signal quartet). On approval, persist config + create wallet with a custom-<chainId> network + auto-grant the origin readAddress on this chain. No-op success if the chain is already added. - eth.switchChain rewritten to be EIP-3326 correct: look up any ready ETH wallet whose adapter reports the requested chainId, make it the selected wallet, fire chainChanged. When no wallet matches, throw with .code = 4902 (the standard 'chain not added' code) so wagmi / RainbowKit / any 3326-aware dapp does the fallback wallet_addEthereumChain call in the same click. - eth.state handler: cheap {address, chainIdHex, networkVersion} peek for the origin's currently-connected wallet (no approval, no key access). The main-world bridge calls it after every switch/add to emit chainChanged + accountsChanged locally — the events MetaMask fires and RainbowKit listens for. - wallet-inject.js: routes wallet_addEthereumChain via eth.addChain, preserves the 4902 code across the postMessage boundary on switch failures, calls pullEthStateAndEmit() to fire the post-switch/add events.
2026-09-07 22:26:27 +02:00
const wantHex = String(p && p.chainId || "").toLowerCase();
const wantId = Number(wantHex);
if (!Number.isFinite(wantId) || wantId <= 0) throw new Error("bad chainId");
// Find any wallet already on that chain and select it.
for (const rt of ctx.runtimes.values()) {
if (rt.entry.chain !== "eth" || rt.phase !== "ready") continue;
const snap = rt.adapter.snapshot();
if (Number(snap.chainId) === wantId) {
api.storage.set("selectedWalletId", rt.entry.id);
emitState();
return null;
}
}
// EIP-3326: throw the well-known "chain not added" code so dapps fall
// back to wallet_addEthereumChain.
const err = new Error(`Aegis: chainId ${wantHex} is not added. Ask via wallet_addEthereumChain.`);
err.code = 4902;
throw err;
});
// EIP-3085: dapp asks Aegis to add a new EVM chain. On approval, we
// persist the chain config and create a wallet on it under the same
// vault-derived key. Existing addresses on that chain remain visible on
// whatever wallet they were funded on — a chain add doesn't move any
// key material, just registers the network.
api.onMessage("eth.addChain", async (p, m) => {
const origin = fromPage(m);
const spec = (p && p.params) || {};
const chainIdHex = String(spec.chainId || "").toLowerCase();
const chainId = Number(chainIdHex);
if (!chainIdHex.startsWith("0x") || !Number.isFinite(chainId) || chainId <= 0) {
throw new Error("wallet_addEthereumChain: chainId must be a positive hex integer (e.g. '0x89')");
}
const chainName = String(spec.chainName || "").trim() || `EVM #${chainId}`;
const rpcUrls = Array.isArray(spec.rpcUrls) ? spec.rpcUrls.filter((u) => /^https?:\/\//i.test(u)) : [];
const rpcUrl = rpcUrls[0];
if (!rpcUrl) throw new Error("wallet_addEthereumChain: at least one https rpcUrls entry is required");
const explorerBase = Array.isArray(spec.blockExplorerUrls) && spec.blockExplorerUrls[0]
? String(spec.blockExplorerUrls[0]).replace(/\/+$/, "")
: null;
const nc = spec.nativeCurrency || {};
const ticker = String(nc.symbol || "ETH").slice(0, 6).toUpperCase();
// Reject if a wallet on this chain already exists — no-op success per
// EIP-3085 conventions.
const existing = walletEntries().find((w) => w.chain === "eth"
&& (w.network === CUSTOM_ETH_PREFIX + chainId
|| (chainMeta("eth", w.network)?.chainId === chainId)));
if (existing) {
// Auto-connect the origin to this wallet — dapps expect the returned
// provider to be pointed at the added chain immediately.
const perms = permissions(api);
perms[origin] = { ...(perms[origin] || {}), eth: { readAddress: true, chainId } };
api.storage.set("permissions", perms);
api.storage.set("selectedWalletId", existing.id);
emitState();
return null;
}
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Add an Ethereum chain?",
origin,
body: "The site is asking to add a new EVM network to Aegis. Verify the RPC and chain ID — a malicious 'chain add' can point you at a fraudulent RPC that intercepts your reads or signs.",
rows: [
{ label: "Chain name", value: chainName },
{ label: "Chain ID", value: `${chainId} (${chainIdHex})` },
{ label: "Native ticker", value: ticker },
{ label: "RPC", value: rpcUrl, mono: true },
{ label: "Explorer", value: explorerBase || "(none)", mono: true },
],
actions: [{ id: "add", label: "Add chain", primary: true }],
});
if (pick !== "add") throw new Error("user rejected");
// Persist chain config + create a wallet on it.
const chains = customEthChains(api);
chains[String(chainId)] = {
chainId, chainName, rpcUrl,
explorerTx: explorerBase ? explorerBase + "/tx/" : "",
explorerAddr: explorerBase ? explorerBase + "/address/" : "",
ticker, addedAt: Date.now(), addedByOrigin: origin,
};
api.storage.set("customEthChains", chains);
const network = CUSTOM_ETH_PREFIX + chainId;
const meta = chainMeta("eth", network);
const list = walletEntries().slice();
const index = nextIndex(list, meta);
const purpose = meta.purposePrefix + index;
const id = makeWalletId(meta, index);
const label = `${chainName}${meta.short}`;
const entry = { id, label, chain: "eth", network, purpose, createdAt: Date.now() };
list.push(entry);
writeWallets(api, list);
api.storage.set("selectedWalletId", id);
// Grant the origin read access on this chain by default (they just
// approved adding it — implicit consent to also see the address).
const perms = permissions(api);
perms[origin] = { ...(perms[origin] || {}), eth: { readAddress: true, chainId } };
api.storage.set("permissions", perms);
ctx.runtimes.set(id, { entry, phase: "locked", error: null, adapter: null });
emitState();
await mountWallet(entry);
return null;
});
});
// Cheap state peek — used by the main-world bridge right after a switch
// or add to emit accountsChanged / chainChanged without needing another
// approval overlay. Only returns the wallet the origin already sees.
api.onMessage("eth.state", (_p, m) => {
const origin = fromPage(m);
if (!ethConnectedFor(origin)) return { address: null, chainIdHex: "0x0", networkVersion: "0" };
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
const rt = activeEthRuntime();
feat(theseus/aegis): EIP-3085 wallet_addEthereumChain + EIP-3326 switchChain Aegis now handles the standard MetaMask try-switch-then-add flow. A dapp that wants to route through Polygon (or Base, or Arbitrum, or any other EVM the Silent Mode user hasn't added yet) calls the pair the industry already wrote for it — Aegis registers the chain, provisions a wallet on it under the same vault seed, auto-connects the origin, fires chainChanged, and hands the dapp back a provider pointed at the new chain. No sidebar detour, no Custom RPC copy-paste. Users still see every chain in the picker post-add and can revoke sites in Settings. - lib/chain-eth.js: EthWallet accepts a customNetwork override ({id, label, chainId, defaultRpc, explorerTx, explorerAddr, ticker}). When present it replaces the NETWORKS lookup so mainnet+Sepolia ship built-in and every EIP-3085 chain is a runtime override the addon persists. The ticker flows into snapshot() so the send approval reads MATIC / BNB / whatever the chain's native currency is, not a hardcoded ETH. - index.js customEthChains storage: `{[chainId]: {chainName, rpcUrl, explorerTx, explorerAddr, ticker, addedAt, addedByOrigin}}`. Persisted under api.storage.customEthChains, so an added chain survives Theseus restarts. chainMeta("eth", "custom-<chainId>") synthesizes the meta from storage so the panel renders custom chains without needing them in COINS at module-load time. - eth.addChain handler (EIP-3085): approval overlay shows chain name, decimal + hex chain id, native ticker, RPC and explorer URLs (the phishing-signal quartet). On approval, persist config + create wallet with a custom-<chainId> network + auto-grant the origin readAddress on this chain. No-op success if the chain is already added. - eth.switchChain rewritten to be EIP-3326 correct: look up any ready ETH wallet whose adapter reports the requested chainId, make it the selected wallet, fire chainChanged. When no wallet matches, throw with .code = 4902 (the standard 'chain not added' code) so wagmi / RainbowKit / any 3326-aware dapp does the fallback wallet_addEthereumChain call in the same click. - eth.state handler: cheap {address, chainIdHex, networkVersion} peek for the origin's currently-connected wallet (no approval, no key access). The main-world bridge calls it after every switch/add to emit chainChanged + accountsChanged locally — the events MetaMask fires and RainbowKit listens for. - wallet-inject.js: routes wallet_addEthereumChain via eth.addChain, preserves the 4902 code across the postMessage boundary on switch failures, calls pullEthStateAndEmit() to fire the post-switch/add events.
2026-09-07 22:26:27 +02:00
const snap = rt.adapter.snapshot();
return {
address: snap.address,
chainIdHex: "0x" + Number(snap.chainId).toString(16),
networkVersion: String(snap.chainId),
};
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
});
// Read passthrough: forward eth_getBalance / eth_call / etc. to the
// wallet's own configured RPC. Nothing here reveals the private key.
api.onMessage("eth.rpc", async (p, m) => {
fromPage(m);
const rt = activeEthRuntime();
const method = String(p && p.method || "");
const params = (p && p.params) || [];
if (!/^eth_|^net_|^web3_/.test(method)) throw new Error("Aegis: only eth_/net_/web3_ read methods are passed through");
return rt.adapter._client.call(method, params);
});
// ---- Solana (wallet-adapter) ------------------------------------------
function activeSolRuntime() {
const selId = selectedWalletId();
const selRt = selId && ctx.runtimes.get(selId);
if (selRt && selRt.entry.chain === "sol" && selRt.phase === "ready") return selRt;
for (const rt of ctx.runtimes.values()) if (rt.entry.chain === "sol" && rt.phase === "ready") return rt;
throw new Error("no Solana wallet available — add one in the Aegis sidebar");
}
function solConnectedFor(origin) {
const p = permissions(api)[origin];
return !!(p && p.sol && p.sol.readAddress);
}
api.onMessage("sol.connect", async (_p, m) => {
const origin = fromPage(m);
const rt = activeSolRuntime();
const snap = rt.adapter.snapshot();
if (solConnectedFor(origin)) return { address: snap.address, network: snap.network };
return withOriginLock(origin, async () => {
const pick = await api.approvalModal({
title: "Connect this site to your Solana wallet?",
origin,
body: "The site will see this address and can build transactions for you to sign.",
rows: [
{ label: "Address", value: snap.address, mono: true },
{ label: "Network", value: snap.network === "mainnet" ? "Mainnet-beta" : "Devnet" },
{ label: "Wallet", value: `${rt.entry.label} — Solana · ${snap.network}` },
],
actions: [{ id: "allow", label: "Connect", primary: true }],
checkbox: { id: "always", label: "Always allow this site to see this address" },
});
if (!pick.startsWith("allow")) throw new Error("user rejected");
if (pick === "allow+always") {
const perms = permissions(api);
perms[origin] = { ...(perms[origin] || {}), sol: { readAddress: true, network: snap.network } };
api.storage.set("permissions", perms);
emitState();
}
return { address: snap.address, network: snap.network };
});
});
api.onMessage("sol.signMessage", async (p, m) => {
const origin = fromPage(m);
if (!solConnectedFor(origin)) throw new Error("not connected — call solana.connect first");
const rt = activeSolRuntime();
const b64 = String(p && p.messageB64 || "");
const bytes = Buffer.from(b64, "base64");
if (bytes.length > 4096) throw new Error("message too long");
return withOriginLock(origin, async () => {
const preview = bytes.every((c) => c >= 0x20 && c < 0x7f) ? bytes.toString("utf8") : `<${bytes.length} bytes: 0x${bytes.toString("hex").slice(0, 60)}…>`;
const pick = await api.approvalModal({
title: "Sign a Solana message?",
origin,
body: "Signing proves you control this address. It moves no SOL.",
rows: [
{ label: "Message", value: preview.length > 400 ? preview.slice(0, 400) + "…" : preview, mono: true },
{ label: "Address", value: rt.adapter.snapshot().address, mono: true },
],
actions: [{ id: "sign", label: "Sign", primary: true }],
});
if (pick !== "sign") throw new Error("user rejected");
return rt.adapter.signMessage(bytes);
});
});
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
// Dapp-built transaction. The main-world bridge passes the FULL wire
// (tx.serialize({requireAllSignatures:false, verifySignatures:false}))
// — signature slots the dapp already filled with partialSign() are
// preserved; our wallet only overwrites its own slot. That's the only
// way to sign multi-signer transactions the dapp has partially
// co-signed (co-signer sigs, ephemeral session keys, etc.).
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
api.onMessage("sol.signAndSend", async (p, m) => {
const origin = fromPage(m);
if (!solConnectedFor(origin)) throw new Error("not connected — call solana.connect first");
const rt = activeSolRuntime();
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
const wireB64 = String(p && p.wireB64 || "");
if (!wireB64) throw new Error("wireB64 required (full serialized transaction)");
const wire = new Uint8Array(Buffer.from(wireB64, "base64"));
// Parse: compact-u16(sigCount) || sig[0..64]*sigCount || message
let off = 0;
const readCompactU16 = () => {
let n = 0, shift = 0;
while (true) {
const b = wire[off++];
n |= (b & 0x7f) << shift;
if ((b & 0x80) === 0) break;
shift += 7;
if (shift > 21) throw new Error("compact-u16 too long");
}
return n;
};
const sigCount = readCompactU16();
if (sigCount < 1 || sigCount > 32) throw new Error("bad signature count " + sigCount);
const sigsStart = off;
const messageStart = sigsStart + sigCount * 64;
if (wire.length < messageStart) throw new Error("truncated tx wire");
const messageBytes = wire.slice(messageStart);
// Parse the message enough to find our pubkey's index in the account
// list. Layout: header(3) || compactU16(keyCount) || key[32]*keyCount || …
if (messageBytes.length < 3 + 1 + 32) throw new Error("message too short");
const numRequiredSigs = messageBytes[0];
let moff = 3;
const readKeyCount = () => {
let n = 0, shift = 0;
while (true) {
const b = messageBytes[moff++];
n |= (b & 0x7f) << shift;
if ((b & 0x80) === 0) break;
shift += 7;
}
return n;
};
const keyCount = readKeyCount();
if (keyCount < 1 || keyCount > 64) throw new Error("bad account key count");
// Find our public key among the key list.
const ourPub = rt.adapter._pub;
let ourIndex = -1;
for (let i = 0; i < keyCount; i++) {
const key = messageBytes.subarray(moff + i * 32, moff + (i + 1) * 32);
let eq = true;
for (let j = 0; j < 32; j++) if (key[j] !== ourPub[j]) { eq = false; break; }
if (eq) { ourIndex = i; break; }
}
if (ourIndex < 0) throw new Error("this wallet's key is not among the transaction's account keys");
if (ourIndex >= numRequiredSigs) throw new Error(`this wallet's key is not a required signer (index ${ourIndex}, requiredSigs ${numRequiredSigs})`);
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
return withOriginLock(origin, async () => {
const snap = rt.adapter.snapshot();
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
const otherSigners = numRequiredSigs > 1 ? numRequiredSigs - 1 : 0;
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
const pick = await api.approvalModal({
title: "Sign + send a Solana transaction?",
origin,
body: "The site built this transaction. Aegis can't decode arbitrary Solana instructions in this rev — verify the site before signing.",
rows: [
{ label: "Message size", value: `${messageBytes.length} bytes` },
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
{ label: "Required signers", value: otherSigners
? `${numRequiredSigs} — you (slot #${ourIndex}) + ${otherSigners} other${otherSigners === 1 ? "" : "s"}`
: "1 — you" },
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
{ label: "Address", value: snap.address, mono: true },
{ label: "Wallet", value: `${rt.entry.label} — Solana · ${snap.network}` },
],
actions: [{ id: "send", label: "Sign & send", primary: true }],
});
if (pick !== "send") throw new Error("user rejected");
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
// Sign the message and patch our slot. Any partial signatures already
// in the wire (from tx.partialSign()) at other slots are preserved.
const sigInfo = rt.adapter.signMessage(messageBytes);
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
const sigBytes = ctx.d.base58check.decodeBase58(sigInfo.signature);
if (sigBytes.length !== 64) throw new Error("bad ed25519 signature length");
feat(theseus/aegis): EIP-712 signTypedData_v4 + Solana multi-signer send Two follow-ups to the dapp bridges. Both change wire shape only — no new UI, existing wallets keep signing byte-identically for the flows they already covered. - lib/eip712.js: full EIP-712 typed-data encoder — encodeType with alphabetically-sorted transitive sub-types, typeHash, encodeValue for string / address / bool / uint*/int* (any width) / bytes / bytesN / nested structs / dynamic and fixed arrays, hashStruct recursion, digest = keccak256(0x19 || 0x01 || domainSeparator || hashStruct). Verified against the spec §"Ether Mail" test vector — hashStruct on both the domain and the message plus the final digest all match the canonical values byte-for-byte (see scratchpad/verify-eip712.mjs). - chain-eth.js: exposes signTypedDataDigest(digest32) that signs the precomputed digest with r||s||v (v = 27+recid), the same envelope personal_sign uses. Aegis computes the digest server-side (in the addon) so a bug in the encoder can't be tricked by a malicious dapp into signing over data the user never saw. - index.js: eth.signTypedData handler shows domain (name · version · chainId), primary type, and a truncated JSON preview of the message in the approval overlay — every classic phishing signal (mismatched domain, unexpected primary type) is in front of the user before they hit Sign. Accepts either an already-parsed typedData object or the JSON-string form older MetaMask specs used. - wallet-inject.js router: eth_signTypedData_v4 (and _v3 for the same payload shape) route to eth.signTypedData. v1's flat "type[]" form is unwired — dapps that still use v1 should upgrade. - Solana signAndSend: bridge now passes the FULL wire (from tx.serialize({requireAllSignatures:false, verifySignatures:false})) instead of just the message. The addon parses compact-u16 signature count, finds this wallet's pubkey in the message's account-key list, signs the message, and patches ONLY its own slot in the signature array — any partial signatures the dapp had already filled with tx.partialSign() (session keys, escrow co-signers, permissioned authorities) are preserved. Multi-signer flows work now; single-signer is the degenerate case of sigCount=1. - Approval overlay for sol.signAndSend now shows required-signer count and the wallet's slot index so multi-signer requests are visibly distinct from a plain single-signer send.
2026-09-07 22:19:51 +02:00
const wireOut = new Uint8Array(wire); // copy so we don't mutate caller
wireOut.set(sigBytes, sigsStart + ourIndex * 64);
const wireB58 = ctx.d.base58check.encodeBase58(wireOut);
feat(theseus/aegis): EIP-1193 + Solana wallet-adapter bridges; BTC signet 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.
2026-09-07 22:08:56 +02:00
const txid = await rt.adapter._client.call("sendTransaction", [wireB58]);
if (typeof txid !== "string" || !txid.length) throw new Error("bad txid from RPC: " + JSON.stringify(txid));
setTimeout(() => rt.adapter.refresh().catch(() => {}), 4000);
return { txid };
});
});
}
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// ---- activate ---------------------------------------------------------------
module.exports = {
activate(api) {
// No explicit icon — inherit manifest.icon (branded shield data URI)
// so the toolbar dock button renders the aegis.x brand mark instead
// of a fallback emoji.
api.registerSidebarPanel({ id: "main", title: "Wallet", page: "panel.html" });
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
const c = ctx = {
api,
d: null,
runtimes: new Map(), // walletId -> { entry, phase, error, adapter }
};
migrateLegacyStorage(api);
registerPanelMessages(api);
registerPageMessages(api);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
loadDeps(api).then((d) => {
if (ctx !== c) return;
c.d = d;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
return mountAllWallets();
}).catch((e) => {
if (ctx !== c) return;
api.log("startup failed:", e?.message);
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
emitState();
});
},
deactivate() {
const c = ctx; ctx = null;
if (!c) return;
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
for (const rt of c.runtimes.values()) {
try { rt.adapter && rt.adapter.dispose(); } catch {}
}
c.runtimes.clear();
},
};