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.
This commit is contained in:
parent
5769d837bd
commit
03140fae9d
1 changed files with 48 additions and 12 deletions
|
|
@ -953,16 +953,48 @@ function deriveCashaddrFromWif(wif, prefix) {
|
||||||
return d.cashaddr.encode(prefix, 0, h160);
|
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 req = payload?.request || {};
|
||||||
const tx = req.transaction || {};
|
const tx = req.transaction || {};
|
||||||
const dappName = tx.userPrompt || "unknown dapp";
|
const dappName = String(tx.userPrompt || payload?.dappName || "unknown dapp").slice(0, 120);
|
||||||
const inputCount = Array.isArray(req.inputPaths) ? req.inputPaths.length : "?";
|
|
||||||
const bc = tx.broadcast ? "Dapp will broadcast after signing." : "Signed hex returned to dapp.";
|
|
||||||
const walletName = payload?.label || payload?.walletId || "";
|
const walletName = payload?.label || payload?.walletId || "";
|
||||||
return "<div><b>" + dappName + "</b> requests a BCH transaction signature.</div>"
|
const network = ctx.runtimes.get(payload?.walletId)?.entry?.network;
|
||||||
+ "<div>Wallet: <b>" + walletName + "</b> · " + inputCount + " input(s).</div>"
|
const prefix = network === "chipnet" ? "bchtest" : "bitcoincash";
|
||||||
+ "<div class=hint>" + bc + " Signs with SIGHASH_ALL|FORKID|UTXOS.</div>";
|
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 -------------------------------------------
|
// ---- per-purpose default wallets -------------------------------------------
|
||||||
|
|
@ -3655,12 +3687,16 @@ module.exports = {
|
||||||
api,
|
api,
|
||||||
// Bridge sign approvals through the addon's approval-modal capability.
|
// Bridge sign approvals through the addon's approval-modal capability.
|
||||||
approvalRequest: async (payload) => {
|
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({
|
const pick = await api.approvalModal({
|
||||||
title: `Sign transaction for ${dappName}`,
|
title: "Sign a Bitcoin Cash transaction (WizardConnect)?",
|
||||||
body: buildWcApprovalBody(payload),
|
origin: dappName,
|
||||||
approve: "Sign",
|
body, rows,
|
||||||
reject: "Reject",
|
actions: [{ id: "approve", label: "Sign", primary: true }],
|
||||||
});
|
});
|
||||||
return { approved: pick === "approve" };
|
return { approved: pick === "approve" };
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue