docs(snapshots): 0282 chipnet A-H full FT redeem + consolidation to H

Operation ran today: swept every FT holding across A-H + gmx
(backed MBAs + non-state NBAs) back to BCH via implicit fungible-token
burn, consolidated into wallet H. 9 txs total, 51 covenant-state NFTs
preserved. H now at 2.6967 BCH (up from 1.86).

Also kept two new audit helpers from the operation:
  scripts/scan-all-wallets-ft.mjs    one-shot inventory across every
                                     .chipnet-keys-*.json
  scripts/inspect-h-utxos.mjs        classifies H's UTXOs
                                     (bch-only / ft / nft / hybrid)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Local Dev 2026-10-23 21:26:44 +03:00
parent 23714aac43
commit cdf197e751
4 changed files with 216 additions and 2 deletions

View file

@ -0,0 +1,27 @@
import { readFileSync } from 'node:fs';
import { ElectrumNetworkProvider } from 'cashscript';
const j = JSON.parse(readFileSync('.chipnet-keys-h.json', 'utf8'));
const addr = j.cashaddr || j.address || j.tokenaddr;
const p = new ElectrumNetworkProvider('chipnet');
const u = await p.getUtxos(addr);
let ftOnly=0, nftOnly=0, both=0, bchOnly=0, ftZeroNoNft=0;
const ftWithUnitsBreakdown = new Map();
for (const x of u) {
if (!x.token) { bchOnly++; continue; }
const amt = BigInt(x.token.amount ?? 0);
const isNft = !!x.token.nft;
if (isNft && amt > 0n) both++;
else if (isNft) nftOnly++;
else if (amt > 0n) {
ftOnly++;
const c = x.token.category.toLowerCase();
ftWithUnitsBreakdown.set(c, (ftWithUnitsBreakdown.get(c) ?? 0n) + amt);
} else ftZeroNoNft++;
}
console.log({ total: u.length, bchOnly, ftOnly, nftOnly, bothNftAndFt: both, ftZeroNoNft });
console.log(`\nFT with units (will burn, satoshis recovered):`);
for (const [c, amt] of ftWithUnitsBreakdown) {
// find sat total for this cat
const sats = u.filter(x => x.token?.category?.toLowerCase() === c && !x.token?.nft).reduce((s,x)=>s+BigInt(x.satoshis??0),0n);
console.log(` ${c.slice(0,16)}… ${amt.toString().padStart(22)} units ${sats.toString().padStart(8)} sats backing`);
}

View file

@ -0,0 +1,58 @@
#!/usr/bin/env node
// Inventory FT holdings across every chipnet wallet (.chipnet-keys-*.json)
// Prints per-wallet BCH + per-category FT balances. Read-only.
import { readFileSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
import { ElectrumNetworkProvider } from 'cashscript';
const ROOT = join(import.meta.dirname, '..');
const KEYS = readdirSync(ROOT).filter(f => /^\.chipnet-keys.*\.json$/.test(f)).sort();
const provider = new ElectrumNetworkProvider('chipnet');
const KNOWN = {};
try {
for (const p of ['ChipnetTests/.chipnet-asm-token.json',
'ChipnetTests/.chipnet-asm2-token.json',
'ChipnetTests/.chipnet-cap-token.json',
'ChipnetTests/.chipnet-t001-token.json']) {
const o = JSON.parse(readFileSync(join(ROOT, p), 'utf8'));
const cat = (o.tokenId || o.categoryId || '').toLowerCase();
if (cat) KNOWN[cat] = o.ticker || o.name || p.split('/').pop();
}
} catch {}
for (const k of KEYS) {
let j;
try { j = JSON.parse(readFileSync(join(ROOT, k), 'utf8')); } catch { continue; }
const addr = j.cashaddr || j.address || j.tokenaddr;
if (!addr) { console.log(`${k}: no address field`); continue; }
try {
const utxos = await provider.getUtxos(addr);
let bch = 0n;
const byCat = new Map();
for (const u of utxos) {
if (u.token) {
bch += BigInt(u.satoshis ?? 0);
const c = u.token.category.toLowerCase();
byCat.set(c, (byCat.get(c) ?? 0n) + BigInt(u.token.amount ?? 0));
} else {
bch += BigInt(u.satoshis ?? 0);
}
}
const bchStr = (Number(bch) / 1e8).toFixed(8);
const label = k.replace(/^\.chipnet-keys-?/, '').replace(/\.json$/, '') || '(default)';
console.log(`\n${label.padEnd(20)} ${addr.slice(0, 18)}...${addr.slice(-6)}`);
console.log(` BCH ${bchStr} (${utxos.length} UTXOs)`);
if (byCat.size === 0) {
console.log(` no FTs`);
} else {
for (const [cat, amt] of byCat) {
const name = KNOWN[cat] || cat.slice(0, 10) + '…';
console.log(` ${name.padEnd(14)} ${amt.toString()} units [${cat.slice(0, 12)}…]`);
}
}
} catch (e) {
console.log(`${k}: ERROR ${e.message?.slice(0, 80)}`);
}
}

View file

@ -0,0 +1,127 @@
---
domain: infra
summary: All chipnet FT holdings (MBA + non-state NBA) redeemed to BCH across A-H + gmx; everything consolidated to wallet H. 9 txs, 51 covenant-state NFTs preserved.
tags: chipnet, wallet-h, consolidation, ft-redeem, mba-redeem, nba-burn
---
# 0282 — Chipnet wallets consolidated to H via FT→BCH redeem
## What
Cleared every chipnet experimental wallet of FT holdings and pulled all BCH
into wallet H (coordinator reserve). Two-step operation:
**Step 1** — H local sweep (`scripts/chipnet-wallet-sweep.mjs`, `WALLETS=h`):
burned H's own 40 FT-with-units UTXOs (ASM, ASM2, DEA, and 11 non-state
NBAs with real backing) in one atomic 42-input self-spend. Backing BCH
plus 2 plain change UTXOs → one consolidated plain-BCH output at H.
NFT UTXOs (48 pure + 3 FT+NFT hybrid) skipped by the `!u.token.nft`
filter so covenant state stays intact.
- txid: `1c1635892028a800…`
- 42 inputs (2 plain + 40 FT) → 1 output, 257,206,854 → 257,198,214 bits
(0.000086 BCH miner fee)
**Step 2** — A-G + gmx sweep to H (`scripts/chipnet-consolidate-to-dest.mjs
--redeem-mba --sources=a,b,c,d,e,f,g,gmx --dest=h`): one tx per source
wallet, each burns the source's FT UTXOs via implicit fungible-token
burn and sends backing BCH + plain BCH to H's address. FT backing
covered miner fees so no pre-fund needed.
| Source | Txid | Delivered to H (bits) |
|---|---|---|
| a | `de126311be38b301…` | 54,290 |
| b | `367401e9eb597aa9…` | 109,120 |
| c | `ddcc917281618ad9…` | 163,950 |
| d | `de2070855146db38…` | 587,760 |
| e | `4f677f0d964a297a…` | 163,950 |
| f | `a8ffbcfa0d502b8a…` | 163,950 |
| g | `32e7309f5741edc9…` | 163,950 |
| gmx | `cc79c3db89884543…` | 11,013,927 |
Total A-G+gmx → H: 12,420,897 bits (0.1242 BCH), 8,910 bits total fees.
## Scope decisions
The operation deliberately touched three asset classes:
- **MBAs** (ASM, ASM2, DEA, 7 others with non-dust backing): redeemed
cleanly — backing BCH released, FT units destroyed.
- **Non-state NBAs** (`74b6e11144…` 1e9 units, `37dcf7ee55…` and
`e6b84286de…` both 1e18 units, plus smaller ones): burned. Satoshi
dust in each UTXO returned to BCH; the units themselves are gone.
These were test genesis NBAs with no market value.
- **NFT/covenant-state UTXOs** (51 on H): LEFT ALONE. Each carries a
commitment (KatalogosRegistryV1 state, Anthemus platform state,
FIATm genesis batons, DVMB sidecars, mint batons, etc.). Burning
them would break live covenants. The NFT-skip rule in
`chipnet-wallet-sweep.mjs:49` enforces this.
One NFT+FT hybrid at `1ef063a431…` carries 100,000 fungible units
alongside its NFT commitment. Preserved intact — commitments ride
with their FT amount, as per CashTokens consensus.
## Final state
**Non-H wallets** (a, b, c, d, e, f, g, gmx): all empty — 0 UTXOs, 0 BCH.
**H** (coordinator reserve) `bchtest:qphhjmzplv…7yy99m`:
- **2.69670111 BCH** across 60 UTXOs
- 1 fresh consolidated plain-BCH output from step 1 (2.572 BCH)
- 8 small plain-BCH outputs from the A-G + gmx sweeps (one per source)
- 51 preserved NFT/covenant-state UTXOs
Up from 1.86 BCH after the 2026-09-29 consolidation to 2.6967 BCH today
(incoming A-G totals + previously-locked FT backing recovery).
## Why
Three motivations:
1. **Memory accuracy**: the chipnet-wallet-h memory says H is the
coordinator reserve, but a growing fraction of A-H satoshis were
locked inside FT UTXOs from months of swap/genesis/redeem tests.
Hard to answer "how much tBCH do we actually have" without
redeeming everything first.
2. **Clean slate**: no stale balances across experimental wallets.
Future chipnet runs start from H and distribute as needed, instead
of spelunking old wallets for leftover test funds.
3. **Potidaea balance-flash root cause** (snapshot 0281) wasn't these
funds — but tidying up the wallet state while diagnosing the issue
was useful adjacent work.
## Verification
- Pre-op scan: `node scripts/scan-all-wallets-ft.mjs` (new helper,
reads every `.chipnet-keys-*.json`) showed H at 2.57 BCH + 93
UTXOs across ~45 categories, A-G + gmx with small FTs.
- Post-op scan same script: non-H wallets all 0, H at 2.697 BCH
with 60 UTXOs, zero redeemable FTs remaining.
- `scripts/inspect-h-utxos.mjs` (also new, kept for future audits)
categorized H's UTXOs before the sweep: 2 bch-only, 40 ft-only,
48 nft-only, 3 nft+ft hybrid, 0 zero-amount-no-nft (nothing
weird to mishandle).
## Operational notes
- Keystore was decrypted in-place for the operation
(`chipnet-keystore-decrypt.mjs --in-place`). Must re-encrypt after
(`chipnet-keystore-encrypt.mjs --in-place`) — the decrypt classifier
rule applies: cleartext keys on disk are a credential-exposure risk.
- Both scripts broadcast via the multi-source pool
(`scripts/lib/chipnet-multisource.mjs`); every tx landed on first
attempt via bch.ninja. Fulcrum catches it; `fetchAddressUtxos`
returned post-op state within seconds.
## Related
- Memory [chipnet-wallet-h-coordinator-reserve] — updated with the new
balance and today's op
- Previous consolidation: 2026-09-29 (A→G → H ≈ 1.86 BCH); memory file
previously named that as the most recent event
- Script `chipnet-wallet-sweep.mjs` was already in place from an older
consolidation; `chipnet-consolidate-to-dest.mjs --redeem-mba` is the
newer superset. Both work; the former is simpler for single-wallet
local redeems.

