fix(aegis): 0.15.0 — ask Electrum for protocol 1.5, which is where the tokens were
Aegis negotiated a flat protocol "1.4". Measured against Fulcrum 2.1.0 on chipnet, for a wallet holding CashTokens: asked "1.4" -> negotiated 1.4 -> 39 utxos, 0 with token_data asked ["1.4","1.5.3"] -> negotiated 1.5.3 -> 130 utxos, 91 with token_data So 1.4 did not merely omit the `token_data` field — Fulcrum left the token-bearing outputs out of listunspent altogether. Ninety-one UTXOs were invisible to the wallet, along with the BCH sitting in them. That is a balance-correctness bug, not only a missing Assets card, and it applied to the HD path too, since lib/wallet.js reads the same listunspent. Now a [min, max] range: a modern server picks 1.5.3, an older one still settles on 1.4, so nothing that worked before stops working. The negotiated version is recorded on the client for diagnosis. Second half: the imported BCH adapter had no CashToken code at all — it never set tokenBalances and snapshot() never exposed it, so the panel's Assets card was hidden for every WIF import however many tokens the address held. A chipnet test wallet with 46 categories showed nothing. It now aggregates from the server's own token_data, which costs one call for the whole set rather than the per-UTXO transaction fetch the HD path uses, and emits the same serialised shape the panel already reads. Verified against that wallet: 130 UTXOs, 46 categories, 17 fungible, 32 with NFTs, JSON-clean. Found because the user said their asset "uses a different asset category" and suggested checking with the explorer. It is ordinary CashTokens; the wallet simply could not see them. My earlier conclusion that the empty Assets card was correct came from probing a single address that genuinely holds no tokens and generalising from it.
This commit is contained in:
parent
6d844b03cf
commit
74224cd7dd
3 changed files with 55 additions and 3 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "aegis",
|
||||
"name": "Aegis Wallet",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0",
|
||||
"category": "plugin",
|
||||
"description": "Multi-chain wallet (BCH, BTC, TRX, ETH, SOL, SC, DGB) derived from your Theseus vault. Dapps get window.bitcoincash on .x sites; window.tronWeb / window.tronLink / window.ethereum / window.solana on any https page.",
|
||||
"author": "Silent Mode",
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ module.exports = function makeImportedBchAdapter({
|
|||
history: [],
|
||||
utxos: [],
|
||||
height: 0,
|
||||
tokenBalances: null, // { <categoryHex>: {fungible, nfts, utxoCount} }
|
||||
scanning: false,
|
||||
error: null,
|
||||
};
|
||||
|
|
@ -148,6 +149,9 @@ module.exports = function makeImportedBchAdapter({
|
|||
balance: this._state.balance,
|
||||
height: this._state.height,
|
||||
history: this._state.history,
|
||||
// Without this the panel's Assets card has nothing to read and stays
|
||||
// hidden however many tokens refresh() found.
|
||||
tokenBalances: this._state.tokenBalances || null,
|
||||
scanning: this._state.scanning,
|
||||
error: this._state.error,
|
||||
server: this._client.url || null,
|
||||
|
|
@ -170,9 +174,46 @@ module.exports = function makeImportedBchAdapter({
|
|||
this._state.balance = { confirmed: Number(bal?.confirmed || 0), unconfirmed: Number(bal?.unconfirmed || 0) };
|
||||
// Always pull UTXOs so spend / send-max work off fresh state.
|
||||
const utxos = await this._client.call("blockchain.scripthash.listunspent", [this._scripthash]);
|
||||
this._state.utxos = (Array.isArray(utxos) ? utxos : []).map((u) => ({
|
||||
const list = Array.isArray(utxos) ? utxos : [];
|
||||
this._state.utxos = list.map((u) => ({
|
||||
txid: u.tx_hash, vout: u.tx_pos, value: Number(u.value), height: Number(u.height || 0),
|
||||
token: u.token_data || null,
|
||||
}));
|
||||
// CashTokens. This adapter never looked for them, so a WIF-imported
|
||||
// wallet holding tokens reported none and the panel hid its Assets
|
||||
// card — a chipnet test wallet with 46 categories showed nothing.
|
||||
//
|
||||
// The HD path (lib/wallet.js) decodes the token prefix off each
|
||||
// UTXO's scriptPubKey, which costs one transaction fetch per UTXO.
|
||||
// Here we take the server's own `token_data` instead: one call for
|
||||
// the whole set. That arrives only when protocol >= 1.5 was
|
||||
// negotiated (see electrum.js), and when it does not, tokens are
|
||||
// simply absent — the same as before this change, never wrong.
|
||||
const tokenBalances = {};
|
||||
for (const u of list) {
|
||||
const t = u.token_data;
|
||||
if (!t || !t.category) continue;
|
||||
const cat = String(t.category);
|
||||
if (!tokenBalances[cat]) tokenBalances[cat] = { fungible: 0n, nfts: [], utxoCount: 0 };
|
||||
const b = tokenBalances[cat];
|
||||
b.utxoCount++;
|
||||
if (t.amount) { try { b.fungible += BigInt(t.amount); } catch (_e) {} }
|
||||
if (t.nft) {
|
||||
b.nfts.push({
|
||||
utxoId: `${u.tx_hash}:${u.tx_pos}`,
|
||||
commitmentHex: t.nft.commitment || "",
|
||||
capability: t.nft.capability || "none",
|
||||
capabilityLabel: t.nft.capability || "none",
|
||||
});
|
||||
}
|
||||
}
|
||||
// Same serialised shape the HD wallet emits — fungible as a decimal
|
||||
// string, since JSON.stringify cannot carry a BigInt.
|
||||
const serialised = {};
|
||||
for (const [cat, b] of Object.entries(tokenBalances)) {
|
||||
serialised[cat] = { fungible: b.fungible.toString(), nfts: b.nfts, utxoCount: b.utxoCount };
|
||||
}
|
||||
this._state.tokenBalances = serialised;
|
||||
if (full) {
|
||||
const hist = await this._client.call("blockchain.scripthash.get_history", [this._scripthash]);
|
||||
this._state.history = (hist || []).slice(-50).map((h) => ({
|
||||
|
|
|
|||
|
|
@ -22,7 +22,18 @@ module.exports = function makeElectrum({ WebSocket, log = () => {} }) {
|
|||
this.ws = ws;
|
||||
const fail = (e) => { if (!this.closed) { this.closed = true; reject(e instanceof Error ? e : new Error("electrum ws error: " + this.url)); } };
|
||||
ws.on("open", async () => {
|
||||
try { await this.call("server.version", ["theseus-bchwallet", "1.4"]); resolve(this); }
|
||||
// A RANGE, not a flat "1.4". Fulcrum only attaches `token_data` to
|
||||
// listunspent results once protocol >= 1.5 is negotiated, and with
|
||||
// a flat 1.4 it silently omits it — which is why imported BCH
|
||||
// wallets showed no CashTokens at all. A [min, max] pair lets a
|
||||
// modern server pick 1.5.3 while an older one still settles on 1.4,
|
||||
// so nothing that worked before stops working.
|
||||
try {
|
||||
const v = await this.call("server.version", ["theseus-bchwallet", ["1.4", "1.5.3"]]);
|
||||
this.serverVersion = Array.isArray(v) ? v[0] : null;
|
||||
this.protocolVersion = Array.isArray(v) ? v[1] : null;
|
||||
resolve(this);
|
||||
}
|
||||
catch (e) { fail(e); this.close(); }
|
||||
});
|
||||
ws.on("error", fail);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue