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.
122 lines
4.8 KiB
JavaScript
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 };
|
|
};
|