diff --git a/bundled-addons/aegis/index.js b/bundled-addons/aegis/index.js index df8bebea..2eff0291 100644 --- a/bundled-addons/aegis/index.js +++ b/bundled-addons/aegis/index.js @@ -3351,15 +3351,31 @@ function registerPageMessages(api) { const perms = permissions(api); const budget = perms[origin] && perms[origin].sendTx; const remaining = budget ? Math.max(0, (budget.capSats | 0) - (budget.usedSats | 0)) : 0; + // The PIN wait below can last two minutes, during which the user may + // revoke the site or other permissions may change. Everything after it + // re-reads permissions and changes only this origin's sendTx; writing + // back the snapshot taken here used to restore a revoked allowance and + // wipe any other change made meanwhile. + const patchSendTx = (fn) => { + const now = permissions(api); + const cur = { ...(now[origin] || {}) }; + const next = fn(cur.sendTx); + if (!next && !now[origin]) return; // revoked meanwhile: nothing to change + if (next) cur.sendTx = next; else delete cur.sendTx; + now[origin] = cur; + api.storage.set("permissions", now); + }; if (budget && d.total <= remaining) { // An allowance skips the overlay, not the PIN the user asked for on // every transaction. await requireDappTxPin(origin, "Bitcoin Cash payment"); + const fresh = (permissions(api)[origin] || {}).sendTx; + const left = fresh ? Math.max(0, (fresh.capSats | 0) - (fresh.usedSats | 0)) : 0; + if (!fresh || fresh.grantedAt !== budget.grantedAt || d.total > left) throw new Error("this site's allowance changed while waiting for the PIN; ask again"); const r = await rt.adapter.signAndBroadcast(plan); - budget.usedSats = (budget.usedSats | 0) + d.total; - api.storage.set("permissions", perms); + patchSendTx((t) => (t ? { ...t, usedSats: (t.usedSats | 0) + d.total } : t)); emitState(); - api.log(`silent send ${d.total} sat for ${origin}, ${remaining - d.total} sat of allowance left`); + api.log(`silent send ${d.total} sat for ${origin}, ${left - 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 })); @@ -3384,13 +3400,8 @@ function registerPageMessages(api) { await requireDappTxPin(origin, "Bitcoin Cash payment"); const cap = flags.find((f) => f.startsWith("cap=")); const capSats = cap ? Number(cap.slice(4)) : 0; - 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); - } + if (BCH_ALLOWANCES.includes(capSats)) patchSendTx(() => ({ capSats, usedSats: 0, grantedAt: Date.now() })); + else if (budget) patchSendTx(() => null); emitState(); const r = await rt.adapter.signAndBroadcast(plan); return { txid: r.txid };