feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
|
|
|
// WizardConnect transaction signing for Aegis.
|
|
|
|
|
//
|
|
|
|
|
// The dapp hands us a full BCH transaction plus its source outputs. Per the
|
|
|
|
|
// WC protocol, we must sign every input with SIGHASH_ALL | FORKID | UTXOS.
|
|
|
|
|
// Any other sighash flag combination MUST be rejected (protocol/security).
|
|
|
|
|
//
|
|
|
|
|
// This module supports P2PKH inputs only. Contract inputs (a source output
|
|
|
|
|
// carrying a `contract` field) are rejected with a clear error — they need
|
|
|
|
|
// script-aware signing that Aegis's BCH runtime doesn't do today.
|
|
|
|
|
|
|
|
|
|
// SIGHASH byte required for this protocol: SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_UTXOS
|
|
|
|
|
// = 0x01 | 0x40 | 0x20 = 0x61.
|
|
|
|
|
const REQUIRED_SIGHASH = 0x61;
|
|
|
|
|
|
|
|
|
|
function toHex(u8) { let s = ""; for (let i = 0; i < u8.length; i++) s += u8[i].toString(16).padStart(2, "0"); return s; }
|
|
|
|
|
function fromHex(h) {
|
|
|
|
|
const s = String(h || "").replace(/^0x/i, "");
|
|
|
|
|
const out = new Uint8Array(s.length / 2);
|
|
|
|
|
for (let i = 0; i < out.length; i++) out[i] = parseInt(s.substr(i * 2, 2), 16);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ensureTransaction(txOrHex, libauth) {
|
|
|
|
|
if (typeof txOrHex === "string") {
|
|
|
|
|
const dec = libauth.decodeTransactionCommon
|
|
|
|
|
? libauth.decodeTransactionCommon(fromHex(txOrHex))
|
|
|
|
|
: libauth.decodeTransaction(fromHex(txOrHex));
|
|
|
|
|
if (typeof dec === "string") throw new Error(`wc-sign: bad tx hex — ${dec}`);
|
|
|
|
|
return dec;
|
|
|
|
|
}
|
|
|
|
|
return txOrHex;
|
|
|
|
|
}
|
|
|
|
|
|
Aegis 0.31.0: chain adapters stop trusting what they should check
Start of the next batch; 0.30.0 is published.
- Tron panel sends signed whatever /wallet/createtransaction returned while
the approval showed the local request. The returned bytes are now decoded
and must be one transfer from this wallet, to that address, for that
amount, with a matching txID - checked at plan time and again at signing.
- Importing a Solana wallet from a seed phrase threw on every attempt (a
mis-parenthesised `new require("crypto").createHmac` plus a bare require
of an ESM-only subpath). SLIP-0010 now uses Node's HMAC, as chain-sol does.
- Imported BCH wallets put token-bearing UTXOs into coin selection. They are
excluded, as in the HD wallet, and the balance counts what can be spent.
- WizardConnect dropped the token from each spent output before signing, so
under SIGHASH_UTXOS every signature of a token transaction was invalid.
- A wallet disposed while a refresh was in flight re-armed its poll timer.
- BCMR registry content is bounded before it reaches the panel: control and
bidi characters stripped, lengths capped, decimals 0-18, icons https/ipfs
only, registries https only.
2026-10-04 01:38:50 +02:00
|
|
|
// libauth Output.token: { amount: bigint, category: Uint8Array,
|
|
|
|
|
// nft?: { capability, commitment: Uint8Array } }. Over the wire the byte
|
|
|
|
|
// fields may arrive as hex and the amount as a string or number.
|
|
|
|
|
function normalizeToken(t) {
|
|
|
|
|
if (!t || typeof t !== "object") return null;
|
|
|
|
|
const bytes = (v) => (v instanceof Uint8Array ? v : fromHex(String(v || "")));
|
|
|
|
|
const out = {
|
|
|
|
|
amount: typeof t.amount === "bigint" ? t.amount : BigInt(t.amount ?? 0),
|
|
|
|
|
category: bytes(t.category),
|
|
|
|
|
};
|
|
|
|
|
if (out.category.length !== 32) throw new Error("wc-sign: token category must be 32 bytes");
|
|
|
|
|
if (t.nft) {
|
|
|
|
|
out.nft = {
|
|
|
|
|
capability: String(t.nft.capability || "none"),
|
|
|
|
|
commitment: bytes(t.nft.commitment),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
|
|
|
async function signTx({ request, account, branches, libauth, secp256k1 }) {
|
|
|
|
|
const {
|
|
|
|
|
generateSigningSerializationBCH,
|
|
|
|
|
hash256, encodeTransaction,
|
|
|
|
|
} = libauth;
|
|
|
|
|
|
fix(aegis): 0.9.5 — WizardConnect signing actually works
Pairing already worked; signing would have thrown on the first request
a dapp ever sent. Found by testing against the real relay and the real
@wizardconnect/wallet library rather than reading the code.
Two bugs in wc-sign.js, both fatal:
- The WC message nests the whole WcSignTransactionRequest under
`.transaction`, so the tx is at request.transaction.transaction and
the spent outputs at request.transaction.sourceOutputs. We read
request.transaction as the tx and request.sourceOutputs as the
outputs, so tx.inputs was undefined. index.js already read the nested
request.transaction.userPrompt for the approval dialog, so only the
signer had it wrong. The flat shape is still accepted.
- generateSigningSerializationBCH takes TWO positional arguments,
(compilationContext, {coveredBytecode, signingSerializationType}).
We passed one merged object, leaving coveredBytecode undefined and
throwing inside libauth. For P2PKH the covered bytecode is the spent
output's locking script.
Now verified end to end: a two-input transaction spending from two
different derivation paths signs, decodes, and passes
createVirtualMachineBCH().verify() — consensus-valid, with
SIGHASH_ALL|FORKID|UTXOS (0x61) on every input as the protocol
requires.
Also: RelayStatus is an object ({status: "connected" | "reconnecting" |
"disconnected" | "session_deleted"}), and the snapshot read a
non-existent `.kind`, so every connection reported the literal
"[object Object]". Reads `.status` now, uses the documented
getConnections() accessor instead of the private connections Map, and
carries the library's own `label` ("dapp name once known, otherwise
Connecting…"). The panel shows a tag for anything other than connected
— "reconnecting" is the difference between a pairing that will see the
next signature and one that is dead, which was invisible before.
2026-09-23 03:23:41 +02:00
|
|
|
// The WC message nests the whole WcSignTransactionRequest under
|
|
|
|
|
// `.transaction`, so the real shape is:
|
|
|
|
|
// request.transaction.transaction — the tx (object or hex)
|
|
|
|
|
// request.transaction.sourceOutputs — the spent outputs
|
|
|
|
|
// request.inputPaths / request.sequence — on the outer message
|
|
|
|
|
// Reading request.transaction as the tx (and request.sourceOutputs as
|
|
|
|
|
// the outputs) meant `tx.inputs` was undefined and signing threw on the
|
|
|
|
|
// first real request. index.js already read the nested
|
|
|
|
|
// request.transaction.userPrompt for the approval dialog, so only this
|
|
|
|
|
// module had it wrong. The flat shape is still accepted so a caller
|
|
|
|
|
// that hands us an already-unwrapped payload keeps working.
|
|
|
|
|
const inner = (request.transaction && (request.transaction.transaction !== undefined
|
|
|
|
|
|| request.transaction.sourceOutputs !== undefined))
|
|
|
|
|
? request.transaction
|
|
|
|
|
: request;
|
|
|
|
|
const tx = ensureTransaction(inner.transaction, libauth);
|
|
|
|
|
const sourceOutputs = (inner.sourceOutputs || []).map((o, i) => {
|
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
|
|
|
if (o.contract) throw new Error(`wc-sign: input ${i} spends a contract — unsupported`);
|
Aegis 0.31.0: chain adapters stop trusting what they should check
Start of the next batch; 0.30.0 is published.
- Tron panel sends signed whatever /wallet/createtransaction returned while
the approval showed the local request. The returned bytes are now decoded
and must be one transfer from this wallet, to that address, for that
amount, with a matching txID - checked at plan time and again at signing.
- Importing a Solana wallet from a seed phrase threw on every attempt (a
mis-parenthesised `new require("crypto").createHmac` plus a bare require
of an ESM-only subpath). SLIP-0010 now uses Node's HMAC, as chain-sol does.
- Imported BCH wallets put token-bearing UTXOs into coin selection. They are
excluded, as in the HD wallet, and the balance counts what can be spent.
- WizardConnect dropped the token from each spent output before signing, so
under SIGHASH_UTXOS every signature of a token transaction was invalid.
- A wallet disposed while a refresh was in flight re-armed its poll timer.
- BCMR registry content is bounded before it reaches the panel: control and
bidi characters stripped, lengths capped, decimals 0-18, icons https/ipfs
only, registries https only.
2026-10-04 01:38:50 +02:00
|
|
|
const out = {
|
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
|
|
|
lockingBytecode: o.lockingBytecode instanceof Uint8Array ? o.lockingBytecode : fromHex(o.lockingBytecode),
|
|
|
|
|
valueSatoshis: typeof o.valueSatoshis === "bigint" ? o.valueSatoshis : BigInt(o.valueSatoshis),
|
|
|
|
|
};
|
Aegis 0.31.0: chain adapters stop trusting what they should check
Start of the next batch; 0.30.0 is published.
- Tron panel sends signed whatever /wallet/createtransaction returned while
the approval showed the local request. The returned bytes are now decoded
and must be one transfer from this wallet, to that address, for that
amount, with a matching txID - checked at plan time and again at signing.
- Importing a Solana wallet from a seed phrase threw on every attempt (a
mis-parenthesised `new require("crypto").createHmac` plus a bare require
of an ESM-only subpath). SLIP-0010 now uses Node's HMAC, as chain-sol does.
- Imported BCH wallets put token-bearing UTXOs into coin selection. They are
excluded, as in the HD wallet, and the balance counts what can be spent.
- WizardConnect dropped the token from each spent output before signing, so
under SIGHASH_UTXOS every signature of a token transaction was invalid.
- A wallet disposed while a refresh was in flight re-armed its poll timer.
- BCMR registry content is bounded before it reaches the panel: control and
bidi characters stripped, lengths capped, decimals 0-18, icons https/ipfs
only, registries https only.
2026-10-04 01:38:50 +02:00
|
|
|
// The token rides along. SIGHASH_UTXOS commits every input's signature
|
|
|
|
|
// to ALL source outputs including their token prefix, so dropping it
|
|
|
|
|
// (as this did) made every signature of a token-spending transaction
|
|
|
|
|
// invalid.
|
|
|
|
|
const tok = normalizeToken(o.token);
|
|
|
|
|
if (tok) out.token = tok;
|
|
|
|
|
return out;
|
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
|
|
|
});
|
|
|
|
|
if (sourceOutputs.length !== tx.inputs.length) {
|
|
|
|
|
throw new Error(`wc-sign: sourceOutputs (${sourceOutputs.length}) ≠ inputs (${tx.inputs.length})`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const inputPathMap = new Map(); // inputIndex -> { branch, addressIndex }
|
|
|
|
|
for (const [inputIndex, pathName, addressIndex] of (request.inputPaths || [])) {
|
|
|
|
|
inputPathMap.set(Number(inputIndex), { pathName: String(pathName), addressIndex: Number(addressIndex) });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const signedInputs = tx.inputs.map((inp, i) => ({ ...inp }));
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < tx.inputs.length; i++) {
|
|
|
|
|
const hint = inputPathMap.get(i);
|
|
|
|
|
if (!hint) throw new Error(`wc-sign: no path for input ${i}`);
|
|
|
|
|
const branch = branches[hint.pathName];
|
|
|
|
|
if (!branch) throw new Error(`wc-sign: unknown path "${hint.pathName}"`);
|
|
|
|
|
const node = branch.deriveChild(hint.addressIndex);
|
|
|
|
|
|
fix(aegis): 0.9.5 — WizardConnect signing actually works
Pairing already worked; signing would have thrown on the first request
a dapp ever sent. Found by testing against the real relay and the real
@wizardconnect/wallet library rather than reading the code.
Two bugs in wc-sign.js, both fatal:
- The WC message nests the whole WcSignTransactionRequest under
`.transaction`, so the tx is at request.transaction.transaction and
the spent outputs at request.transaction.sourceOutputs. We read
request.transaction as the tx and request.sourceOutputs as the
outputs, so tx.inputs was undefined. index.js already read the nested
request.transaction.userPrompt for the approval dialog, so only the
signer had it wrong. The flat shape is still accepted.
- generateSigningSerializationBCH takes TWO positional arguments,
(compilationContext, {coveredBytecode, signingSerializationType}).
We passed one merged object, leaving coveredBytecode undefined and
throwing inside libauth. For P2PKH the covered bytecode is the spent
output's locking script.
Now verified end to end: a two-input transaction spending from two
different derivation paths signs, decodes, and passes
createVirtualMachineBCH().verify() — consensus-valid, with
SIGHASH_ALL|FORKID|UTXOS (0x61) on every input as the protocol
requires.
Also: RelayStatus is an object ({status: "connected" | "reconnecting" |
"disconnected" | "session_deleted"}), and the snapshot read a
non-existent `.kind`, so every connection reported the literal
"[object Object]". Reads `.status` now, uses the documented
getConnections() accessor instead of the private connections Map, and
carries the library's own `label` ("dapp name once known, otherwise
Connecting…"). The panel shows a tag for anything other than connected
— "reconnecting" is the difference between a pairing that will see the
next signature and one that is dead, which was invisible before.
2026-09-23 03:23:41 +02:00
|
|
|
// generateSigningSerializationBCH takes TWO positional arguments:
|
|
|
|
|
// (compilationContext, { coveredBytecode, signingSerializationType })
|
|
|
|
|
// Passing one merged object left coveredBytecode undefined, which threw
|
|
|
|
|
// "Cannot destructure property 'coveredBytecode' of 'undefined'".
|
|
|
|
|
// For P2PKH the covered bytecode is the spent output's locking script.
|
|
|
|
|
const preimage = generateSigningSerializationBCH(
|
|
|
|
|
{
|
|
|
|
|
inputIndex: i,
|
|
|
|
|
sourceOutputs,
|
|
|
|
|
transaction: { ...tx, inputs: signedInputs },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
coveredBytecode: sourceOutputs[i].lockingBytecode,
|
|
|
|
|
signingSerializationType: new Uint8Array([REQUIRED_SIGHASH]),
|
|
|
|
|
},
|
|
|
|
|
);
|
feat(theseus/aegis): 0.6.1 — in-panel vault setup/unlock, BCH wallet imports, opt-in fiat prices, WizardConnect
Aegis Wallet 0.4.4 → 0.6.1:
- Vault lifecycle from the wallet gate. The locked / not-yet-created states
now show a master-password form (with optional BIP39 mnemonic on setup)
instead of redirecting users to Settings › Passwords. New
api.vault.lifecycle {status, setup, unlock, lock} in addons-host, gated by
the existing "vault-derive" capability. api.openSettings(section) also
added; settings.html honours a #section hash on open.
- Imported BCH wallets (design M.1a, read-only). Paste a mnemonic + BIP44
path or a WIF; the cashaddr is derived in the add-on, the signer material
goes to a separate wallet-imports.enc via api.vault.imports {list, add,
remove, signer}. Argus password-vault gains createImports / unlockImports /
saveImports with its own KDF salt so the imports key is disjoint from the
passwords key. lib/chain-bch-imported.js is a single-address Electrum
adapter; spend support is deferred to M.1b.
- Opt-in USD prices via CoinGecko (lib/prices.js), off by default, persisted
in add-on storage. Fiat lines under balances, in the wallet picker, and a
portfolio total when 2+ wallets are open. Settings tab is now reachable
while the vault is locked so the toggle is always available.
- WizardConnect wallet-side pairing for BCH wallets (lib/wc.js, lib/wc-sign.js).
@wizardconnect/{core,wallet} are loaded dynamically via api.import to stay
on the right side of LGPL §4d. Sign requests go through approvalModal and
are restricted to P2PKH inputs with SIGHASH_ALL|FORKID|UTXOS.
- DGB adapter load is now soft-fail: when Aegis runs from userData/addons the
bundled ESM can't resolve peer deps, so DGB becomes unavailable instead of
taking the whole add-on down.
2026-09-09 10:33:21 +02:00
|
|
|
const digest = hash256(preimage);
|
|
|
|
|
const sig = secp256k1.sign(digest, node.privateKey, { prehash: false, lowS: true, format: "der" });
|
|
|
|
|
// signature || sighashType byte
|
|
|
|
|
const sigWithHash = new Uint8Array(sig.length + 1);
|
|
|
|
|
sigWithHash.set(sig, 0); sigWithHash[sig.length] = REQUIRED_SIGHASH;
|
|
|
|
|
|
|
|
|
|
// P2PKH unlocking: <sig+hashtype> <pubkey>
|
|
|
|
|
const pushSig = new Uint8Array(1 + sigWithHash.length);
|
|
|
|
|
pushSig[0] = sigWithHash.length;
|
|
|
|
|
pushSig.set(sigWithHash, 1);
|
|
|
|
|
const pushPk = new Uint8Array(1 + node.publicKey.length);
|
|
|
|
|
pushPk[0] = node.publicKey.length;
|
|
|
|
|
pushPk.set(node.publicKey, 1);
|
|
|
|
|
|
|
|
|
|
const unlocking = new Uint8Array(pushSig.length + pushPk.length);
|
|
|
|
|
unlocking.set(pushSig, 0); unlocking.set(pushPk, pushSig.length);
|
|
|
|
|
signedInputs[i].unlockingBytecode = unlocking;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const encoded = encodeTransaction({ ...tx, inputs: signedInputs });
|
|
|
|
|
return { signedTransaction: toHex(encoded) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = { signTx, REQUIRED_SIGHASH };
|