theseus/bundled-addons/bchwallet/lib/chain-bch.js
Local Dev c9106d4ede fix(theseus/light): darker acid (#3a5c00) + add missing overrides in addon panels
Previous #4d7300 (0.3.10) was still too light against actual white
backgrounds — several tint fills (rgba(214,255,61,X)) and unpatched
addon panels were making the effective color feel bright green. Two
fixes bundled:

1) Bump --acid in every top-level page's light-media block from
   #4d7300 to #3a5c00 — same hue, ~7:1 contrast on #ffffff (was ~5.5:1).
2) Add the missing light-media --acid override to the addon panels
   that were still resolving to #d6ff3d: bchwallet/panel.html,
   siawallet/panel.html, and screenshot/editor.css (was #b4e024, now
   #3a5c00 to match).

Dark mode unchanged. Tint fills (rgba backgrounds at low alpha) still
stay as-is — at 8–15% opacity the specific hue barely matters and the
darker foreground now dominates.
2026-09-07 00:56:33 +02:00

122 lines
4.8 KiB
JavaScript

// BCH chain adapter — wraps the existing keys.js / wallet.js / tx.js /
// electrum.js / cashaddr.js code with the common adapter shape that the
// multi-wallet manager talks to. Every BCH wallet is one BIP32 account
// derived from its own 32-byte root (from api.vault.derive).
//
// Deps are handed in so index.js loads @noble/* once and shares them across
// every wallet, rather than each adapter dynamic-importing on its own.
module.exports = function makeBchAdapter({
HDKey, secp256k1, sha256, ripemd160, cashaddr,
keysLib, tx, electrum, WebSocket,
}) {
const NETWORK = "mainnet";
const PREFIX = "bitcoincash";
const DEFAULT_ACCOUNT_PATH = "m/44'/145'/0'";
const EXPLORER_TX = "https://blockchair.com/bitcoin-cash/transaction/";
const EXPLORER_ADDR = "https://blockchair.com/bitcoin-cash/address/";
// storage is the FULL api.storage. keyPrefix scopes every read/write under
// "wallets/<walletId>/…" so multiple BCH wallets don't stomp each other.
function scopedStorage(storage, keyPrefix) {
const k = (key) => keyPrefix + key;
return {
get: (key, fallback = null) => storage.get(k(key), fallback),
set: (key, value) => storage.set(k(key), value),
};
}
class BchWallet {
constructor(root32, {
walletId, storage, log = () => {}, onChange = () => {}, servers,
accountPath = DEFAULT_ACCOUNT_PATH,
} = {}) {
if (!walletId) throw new Error("chain-bch: walletId required");
this.walletId = walletId;
this.chain = "bch";
this.network = NETWORK;
this.log = log;
this.onChange = onChange;
this.storage = scopedStorage(storage, `wallets/${walletId}/`);
this._servers = Array.isArray(servers) && servers.length ? servers : [];
this._accountPath = /^m(\/\d+'?)+$/.test(accountPath) ? accountPath : DEFAULT_ACCOUNT_PATH;
this._client = new electrum.Client(this._servers);
this._client.onServer = () => this._emit();
this._keys = new keysLib.WalletKeys(root32, this._accountPath, PREFIX);
this._root = new Uint8Array(root32);
const walletFactory = require("./wallet.js");
this._wallet = walletFactory({
client: this._client, keys: this._keys, tx, cashaddr, sha256,
storage: this.storage,
log: (...a) => this.log(...a),
onChange: () => this._emit(),
});
}
setServers(list) {
this._servers = Array.isArray(list) && list.length ? list : [];
this._client.setServers(this._servers);
}
_emit() { try { this.onChange(); } catch {} }
snapshot() {
const w = this._wallet.snapshot();
return {
chain: "bch",
network: NETWORK,
ticker: "BCH",
decimals: 8,
address: w.address,
addressIndex: w.addressIndex,
addressPath: w.addressPath,
balance: w.balance,
height: w.height,
history: w.history,
scanning: w.scanning,
error: w.error,
server: this._client.url || null,
servers: this._servers,
accountPath: this._accountPath,
xpub: this._keys.xpub,
explorerTx: EXPLORER_TX,
explorerAddr: EXPLORER_ADDR,
faucet: null,
};
}
async refresh(full) { return this._wallet.refresh(!!full); }
nextAddress() { return this._wallet.nextUnusedAddress(); }
current() { return this._wallet.current(); }
plan(spec) {
const targets = Array.isArray(spec.outputs) && spec.outputs.length
? spec.outputs.map((o) => ({ to: o.to, value: o.amount ?? o.value }))
: [{ to: spec.to, value: spec.amount ?? spec.value }];
return this._wallet.plan({ targets, feeRate: spec.feeRate, sendMax: !!spec.sendMax });
}
async signAndBroadcast(plan) { return this._wallet.signAndBroadcast(plan); }
// 65-byte BIP-137 recoverable signature — the format Electron Cash and
// most BCH tooling verify against.
signMessage(message) {
const enc = new TextEncoder();
const varstr = (s) => { const b = enc.encode(s); if (b.length >= 0xfd) throw new Error("too long"); return Uint8Array.from([b.length, ...b]); };
const MAGIC = "Bitcoin Signed Message:\n";
const payload = Uint8Array.from([...varstr(MAGIC), ...varstr(String(message))]);
const digest = sha256(sha256(payload));
const entry = this.current();
const sig = this._keys.signRecoverable(entry, digest);
return { address: entry.address, signature: Buffer.from(sig).toString("base64") };
}
recovery() {
return { accountPath: this._accountPath, xpub: this._keys.xpub, xprv: this._keys.xprv };
}
dispose() {
try { this._wallet.dispose(); } catch {}
try { this._keys.wipe(); } catch {}
try { this._client.disconnect(); } catch {}
if (this._root) this._root.fill(0);
}
}
return { BchWallet, DEFAULT_ACCOUNT_PATH, PREFIX, EXPLORER_TX, EXPLORER_ADDR };
};