deviant/scripts/chipnet-uniform-aggregate.mjs
Local Dev debd6a6fbb chore: sweep in-flight work across all domains
Snapshot commit rolling up ~255 files of coherent-but-multi-domain in-flight
work that had accumulated on this branch. Split by area:

Contracts + covenants
- New: AsmFxSwap, DeviantOpenMintNBA, DeviantTieredMintOracleFT,
  TermCouponVault, UniformReserve, UniformReserveAggregate.
- Verify-artifact refresh (src/verify/artifacts/*.json + canonical.ts).

Wallet + shell (shells/app + src/wallet)
- App.tsx, MainScreen, ArtemisionScreen, AssetDetail, AgoraScreen,
  SettingsScreen, Unlock, TokenImportModal, UniformFiat/DepositPanel,
  KatalogosPanel (renamed from PinaxPanel), shell.css.
- src/wallet: wallet.ts, runtime.ts, index.ts. Core registry/chipnetFile.
- Deleted stale hook: useAllDevBalances.ts.

Cross-chain + correspondent (services/correspondent + scripts)
- Correspondent server/bch/config updates for PYUSD Solana, EURC Sepolia,
  aggregate reserve flows.
- New scripts: PYUSD roundtrip, EURC onboarding, GMX game loop /
  genesis / mint sanity, tiered mint fee, aggregate reserve, product2
  shares dividends, product3 t-bond, uniform fiat cell tests.

Telegram order-book (P2PExchange bot)
- db.ts, index.ts, en/ru locales — updates upstream from the extracted
  standalone repo at D:/Dev/P2PExchange.

Docs — Parameters (specs), wiki, snapshots, design mockups
- Parameters: FIATm/MBA_Shares/Potidaea/Anthemus refinements +
  ASM_CrossChain/DebitCredit + UniformFiat + Anthemus DEX MVP prompt +
  Artemision + Athenae Politeia + Philosophy + SSHkeyServer runbook +
  new dApp/{Artemision,Katalogos,Pinax,Pari_escrow,SURFACES}.
- wiki: cross-chain, games-tote, glossary, governance/voting-systems,
  infra, wallet, wallet-ui-design, decisions (ADR log).
- snapshots: catch-up entries and INDEX regeneration.
- design: mockups refreshed (agora, asset-detail, assets-tab, potidaea)
  + Potidaea assets + tote deploy notes.

Housekeeping
- .gitignore: exclude the new root test/session state files
  (.sol-vault-keys.json, .football-tote-state.json,
  .tote-platform-state.json, .redeem-roundtrip-state.json,
  .eurc-onboard-state.json — some contain oracle privkeys / vault
  secretKeyBase58; NEVER commit) + the .usdm-historical-chart.html
  chart output + MS Office `~$*` lock files + scripts/bundle-surfaces
  `build/` output. Also add scripts/bundle-surfaces.mjs.
- CLAUDE.md, WALLET-COORDINATION.md, snapshots/INDEX.md, design/README.md
  refreshed.
- package.json / package-lock.json bumps.

Verified: sensitive files at repo root confirmed ignored (git check-ignore
each). No `.env`, no raw WIF/xprv/mnemonic/secretKey/oraclePriv strings in
the staged diff. MS Word autosave file (`Parameters/Philosophy/~$*.docx`)
excluded and gitignored.

The WizardConnect responder work landed separately in the previous commit
(6987591); this is everything else that had accumulated alongside it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-29 01:14:33 +02:00

195 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* chipnet-uniform-aggregate.mjs — the AGGREGATE-model Uniform Fiat loop on LIVE CHIPNET (ADR-0053).
*
* Same killer-app loop as chipnet-uniform-fiat.mjs, but on the production `UniformReserveAggregate`
* covenant (40-byte commitment = aggregate(8) ‖ root(32); cells UNLIMITED, off-chain) driven by the
* `services/uniform-registry` ledger indexer. Proves the migration end-to-end AND that a swap co-spend
* now fits under BCH's 1650-byte per-input standardness limit (UniformReserve hit 1666).
*
* genesis USD+EUR → register cells → prefund EUR headroom → oracle-priced deposits (par + $0.87)
* → USD→EUR fxSwap (co-spends both) → pro-rata EUR withdraw. Every priced op = a 2-of-3 quorum.
*
* Run: node --import tsx scripts/chipnet-uniform-aggregate.mjs --yes
*/
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { TestNetWallet } from 'mainnet-js';
import { Contract, ElectrumNetworkProvider, TransactionBuilder, SignatureTemplate } from 'cashscript';
import { secp256k1, sha256 as laSha256 } from '@bitauth/libauth';
import { codeOf } from '../services/uniform-registry/ledger.mjs';
import { buildLedger } from '../services/uniform-registry/registry.mjs';
const ROOT = join(import.meta.dirname, '..');
const ART = JSON.parse(readFileSync(join(ROOT, 'contracts/dist/UniformReserveAggregate.json'), 'utf8'));
const KEYS = {
c: join(ROOT, '.chipnet-keys-c.json'), b: join(ROOT, '.chipnet-keys-b.json'),
e: join(ROOT, '.chipnet-keys-e.json'), f: join(ROOT, '.chipnet-keys-f.json'), g: join(ROOT, '.chipnet-keys-g.json'),
};
const SUPPLY = 1_000_000_000n;
const FX_SCALE = 1_000_000n, FX_BAND = 0n;
const PAR = 1_000_000n, DEPEG = 870_000n, RATE = 1_080_000n;
const PREFUND_EUR = 20_000_000n, DEP_USDT = 5_000_000n, DEP_USDC = 10_000_000n;
const SWAP_USD = 5_400_000n, SWAP_EUR = 5_000_000n, WITHDRAW_EUR = 2_000_000n;
const CAP = 500_000_000n;
const DUST = 1000n, MF = 15_000n;
const hexToBin = (h) => { const o = new Uint8Array(h.length / 2); for (let i = 0; i < o.length; i++) o[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16); return o; };
const binToHex = (b) => [...b].map((x) => x.toString(16).padStart(2, '0')).join('');
const txidLE = (h) => { const d = hexToBin(h); const o = new Uint8Array(d.length); for (let i = 0; i < d.length; i++) o[i] = d[d.length - 1 - i]; return o; };
const p2pkh = (pkh) => { const o = new Uint8Array(25); o[0] = 0x76; o[1] = 0xa9; o[2] = 0x14; o.set(pkh, 3); o[23] = 0x88; o[24] = 0xac; return o; };
const u32le = (n) => { const b = new Uint8Array(4); new DataView(b.buffer).setUint32(0, Number(n), true); return b; };
const u64le = (n) => { const b = new Uint8Array(8); new DataView(b.buffer).setBigUint64(0, BigInt(n), true); return b; };
const oSign = (priv, msg) => { const s = secp256k1.signMessageHashSchnorr(priv, laSha256.hash(msg)); if (typeof s === 'string') throw new Error(s); return s; };
const EMPTY = new Uint8Array(0);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const div = (c = '=') => console.log(c.repeat(84));
const step = (t) => { console.log(''); div(); console.log(t); div(); };
const txidOf = (s) => (typeof s === 'string' ? s : s?.txid ?? s?.txId);
const fmt = (n) => (Number(n) / 1e6).toFixed(2);
// commitment = aggregate(8 LE) ‖ root(32)
const commit = (agg, root) => binToHex(u64le(agg)) + binToHex(root);
// deposit (101): recipientLB(25) ‖ minted(8) ‖ newRoot(32) ‖ opTx(32) ‖ opIdx(4)
const depositMsg = (lb, minted, root, ctrl) => { const m = new Uint8Array(101); m.set(lb, 0); m.set(u64le(minted), 25); m.set(root, 33); m.set(txidLE(ctrl.txid), 65); m.set(u32le(ctrl.vout), 97); return m; };
// adjust (77): tag(1) ‖ opTx(32) ‖ opIdx(4) ‖ amount(8) ‖ newRoot(32)
const adjustMsg = (tag, amount, root, ctrl) => { const m = new Uint8Array(77); m[0] = tag; m.set(txidLE(ctrl.txid), 1); m.set(u32le(ctrl.vout), 33); m.set(u64le(amount), 37); m.set(root, 45); return m; };
// fxSwap (97): usdTx(32) ‖ usdIdx(4) ‖ eurTx(32) ‖ eurIdx(4) ‖ dir(1) ‖ amtUsd(8) ‖ amtEur(8) ‖ rate(8)
const fxMsg = (u, e, dir, aU, aE, r) => { const m = new Uint8Array(97); m.set(txidLE(u.txid), 0); m.set(u32le(u.vout), 32); m.set(txidLE(e.txid), 36); m.set(u32le(e.vout), 68); m[72] = dir; m.set(u64le(aU), 73); m.set(u64le(aE), 81); m.set(u64le(r), 89); return m; };
const pickBch = (u, need) => { const p = []; let t = 0n; for (const x of u) { p.push(x); t += BigInt(x.satoshis); if (t >= need) break; } return { p, t }; };
async function main() {
if (!process.argv.includes('--yes')) { console.log('Re-run with --yes to broadcast on chipnet.'); return; }
for (const k of ['e', 'f', 'g']) if (!existsSync(KEYS[k])) { const w = await TestNetWallet.newRandom(); writeFileSync(KEYS[k], JSON.stringify({ wif: w.privateKeyWif }, null, 2)); }
const C = await TestNetWallet.fromWIF(JSON.parse(readFileSync(KEYS.c, 'utf8')).wif);
const B = await TestNetWallet.fromWIF(JSON.parse(readFileSync(KEYS.b, 'utf8')).wif);
const [E, F, G] = await Promise.all(['e', 'f', 'g'].map(async (k) => TestNetWallet.fromWIF(JSON.parse(readFileSync(KEYS[k], 'utf8')).wif)));
const provider = new ElectrumNetworkProvider('chipnet');
const sigC = new SignatureTemplate(C.privateKey), sigB = new SignatureTemplate(B.privateKey);
const quorum = (msg) => [oSign(E.privateKey, msg), oSign(F.privateKey, msg), EMPTY]; // 2-of-3: E,F sign, G abstains
const instance = (cat) => new Contract(ART, [txidLE(cat), E.publicKey, F.publicKey, G.publicKey, SUPPLY, FX_SCALE, FX_BAND], { provider });
const ctrlOf = async (inst, cat) => (await provider.getUtxos(inst.tokenAddress)).find((x) => x.token?.category === cat && x.token?.nft);
const ftUtxo = async (w, cat, min) => (await provider.getUtxos(w.cashaddr)).find((x) => x.token?.category === cat && !x.token?.nft && BigInt(x.token.amount) >= min);
const ftBal = async (w, cat) => (await provider.getUtxos(w.cashaddr)).filter((x) => x.token?.category === cat && !x.token?.nft).reduce((s, x) => s + BigInt(x.token.amount), 0n);
async function pick(need) { const u = (await provider.getUtxos(C.cashaddr)).filter((x) => !x.token).sort((a, b) => Number(b.satoshis) - Number(a.satoshis)); const { p, t } = pickBch(u, need); if (t < need) throw new Error(`low BCH ${t}<${need}`); return { p, t }; }
async function eligible() { let u = await C.getUtxos(); let e = u.filter((x) => x.vout === 0 && !x.token); if (e.length) return e[0]; const bad = u.filter((x) => !(x.vout === 0 && !x.token)); await C.send([{ cashaddr: C.cashaddr, value: 3000n, unit: 'sat' }], bad.length ? { utxoIds: bad } : undefined); await sleep(12000); u = await C.getUtxos(); e = u.filter((x) => x.vout === 0 && !x.token); if (!e.length) throw new Error('splitter failed'); return e[0]; }
console.log(`operator ${C.cashaddr}\nuser ${B.cashaddr}\ncovenant UniformReserveAggregate (40-byte commitment = aggregate ‖ root; cells off-chain, unlimited)`);
async function genesis(label, ledger) {
const u0 = await eligible(); const cat = u0.txid; const inst = instance(cat);
const uSat = BigInt(u0.satoshis); const need = DUST + MF + 1000n;
const all = (await provider.getUtxos(C.cashaddr)).filter((x) => !x.token && !(x.txid === u0.txid && x.vout === u0.vout)).sort((a, b) => Number(b.satoshis) - Number(a.satoshis));
const { p, t } = pickBch(all, need > uSat ? need - uSat : 0n); const total = uSat + t;
const tb = new TransactionBuilder({ provider });
tb.addInput(u0, sigC.unlockP2PKH()); for (const x of p) tb.addInput(x, sigC.unlockP2PKH());
tb.addOutput({ to: inst.tokenAddress, amount: DUST, token: { amount: SUPPLY, category: cat, nft: { capability: 'mutable', commitment: commit(0n, new Uint8Array(32)) } } });
tb.addOutput({ to: C.cashaddr, amount: total - DUST - MF });
const tx = await tb.send(); await sleep(9000);
console.log(` ${label} · category ${cat.slice(0, 16)}… · ${txidOf(tx)}`);
return { cat, inst, ledger };
}
step('1. Genesis the USD and EUR aggregate baskets (cells from the VETTED CATALOG)');
// The off-chain ledgers come straight from services/uniform-registry — every vetted (issuer×chain)
// cell on the funded testnets, unlimited, the covenant never sees them.
const USD = await genesis('USD', buildLedger('USD', { supply: SUPPLY }));
const EUR = await genesis('EUR', buildLedger('EUR', { supply: SUPPLY }));
console.log(` catalog: ${USD.ledger.cells.size} USD cells + ${EUR.ledger.cells.size} EUR cells (all off-chain, vetted)`);
step(`2. Prefund ${fmt(PREFUND_EUR)} EUR headroom (adjust CREDIT — aggregate up, no minting)`);
{
const ctrl = await ctrlOf(EUR.inst, EUR.cat);
const { aggregate, root } = EUR.ledger.fund(codeOf('EURC', 'Ethereum'), PREFUND_EUR);
const msg = adjustMsg(1, PREFUND_EUR, root, ctrl);
const { p, t } = await pick(MF + 1000n);
const tb = new TransactionBuilder({ provider });
tb.addInput(ctrl, EUR.inst.unlock.adjustReserve(...quorum(msg), msg)); for (const x of p) tb.addInput(x, sigC.unlockP2PKH());
tb.addOutput({ to: EUR.inst.tokenAddress, amount: BigInt(ctrl.satoshis), token: { amount: BigInt(ctrl.token.amount), category: EUR.cat, nft: { capability: 'mutable', commitment: commit(aggregate, root) } } });
tb.addOutput({ to: C.cashaddr, amount: t - MF });
console.log(` EUR aggregate = ${fmt(aggregate)} · FT UNCHANGED · ${txidOf(await tb.send())}`); await sleep(9000);
}
step('3. ORACLE-PRICED DEPOSITS into the USD basket (par + a DEPEGGED coin at $0.87)');
async function deposit(code, amount, price, label) {
const ctrl = await ctrlOf(USD.inst, USD.cat);
const minted = amount * price / 1_000_000n;
const { aggregate, root } = USD.ledger.deposit(code, minted); // off-chain: cap check + cell bump + root
const msg = depositMsg(p2pkh(B.publicKeyHash), minted, root, ctrl);
const { p, t } = await pick(DUST + MF + 1000n);
const tb = new TransactionBuilder({ provider });
tb.addInput(ctrl, USD.inst.unlock.deposit(...quorum(msg), msg)); for (const x of p) tb.addInput(x, sigC.unlockP2PKH());
tb.addOutput({ to: USD.inst.tokenAddress, amount: BigInt(ctrl.satoshis), token: { amount: BigInt(ctrl.token.amount) - minted, category: USD.cat, nft: { capability: 'mutable', commitment: commit(aggregate, root) } } });
tb.addOutput({ to: B.tokenaddr, amount: DUST, token: { amount: minted, category: USD.cat } });
tb.addOutput({ to: C.cashaddr, amount: t - DUST - MF });
const tx = await tb.send(); await sleep(9000);
console.log(` ${label}: locked ${fmt(amount)} @ $${fmt(price)} → minted ${fmt(minted)} USD · agg ${fmt(aggregate)} · ${txidOf(tx)}`);
return minted;
}
const m1 = await deposit(codeOf('USDT', 'Tron'), DEP_USDT, PAR, 'USDT·Tron ');
const m2 = await deposit(codeOf('USDC', 'Ethereum'), DEP_USDC, DEPEG, 'USDC·Ethereum');
console.log(` → user holds ${fmt(m1 + m2)} USD (10 USDC credited only ${fmt(m2)} — MARKET value)`);
step(`4. fxSwap ${fmt(SWAP_USD)} USD → ${fmt(SWAP_EUR)} EUR (co-spends both — must fit 1650B per input)`);
{
const usdCtrl = await ctrlOf(USD.inst, USD.cat), eurCtrl = await ctrlOf(EUR.inst, EUR.cat);
const userUsd = await ftUtxo(B, USD.cat, SWAP_USD);
const msg = fxMsg(usdCtrl, eurCtrl, 0, SWAP_USD, SWAP_EUR, RATE); const q = quorum(msg);
const U = BigInt(userUsd.token.amount);
const { p, t } = await pick(3n * DUST + MF + 1000n);
const tb = new TransactionBuilder({ provider });
tb.addInput(usdCtrl, USD.inst.unlock.fxSwap(...q, msg));
tb.addInput(eurCtrl, EUR.inst.unlock.fxSwap(...q, msg));
tb.addInput(userUsd, sigB.unlockP2PKH()); for (const x of p) tb.addInput(x, sigC.unlockP2PKH());
tb.addOutput({ to: USD.inst.tokenAddress, amount: BigInt(usdCtrl.satoshis), token: { amount: BigInt(usdCtrl.token.amount) + SWAP_USD, category: USD.cat, nft: { capability: 'mutable', commitment: usdCtrl.token.nft.commitment } } });
tb.addOutput({ to: EUR.inst.tokenAddress, amount: BigInt(eurCtrl.satoshis), token: { amount: BigInt(eurCtrl.token.amount) - SWAP_EUR, category: EUR.cat, nft: { capability: 'mutable', commitment: eurCtrl.token.nft.commitment } } });
tb.addOutput({ to: B.tokenaddr, amount: DUST, token: { amount: SWAP_EUR, category: EUR.cat } });
if (U - SWAP_USD > 0n) tb.addOutput({ to: B.tokenaddr, amount: DUST, token: { amount: U - SWAP_USD, category: USD.cat } });
tb.addOutput({ to: C.cashaddr, amount: BigInt(userUsd.satoshis) + t - 2n * DUST - MF });
const tx = await tb.send(); await sleep(10000);
// fxSwap changes CIRCULATING only (backing untouched): EUR minted, USD absorbed.
EUR.ledger.mintOut(SWAP_EUR); USD.ledger.absorbIn(SWAP_USD);
console.log(` USD basket +${fmt(SWAP_USD)} · EUR basket −${fmt(SWAP_EUR)} · commitments UNCHANGED · ${txidOf(tx)}`);
console.log(' ✅ the two-controller co-spend was ACCEPTED under the 1650-byte per-input limit');
}
step(`5. Withdraw ${fmt(WITHDRAW_EUR)} EUR (pro-rata from the aggregate; largest-cell-first off-chain)`);
{
const ctrl = await ctrlOf(EUR.inst, EUR.cat);
const userEur = await ftUtxo(B, EUR.cat, WITHDRAW_EUR);
const { payout, aggregate } = EUR.ledger.withdraw(WITHDRAW_EUR);
const root = hexToBin(ctrl.token.nft.commitment.slice(16)); // root UNCHANGED on withdraw (ADR-0053 §4)
const U = BigInt(userEur.token.amount);
const { p, t } = await pick(2n * DUST + MF + 1000n);
const tb = new TransactionBuilder({ provider });
tb.addInput(ctrl, EUR.inst.unlock.withdraw(WITHDRAW_EUR)); tb.addInput(userEur, sigB.unlockP2PKH()); for (const x of p) tb.addInput(x, sigC.unlockP2PKH());
tb.addOutput({ to: EUR.inst.tokenAddress, amount: BigInt(ctrl.satoshis), token: { amount: BigInt(ctrl.token.amount) + WITHDRAW_EUR, category: EUR.cat, nft: { capability: 'mutable', commitment: commit(aggregate, root) } } });
if (U - WITHDRAW_EUR > 0n) tb.addOutput({ to: B.tokenaddr, amount: DUST, token: { amount: U - WITHDRAW_EUR, category: EUR.cat } });
tb.addOutput({ to: C.cashaddr, amount: BigInt(userEur.satoshis) + t - DUST - MF });
const tx = await tb.send(); await sleep(10000);
console.log(` burned ${fmt(WITHDRAW_EUR)} EUR · payout ${fmt(payout)} (agg ${fmt(aggregate)} ≥ circ → full 1:1) · ${txidOf(tx)}`);
}
step('VERDICT');
const usdCtrl = await ctrlOf(USD.inst, USD.cat), eurCtrl = await ctrlOf(EUR.inst, EUR.cat);
const usdAggOnchain = BigInt('0x' + [...hexToBin(usdCtrl.token.nft.commitment.slice(0, 16))].reverse().map((x) => x.toString(16).padStart(2, '0')).join(''));
const bUsd = await ftBal(B, USD.cat), bEur = await ftBal(B, EUR.cat);
const checks = [
['USD minted at MARKET value, not par (10 USDC → 8.70)', m2 === 8_700_000n],
['on-chain USD aggregate == off-chain ledger aggregate', usdAggOnchain === USD.ledger.aggregate],
['user holds USD + EUR on BCH', bUsd > 0n && bEur > 0n],
['USD basket registered MORE than 4 cells (off-chain)', USD.ledger.cells.size > 4],
['fxSwap co-spend fit the 1650-byte per-input limit', true], // it broadcast, so it did
];
for (const [label, ok] of checks) console.log(` ${ok ? '✅' : '❌'} ${label}`);
console.log(`\n USD: on-chain agg ${fmt(usdAggOnchain)} · ledger agg ${fmt(USD.ledger.aggregate)} · ${USD.ledger.cells.size} cells · user ${fmt(bUsd)}`);
console.log(` EUR: ledger agg ${fmt(EUR.ledger.aggregate)} · user ${fmt(bEur)}`);
const allOk = checks.every(([, ok]) => ok);
console.log(allOk
? '\nAGGREGATE MODEL VALIDATED ON CHIPNET — unlimited cells off-chain, one 8-byte aggregate on-chain,\nfxSwap co-spend under the 1650-byte limit. The four-cell ceiling is gone (ADR-0053).'
: '\nSOME CHECKS FAILED — inspect the steps above.');
}
main().catch((e) => { console.error('FAILED:', e?.message ?? e); process.exit(1); });