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>
195 lines
16 KiB
JavaScript
195 lines
16 KiB
JavaScript
#!/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); });
|