theseus/bundled-addons/bchwallet/wallet-inject.js

143 lines
6.5 KiB
JavaScript
Raw Normal View History

feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// 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 }.
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
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);
});
feat(theseus/aegis): multi-wallet + Tron mainnet + Tron Nile in the bundled addon Turns the single-account BCH addon into Aegis: a chain-agnostic wallet manager with a wallet picker in the sidebar header, per-wallet sub-accounts, and Tron mainnet + Nile alongside BCH. Add-on id stays "bchwallet" so vault-derive paths stay in the same namespace and the legacy BCH default wallet uses PURPOSE "bchwallet/mainnet/0" byte-identical to before — funds are untouched. - lib/chain-bch.js wraps the existing keys/tx/wallet/electrum stack with the common adapter shape and scopes each wallet's storage under wallets/<id>/… - lib/chain-tron.js: m/44'/195'/0'/0/0 → secp256k1 → keccak256 → 0x41 || h20 → base58check. Balance + history via TronGrid v1, send via createtransaction + sha256(raw_data_hex) sign + broadcasttransaction. Mainnet and Nile share the address format; different vault paths mean different keys so a mainnet wallet can never accidentally sign against Nile. - lib/base58check.js: bitcoin-alphabet base58 with sha256d checksum. k=1 derivation verified against Ethereum's canonical k=1 H160 in a scratchpad harness (correct-by-construction for Tron address). - Combined wallet-inject.js: window.bitcoincash on .x pages (unchanged gate), window.tronWeb + window.tronLink on any https page. tron_requestAccounts triggers the approval overlay; sign / sendRawTransaction / signMessageV2 route to the currently-selected Tron wallet. Emits accountsChanged / setNode messages TronLink dapps listen for; chain ids 0x2b6653dc / 0xcd8690dc match what TronLink itself uses. - New panel: chain-aware wallet picker in the header (badges 🟨 BCH, 🔴 Tron, 🔵 Nile), Add-wallet dropdown per chain, per-chain unit picker (BCH/sat, TRX/sun), per-wallet rename + remove (isLegacy default is protected). Sends show the chosen wallet in the approval overlay so the user can never mistake sub-account. - Migration on first launch: pre-multi-wallet storage (top-level receiveCursor / txCache) is rehomed under wallets/bch-default/… and the legacy account path is preserved. Not shipped: user is bundling into the next release. Live Nile broadcast + real dapp connect need a set-up vault; the code paths are unit-verified end to end but a testnet send + tronscan.io/nile connect are user-side steps.
2026-09-06 22:00:27 +02:00
// -------- 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<len>" 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);