Brings the whole project into git (previously only the on-ramp feature and a few parallel files were tracked). Includes: wallet core (src/), CashScript contracts/, the Capacitor shell (shells/), scripts/, tests, and the docs corpus (wiki/, snapshots/, Parameters/, Archive/, KnowledgeBase text). Excluded via .gitignore: node_modules/, dist/, .env (secrets), testnet WIF key files (.chipnet-*.json, devWallets.ts), KnowledgeBase/RAW media (~9.6 GB), transcripts/ (session logs), and machine-local config (.claude/*.local, *.lock). Secret-scanned before commit — no credentials in the tree. Note: src/swapMath.ts, src/swapBuilder.ts, contracts/OpenMintToken.cash are legacy (OP_RETURN era) but still imported; committed as-is, flagged for a separate removal once de-referenced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
15 KiB
Architecture
This page describes the current implementation (snapshot 0003). The authoritative future direction is
Deviant_Technical_Specification_1.mdplus ADR-0006, which rebuilds Architecture A on native CashTokens. The content below is preserved for context — it documents the OP_RETURN-tagged P2SH approach used to validate the swap math in Stage 1. The CashTokens rebuild is the next architectural step.
References:
../Deviant_Technical_Specification_1.md(authoritative)BCH_Token_Economy_Concept_Paper.md§3 (original framing)decisions.mdADR-0006 (the CashTokens shift)
Two-tier model
| Tier | UTXO type | Enforcement | tokenId location |
|---|---|---|---|
| B (UI) | Plain P2PKH | Wallet convention only | Off-chain metadata (BIP21-style URI or wallet DB) |
| A (Contract) | P2SH32 with CashScript | BCH consensus | OP_RETURN output on the funding tx |
Both tiers share the same tokenId byte string. The escalation path B → A wraps an existing UTXO inside a contract transaction without changing the tokenId or backing rate.
Why the tiers differ — trust model
Carriage-independent. This frame holds whether Architecture A carries tokenId in OP_RETURN (snapshot 0003) or as a native CashTokens category (ADR-0006). It explains why the two tiers have different trust properties, not how either is encoded.
The A/B split is usually described mechanically (P2SH32 + consensus vs. plain P2PKH + wallet convention). The deeper distinction is about what each tier trusts. Two independent axes:
- Custody — is there a third party who holds the asset and can withhold it?
- Representation integrity — can the token's claim/label be faked?
| Fakeable representation | Enforced representation | |
|---|---|---|
| Custodial | Gold standard | proof-of-reserve / federated pegs (partial) |
| Non-custodial | Architecture B | Architecture A |
Both Deviant tiers are non-custodial — the BCH is in the holder's own UTXO, never a vault. They differ on the second axis:
- Architecture A enforces representation. The peg is a fact established on-chain (category ID, contract bytecode, the value-equality check). The wallet does not decide what an A token is — it verifies facts the chain already settled. The A-tier wallet is a reporter: if it lies, you can check it against the chain and catch it.
- Architecture B cannot enforce representation. A B token is plain BCH plus a local convention; the wallet computes
balance = sats / satsPerUnitfrom a definition it holds. Something must decide what the BCH "means", and that something — the wallet UI or the definition registry — is an authority. The B-tier wallet's word is the only word; there is nothing to check it against.
The categorical line: Architecture A needs a verifier; Architecture B needs an authority. A difference of kind, not degree — and the reason the two tiers need structurally different defenses (snapshot 0018 D-1–D-3 for A, snapshot 0019 D-4–D-8 for B; trust-decision policy in ADR-0008). It is also why B model must never be used for tokens received from untrusted parties (spec §3.2): the authority it depends on is only as trustworthy as whoever supplied the definition.
See snapshots 0018 and 0019 for the full threat write-ups and the historical (gold-standard) framing.
The minting-option grid — where the no-counterparty market lives
The brainstorming behind snapshots 0017–0019 and ADRs 0008–0009 keeps returning to one finding: of all the ways to configure a token, exactly one combination yields a market with no counterparty at any step. This grid lays out the design space to show why.
Two primary axes — both assume Architecture A (see the caveat at the end):
- Mint access — Open vs Closed. This is about who may issue, not about scarcity (ADR-0009). Open = the minting NFT / reservoir sits in a permissionless
claim()covenant; anyone may mint by collateralising BCH. Closed = the issuer gates issuance. The genesis-flow question that picks this axis is "is the token a BCH-denominated unit, or a claim on off-chain value the contract cannot custody?" - Peg — Fixed rate vs No peg. Fixed rate = every FT UTXO is consensus-required to carry
N × backingRateSatsof BCH; the holder can always exit to that floor (self-backed UTXO, snapshot 0017). No peg = the FT carries only dust; there is no BCH floor — value is whatever a market or a use-case assigns.
| No peg — FT carries dust only, no BCH floor | Fixed rate — every FT UTXO carries N × rate BCH (consensus-enforced floor) |
|
|---|---|---|
Open mint — permissionless claim() covenant holds the reservoir |
Degenerate. Anyone pulls FTs for ~dust → an infinite faucet with no floor and no value. In Deviant terms this is the rate-0 case: bricked on the canonical contract by the dust limit, or a T-001 non-canonical contract otherwise (snapshot 0018). Counterparty-free, but worthless by construction. | ★ The novel quadrant. Mint by locking N × rate BCH into a permissionless covenant; exit by burning the UTXO back to its BCH floor; swap to any other open+fixed token via a single atomic protocol-swap tx. No counterparty to mint, to exit, or to swap. Every unit is fully BCH-collateralised, so the token is scarce by inheriting base-layer BCH scarcity (ADR-0009). This combination does not exist in any other token system. |
| Closed mint — issuer gates issuance | Issuer-gated bare token. Two faces, same structure: legitimate non-redeemable issuer tokens (tickets, membership, governance, credentials — ADR-0009 Variant 1), and the conventional CashTokens memecoin / project token. Either way there is no BCH floor, and you need a counterparty to exit — a buyer, or an AMM LP who can pull liquidity and strand you (the "soft rug" from the AMM-ecosystem research). | Issuer-backed redeemable token (ADR-0009 Variant 2: closed mint, open redeem). The issuer gates issuance; holders keep a guaranteed BCH exit at the peg. Correct home for a claim on off-chain value the contract cannot custody — fiat, equity, goods — and specifically for a bank-backed stablecoin (see the worked example that follows): closed mint + BCH floor + an off-chain dollar peg layered on top. |
Why only open + fixed is counterparty-free
| Cell | Counterparty to mint? | Counterparty to exit? | Counterparty to swap? |
|---|---|---|---|
| Closed + no peg | Yes — issuer | Yes — a buyer | Yes — a buyer / AMM LP |
| Closed + fixed | Yes — issuer | No — redeem to floor | Usually yes — closed tokens are typically not open-swap-eligible |
| Open + no peg | No | N/A — no value to exit to | N/A |
| Open + fixed | No — permissionless covenant | No — burn UTXO → BCH floor | No — atomic redeem+mint, no pool, no LP |
Every established token model has a counterparty somewhere: an issuer who gates supply, a buyer you need to exit, or an AMM LP who can remove liquidity and strand holders. Open mint + fixed rate removes all three at once — the mint is a permissionless covenant, the exit is consensus-mechanical, and the cross-token swap is a single atomic transaction with no pool and no LP. That is the novel claim the spec stakes (§1.4, §10) and ADR-0007 validated on chipnet.
Reservoir cap — a secondary modifier, not a third axis
CashTokens fixes FT supply at genesis (ADR-0007), so "limited vs unlimited supply" is really small reservoir vs large reservoir — how many units the contract can ever release:
- Small reservoir (capped) — the contract goes permanently inert once drained (snapshot 0017, E8 / CAP token). A capped open+fixed token is a limited-run no-counterparty asset.
- Large reservoir (~10¹⁸, effectively uncapped) — mint-on-demand for practical purposes.
This is orthogonal to the counterparty property. It does not decide scarcity in the "is each unit backed" sense — under fixed rate every released unit is fully collateralised regardless of reservoir size (ADR-0009). Reservoir cap tunes how many units can exist; the peg decides whether each one has a floor.
Caveat — the grid assumes Architecture A
"Fixed rate" only means something where the peg is enforced. In Architecture B there is no contract, no covenant, no consensus check — satsPerUnit is a label in a local definition, a display convention poisonable by anyone (T-002, snapshot 0019). The grid above describes Architecture A; in Architecture B the "Fixed rate" column collapses into "claimed fixed rate", carrying all the trust caveats of the B tier — the wallet is then an authority asserting the rate, not a verifier checking it.
Stablecoins in each tier — a worked example
A bank-backed stablecoin is the sharpest test of the A/B distinction, because it exposes a conflation people reach for instinctively. Two separate numbers are in play and must never be merged:
- BCH backing rate (
satsPerUnit) — the BCH physically locked inside each token UTXO. This is the consensus-guaranteed floor. At a "symbolic" rate like 100 sats/$1 it is bankruptcy-protection crumbs, not the value of the token (concept paper §4.1). - The dollar peg ($1) — the issuer's off-chain promise, backed by bank reserves. The protocol neither sets nor enforces it. This is the entire reason for
tokenType: "stablecoin"(spec Rule 3): it tells the wallet not to treat the backing rate as the exchange rate, and to display the BCH floor and the dollar value as two separate figures.
Architecture A — the only correct home for a real stablecoin
A bank-backed stablecoin is a closed-mint CashToken (concept paper §3.2 table):
- The minting NFT / reservoir is held by the issuer, not by a permissionless open-mint contract. There is no
claim()anyone can call — no path for an outsider to pull FTs out of a reservoir. - CashTokens consensus fixes FT supply at genesis (ADR-0007). No one can conjure FTs of the issuer's category; they can only receive them downstream of the issuer.
- Every circulating stablecoin FT therefore traces back to a real dollar the issuer received at issuance. The issuer funds the backing-rate sats into each UTXO when it issues.
- Token identity is the Category ID — that is what the issuer checks before honouring a $1 redemption.
The A-tier wallet verifies all of this against the chain. It is a reporter, not an authority.
Architecture B — structurally cannot carry a stablecoin
Spec §3.2 explicitly lists "Stablecoins or high-value tokens" under "Not appropriate for" Architecture B. The mechanism behind that ban:
In B model there is no token — no category, no FT, no contract, no minting NFT. A B "token" is plain BCH at some addresses plus a local definition {satsPerUnit, …}. Consequently:
- "Converting BCH into a B-model stablecoin" is a category error. Nothing is converted or issued; the wallet merely relabels the holder's own BCH through a definition they imported. No transaction with the issuer occurs. (This is exactly the T-002 definition-poisoning surface — snapshot 0019.)
- "1 unit" in B model literally is
satsPerUnitsats of plain BCH. There is no separation between token and backing. Send the issuer "1 USDX" and they receivesatsPerUnitsats of BCH — nothing they issued, no category to verify, no record. - An issuer who honoured B-model redemptions would be drained instantly — anyone relabels their own BCH "USDX" and cashes out. That is the structural reason the spec bans it.
The "self-issue at the backing rate, then claim a dollar" attack
| Tier | Self-issue at the backing rate → claim $1? | Why not |
|---|---|---|
| A | No | Closed mint — no permissionless mint path; FT supply consensus-fixed; the issuer gates $1 redemption by Category ID. |
| B | No | There is no issuance at all — only relabelled BCH. The issuer cannot verify it and would be drained if it tried to honour it. Hence spec §3.2. |
The premise confuses the BCH floor (the few sats the protocol returns) with the dollar peg (a closed-mint, Category-ID-gated, off-chain promise attached only to tokens the issuer actually sold for dollars). The backing rate is not a mint price that buys into the dollar promise.
This is the authority/verifier frame in miniature: Architecture A can host a stablecoin because the chain verifies which units are real; Architecture B cannot, because it would have to assert it — and a B-model assertion is free for anyone to forge.
tokenId schema
Fixed 32 bytes. Constructed once at genesis as:
tokenId = sha256(
LE64(backingRateSats) ||
LE8(openMint ? 1 : 0) ||
LE8(stablecoinFlag ? 1 : 0) ||
LE16(founderFeeBps) ||
LE160(founderAddressHash) ||
LE32(genesisHeight)
)
- 32 bytes is the standard hash size on BCH and fits comfortably inside OP_RETURN with room for protocol prefix bytes.
- All inputs are immutable post-genesis (paper §3.2: "set immutably at genesis").
genesisHeightprovides uniqueness across token creators using identical parameters.
OP_RETURN layout (Architecture A)
OP_RETURN
<4-byte protocol prefix: 'DVNT'>
<1-byte version: 0x01>
<32-byte tokenId>
<1-byte tier: 0x41 'A' or 0x42 'B'>
Total: 38 bytes data + opcodes ≤ 40 bytes — well under the 220-byte standardness limit on mainnet.
Protocol swap mechanism
For two open-mint tokens with backing rates r_a (sats/unit of A) and r_b (sats/unit of B), with source-side fee bps f:
gross_a = units_a * r_a
fee_a = gross_a * f / 10000
net_a = gross_a - fee_a
target_utxo_sats = net_a // entire net paid to target contract
equiv_units_b = net_a / r_b // whole units redeemable from target UTXO
sub_rate_at_target = net_a mod r_b // stays inside target UTXO as change
There is no separate "swap remainder" output. The source contract's redeem() enforces tx.outputs[0].value == netSats. In a swap, that output[0] is paid to the target contract address. Any value not absorbed as whole target units lives inside the target UTXO and surfaces on the next redemption from it. This keeps the contract simple and makes atomicity trivially structural.
The swap is a single BCH transaction with structure:
inputs:
[0] source token UTXO (unlocked by redeem(unitsRedeemed))
[1..] optional fee-funding UTXOs from caller wallet
outputs:
[0] target contract address, value = net_a // contract-enforced
[1] source founder fee (if fee_a > 0) // contract-enforced
[2] source contract change (if partial redemption) // contract-enforced
[3] OP_RETURN with target tokenId // wallet convention
[4..] miner-fee change to caller (optional)
Atomicity is inherent: a BCH tx either fully confirms or doesn't exist.
Open questions
- P2SH20 vs P2SH32: P2SH32 is preferred for mainnet (CHIP-2022-05) but increases tx size. Decision deferred to first regtest run.
- Founder-fee precision at small amounts: rounding may produce dust. Mitigated by minimum-swap-size enforced by contract
require. - Stablecoin tier: out of scope for first slice; design must not preclude later addition.