From 03140fae9d190fa4ded07c5c1cd3bfc979f12b83 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sat, 3 Oct 2026 23:01:15 +0200 Subject: [PATCH] Aegis: WizardConnect signing requests can be approved approvalRequest passed approve/reject keys the host overlay does not know, so it showed a lone "OK" button whose id never equalled "approve": every WizardConnect signing request was refused, and the HTML body was shown as literal markup. It now passes a Sign action and plain rows: wallet, input count, each output decoded to a cashaddr on the wallet's network (or OP_RETURN / raw script), total, and who broadcasts. --- bundled-addons/aegis/index.js | 60 ++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/bundled-addons/aegis/index.js b/bundled-addons/aegis/index.js index 222dbe6f..b630c825 100644 --- a/bundled-addons/aegis/index.js +++ b/bundled-addons/aegis/index.js @@ -953,16 +953,48 @@ function deriveCashaddrFromWif(wif, prefix) { return d.cashaddr.encode(prefix, 0, h160); } -function buildWcApprovalBody(payload) { +// Plain-text body + rows for the host overlay (it escapes everything, so +// markup would show up literally). Outputs are decoded best-effort from the +// libauth transaction the dapp sent: P2PKH / P2SH locking bytecode → cashaddr. +// The WC message nests the WcSignTransactionRequest under .transaction, so +// the libauth tx itself is request.transaction.transaction (see wc-sign.js). +function buildWcApproval(payload) { const req = payload?.request || {}; const tx = req.transaction || {}; - const dappName = tx.userPrompt || "unknown dapp"; - const inputCount = Array.isArray(req.inputPaths) ? req.inputPaths.length : "?"; - const bc = tx.broadcast ? "Dapp will broadcast after signing." : "Signed hex returned to dapp."; + const dappName = String(tx.userPrompt || payload?.dappName || "unknown dapp").slice(0, 120); const walletName = payload?.label || payload?.walletId || ""; - return "
" + dappName + " requests a BCH transaction signature.
" - + "
Wallet: " + walletName + " · " + inputCount + " input(s).
" - + "
" + bc + " Signs with SIGHASH_ALL|FORKID|UTXOS.
"; + const network = ctx.runtimes.get(payload?.walletId)?.entry?.network; + const prefix = network === "chipnet" ? "bchtest" : "bitcoincash"; + let inner = tx.transaction; + if (typeof inner === "string") { + try { + const lib = ctx.d.libauth; + const dec = (lib.decodeTransactionCommon || lib.decodeTransaction)(ctx.d.tx.fromHex(inner)); + inner = typeof dec === "string" ? null : dec; + } catch { inner = null; } + } + const inputCount = Array.isArray(req.inputPaths) ? req.inputPaths.length : (Array.isArray(inner?.inputs) ? inner.inputs.length : "?"); + const rows = [{ label: "Wallet", value: walletName }, { label: "Inputs", value: String(inputCount) }]; + try { + const outs = inner?.outputs || []; + let total = 0n; + outs.slice(0, 8).forEach((o, i) => { + const lb = o.lockingBytecode; + const hexScript = typeof lb === "string" ? lb.toLowerCase() : Buffer.from(lb || []).toString("hex"); + let addr = null; + if (/^76a914[0-9a-f]{40}88ac$/.test(hexScript)) addr = ctx.d.cashaddr.encode(prefix, 0, ctx.d.tx.fromHex(hexScript.slice(6, 46))); + else if (/^a914[0-9a-f]{40}87$/.test(hexScript)) addr = ctx.d.cashaddr.encode(prefix, 1, ctx.d.tx.fromHex(hexScript.slice(4, 44))); + const v = BigInt(o.valueSatoshis ?? 0); + total += v; + const token = o.token ? " + CashTokens" : ""; + rows.push({ label: `Output #${i + 1}`, value: `${fmtBch(Number(v))} BCH${token} → ${addr || (hexScript.startsWith("6a") ? "OP_RETURN data" : "script " + hexScript.slice(0, 24) + "…")}`, mono: true }); + }); + if (outs.length > 8) rows.push({ label: "Outputs", value: `… and ${outs.length - 8} more` }); + if (outs.length) rows.push({ label: "Total out", value: `${fmtBch(Number(total))} BCH`, strong: true }); + } catch {} + rows.push({ label: "After signing", value: tx.broadcast ? "the dapp broadcasts it" : "signed hex is returned to the dapp" }); + rows.push({ label: "Sighash", value: "ALL | FORKID | UTXOS" }); + return { body: `${dappName} asks you to sign a Bitcoin Cash transaction.`, rows, dappName }; } // ---- per-purpose default wallets ------------------------------------------- @@ -3655,12 +3687,16 @@ module.exports = { api, // Bridge sign approvals through the addon's approval-modal capability. approvalRequest: async (payload) => { - const dappName = payload.request?.transaction?.userPrompt || "dapp"; + // The host overlay only knows `actions`; the old `approve`/`reject` + // keys fell back to a lone "OK" button whose id never matched, so + // every WizardConnect signing request was refused, and the HTML + // body was shown as literal markup. + const { body, rows, dappName } = buildWcApproval(payload); const pick = await api.approvalModal({ - title: `Sign transaction for ${dappName}`, - body: buildWcApprovalBody(payload), - approve: "Sign", - reject: "Reject", + title: "Sign a Bitcoin Cash transaction (WizardConnect)?", + origin: dappName, + body, rows, + actions: [{ id: "approve", label: "Sign", primary: true }], }); return { approved: pick === "approve" }; },