diff --git a/bundled-addons/aegis/index.js b/bundled-addons/aegis/index.js index 7a4163c2..25db68a9 100644 --- a/bundled-addons/aegis/index.js +++ b/bundled-addons/aegis/index.js @@ -71,17 +71,30 @@ async function loadDeps(api) { // userData/addons/, Node's ESM resolver can't reach Theseus's node_modules // from that path — so the import throws. Wrap it: DGB just becomes // unavailable, the rest of Aegis keeps working. + const bitcoinjs = api.require("bitcoinjs-lib"); + const { BIP32Factory } = api.require("bip32"); + const { ECPairFactory } = api.require("ecpair"); + const ecc = api.require("@bitcoinerlab/secp256k1"); + let dgbCore = null, dgbPsbt = null, dgbAdapter = null; try { + // Inject before importing anything under lib/dgb/. Those modules used to + // import "bitcoinjs-lib" and friends as bare specifiers, which only + // resolves when the add-on sits inside the app tree — so DigiByte worked + // on a fresh install and vanished after the first OTA update, where Aegis + // runs from /extensions/aegis/. See lib/dgb/deps.js. + const dgbDeps = await import(pathToFileURL(path.join(api.folder, "lib/dgb/deps.js")).href); + dgbDeps.setDgbDeps({ + bitcoinjs, ecc, + bip32Factory: BIP32Factory, + ecpairFactory: ECPairFactory, + bip39: api.require("bip39"), + }); dgbCore = await import(pathToFileURL(path.join(api.folder, "lib/dgb/core/index.js")).href); dgbPsbt = await import(pathToFileURL(path.join(api.folder, "lib/dgb/psbt/index.js")).href); } catch (e) { api.log("dgb unavailable:", e?.message || e); } - const bitcoinjs = api.require("bitcoinjs-lib"); - const { BIP32Factory } = api.require("bip32"); - const { ECPairFactory } = api.require("ecpair"); - const ecc = api.require("@bitcoinerlab/secp256k1"); if (dgbCore && dgbPsbt) { dgbAdapter = require("./lib/chain-dgb.js")({ dgbCore, dgbPsbt, bitcoinjs, diff --git a/bundled-addons/aegis/lib/dgb/core/address.js b/bundled-addons/aegis/lib/dgb/core/address.js index f41fad52..cdc8ccf0 100644 --- a/bundled-addons/aegis/lib/dgb/core/address.js +++ b/bundled-addons/aegis/lib/dgb/core/address.js @@ -1,10 +1,7 @@ -import { payments, initEccLib } from 'bitcoinjs-lib'; -import * as ecc from '@bitcoinerlab/secp256k1'; +import { payments, ensureEcc } from '../deps.js'; import { digibyte, digibyteLegacyP2SH } from './network.js'; -// Required for Taproot address derivation (P2TR). -initEccLib(ecc); export function p2pkhAddress(node, network = digibyte) { - const { address } = payments.p2pkh({ + const { address } = payments().p2pkh({ pubkey: Buffer.from(node.publicKey), network, }); @@ -13,11 +10,11 @@ export function p2pkhAddress(node, network = digibyte) { return address; } export function p2shP2wpkhAddress(node, network = digibyte) { - const redeem = payments.p2wpkh({ + const redeem = payments().p2wpkh({ pubkey: Buffer.from(node.publicKey), network, }); - const { address } = payments.p2sh({ redeem, network }); + const { address } = payments().p2sh({ redeem, network }); if (!address) throw new Error('p2sh-p2wpkh derivation returned no address'); return address; @@ -33,7 +30,7 @@ export function p2shP2wpkhAddressesBoth(node) { }; } export function p2wpkhAddress(node, network = digibyte) { - const { address } = payments.p2wpkh({ + const { address } = payments().p2wpkh({ pubkey: Buffer.from(node.publicKey), network, }); @@ -44,8 +41,9 @@ export function p2wpkhAddress(node, network = digibyte) { // BIP86 Taproot address using the x-only pubkey with no script tree, // which applies the standard BIP86 tweak internally in bitcoinjs-lib. export function p2trAddress(node, network = digibyte) { + ensureEcc(); // Taproot needs initEccLib; done on demand, once. const internalPubkey = Buffer.from(node.publicKey.subarray(1, 33)); - const { address } = payments.p2tr({ internalPubkey, network }); + const { address } = payments().p2tr({ internalPubkey, network }); if (!address) throw new Error('p2tr derivation returned no address'); return address; diff --git a/bundled-addons/aegis/lib/dgb/core/hd.js b/bundled-addons/aegis/lib/dgb/core/hd.js index d0b6f19c..12063427 100644 --- a/bundled-addons/aegis/lib/dgb/core/hd.js +++ b/bundled-addons/aegis/lib/dgb/core/hd.js @@ -1,7 +1,5 @@ -import { BIP32Factory } from 'bip32'; -import * as ecc from '@bitcoinerlab/secp256k1'; +import { bip32 } from '../deps.js'; import { digibyte, DGB_COIN_TYPE } from './network.js'; -const bip32 = BIP32Factory(ecc); export const PURPOSE_LABEL = { 44: 'BIP44 legacy P2PKH', 49: 'BIP49 P2SH-wrapped SegWit', @@ -9,7 +7,7 @@ export const PURPOSE_LABEL = { 86: 'BIP86 Taproot (SegWit v1)', }; export function rootFromSeed(seed, network = digibyte) { - return bip32.fromSeed(seed, network); + return bip32().fromSeed(seed, network); } // Standard account-level derivation: m/purpose'/coin'/account'. // account defaults to 0 (the first account). diff --git a/bundled-addons/aegis/lib/dgb/core/seed.js b/bundled-addons/aegis/lib/dgb/core/seed.js index 49f8a26a..83dcae10 100644 --- a/bundled-addons/aegis/lib/dgb/core/seed.js +++ b/bundled-addons/aegis/lib/dgb/core/seed.js @@ -1,17 +1,17 @@ -import { generateMnemonic, validateMnemonic, mnemonicToSeed, wordlists, } from 'bip39'; +import { bip39 } from '../deps.js'; // Word count → entropy strength for BIP39. // 12 → 128, 15 → 160, 18 → 192, 21 → 224, 24 → 256. export function generateSeedPhrase(strength = 128) { - return generateMnemonic(strength); + return bip39().generateMnemonic(strength); } // BIP39 checksum + wordlist validation. Returns false for typos, bad // word counts, and out-of-wordlist words. -export function validateSeedPhrase(phrase, wordlist = wordlists.english) { - return validateMnemonic(phrase.trim(), wordlist); +export function validateSeedPhrase(phrase, wordlist = undefined) { + return bip39().validateMnemonic(phrase.trim(), wordlist || bip39().wordlists.english); } // BIP39 seed derivation. Passphrase is the optional "25th word"; // changing it produces a different wallet from the same mnemonic. export async function seedFromPhrase(phrase, passphrase = '') { - return mnemonicToSeed(phrase.trim(), passphrase); + return bip39().mnemonicToSeed(phrase.trim(), passphrase); } //# sourceMappingURL=seed.js.map \ No newline at end of file diff --git a/bundled-addons/aegis/lib/dgb/core/wif.js b/bundled-addons/aegis/lib/dgb/core/wif.js index 159b093c..0dd0ea56 100644 --- a/bundled-addons/aegis/lib/dgb/core/wif.js +++ b/bundled-addons/aegis/lib/dgb/core/wif.js @@ -1,9 +1,5 @@ -import { ECPairFactory } from 'ecpair'; -import * as ecc from '@bitcoinerlab/secp256k1'; -import { payments, initEccLib } from 'bitcoinjs-lib'; +import { payments, ECPair, ensureEcc } from '../deps.js'; import { digibyte, digibyteLegacyP2SH, digibyteLegacyWIF } from './network.js'; -const ECPair = ECPairFactory(ecc); -initEccLib(ecc); // Import a DigiByte private key in WIF (Wallet Import Format). // Tries the modern 0x80 prefix first, falls back to the legacy 0x9e // prefix that older DGB tools produced. Rejects anything else with a @@ -11,13 +7,13 @@ initEccLib(ecc); export function importWif(wif) { const trimmed = wif.trim(); try { - return { keyPair: ECPair.fromWIF(trimmed, digibyte), variant: 'modern' }; + return { keyPair: ECPair().fromWIF(trimmed, digibyte), variant: 'modern' }; } catch { // fall through to legacy attempt } try { - return { keyPair: ECPair.fromWIF(trimmed, digibyteLegacyWIF), variant: 'legacy' }; + return { keyPair: ECPair().fromWIF(trimmed, digibyteLegacyWIF), variant: 'legacy' }; } catch (e) { throw new Error(`Not a valid DigiByte WIF (tried both current 0x80 and legacy 0x9e prefixes). ` + @@ -26,23 +22,24 @@ export function importWif(wif) { } // Import against a specific network only (advanced / testing). export function importWifStrict(wif, network = digibyte) { - return ECPair.fromWIF(wif.trim(), network); + return ECPair().fromWIF(wif.trim(), network); } export function exportWif(keyPair) { return keyPair.toWIF(); } export function addressesForPubkey(pubkey, network = digibyte) { - const p2pkh = payments.p2pkh({ pubkey, network }); - const wpkhRedeem = payments.p2wpkh({ pubkey, network }); + ensureEcc(); // p2tr below needs initEccLib. + const p2pkh = payments().p2pkh({ pubkey, network }); + const wpkhRedeem = payments().p2wpkh({ pubkey, network }); // bitcoinjs-lib enforces `redeem.network === outerNetwork` (identity // comparison, not shape). To render the legacy 3-prefix P2SH address // we need a fresh redeem whose .network property is the legacy variant // — same bytes on the wire, different object identity. - const wpkhRedeemLegacy = payments.p2wpkh({ pubkey, network: digibyteLegacyP2SH }); - const p2shModern = payments.p2sh({ redeem: wpkhRedeem, network }); - const p2shLegacy = payments.p2sh({ redeem: wpkhRedeemLegacy, network: digibyteLegacyP2SH }); - const p2wpkh = payments.p2wpkh({ pubkey, network }); - const p2tr = payments.p2tr({ internalPubkey: pubkey.subarray(1, 33), network }); + const wpkhRedeemLegacy = payments().p2wpkh({ pubkey, network: digibyteLegacyP2SH }); + const p2shModern = payments().p2sh({ redeem: wpkhRedeem, network }); + const p2shLegacy = payments().p2sh({ redeem: wpkhRedeemLegacy, network: digibyteLegacyP2SH }); + const p2wpkh = payments().p2wpkh({ pubkey, network }); + const p2tr = payments().p2tr({ internalPubkey: pubkey.subarray(1, 33), network }); if (!p2pkh.address || !p2shModern.address || !p2shLegacy.address || !p2wpkh.address || !p2tr.address) { throw new Error('bitcoinjs-lib returned an empty address for one of the payment types'); } diff --git a/bundled-addons/aegis/lib/dgb/deps.js b/bundled-addons/aegis/lib/dgb/deps.js new file mode 100644 index 00000000..dcc4eb71 --- /dev/null +++ b/bundled-addons/aegis/lib/dgb/deps.js @@ -0,0 +1,60 @@ +// Dependency injection for the vendored DGB modules. +// +// These files were vendored from @dgb-wallet/core and imported +// "bitcoinjs-lib", "bip32", "ecpair" and "@bitcoinerlab/secp256k1" as bare +// specifiers. That resolves fine in a dev checkout, where Theseus's +// node_modules sits above the add-on — and fails for every OTA install, where +// Aegis lives in /extensions/aegis/ and Node's ESM resolver has +// nowhere to find them. index.js caught the failure and set dgbCore = null, +// so DigiByte quietly stopped existing and an import died with "DGB adapter +// not available". It worked on a fresh install and broke after the first +// update, which is the worst possible shape for a bug. +// +// index.js already holds all of these via api.require (which resolves against +// the app tree), so it injects them here before importing anything under +// lib/dgb/. Every consumer reads them through the accessors below rather than +// capturing them at module scope, so nothing depends on import order and a +// missing injection fails loudly at the call instead of silently at load. + +let deps = null; + +export function setDgbDeps(d) { + const need = ["bitcoinjs", "ecc", "bip32Factory", "ecpairFactory", "bip39"]; + for (const k of need) { + if (!d || !d[k]) throw new Error(`setDgbDeps: missing ${k}`); + } + deps = d; +} + +function need() { + if (!deps) throw new Error("DGB modules used before setDgbDeps() — see lib/dgb/deps.js"); + return deps; +} + +export const bitcoinjs = () => need().bitcoinjs; +export const payments = () => need().bitcoinjs.payments; +export const Psbt = () => need().bitcoinjs.Psbt; +export const ecc = () => need().ecc; +export const bip39 = () => need().bip39; + +// initEccLib must run before any Taproot derivation, and exactly once. +let eccInstalled = false; +export function ensureEcc() { + if (eccInstalled) return; + need().bitcoinjs.initEccLib(need().ecc); + eccInstalled = true; +} + +// Built on first use rather than at module scope, so hd.js no longer forces +// the deps to exist merely by being imported. +let bip32Cached = null; +export function bip32() { + if (!bip32Cached) bip32Cached = need().bip32Factory(need().ecc); + return bip32Cached; +} + +let ecpairCached = null; +export function ECPair() { + if (!ecpairCached) ecpairCached = need().ecpairFactory(need().ecc); + return ecpairCached; +} diff --git a/bundled-addons/aegis/lib/dgb/psbt/build.js b/bundled-addons/aegis/lib/dgb/psbt/build.js index 1fe91424..a353e0f2 100644 --- a/bundled-addons/aegis/lib/dgb/psbt/build.js +++ b/bundled-addons/aegis/lib/dgb/psbt/build.js @@ -1,11 +1,11 @@ -import { Psbt } from 'bitcoinjs-lib'; +import { Psbt } from '../deps.js'; import { digibyte } from '../core/index.js'; // Construct an unsigned PSBT from a set of UTXOs and destination outputs. // Does not add a change output — the caller decides change amount and // address. Does not compute fees — the caller must have already subtracted // fee from outputs. export function buildPsbt(params, network = digibyte) { - const psbt = new Psbt({ network }); + const psbt = new (Psbt())({ network }); const inputs = params.sortBip69 === false ? params.inputs : sortInputs(params.inputs); const outputs = params.sortBip69 === false ? params.outputs : sortOutputs(params.outputs); for (const u of inputs) {