View file

@ -10,7 +10,7 @@
> Duplicate snapshot numbers exist (0018, 0019, 0022, 0027, 0053, 0054, 0057, 0062, 0083, 0084,
> 0144, 0145, 0153, 0164) — they are distinct files, disambiguated below by slug.
_327 snapshots · 327 tagged · 8 domains · regenerate after adding one._
_328 snapshots · 328 tagged · 8 domains · regenerate after adding one._
## Quick-find — all snapshots (Ctrl-F by №, domain, tag, or word)
@ -343,6 +343,7 @@ _327 snapshots · 327 tagged · 8 domains · regenerate after adding one._
| [0279](0279-asm-sequencer-full-stack-userfunded-wallet-wire-systemd.md) | asm | asm sequencer tier-2 zero-contention chipnet systemd user-funded wallet-wire potidaea proof | Three-part completion of the tier-2 sequencer arc: (1) systemd unit + deploy README for running the daemon as a long-lived service; (2) user-funded /tip + /submit endpoints that remove the 'relay holds funds' trust step, chipnet-proved 5/5 (users signed their own BCH inputs, mean 3 attempts via 409-stale retry); (3) wallet-side sequencerRelay.ts client in both shells/app and design/potidaea with probe-first-then-commit race-free dispatch. Plus a chipnet-fund-from-h.mjs helper that topped up A/C/E/F/G from wallet H. |
| [0280](0280-asm-sequencer-settings-ui-toggle.md) | asm | asm sequencer tier-2 zero-contention settings-ui wallet-wire potidaea cors | Settings-UI toggle for the ASM sequencer relay in both surfaces. Beta wallet gets a new 'ASM sequencer relay' group in SettingsScreen with URL input + live reachability probe. Potidaea's dynamic walletMenu gains the same input + status chip (drafted, not yet committed — needs the DEA session's co-mingled main.ts/app.html changes to land first). Server gained permissive CORS so browser probes succeed. Browser-verified reachable↔unreachable transitions against a live chipnet daemon. |
| [0281](0281-wallet-potidaea-balance-flash-overlay-removed.md) | wallet | potidaea bug-fix cleanup balance-paint theseus | Potidaea's "0.82 BCH flash → empty" balance race fixed by removing the pending-swap optimistic overlay layer (−251 lines) and the wc.on('idle') auto-wipe. |
| [0282](0282-infra-chipnet-wallets-consolidated-to-h-ft-redeem.md) | infra | chipnet wallet-h consolidation ft-redeem mba-redeem nba-burn | All chipnet FT holdings (MBA + non-state NBA) redeemed to BCH across A-H + gmx; everything consolidated to wallet H. 9 txs, 51 covenant-state NFTs preserved. |
## Wallet UI (shells/app) `(107)`
@ -669,7 +670,7 @@ _327 snapshots · 327 tagged · 8 domains · regenerate after adding one._
- [0092-onramp-api-verify-jurisdiction-discover-](0092-onramp-api-verify-jurisdiction-discover-.md) — onramp api-verify jurisdiction discover-ui · _onramp, verify_
- [0122-combined-x10-price-plus-share-ramp-escal](0122-combined-x10-price-plus-share-ramp-escal.md) — combined x10 price plus share-ramp escalating NFT chipnet proven
## Infra / build / deploy / tooling `(23)`
## Infra / build / deploy / tooling `(24)`
- [0001-init](0001-init.md) — bootstrap
- [0003-regtest-harness](0003-regtest-harness.md) — regtest harness landed
@ -694,3 +695,4 @@ _327 snapshots · 327 tagged · 8 domains · regenerate after adding one._
- [0245-infra-deviantx-website-live](0245-infra-deviantx-website-live.md) — deviant.x now serves the static landing page (Parameters/webfiles/deviant_website.html) via Sia — repointed from p→testchipnet.duckdns.org to s3→bns/deviant.x/ in UPD tx c0914f24…; wallet CTAs on the page swapped to https://deviant.navigate.st (still needs an nginx vhost + DNS). · _infra, hosting, bns, deviant-x, sia, silentmode, navigate.st, deploy, landing_
- [0246-infra-deviantx-silentmode-primary-plus-navigate-st-vhost](0246-infra-deviantx-silentmode-primary-plus-navigate-st-vhost.md) — Three deploys: (1) https://deviant.navigate.st live on Checkers VPS 130.12.242.84 (nginx vhost + LetsEncrypt via webroot, ICANN mirror of the wallet — same docroot as testchipnet.duckdns.org); (2) landing page mirrored to SilentMode VPS 195.184.247.106 at https://navigate.st/deviant/ (new nginx location on the navigate-st site, alias /opt/silent-mode/site/deviant/); (3) deviant.x BNS record updated to {p: https://navigate.st/deviant/, s3: bns/deviant.x/} in UPD tx f7c030d4… — SilentMode is now the intended primary via p, Sia stays as s3 fallback. Discovered: navigate.st gateway prefers s3 over p (content-length delta 38678 via /bns/ vs 38649 direct), so the gateway route still serves Sia; direct BNS clients may pick p per their own precedence. Options to force SilentMode primary across the gateway captured in follow-ups. Plus: brand/ SVGs now @import the site's own fonts (Fraunces + DM Sans + JetBrains Mono), 4 new social sizes (twitter card 1200×675, twitter header 1500×500, FB cover, LinkedIn banner, IG square), retired the wrong twitter card 1200×600. Deploy script bugs fixed for greenfield re-runs. · _infra, hosting, bns, deviant-x, navigate.st, silentmode, sia, letsencrypt, nginx, brand, social, deploy_
- [0247-infra-anthemus-subpath-plus-dex-tld](0247-infra-anthemus-subpath-plus-dex-tld.md) — Anthemus now has a first-party landing at https://deviant.x/anthemus (self-hosted on SilentMode + Sia, links from the deviant.x main nav + pathways). Registered anthemus.dex BNS name (tx 3867797854d67a6dda6876bc4c4a8f2826f9175bf094f2fa3eb32d2c35cbe481, cat 0a70e739…) with records {p: https://navigate.st/deviant/anthemus/, s3: bns/deviant.x/anthemus/} — same content, three URLs (deviant.x/anthemus, anthemus.dex, anthemus.bch left in place as the mockup home). Plus: refreshed 'tests passing' metric on the landing 445 → 564 based on this session's npm test (564 pass, 0 fail). New deployment source-of-truth at site/deviant/; Parameters/webfiles/deviant_website.html is now frozen archive. · _infra, hosting, bns, deviant-x, anthemus, anthemus.dex, silentmode, sia, landing, dex_
- [0282-infra-chipnet-wallets-consolidated-to-h-ft-redeem](0282-infra-chipnet-wallets-consolidated-to-h-ft-redeem.md) — All chipnet FT holdings (MBA + non-state NBA) redeemed to BCH across A-H + gmx; everything consolidated to wallet H. 9 txs, 51 covenant-state NFTs preserved. · _chipnet, wallet-h, consolidation, ft-redeem, mba-redeem, nba-burn_