diff --git a/bundled-addons/aegis/addon.json b/bundled-addons/aegis/addon.json index d7d001d9..4b817481 100644 --- a/bundled-addons/aegis/addon.json +++ b/bundled-addons/aegis/addon.json @@ -1,7 +1,7 @@ { "id": "aegis", "name": "Aegis Wallet", - "version": "0.32.2", + "version": "0.32.3", "category": "plugin", "description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash and window.wizardconnect on any site; window.tronWeb / window.tronLink / window.ethereum / window.solana too. Every call needs your approval.", "author": "Silent Mode", diff --git a/bundled-addons/aegis/lib/wc.js b/bundled-addons/aegis/lib/wc.js index 466cbace..4f80ceba 100644 --- a/bundled-addons/aegis/lib/wc.js +++ b/bundled-addons/aegis/lib/wc.js @@ -23,7 +23,44 @@ const WALLET_ICON = "data:image/svg+xml;utf8," + encodeURIComponent( ` ); +// Reconnect backoff for WizardConnect relays. +// +// RelayClient.connect() resolves as soon as the subscription is created, before +// any socket opens. When every relay is unreachable (DNS down after a sleep, a +// relay offline) the pool closes the subscription at once, the connection +// manager hears "disconnected" and calls connect() again with no delay — its +// reconnectInterval only applies when connect() throws, which it never does. +// Every lap also re-wraps the queued wallet_ready (two Schnorr signatures and +// two ECDH per relay), all on Theseus's main thread: on 2026-10-07 that spun +// one core flat out for an hour, grew the process to 2.5 GB and froze the +// browser. Delaying connect() itself throttles the loop, whoever calls it: the +// first try is immediate, then 2 s, 4 s ... up to 60 s; a connection that has +// held for a minute starts the count over. +const RECONNECT_FIRST_MS = 2000; +const RECONNECT_MAX_MS = 60_000; +const RECONNECT_HEALTHY_MS = 60_000; +function throttleRelayReconnects(wcCore, { now = Date.now, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) { + const proto = wcCore?.RelayClient?.prototype; + if (!proto || typeof proto.connect !== "function" || proto.__aegisThrottled) return false; + const connect = proto.connect; + proto.connect = async function throttledConnect(...args) { + const st = this.__aegisReconnect || (this.__aegisReconnect = { last: 0, n: 0 }); + if (now() - st.last > RECONNECT_HEALTHY_MS) st.n = 0; + if (st.n > 0) { + const gap = Math.min(RECONNECT_MAX_MS, RECONNECT_FIRST_MS * 2 ** (st.n - 1)); + const wait = st.last + gap - now(); + if (wait > 0) await sleep(wait); + } + st.n++; + st.last = now(); + return connect.apply(this, args); + }; + proto.__aegisThrottled = true; + return true; +} + module.exports = function makeWc({ HDKey, secp256k1, sha256, hkdf, WalletConnectionManager, wcCore, libauth, log = () => {}, api, approvalRequest }) { + throttleRelayReconnects(wcCore); // ---- WalletAdapter -------------------------------------------------------- // @@ -265,3 +302,5 @@ module.exports = function makeWc({ HDKey, secp256k1, sha256, hkdf, WalletConnect return { startForWallet, stopForWallet, connectUri, disconnect, disconnectOrigin, snapshot, onStateChange }; }; + +module.exports.throttleRelayReconnects = throttleRelayReconnects;