// Aegis dapp bridges. Runs in the isolated world of every matching tab and // puts up to two dapp APIs on the page's main world: // // window.bitcoincash Silent Mode's own BCH API (only on .x pages — // matches the pre-multi-wallet gate). // window.tronWeb TronLink-shaped Tron dapp bridge (on any page). // window.tronLink TronLink shim; `tron_requestAccounts` triggers // an approval overlay via activate(). // // Everything crosses into the wallet's activate() context in main, which // shows the approval overlay and enforces per-origin permissions. Nothing // here can sign, read a balance, or read an address on its own. // // `theseus` is provided by the host: { id, origin, contextBridge, invoke }. const call = (msg, payload) => theseus.invoke(msg, payload).catch((e) => { const text = String(e && e.message || e).replace(/^Error invoking remote method '[^']+': (Error: )?/, ""); throw new Error(text); }); // -------- BCH bridge (window.bitcoincash), only on .x pages ------------------ const isBchOrigin = (() => { try { return /\.x$/i.test(location.hostname); } catch { return false; } })(); if (isBchOrigin) { theseus.contextBridge.exposeInMainWorld("bitcoincash", { isTheseus: true, version: "0.2.0", network: "mainnet", getAddress: () => call("getAddress"), signAndSend: (txSpec) => call("signAndSend", txSpec && typeof txSpec === "object" ? txSpec : {}), signMessage: (message) => call("signMessage", { message: String(message ?? "") }), }); } // -------- Tron bridge (window.tronWeb, window.tronLink), everywhere --------- // // Matches enough of the TronLink surface for read + send flows on dapps like // tronscan, sunswap, and justlend (mainnet or Nile). Not implemented: // TRC20/TRC721 abstractions, address utilities beyond base58<->hex, event // emitters beyond the "message" events TronLink documents. let CACHED = { address: null, network: null }; const listeners = new Set(); // Public copy of tronWeb.trx. const trx = { // Returns the account object TronGrid gives (may be {} for never-funded). // Deliberately mirrors tronWeb.trx.getAccount. getAccount: async () => call("trx.getAccount"), // Sign an assembled transaction (dapp built it via its own tronWeb). // Returns the same object with a `signature` field populated. sign: async (transaction) => { if (!transaction || typeof transaction !== "object" || !transaction.raw_data_hex) { throw new Error("tronWeb.trx.sign: expected an assembled transaction with raw_data_hex"); } return call("trx.signTransaction", { transaction }); }, // Broadcast a signed transaction. Returns { result: true, txid } on success. sendRawTransaction: async (signedTx) => { const r = await call("trx.sendRawTransaction", { transaction: signedTx }); return { result: true, txid: r.txid }; }, // Convenience wrappers matching the tronWeb surface — dapps call these // when the wallet is supposed to build the tx too. Left unimplemented // in this build so a dapp that hits them gets a clear error instead of // silently unassembled behavior; assemble on the dapp side. sendTrx: async () => { throw new Error("tronWeb.trx.sendTrx not implemented — build the transaction with tronWeb.transactionBuilder and call sign+sendRawTransaction"); }, // TronLink documents signMessage (legacy, keccak of just the message) and // signMessageV2 (adds the "\x19TRON Signed Message:\n" prefix). We // only implement V2 because that is what modern dapps use. signMessageV2: async (message) => { const r = await call("trx.signMessageV2", { message: String(message ?? "") }); return r.signature; }, }; // Shallow tronWeb — enough for `tronWeb.defaultAddress.base58` / `.hex` reads // and the trx methods. Populated once the user connects. const tronWeb = { ready: false, defaultAddress: { base58: false, hex: false, name: false }, fullNode: { host: "" }, solidityNode: { host: "" }, eventServer: { host: "" }, trx, // Utility helpers a few dapps poke at. isConnected: () => tronWeb.ready === true && !!tronWeb.defaultAddress.base58, address: { fromHex: (hex) => hex, // no-op stub; dapps that need a real toHex: (base58) => base58, // conversion should use their own copy. }, // Some dapps read this to decide "Tron mainnet vs testnet". get currentNetwork() { return CACHED.network || null; }, }; function nodeHost(network) { return network === "nile" ? "https://nile.trongrid.io" : "https://api.trongrid.io"; } function applyConnection({ address, network }) { CACHED = { address, network }; tronWeb.defaultAddress = { base58: address || false, hex: false, name: false }; tronWeb.ready = !!address; tronLink.ready = !!address; const host = nodeHost(network); tronWeb.fullNode = { host }; tronWeb.solidityNode = { host }; tronWeb.eventServer = { host }; // Broadcast on the window in the way TronLink documents. try { window.postMessage({ message: { action: "accountsChanged", data: { address } }, isTronLink: true }, location.origin); window.postMessage({ message: { action: "setAccount", data: { address } }, isTronLink: true }, location.origin); window.postMessage({ message: { action: "setNode", data: { node: { fullNode: host, solidityNode: host, eventServer: host, chain: network === "nile" ? "0xcd8690dc" : "0x2b6653dc" } } }, isTronLink: true }, location.origin); } catch {} for (const fn of listeners) { try { fn({ address, network }); } catch {} } } const tronLink = { ready: false, tronWeb: tronWeb, // TronLink's canonical request router. Handles at minimum tron_requestAccounts. request: async (opts) => { const method = String(opts?.method || ""); if (method === "tron_requestAccounts" || method === "requestAccounts") { const r = await call("trx.requestAccounts"); applyConnection({ address: r.address, network: r.network }); return { code: 200, message: "ok" }; } if (method === "tron_accounts") { const r = await call("trx.getAccount").catch(() => null); return r ? [r.address] : []; } throw new Error("Aegis: unsupported tronLink method " + method); }, // Dapps sometimes attach here to react to wallet changes. on: (event, fn) => { if (event === "message" || event === "accountsChanged" || event === "networkChanged") listeners.add(fn); }, removeListener: (_event, fn) => listeners.delete(fn), }; theseus.contextBridge.exposeInMainWorld("tronWeb", tronWeb); theseus.contextBridge.exposeInMainWorld("tronLink", tronLink); // -------- Main-world bridges (window.ethereum, window.solana) --------------- // // EIP-1193 (Ethereum) and the Solana wallet-adapter both expect the wallet // object to touch the dapp's own JS values — Solana in particular passes // `Transaction` instances whose `.serializeMessage()` method the wallet has // to call. Electron's contextBridge shallow-copies function arguments // between worlds and strips methods, so bridges that need to call methods // on dapp-side objects have to live in the main world. We inject a `