Wallet a on chipnet had 9,359 dust UTXOs (600-bit fee-signature outputs
from throughput runs). A single tx over that whole set is ~1.4 MB — well
above mempool tx-size limits, so the source would fail hard rather than
consolidate.
Add --batch-inputs=N (default 400, matches chipnet-wallet-sweep.mjs) to
split any source with too many inputs into a sequence of self-contained
txs, each ≤ ~60 KB. When the source has FT/NFT specials (passthrough
mode), the LAST batch carries them + enough plain BCH for its fee;
earlier batches are pure plain-BCH consolidates.
Also fix a latent bug in the batching logic: when a source had NO FT/NFT
specials (redeem-mba or plain-only source), `batches` came out empty
because the `specials.length ? ... : earlyBatches` branch discarded the
plain-only chunks. Now the two branches are separated: specials-present
uses the last-batch carve-out; specials-absent chunks plain into fixed-
size batches directly.
Verified by sweeping the a/b/c dust to h: a → 24 batches (400 inputs
each × 23 + 159 final) recovered 4.03M bits at ~1.57M fee (600-bit inputs
are inherently uneconomical, but the sweep now COMPLETES). b → 1 tx, c
→ 1 tx. All 26 broadcasts OK. a/b/c/d/e/f/g balances now zero; h holds
185.76M plain + 34.20M MBA backing + 50 NFTs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New scripts/chipnet-consolidate-to-dest.mjs — a general-purpose consolidate
that supersedes the wallet UI's plain-BCH-only ConsolidateFlow. Handles the
three UTXO classes the UI leaves stranded:
• Plain BCH — merged into one output at the destination.
• MBA fungible tokens — passthrough (one output per category, amounts summed)
or --redeem-mba to burn implicitly and recover backing bits as plain BCH.
• NFTs (incl. mutable, minting-batons) — 1:1 output preserving category,
commitment, and capability. --leave-nfts to keep them on the source.
Modes:
default (passthrough) — dest ends up with plain BCH + all FT categories
+ all NFT identities preserved. Source must have some plain BCH to
cover the miner fee; script auto pre-funds any zero-plain source from
the destination in ONE small tx before the sweep.
--redeem-mba — implicit fungible-token burn (no Potidaea call, no swap
fee, only the standard BCH miner fee). Backing satoshis fold into
the plain-BCH output. NFTs skipped (they carry commitments).
--leave-nfts — never sweep NFTs (match old wallet-sweep behaviour).
Also fixes chipnet-wallet-balances.mjs to work with the master-password-
encrypted keystore (calls ensureKeystoreUnlocked() at boot).
Verified end-to-end on chipnet:
Pre-fund tx 1639ee5d…, then a→h eb541121…, b→h 28037c34…, c→h 5e94663a…,
d→h d1642b55…, e→h 95dc1f33…, f→h 4e34e699…, g→h 0b82c6ee… — 7/7 OK
at height 325269. 91.4M bits + 25 FT categories + 50 NFTs consolidated
into h (which is HD-seed, m/44'/145'/0'/0/0 — WizardConnect-ready).
The WIF-only a-g wallets couldn't do WalletConnect because the shipped
@wizardconnect/wallet SDK requires an xpub via getXpub(path), which only
works with a seed. This script gets those funds onto a seed-backed wallet
without stranding the MBA CashTokens.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Design-only sketch — does not touch any live rail. Follows 0269's honest trust
discussion by walking a legitimate reduction in the deposit-time trust window
that the current bonded-operator model has.
contracts/WithdrawalHTLC.cash
BCH side of a WITHDRAWAL atomic swap. Two branches:
claim(sig dexSig, bytes preimage)
sha256(preimage) == hashLock, DEX signature, output[0] pinned to return
the CashToken to the Deposit reservoir's locking bytecode (burn on
claim — supply un-mints to match the DGB drained on the source chain).
refund(sig aliceSig)
Alice signature + tx.time >= timeout — freeform spend.
Uses SHA-256 (matches DGB / BTC OP_SHA256 in the mirror HTLC), absolute
Unix-timestamp locktime, and enforces the reservoir shape via
OP_OUTPUTBYTECODE / OP_OUTPUTTOKENCATEGORY / OP_OUTPUTTOKENAMOUNT / OP_OUTPUTVALUE.
Compiled cleanly against cashc 0.12.x on first pass. Both branches
materialize as expected in the ASM (OP_IF … OP_ELSE … OP_ENDIF); reservoir
dust check emits >= 546. Artifact reproducible via
npx cashc contracts/WithdrawalHTLC.cash -o build/WithdrawalHTLC.json
scripts/design/withdrawal-atomic-swap-sketch.mjs
Mocked driver using MockNetworkProvider. Walks Phases 1-5: preimage +
hashlock generation, HTLC funding on both chains (BCH side sketched via
cashscript TransactionBuilder, DGB side described in comments as a plain
P2SH-HTLC), happy-path claim with burn to reservoir, refund path with
timeouts, reconciliation. Not runnable end-to-end (reservoirLB shim + a
couple of illustrative UTXO shapes); it reads as a sequence diagram in
code, which is what a design sketch wants.
snapshots/0270-atomic-swap-deposit-withdrawal-htlc-design.md
Design doc. Covers the paired-covenant pattern (WithdrawalHTLC +
DepositHTLC sibling), the T_A >= 2·T_B timeout safety rule, what atomic
swaps FIX (transactional trust window on both deposit and withdrawal)
and what they DON'T FIX (accumulated custody trust in the DEX's
source-chain address, which still needs MPC/multi-sig + slashable-bond
as separate composable layers). Also flags the Deposit.cash v2 companion
requirement — a burn() function that accepts an incoming CashToken input
and increments the reservoir's FT amount. Not yet coded; symmetric to
the existing deposit() function.
Roadmap position: bonded-operator rails (0267-0269) stay as-is. MPC signing
is the honest first upgrade to those. Atomic-swap deposits (this sketch)
is the second, and would migrate one rail at a time starting with DGB.
Slashable-bond covenant is the third. Trustless BCH-verified DGB release
remains blocked by DGB Script (no zk opcodes, no BCH light client).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Renames the deposit primitive from DeviantWrapMintIOU → Deposit (byte-identical
bytecode; a deposit is a balance, not a wrapped IOU — matches Binance/Bybit UX).
Adds the DGB correspondent rail and drives the first native-coin (non-stablecoin)
deposit end-to-end across two real chains.
New primitive
- contracts/Deposit.cash — cleaner constructor + deposit() naming (was mint()); the
old wrap/IOU language is gone, bytecode unchanged so the on-chain semantics carry
over from snapshots 0198/0199. DeviantWrapMintIOU.cash header now points here.
- test/regtest/deposit.test.ts — happy path + forged-sig rejection + replay guard
against stale outpoint attestation (3/3 passing).
New rail
- services/correspondent/dgb.mjs — DGB mainnet keypair loader, P2PKH + P2WPKH
address derivation, Blockbook + Esplora (digiexplorer.info) UTXO readers,
buildLockTx() with auto-detected input type, verifyLock() covering 3 API shapes,
release() for bonded-operator vault spends, broadcastEsplora() for the DGB
free-tier landscape.
- scripts/onboard-dgb-mainnet-real.mjs — --yes gated onboarding driver with
--dgb-txid= mode for user-broadcast locks; generates + persists a P2WPKH
vault key on first run (gitignored).
- scripts/redeem-dgb-mainnet-real.mjs — --yes gated redeem driver; leg 1 returns
the CashToken to operator D on chipnet, leg 2 releases the DGB from the mainnet
vault to the user's destination.
Real bugs found + fixed against real coin
- P2WPKH signing per BIP143 requires a P2PKH-shaped scriptCode
(OP_DUP HASH160 <hash> EQUALVERIFY CHECKSIG), not the P2WPKH output script;
earlier code used the output script and hit
"mandatory-script-verify-flag-failed (Signature must be zero…)".
- DGB min-relay fee ≈ 200 sat/vB (not BTC's 20). Default bumped in both
buildLockTx and release() so the default fee schedule no longer under-quotes.
- ALICE.address doesn't exist on mainnet-js TestNetWallet — use .cashaddr.
- Log lines no longer double-print " DGB DGB" (fmt() now emits the ticker once).
Verified on-chain
- DGB mainnet lock f334cd5293c3f8e3de777a6a477ba0f4d9a8a0f017c4ee9ccd3de2aa47d06319 block 24154002
- BCH chipnet mint e21ceafcd8… (category 74b6e111446253bb…)
- BCH chipnet return d67d9205ec06b0d9031fbbc027c84edaf130e5bd2f3332b511eaa065d90f68d0
- DGB mainnet release 317e73d3046132eca9eda416a6c0d0e4a616902f1664b842d0418525d7c2c114 block 24154111
- Reconcile: 10 DGB locked == 10 DGB credited; on redeem 9.9995 DGB returned to
DKFGD4nGguXRtT9adRF8bymUHYUSudd7mx (net of 28,200 sat DGB network fee).
Honest trust note (documented in snapshot 0269)
The mainnet vault is a plain P2WPKH — bonded-operator custody, the same trust
floor as evm/tron/solana rails (ADR-0047). Spending needs only the operator key;
the release is NOT covenant-gated by the BCH CashToken burn. Reducing this trust
means moving to a federated M-of-N vault with signers that independently attest
to the BCH burn before signing the DGB release. A trustless BCH-verified release
isn't reachable without zk opcodes or a BCH light client on DGB, neither of
which DGB Script supports.
.dgb-vault-keys.json is added to .gitignore alongside .chipnet-keys.json — it
holds a WIF for real DGB mainnet coin and must never be committed.
Snapshots 0267 (build), 0268 (deposit), 0269 (redeem). Index rebuilt to 310.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Realises ADR-0011's two-knob fee model on chain: feeBps is mutable (carried in
the reservoir NFT's commitment, rotated by a founder-signed updateFee), bounded
by immutable maxFeeBps constructor arg. Reuses the "carried-rate trick" from
snapshot 0128 (DeviantExpDRA). Single contract address forever — holders never
migrate on fee changes.
- contracts/DeviantAdjustableFeeCapped.cash — new covenant (mutable NFT + cap).
- scripts/chipnet-adjustable-fee-test.mjs — 6-scenario chipnet test, 6/6 pass,
two negatives rejected on-chain at the exact require() lines (cap + auth).
- snapshots/0259-*.md — full write-up, evidence table, design comparison,
cross-refs to ADR-0011/0029 and snapshots 0128/0089.
- Parameters/Artemision/SESSION_PROMPT_adjustable-fee-wallet-integration.md —
self-contained parallel-session brief to wire the template into wallet UI
(types → portable → D-1 → live-fee claim → wizard genesis → founder-only
updateFee action), following the Route B (snapshot 0089) pattern.
- Parameters/dApp/Agora/README.md — Agora folder index pointing to the
Artemision session prompt (per user note: Agora should keep links to
Artemision for cross-domain discoverability).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds AES-256-GCM + PBKDF2-SHA256 (200,000 iterations) encryption for the
consolidated chipnet keystore. Loader auto-detects the encrypted format
(`chipnet-keystore/2-encrypted`) and prompts for the master password on
first use — or reads CHIPNET_KEYSTORE_PASSWORD env for scripted/CI runs.
Password cached in-process only.
- scripts/chipnet-keystore-encrypt.mjs — one-shot encrypt in place;
backs up plaintext to Keys/chipnet-keystore.plain.bak (also gitignored)
- scripts/chipnet-keystore-decrypt.mjs — --check, --in-place, --stdout,
--out=<path> modes for verification and one-time exports (bulk import
into a wallet UI, migration, key rotation)
- scripts/lib/chipnet-keystore.mjs — adds loadAllAsync(),
getEntryAsync(), ensureKeystoreUnlocked(); sync API still works on
plain keystores and throws a clear "call *Async instead" on encrypted
ones until unlocked once at boot
Test coverage: round-trip encrypt+verify+load-async validated end-to-end;
plain-keystore path still loads all 12 wallets unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
require() failed in Node ESM (worked in the browser only because Vite
polyfills CJS). Caught by a new end-to-end chipnet test that drives the
same modules the Publish button imports.
Test passes on chipnet: registration broadcasts, entry lands, discovery
picks it up. Katalogos v1 inscriptions: 2 → 3.
Correcting my own script and my earlier claim that the auth-proxy dropped
OAuth `state` on the callback. Reading /root/hephaestus/auth-proxy/src on
p2pexchange:
app.post("/challenge", async (req) => {
const { cashaddr, state, redirect_uri } = req.body ?? {};
const rec = createChallenge(cashaddr, state, redirect_uri);
...
});
app.post("/verify", ...) {
...
if (rec.clientState) url.searchParams.set("state", rec.clientState);
...
}
The endpoint takes `state` + `redirect_uri` in the body, stores them, and
echoes `state` back on the callback URL per RFC 6749 §4.1.2. This script
was posting only { cashaddr }, so clientState was empty and the callback
came back without state — Forgejo then 500'd with "state token mismatch".
The correct behaviour is exactly what the browser wallet.js does:
body: JSON.stringify({ cashaddr, state, redirect_uri: redirectUri }),
Fix: read `state` + `redirect_uri` off the /auth/authorize URL emitted by
Forgejo's kickoff, and pass them through. Removes the client-side "re-add
state" workaround entirely.
Also drops two debug log lines that leaked into the previous commit.
No bug in the auth-proxy. No issue to file.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
scripts/hephaestus-setup.mjs signs into code.silentmode.st using a BIP-137
message signed by a chipnet WIF (via mainnet-js), then:
- creates the deviant repo,
- uploads a chosen SSH public key,
- prints ready-to-paste git remote commands.
Two things worth noting about the auth-proxy flow that this script had to
work around:
1. Forgejo's OAuth kickoff stores the state + PKCE verifier in the SAME
session cookie it sets on the first hit — so the flow must START at
/user/oauth2/hephaestus-wallet, not directly at /auth/challenge. The
auth-proxy's Set-Cookie responses on the same origin would otherwise
clobber Forgejo's session; the script pins i_like_gitea + _csrf.
2. The auth-proxy's /verify currently returns a callback URL WITHOUT the
state param — the OAuth spec requires the provider to echo it back
(RFC 6749 §4.1.2), and Forgejo 500s on absence. The script re-adds
`state` from the original kickoff. A proper fix would land on the
auth-proxy; open until then.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two-tier registration per ADR-0055: your own list stays free (ADR-0008),
inscribing into the shared Katalogos writes on-chain state and costs
$1-worth of DEA, oracle-priced (ADR-0052 pattern), covenant-enforced.
Entries are immutable per-entry NFTs whose commitment IS the target
category id; a shared minting NFT is the state UTXO.
The registry is version-aware from day one (KATALOGOS_VERSIONS in the
shell config). Re-pricing or switching to the paymaster fallback = one
config line; discovery reads across every known version; old entries
stay valid forever.
Shipped:
contracts/KatalogosRegistryV1.cash — covenant (7/7 regtest)
src/wallet/katalogos.ts — planner + discovery (11/11 unit)
shells/app/src/core/katalogos.ts — shell publisher + version list
shells/app/src/core/katalogos-oracle-dev.ts — chipnet dev oracle for signing
shells/app/src/components/TokenImportModal.tsx — Publish button on success screen
shells/app/src/asm-core/AsmCore.tsx — picker reads shared list
scripts/chipnet-katalogos-register.mjs — deploy + register end-to-end
Live on chipnet: registry at
bchtest:rwm0e4rnq0htx97g3sllrgld652w7ztxlnqlx7820kd3mhklk6v8vmtth22xs,
CHE + ATD inscribed. Full deploy record in ChipnetTests/.chipnet-katalogos.json.
Amends ADR-0048: featured discovery is paid; unlisted assets stay swappable
at the protocol layer (paste-a-category). "Never the right to swap" invariant
preserved — the ASM covenant consults no registry.
Not yet built: paymaster fallback (v2, designed only), Katalogos second-tab
paste-address lookup, mainnet oracle wiring (dev key baked for chipnet only).
Full record: wiki/decisions.md ADR-0055 amendment, snapshot 0248.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- twitter-header-1500x500.svg: copy shifted from x=330 to x=360 and tagline
lifted (y=340/378 vs 360/400) so X/Twitter desktop's ~200px avatar overlay
in the lower-left corner no longer covers "A response to a default trajectory"
or the second-line body copy. Added a comment in the SVG documenting the
avatar-safe zone.
- twitter-header-deviation-1500x500.svg: new banner variant using the site's
hero deviation chart (path geometry copied verbatim from deviant_website.html
line 679-684, scaled 1.85× and placed on the right half). Same avatar-safe
copy on the left. Replaces the orbital ring with the "diverging paths"
motif that carries the project's thesis.
- brand/index.html + scripts/render-brand-pngs.mjs: gallery card for the new
variant, PNG regen list updated. Both banners PNG-rendered via Puppeteer
and pushed to SilentMode + Sia.
Three refinements:
1. **PNG pipeline switched to headless Chrome via Puppeteer.** resvg-js ignored
remote CSS @import, so exports rendered in Georgia fallback. New script
scripts/render-brand-pngs.mjs launches Chromium, sets viewport to target
pixels, waits on document.fonts.ready, screenshots. Fonts now match the
site exactly (Fraunces + DM Sans + JetBrains Mono). All 14 exports
regenerated — sizes roughly double (glyph anti-aliasing takes more pixels)
but they're accurate now.
2. **Gallery PNG links now save-to-disk, not open-in-new-tab.** Every
<a href="source/exports/*.png"> in brand/index.html got a `download`
attribute — browsers now trigger Save As dialog instead of navigating.
3. **/brand no-trailing-slash now redirects.** The Sia s3d gateway returned
the raw NoSuchKey XML error when a client asked for bare deviant.x/brand.
Uploaded a bare-key HTML redirect at bns/deviant.x/brand (no slash) that
meta-refreshes + JS-redirects to ./brand/. Both routes now behave sanely.
brand/source/README.md updated to name Puppeteer as the preferred pipeline
and demote rsvg-convert / Inkscape / ImageMagick to fallback (no-text assets).
Fonts: every text-carrying brand SVG (wordmark, OG, twitter-card, twitter-header,
FB cover, LinkedIn banner, IG square) now @imports Fraunces + DM Sans + JetBrains
Mono from Google Fonts inside a <style> block — so browsers render the website's
own faces. Fallback stack (Georgia / system sans) still applies in renderers that
ignore remote CSS (rsvg-convert). README documents the trade-off.
New social sizes:
- twitter-card-1200x675.svg — Twitter's current summary_large_image spec (was 1200×600, wrong)
- twitter-header-1500x500.svg — profile banner with lower-left profile-photo safe zone
- facebook-cover-820x312.svg — Facebook page cover
- linkedin-banner-1584x396.svg — LinkedIn company banner with profile-photo safe zone
- instagram-square-1080x1080.svg — IG feed / Threads / Discord
(retired the wrong 1200×600 Twitter card)
Deploy script fixes (scripts/deploy-deviant-navigate-st.sh):
- Portable DNS check — getent → dig/nslookup fallback so it runs on Git Bash / macOS
- Phase 1 awk: match the testchipnet :80 block SPECIFICALLY (was matching :80 and :443,
causing two duplicate deviant.navigate.st :80 blocks and no :443)
- Phase 3 skip check: verify a :443 block actually exists (was counting occurrences of
the server_name line, which the Phase 1 bug already inflated)
Live prod was patched manually before this commit; the script is now safe for greenfield.
Two pieces:
1. Pattern A · fee-chain roll-in (default) — the OpenMint Potidaea
swap-relay now grows ONE accumulator UTXO on the platform wallet
instead of one 600-bit UTXO per swap. Each claim spends potidaea's
prior fee-tip as an extra input and emits a bigger consolidated
fee-tip; same per-swap net revenue as raw emission, zero
fragmentation. Cauldron-shaped (state-embedded fees) but with an
external recipient since Potidaea's reservoir is peg-locked.
Enforces MIN_GROSS=200,000 bits (~$0.01) so per-swap platform-fee
outputs are always meaningfully above the ~148-bit per-input miner
cost. FEE_CHAIN=0 opts out for A/B comparison. New standalone demo
at chipnet-potidaea-fee-chain-relay.mjs (75 swaps → 1 UTXO of
83,250 bits, verified live on chipnet).
2. Signed / encrypted profile explorer — the address-list explorer
now accepts ?profile=<url> in two shapes:
- v1 signed-public (deviant-explorer-profile/1): anyone with the
URL sees the addresses; ECDSA/DER signature verified in-browser
via @noble/curves prevents tampered mirrors from swapping the
list. Same on-chain BCH address the wallet owns is the signer.
- v2 encrypted (deviant-explorer-profile/2): AES-256-GCM ciphertext;
key = sha256(BCH signMessage(fixed challenge)). Login modal
prompts for owner WIF (in-browser signMessage via noble-curves
RFC-6979 ECDSA); paste-a-signature-from-wallet path is
scaffolded and deferred to a future WalletConnect requester or
Deviant-wallet postMessage bridge. AES-GCM auth-tag catches
wrong-key and tampering in one check.
New:
- scripts/potidaea-explorer-mkprofile.mjs — CLI signer with --encrypt
for v2. Self-verify supports --verify + --wif for v2 decrypt.
- Parameters/dApp/Explorer/PROFILE.md — v1 + v2 spec, KDF, login
paths, wallet-vendor guidance.
- design/potidaea/DEPLOY.md §5 — rsync + Caddyfile snippet to
co-host the explorer + /profiles/ on potidaea.deviant.bch with
CORS-open profile serving.
Chose ECDSA/DER over Schnorr because BCH-Schnorr (libauth) is NOT
BIP-340 Schnorr (noble) — the two libs would produce mutually invalid
signatures. ECDSA/DER (v1) and BCH signMessage (v2 KDF) are both
one well-standardized format that libauth and noble-curves handle
identically. Round-trip live-tested in the browser for both v1 (verified
✓ badge) and v2 (unlock → 6 addresses render with live chipnet balances).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore: checkpoint in-flight work across domains
Sweep of accumulated uncommitted work on design/dex-p2p-architecture into
one checkpoint (multiple sessions share this tree). Main areas:
- anthemus: covenant-wiring follow-ups (engine, ExchangePanel), AgoraScreen seam
- spv: ServerPool + p2p header transport (pool.ts, core/spv/*), wallet.ts wiring
- walletconnect: responder/useConnect updates + connect-responder-compat test
- wallet: send-tier-a rework + swap.ts, plus send-tier-a unit tests
- asm: chipnet throughput/stress harness (scripts/chipnet-asm-*) + ASM_Native docs
- app UI: screens/* and shell.css touch-ups; sat -> bits copy migration
- docs: new snapshots (INDEX regenerated), Parameters (WalletConnect/SSH/ASM),
KnowledgeBase, WALLET-COORDINATION changelog
Checkpoint only; not a release. Secrets (Keys/, .chipnet-keys*, .env) remain
gitignored and were verified absent from the staged set.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
Prove the Potidaea platform-fee revenue model live on chipnet with two
end-to-end swap-relay tests, then sweep the cumulative chipnet-swap tally
from "17,000+" to the exact figure 52,998 across every referencing doc.
Snapshot 0239 (four revisions) captures the full arc — GMX Potidaea
(9,296 swaps · 5.47 M platform bits) → OpenMint Potidaea (26,599 swaps ·
15.02 M platform bits · 30.03 M founder bits · 44 swaps/sec peak · 2:1
founder/platform ratio matching ADR-0053 spec exactly). Client-added
platform-fee output at tx.outputs[3] — no covenant change required.
Ships the multi-source connectivity layer used throughout:
scripts/lib/chipnet-multisource.mjs (race local Fulcrum + BCHN RPC +
public electrums, chipnet-only endpoint list, wrong-network tip-guard,
belt-and-braces broadcast) and scripts/lib/chipnet-keystore.mjs (single
Keys/chipnet-keystore.json for all wallets, legacy per-file fallback).
Keys/ added to .gitignore.
Also: GMX/Game.X zero-bet-fee policy — chipnet-gmx-game-loop-test.mjs
now returns 0n from escrowFee() by default (feeAmount=0 on the escrow
covenant; already covered by the "if feeAmount > 0" gate at
GamePoolEscrowFlat.cash:54, no covenant change needed). Old formula
kept behind ARBITER_FEE_LEGACY=1 env flag.
Canonical tally table lives in
Parameters/ASM_Native/ASM-single-asset-throughput.md §4a; update it
first when the next throughput runs land, then grep-replace the exact
number across dependent files.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Split the marketplace hub out from Potidaea so Agora has its own address:
- design/agora/app.html — the four-tab hub (Potidaea/Anthemus/Artemision/
Katalogos), copy of the working potidaea/app.html retitled to
"Agora · Deviant marketplace hub"; brand link points back at the
Potidaea landing.
- design/agora/index.html — new Agora-specific landing. Hero + 4-node hub
graph; 2x2 product cards with per-product status badges (live/design);
Stoa/Olympia/Pinax mini-strip; economy section with the shared fee
flywheel + full MBA/NBA schedule; honest 8-row status ledger
(4 live · 2 regtest · 1 spec · 1 design); trust stats + CTA.
Pin the NBA fee schedule (snapshot 0234 open item resolved):
- scripts/potidaea-economics-sim.mjs — add creationFeeNBA, pinaxFeeMBA/NBA,
listingFeeNBA; NBA_UPLIFT_REGISTRY=100, NBA_UPLIFT_CREATION=1.2.
Pinax registration = 1 DEA (MBA) / 100 DEA (NBA · 100x). Creation NBA
= ceil(1.2 x MBA). Olympia NBA = 100x MBA Olympia. Stoa stays FREE
after Pinax for both families.
- wiki/decisions.md — ADR-0048 adds the "NBA fee schedule" paragraph
with rationale + updates the "PINNED" bullet to list both families'
constants.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Step 3+4 of the Anthemus critical path:
- contracts/SharePairAmmV2.cash — fractional LP tokens via the reservoir model
(outstanding = supply − treasury; add releases pro-rata, remove absorbs and
pays the fee-grown slice) + Uniswap-V2-style fee-on-input invariant (k grows
0.30% per swap, 100% to LPs) + treasury-drain guard (swap barred from the
treasury input; locking-bytecode pinning; no dust drain). 4/4 mock tests at
exact fee/mint/withdraw boundaries.
- scripts/chipnet-anthemus-amm-v2.mjs — 5/5 PASS live chipnet: bootstrap →
fee swap (1,000→906) → LP release (exactly 1,000) → LP burn returning
1,100 X + 909 Y (fees included). Pool end-state exact.
- services/anthemus-relay — curveLevels()/poolBook() + GET /pool: the AMM
rendered as a maker on the SAME book (pool-as-maker path).
- wiki/decisions.md — ADR-0049 written against the validated bytecode: brand
locked, book-first ratified, ADR-0016 refined (hub-exclusive pools only),
maker 0 / taker 0.15% consensus-enforced, curve fee 100%-to-LP with the
protocol slice deferred, tollBps accepted-pending, USDu preferred-not-forced.
- critical-path doc → rev. 2 (Steps 1–4 status).
Mixed shared files staged as HEAD+own-edits blobs; the concurrent session's
uncommitted work remains untouched in the working tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four moves (git mv, history preserved on all 39 files):
- ASM_Potidaea_OrbitalSwap -> ASM_Native/Potidaea_OrbitalSwap (renamed, dropped the ASM_ prefix; now nested under ASM_Native)
- MBA_Shares -> ASM_Native/MBA_Shares
- FIATm -> ASM_Native/FIATm
- Pari_P2P -> Betting/Pari_P2P
ASM_Native now groups the native-BCH swap (Potidaea) with the native asset families it swaps (FIATμ/BCHm, MBA/shares). Rewrote ASM_Native/README.md from a pointer stub into a real folder index describing the contained subfolders + how they fit (mechanism vs assets).
Rewrote every reference: Potidaea internal links (depth +1; self-folder refs collapsed), external links from DEX_Anthemus/wiki/ASM_CrossChain, prose/path refs across Parameters + wiki + CLAUDE.md + docs + code-comment doc-pointers (scripts/src/contracts), memory, and .gitignore (transcripts ignore rule). Co-moved siblings (FIATm<->MBA_Shares) kept their ../sibling links untouched since they stayed siblings under ASM_Native.
Verified with a link-resolver: all 453 relative links across 165 files resolve.
Snapshots keep old paths (historical changelog). Parallel FX-desk/uniform-reserve track left untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A test-only stand-in for MetaMask so the DEPOSIT direction is drivable through our own
UI in a browser with no extension. mock-wallet-server.mjs signs+broadcasts with the
funded Sepolia test key; mock-wallet-inject.js installs a fake EIP-1193 window.ethereum
that forwards eth_sendTransaction to it. Never shipped.
Used it to validate deposit end-to-end through the app UI (Wallet B): the app signed
approve + depositToBitcoinCash on real Sepolia via the mock → correspondent minted 5
USDC on BCH → result shown in-wallet ("+5 USDC on Bitcoin Cash", BCH tx). Service
reconciles backed=true, invariantOk=true (5 locked == 5 minted).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the source wallet's popup legible: rename the vault to DeviantBitcoinCashGateway
with intent functions — depositToBitcoinCash(uint256,bytes32) (was lock), release,
bitcoinCashBacked(), event DepositedToBitcoinCash — so wallets decode the call as
"Deposit To Bitcoin Cash" (4byte registry, and fully once verified). Same audited
logic (balance-delta safe, operator release, public backing). Rewired evm/tron.mjs,
connect/{evm,tron}.ts, both deposit e2e scripts. One self-contained file (paris +
istanbul). Re-validated full round-trip on real Sepolia + Nile + chipnet: deposit 7/7
+ 6/6, redeem 2/2. New gateways 0x9D5C7402… (Sepolia) · TUsXiRemR… (Nile).
scripts/verify-gateway.mjs: Etherscan-v2 standard-JSON verify (needs ETHERSCAN_API_KEY)
+ prints Tronscan settings; the .sol has no imports so it is the flattened source.
Plan doc: Parameters/CrossChainTransactions/multichain-wallet-send-ui_PLAN.md — the
From→To "Send" UI for Deviant's OWN multi-chain wallet (one seed → BCH/EVM/Tron, in-app
signer, no foreign popup), kept distinct from the shipping external-wallet Deposit/
Withdraw model. Both coexist. Snapshot 0201; tsc clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the round-trip. Burn the IOU on BCH via UnifiedUsdtReserve.withdraw(chain)
(drops reserve[chain], require reserve>=amount — draining to 0 is by design) → the
operator releases the real USDC/USDT from the source vault back to the user's own
address. Single-chain; the reserve-depleted case (external Rabby-style bridge) stays
a later session.
Service: evm/tron.release() (operator-gated vault.release); server /redeem/quote +
/redeem/confirm (verifies the covenant-enforced reserve drop, idempotent, then
releases); state.redeems; bch.controllerInfo. UI: connect/correspondent-burn.ts
builds+signs the withdraw() burn in-browser with the user's own BCH key
(self-custodial; bundles UnifiedUsdtReserve.json); CrossChainDeposit gains a
Deposit/Withdraw toggle (copy: mint=lock / release=burn, never bridge).
Validated 2/2 on real Sepolia + Nile (correspondent-redeem-e2e.mjs): USDC 110->115,
USDT 10978->10983, reserve->0, release txs on-chain. tsc clean; both UI directions
render-verified in the running app. Snapshot 0200.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second source chain on the same asset-generic rail. services/correspondent/tron.mjs
(sibling of evm.mjs): deploy the Nile TronUSDTVault, decode the Locked event from the
tx log (no event-index server), verify amount+recipient+confirmations. server.mjs
now dispatches by chainKind (adapterFor: evm|tron); evm gets a uniform lockedBalance;
config USDT addr fixed to the Nile test-USDT.
UI: connect/tron.ts (window.tronWeb / TronLink, approve+lock, no tronweb in bundle);
CrossChainDeposit dispatches Tron vs EVM and enables the USDT·Tron option.
Validated end-to-end 6/6 on Nile + chipnet (correspondent-e2e-tron.mjs): 5 real
test-USDT locked -> minted on BCH, reserve[Tron]=5e6, mint==lock. USDC(Eth) and
USDT(Tron) are distinct unified controllers on one generic covenant. tsc clean.
Phase-1 correspondent deposit rail P1-1..P1-6 complete. Snapshot 0199.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Correspondent system (BCH<->chain lock-and-mint; not a bridge) on top of the
Step-1 covenant. services/correspondent/ = source-chain watcher + oracle attestor
+ BCH mint operator + reconciliation, HTTP API /quote /confirm /reconcile; the BCH
credit spends UnifiedUsdtReserve.deposit(chain) (mints + bumps reserve[chain]).
Reuses root node_modules (no new package.json). Asset-generic (category + chain
index; USDC/USDT are examples).
UI: connect/evm.ts (EIP-1193 MetaMask, hand-encoded approve+lock, self-custodial),
correspondent.ts client, CrossChainDeposit.tsx wired as a new Deposit FAB action
(asset.chain from live /ledgers, My-wallet/Someone-else picker; copy = Deposit/
credited/mint=lock, never bridge/IOU; plain ticker).
Validated end-to-end 7/7 on real Sepolia + chipnet (correspondent-e2e-sepolia.mjs):
5 real USDC locked -> minted on BCH, reserve[Ethereum]=5e6, mint==lock. tsc clean;
Deposit modal render verified in the running app. P1-1..P1-5 done; P1-6 (Tron)
remaining. Snapshot 0198.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One fungible token on BCH backed by a per-chain reserve ledger the covenant
enforces on-chain. Mutable (non-minting) NFT commitment = 4x int64 LE counters
[Tron,Ethereum,BSC,Solana]; deposit(chain) mints + bumps reserve[chain] via an
oracle checkDataSig attestation; withdraw(chain) burns + requires reserve>=amount.
Non-minting => consensus FT-conservation makes withdraw a real burn; invariant
Sum(counters) == circulating FT holds by construction.
Validated end-to-end on chipnet (Y scenario + over-reserve reject, 10/10): the
reject fires at the covenant's own require(reserve>=amount), not a builder guard.
Asset-generic (category = identity, chain index explicit). Snapshot 0197.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README + Phase 1 scope: reframe "Phase 3 pool-free router" → de-scoped. Our
correspondent (lock-and-mint) is BCH↔chain only (per-chain-symmetric, no drain,
no rebalancing); chain↔chain uses an external DEX/bridge (Rabby method). Unified
USDT on BCH (fungible + per-chain reserve ledger) noted as an optional design.
- scripts/sim-unified-usdt-reserve.mjs: accounting simulation of unified USDT with
a per-chain reserve ledger. Proves Y's 400 mixed-origin USDT releases fully on
Tron by drawing the Tron reserve (origins not tracked per user; fungible pool
self-balances); invariant Σ reserves == minted == Σ balances; over-reserve
withdrawal BLOCKED not drained. 4/4 checks.
- Mockup asset list: drop DAI/WBTC (BTC handled natively via Model 3 + T6), add
ETH/BNB/SOL/TRX (stablecoins + native base assets).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Snapshot of the shared working tree's outstanding changes (multiple
concurrent workstreams), excluding generated runtime state files
(.football-tote-state.json, .tote-platform-state.json, .usdm-historical-chart.html).
Spans: wallet shell (connect/, keystore MultiKeyStore+vault, screens,
currency picker, sign-message), verify artifacts, new Potidaea/DRA/tote
contracts + regtest suites, cross-chain/T6 + WalletConnect Parameters,
a telegram-orderbook subproject, design mockups, and docs. See
shells/app/WALLET-COORDINATION.md for the per-file ownership split.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a PostToolUse hook (.claude/settings.json) that runs a guard script
after Write/Edit/MultiEdit; the guard regenerates snapshots/INDEX.md only
when the edited file is an actual snapshots/*.md (not INDEX.md), and never
fails the tool call. Keeps the domain index from going stale.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the docs-by-domain reorg. Every snapshot now carries YAML
frontmatter (domain + summary + tags); the auto-guess was hand-corrected
against the full index (T6/interchain -> cross-chain, CDP/MBA/stablecoin
-> fiatm, CitizenToken/Ballot -> governance, etc.). No file renamed/moved.
build-snapshot-index.mjs now reads the frontmatter and emits a greppable
INDEX.md: a top Quick-find table (No | domain | tags | summary) plus
per-domain sections with summary + tags. 224/224 tagged, 8 domains, zero
uncategorised.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The self-custodial agent now drives BOTH chains of a T6 channel, closing the gap
Phase 4b left open (board-only). Open funds a real BTC 2-of-2 on signet coordinated
with board state 0 (1:1 capacity); Close settles the 2-of-2 to the board's latest
split via a cooperative-close PSBT co-signed over the keyless relay — the co-signer
verifies the payout against the board before signing (verifyClose: outputs == board
split AND input spends our 2-of-2; no blind-signing). BTC pubkeys ride the hello
handshake; each agent still holds only its own keys, relay holds none.
Validated on real coins (two agent processes):
BTC fund c0832299 (10000 sat 2-of-2) [signet]
board #0 04ccc060 (6000/4000) [chipnet]
A→B 2000 fd3367bd (4000/6000)
B→A 1000 d968a524 (5000/5000)
coop close a380d0e4 → Alice 4750 / Bob 4750 [signet, 2 outputs spend the 2-of-2]
Adds t6-btc.inspectClose + agent Close flow + UI Close button/BTC panel. Finding:
bitcoinjs v7 returns witnessScript as Uint8Array — Buffer.from() before .toString('hex').
Spec §3.5/§4/§5/§6/§7 + snapshot 0189.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The EVM analog of B3/B4/B5 on Ethereum Sepolia. Locks real Circle test-USDC in
an EVM vault (reuses the generic TronUSDTVault.sol) and mints εUSDC on BCH via
DeviantWrapMintIOU. All 7 checks PASS: lock 5 USDC -> mint 5,000,000 εUSDC (1:1)
-> reconcile (==) -> replay rejected -> redeem releases the USDC -> reconcile
(0:0) -> round-trip. Ethereum now has BOTH flavors (swap + lock-mint), like Tron.
Per-source markers stay real: εUSDC (Ethereum) != τUSDt (Tron).
scripts/eth-bch-lock-mint.mjs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deployed two-party shape of the Phase-4a T6Wallet protocol: a keyless WS relay
(t6-relay.mjs) + per-user agents (t6-wallet-agent.mjs, one key each) + a thin
per-role UI (design/t6-wallet/agent.html). No component holds both keys; the
relay holds none. Supersedes the server-custodial t6-wallet-server.mjs.
Validated on chipnet (two separate agent processes, real coins):
open cad8647c (50000/50000) · channel 68e4b36e
A→B 8000 b8cbe855 (42000/58000)
B→A 3000 2a3571c7 (45000/55000)
Both agents converge on the same on-chain state; pubkeys via hello handshake,
channelId adopted from the relay open announce. Spec §3.5/§4/§5/§7 + snapshot 0188.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
T6Wallet (scripts/lib/t6-wallet.mjs) — one party's self-custodial wallet holding only its
own key. Two wallets pay each other over a transport: payer signs the next state, peer signs
it remotely, board is updated (payUni). No shared keys, no tx co-signing; the transport carries
only a proposal + a 64-byte state signature. Validated on chipnet (t6-wallet-p2p-demo.mjs):
open + 3 payments both directions, monotonic 1->3, final Alice 40000 / Bob 60000.
The MVP functional core is now complete (engine + self-custodial board + BTC 2-of-2 PSBT +
this protocol). Remaining: Phase 4b front-end UI (relay + client-side keys). Snapshot 0187.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The T6 MVP core: both halves of a channel co-signed self-custodially, each party holding
only its own key. Validated on chipnet + BTC signet.
- Phase 1 — engine library (scripts/lib/t6-engine.mjs): T6Board with open/pay/settleHtlc/
readState behind a clean API + injected per-party signers. Demo t6-engine-demo.mjs
(open + 2 pays + read on chipnet).
- Phase 2 — self-custodial BCH board (contracts/BchChannelBoardUni.cash +
t6-selfcustody-uni-demo.mjs): a state update is authorized by BOTH parties' checkDataSig
over the state MESSAGE (channelId||state||balances), signed independently with each own
key — no transaction co-signing. Sidesteps cashscript's inability to co-sign a tx across
two key-holders (the both-signed board would need the WalletConnect signing path). channelId
is a bytes32 constructor arg both sides control (no VM tokenCategory byte-order ambiguity;
makes the address unique per channel = anti-replay). Validated update 932ad30d.
- Phase 3 — self-custodial BTC 2-of-2 (scripts/lib/t6-btc.mjs +
t6-btc-selfcustody-close.mjs): real signet 2-of-2 funded + cooperatively closed to the
board's agreed split, co-signed via bitcoinjs PSBT (Alice signs → base64 → Bob signs →
finalize). Neither party holds the other's key. PSBT multi-party signing is native/clean,
unlike the BCH board. Fund 98fa1d2d, close 413e52f8 (Alice 5700 / Bob 3800).
The transport carries only a PSBT (base64) or a 64-byte state signature — small, dumb, non-LN.
Remaining: Phase 4 (self-custodial wallet UI). Snapshots 0185, 0186.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Using T6/BTC on the BCH DEX, validated on chipnet + signet, plus the design dialogue.
- Path A (swap-out): spend T6 channel-BTC to buy a BCH coin atomically; one preimage links
a board settleHtlc and a CashToken AtomicSwapHTLC (chipnet-t6-buy-token.mjs).
- Path B / B1 (M-of-N mint): DeviantWrapMintIOUMofN.cash + chipnet-mofn-mint-test.mjs — a
2-of-3 oracle quorum must attest before minting; 1-of-3 rejected, replay-bound, no over-issuance.
Oracles are unfunded signing keys (free attestations).
- Path B / B2 (redeem): BurnSink.cash + chipnet-signet-iou-redeem.mjs — burn IOU on BCH,
bonded 2-of-3 releases BTC on signet from a POOL of combined deposits to a fresh unlinked
payout (redeemId bound via OP_RETURN). The fungible-pool anonymity set.
- Anthemus spot market: chipnet-anthemus-btc-t6-usdc.mjs — an order book matches a USDc buyer
with a BTC-over-T6 seller; one preimage settles both legs; BTC stays in T6 (no wrap, no
custody, no signers). Partial fills natural in the interactive/advertised model.
- Docs: ADR-0046 (hub topology + always-on requirement; product shape; BOLT-native interop;
board as unilateral persistence / watchtower / recovery beating DLP; the BOLT-client fork),
complete-record §8/§9, Observability-and-privacy §7 (pool anonymity set), tracker rows,
snapshots 0182/0183/0184.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Option 3 — the hub trading floor. Two real cross-chain IOUs minted by
DeviantWrapMintIOU (τUSDt from Tron, εUSDC from Ethereum) are pooled on the
validated SharePairAMM (ADR-0030) and swapped on BCH. All 4 checks PASS: both
IOUs pooled, trader received τUSDt, constant product held, price discovered
1.00→1.44 εUSDC/τUSDt. Closes the settlement-hub loop: deposit (lock-and-mint)
-> TRADE (this) -> withdraw (redeem).
Names the BCH DEX **Anthemus** (Ἀνθεμοῦς, ancient Macedonian town; root ἄνθεμον
"blossom" → a blossoming marketplace). Brand pair with Potidaea (the ASM):
Potidaea = backed MBAs, fixed rate, no pool, infinite depth; Anthemus = DEX,
Token-class + cross-chain, market price discovery (ADR-0016 routing). Use plain
"Anthemus" (not "Anthemus Sea", an unrelated hotel mark); namespace clear.
scripts/chipnet-hub-dex.mjs
Parameters/DEX_Anthemus/anthemus_dex.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Option 1: the EVM analog of B2 on Ethereum Sepolia. One sha256 hashlock over a
Sepolia EVM HTLC (reuses the generic TronHTLC.sol) escrowing real Circle test
USDC + a chipnet AtomicSwapHTLC. All 3 checks PASS: Alice gets the BCH, the
preimage matches across chains, Bob claims 5 real Sepolia USDC with S. Proves
the per-source marker with two real assets: eUSDC (Ethereum) != tUSDt (Tron).
Three source chains now validated into the hub (BTC/Tron/Ethereum), both
flavors + M-of-N trust floor. Landscape doc gets a validated-rails matrix.
scripts/eth-gen-address.mjs, eth-balance.mjs, eth-bch-htlc-swap.mjs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Option 2: the native-coin sibling of the tUSDt rail. TronTRXVault locks NATIVE
TRX via a payable lock() (no transferFrom); DeviantWrapMintIOU mints a floating
1:1 IOU tTRX on BCH (= 1 TRX, pegged to the native unit, NOT a dollar). All 6
checks PASS on real coins: lock 10 TRX -> mint 10,000,000 tTRX -> reconcile
(==) -> replay rejected -> redeem releases the TRX -> reconcile (0:0). Proves
the hub holds volatile L1 coins, not just stablecoins. Trust = oracle/operator
(B6 M-of-N applies identically).
contracts/tron/TronTRXVault.sol
scripts/tron-bch-lock-mint-trx.mjs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
B6: TronUSDTVaultMofN replaces the single operator with an M-of-N signer set.
release() requires `threshold` distinct ecrecover sigs over (redeemId, to,
amount, vault), ordered ascending for dedup. Validated 2-of-3, all 4 PASS:
- 1 sig -> REVERT below threshold; non-signer -> REVERT not a signer
- 2 valid sigs -> SUCCESS (vault drained); replay -> REVERT redeem used
Tron enforces the THRESHOLD; the BCH half (slashable bond + equivocation
fraud proof) is already validated as M3-bond (BondedSignerSlash.cash). Together
= tBTC-v2-style trust floor; the v1 single-operator bridge becomes trust-minimized.
Build B COMPLETE: B1 HTLC, B2 trustless swap, B3/B4/B5 lock-and-mint+reconcile,
B6 trust floor — both flavors of the first interchain hub rail, on real coins.
contracts/tron/TronUSDTVaultMofN.sol
scripts/tron-vault-mofn.mjs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build B Flavor 2 (the credit/debit lock-and-mint), all 7 checks PASS on real
coins across both chains:
- B3 lock+mint: Alice locks 5 real USDT in TronUSDTVault; bridge oracle attests;
reused DeviantWrapMintIOU mints exactly 5,000,000 tUSDt (covenant-enforced 1:1)
- B5a over-mint: replaying the attestation is rejected (outpoint-bound)
- B4 redeem: return tUSDt on BCH -> operator releases the USDT on Tron
- B5b reconcile: sum(tUSDt circulating) == USDT locked at both states (5:5, 0:0)
Upgrades chipnet-btc-iou-demo (simulated lock) to a REAL Tron lock with
on-chain cross-chain reconciliation. Vault uses balance-delta moves (Tether
gotcha). v1 witness = single oracle/operator (trust-minimized); B6 = bonded M-of-N.
contracts/tron/TronUSDTVault.sol
scripts/tron-bch-lock-mint.mjs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
B2 (Build B milestone 2): the real cross-chain atomic swap, Tron analog of
T4. One sha256 hashlock governs a Nile TronHTLC leg (real test-USDT) and a
chipnet AtomicSwapHTLC leg. Validated end-to-end, all 3 checks PASS:
- Alice locks 5 USDT on Nile, Bob funds the BCH HTLC on chipnet
- Alice claims the BCH leg, revealing S on-chain
- Bob reads S off chipnet and claims the real Nile USDT
No oracle, no wrapped token, no custodian. First interchain rail into the hub.
BCH claim 2fc1dfb0… · Nile USDT claim f5128666…
Token gotcha (fixed): the Nile TetherToken performs transfer but RETURNS
false (declares returns(bool) without setting it), so require(transfer(...))
and even SafeERC20's bool decode wrongly revert and roll back a good move.
TronHTLC._safeTransfer/_From now ignore the return value and verify by
BALANCE DELTA. Applies to USDT on every chain — B3 vault must do the same.
scripts/tron-bch-htlc-swap.mjs (B2 harness)
scripts/tron-trc20-holdings.mjs, tron-usdt-probe.mjs, tron-sim-transfer.mjs,
tron-b2-claim-retry.mjs (discovery + diagnostics)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
B1 (Build B milestone 1): TRC20 hashed-timelock contract on Tron, the
Tron leg of the BCH<->Tron atomic swap. Validated on Nile testnet, all 6
checks PASS:
- claim with correct preimage succeeds, credits receiver
- wrong preimage reverts at the sha256 guard ("bad preimage")
- refund reverts before timelock ("not expired"), succeeds after
Uses SHA-256 (matches AtomicSwapHTLC.cash) so one preimage spans both legs
in B2. Compiled solc 0.8.35 evmVersion=istanbul for TVM; swap id computed
client-side via keccak256(abi.encodePacked(...)) to mirror the contract.
contracts/tron/TronHTLC.sol, TestTRC20.sol
scripts/tron-htlc-b1.mjs (harness), tron-balance.mjs (Nile balance util)
Also folds DigiByte (DGB) into the chain landscape: List A native-Script
HTLC (reuses BTC P2WSH HTLC code, ~15s blocks) + dgbDGB row in List B2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build B scope: both flavors (trustless swap + lock-and-mint tUSDt), a
6-milestone test program (B1 Tron HTLC -> B6 bonded trust floor), deps,
locked decisions (sha256 both legs, oracle witness v1, USDm swap counterpart),
and honest trust-minimized caveat for the bridge. Adds reusable
tron-gen-address.mjs (Nile keypair, gitignored).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>