deviant/wiki/decisions.md
Local Dev 6e57e08884 feat(wallet-metadata): NFT-commitment metadata on genesis — ticker + decimals + class fully on-chain
Implements step 1 of ADR-0057's three-layer metadata split. The 40-byte
minting-NFT commitment, previously set to '' on every genesis, now carries
the irreducible-minimum identity so a wallet with no network at all can
show "ASM (DEA, 2 dp)" for an on-chain amount — no BCMR URL fetch, no
local registry lookup.

Binary layout (pinned per ADR-0057 §1):
    byte  0:    magic   = 0x44 ('D')
    byte  1:    version = 0x01
    byte  2:    decimals (uint8, 0..18)
    byte  3:    assetClass byte (ASSET_CLASS_BYTE table)
    bytes 4..15: ticker, 12 UTF-8 bytes right-padded with 0x00 (ASCII only)
    bytes 16..39: reserved zero (24 B for future flags)

Disambiguation vs AdjustableFeeCapped (ADR-0011) which already uses the
commitment for live feeBps: length (40 vs 1-2) + magic byte. The genesis
planner enforces mutual exclusion at write time — assetClass is honoured
only when adjustableFee is NOT set.

Changes:
- NEW src/wallet/metadata-commitment.ts: pure encoder/decoder + format
  constants, no deps outside src/token/assetClass. Decoder returns null
  for anything not a well-formed metadata commitment (empty, short,
  wrong-magic, wrong-version) so callers can fall through to BCMR / local
  registry / "Unknown".
- NEW test/unit/wallet/metadata-commitment.test.ts: 32/32 pass. Round-
  trips every asset class, pins the exact byte layout, exercises every
  rejection case including the AdjustableFee-ScriptNum ambiguity.
- src/wallet/genesis.ts: GenesisIntent gains optional `assetClass`;
  prepareGenesis populates GenesisPlan.metadataCommitmentHex when the
  caller provides it AND adjustableFee is not set. Encoder failures
  (ticker too long, non-ASCII, decimals out of range) become INFO
  warnings on the plan — never block genesis.
- src/wallet/wallet.ts: createAsset picks the NFT shape by precedence:
  (1) adjustable-fee ScriptNum commitment, (2) ADR-0057 metadata
  commitment, (3) empty default (prior behaviour).
- wiki/decisions.md: ADR-0057 appended — the three-layer split, the
  binary layout, the AdjustableFee disambiguation rule, and what steps
  2 (Sia BCMR mirror) and 3 (BCMR consumer in shells/app) still owe.

Backward-compatible by construction: callers that don't set assetClass
get the exact prior commitment: '' default. 47/47 tests green (existing
genesis.test + new metadata-commitment.test). Core tsc clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-10-22 21:00:32 +03:00

419 KiB
Raw Blame History

Architecture Decision Records

Append-only. Each entry: date, decision, alternatives considered, rationale.

Terminology & spec-version note (2026-05-21). These ADRs are append-only and are not rewritten as terminology and spec section numbers evolve. The current authoritative spec is ../deviant_technical_spec_v2.7.md. Map older wording in the entries below to it as follows:

  • Mint axis: "open mint" → permissionless mint; "closed mint" → managed mint.
  • Asset classes: the V2.1–V2.2 names DEA-L / DMA / DMA-L (and the interim OLA) are now PLA / MCA / MTA. The full taxonomy is eight classes — DEA / PLA / MCA / MTA (MBA, fixed-rate) + PTU / PTL / MTU / MTL (Token, no rate) — in V2.7 §2.4.
  • "Variant 1 / Variant 2" (ADR-0009) is superseded: redemption is a P2PKH spend on every MBA, so there is no "no-redeem" variant; the managed-mint classes are MCA / MTA.
  • Degenerate "permissionless + no-rate" cells (ADR-0009 / earlier framing) are not degenerate — they are the legitimate PTU / PTL Token classes (V2.7 §2.4). Only a rate-0 / sub-floor MBA is malformed.
  • Spec section numbers cited as "spec §N" / "§N" mostly predate V2.6's cut of §4 and the §5–16 → §4–15 renumber. Resolve against V2.7 (e.g. old §5 Contract → §4, §8 Vulnerability → §7, §10 UI → §9, §12 Registry → §11). The full remap and the still-pending fee-model amendment are tracked in spec-amendments.md.
  • feeBps (single knob, V2.7 §4.1) vs the two-knob feeRate / maxFeeRate model retained by ADR-0011 — pending spec amendment, see spec-amendments.md.
  • "ossified" → immutable.

Individual ADR bodies below preserve their original wording as a historical record.


ADR-0001 — TypeScript + CashScript SDK as the implementation stack

Date: 2026-05-11 Status: Accepted

Decision: Implement the wallet, swap builder, and tests in TypeScript using the official cashscript SDK and @bitauth/libauth.

Alternatives considered:

  • Plain Node.js — weaker type safety for contract parameters; same libraries.
  • Python — fewer CashScript examples, immature tooling for this case.
  • Rust — strong correctness story but minimal CashScript ecosystem; would require building primitives.

Rationale: CashScript's own toolchain is TypeScript-first. Type safety on contract arguments (e.g. bigint vs number for satoshi values) catches a real class of bugs at compile time. Library coverage for mainnet, chipnet, and regtest is consistent.


ADR-0002 — Staged testing: regtest → chipnet → mainnet

Date: 2026-05-11 Status: Accepted

Decision: All three networks, run in sequence. Regtest for unit tests, chipnet for integration, mainnet for final smoke tests with real BCH.

Rationale: User directive ("CashScript contract enforcement needs to be tested against current BCH mainnet, not just theoretically verified") rules out regtest-only. Skipping chipnet would mean any mainnet bug costs real BCH. Skipping regtest would make unit-test iteration painfully slow.


ADR-0003 — tokenId is sha256 of immutable genesis params, 32 bytes

Date: 2026-05-11 Status: Accepted

Decision: tokenId = sha256(LE-packed genesis params). 32 bytes.

Alternatives considered:

  • UUIDv4 — uncorrelated with token params, requires separate registry to enforce uniqueness.
  • Address of issuer + nonce — leaks issuer identity into the tokenId.
  • Hash of full metadata blob — varies with non-essential fields (description, name) so identical tokens would have different IDs.

Rationale: A content-addressed tokenId means two tokens with identical economic parameters share an ID — desirable for fungibility and prevents impersonation by metadata variation. 32 bytes fits standardness limits with protocol prefix.


ADR-0004 — OP_RETURN-first tokenId carriage (not CashTokens NFT)

Date: 2026-05-11 Status: SUPERSEDED by ADR-0006 (2026-05-11)

Decision (no longer in force): Carry tokenId in OP_RETURN. Do not initially use the CashTokens (CHIP-2022-02-CashTokens) NFT commitment field.

Rationale (at time of decision): OP_RETURN is universally indexable, no consensus-level dependency beyond what already exists, and works identically across A and B tiers. The CashTokens commitment field would give us a tighter binding but couples Architecture A to a different UTXO type than Architecture B uses, complicating the escalation path. Revisit once swap mechanism is proven.

Why superseded: The technical spec landed on 2026-05-11 explicitly chooses native CashTokens as the foundation, with Category ID (genesis TXID) as the canonical tokenId. The provisional "revisit later" clause kicked in earlier than planned because the spec did the revisiting for us. See ADR-0006.


ADR-0005 — First slice excludes wallet UI

Date: 2026-05-11 Status: Accepted

Decision: First implementation slice is contract + swap builder + tests. No CLI, no GUI.

Rationale: Per directive, the protocol swap is the validation target. A wallet UI adds surface area that doesn't reduce risk on the novel claim. Build the validation artifact first.


ADR-0006 — Native CashTokens + CashScript composite (supersedes ADR-0004)

Date: 2026-05-11 Status: Accepted — implementation pending

Decision: Architecture A is rebuilt on native CashTokens (CHIP-2022-02-CashTokens) rather than plain BCH UTXOs with OP_RETURN tagging.

Key changes:

  • tokenId == Category ID == genesis TXID. No more sha256-of-genesis-params. Uniqueness is cryptographically guaranteed by spending a specific UTXO to create the category.
  • Open mint = a CashTokens minting NFT held inside a CashScript open-mint contract. Anyone can call mint() on that contract by providing the correct BCH; the contract enforces the rate, founder fee, and (via CashTokens consensus) the minting capability.
  • Token UTXOs are CashTokens FT UTXOs, not P2SH outputs with metadata tags. Miners validate balance and supply at consensus level.
  • Metadata standard: extend BCMR (Bitcoin Cash Metadata Registries — https://cashtokens.org/docs/bcmr/chip/) with a deviant namespace, rather than carry params in a custom blob. Spec §2.
  • Minimum rate floor: 1000 bits/unit (per spec §1.3 dust + fee math). Drops ALLOWED_RATE_TIERS entries 1, 10, 100 — they were economically non-viable.

What carries over from the current slice:

  • The protocol swap math in src/swapMath.ts is unchanged in spirit — rate-based redeem+mint atomic transaction. The contract surface around it changes.
  • Architecture B (UI-only) still uses plain BCH UTXOs with wallet metadata; what changes is the tier-A side of the escalation path.
  • The 10 Stage 1 regtest cases stay valid as design exercises but their assertions need to be rewritten against CashTokens semantics before execution.

What's invalidated / needs rework:

  • contracts/OpenMintToken.cash — current contract doesn't touch CashTokens. Needs rewrite to hold a minting NFT and enforce open-mint mechanics atop it.
  • src/tokenId.ts deriveTokenId() — content-addressing replaced by genesis TXID. Function stays useful for tier-B-only display IDs, but tier-A IDs come from chain.
  • src/tokenMetadata.ts ALLOWED_RATE_TIERS — drop sub-1000 values.
  • test/regtest/setup.ts — needs CashTokens support from cashscript SDK; verify the SDK version pin (^0.10.0) supports CashTokens transaction construction.
  • ADR-0004 — superseded above.

Open questions to answer before implementation (from spec §9):

  • Q1 BCMR alignment — which fields belong in BCMR proper vs the deviant extension?
  • Q2 CashScript can govern CashToken minting directly — yes per recent docs, but verify against the pinned SDK version.
  • Q3 Open-mint contract pattern — search the cashtokens.org examples for a known-good open-mint NFT-held-by-contract pattern.
  • Q4 Atomic swap feasibility on CashTokens — the spec calls this the core technical question. Validate before building anything else.
  • Q5 mainnet-js CashTokens coverage — add mainnet-js to deps and test what it supports natively vs. what needs libauth.

Rationale: The spec is the user's authoritative direction; building on a native consensus primitive instead of OP_RETURN tagging produces stronger guarantees and aligns with the existing BCH ecosystem (block explorers, indexers, wallets already recognize CashTokens). The OP_RETURN approach was a useful first slice for proving the math — that work is not wasted, it informs the rebuild.


ADR-0007 — Q4 validated on chipnet; reservoir-model open-mint confirmed

Date: 2026-05-12 Status: Accepted

Decision: The atomic protocol-swap pattern in the spec (§4.4 / §10) works on live CashTokens consensus. The swap is implementable as a single BCH transaction that spends a source-token FT UTXO (P2PKH) and a target-token contract reservoir (CashScript covenant) in the same tx. Adopt this as the canonical pattern. Two corrections to the spec are required and proposed below.

Evidence: Chipnet transaction e68e610f0e4f5374d6bbc8564ca80ce10f11fae7c21cf526c094bec867dd7370 — 100 Token A FTs (rate 1000) atomically converted to 10 Token B FTs (rate 10,000) via the open-mint contract on Token B's category, with Token A FTs burned. See snapshots/0008-q4-result.md for the full trace, pre/post state, and the six-tx prep sequence.

Spec corrections proposed (to be merged into Deviant_Technical_Specification_1.md v0.2 when scheduled):

  1. §1.2, §4.3 — FT supply is fixed at genesis. CashTokens consensus permanently fixes a category's FT supply at the genesis transaction. A minting NFT authorises creating more NFTs of the category, not more FTs. Replace the "mint creates new FT supply" framing with the reservoir model: at genesis, the entire FT supply (typically 10^18, well under the consensus cap 2^63-1) is parked at the contract address as a single UTXO that also carries the minting NFT. mint() transfers FTs out of the reservoir in exchange for BCH at the rate. Burnt circulating FTs are removed from recognised supply permanently — the reservoir is monotonically depleting once seeded.

  2. §4.3 — drop tokenCapability field. CashScript has no tokenCapability introspection field. Capability is encoded in the suffix byte of tokenCategory (32-byte category id + capability marker for NFTs; 32 bytes only for FT-only outputs). The contract preserves capability by propagating the full input tokenCategory verbatim to the corresponding output — consensus does the rest.

Additional implementation notes that should be captured in developer docs:

  • Category byte order: the tokenCategory constructor bytes parameter must be in CashTokens consensus (little-endian) order, which is the reverse of the display TXID hex. Libauth byte-reverses the category string in addOutput token specs but does not reverse bytes constructor arguments. Pass display-order bytes and the contract's introspection comparison silently fails.
  • Swap FT burn: the source token's FTs are burned (no output preserves them). TransactionBuilder({ allowImplicitFungibleTokenBurn: true }) is required at construction time — setting the flag post-construction is a silent no-op.
  • Chipnet min relay fee: the naïve "~700 byte" estimate for our CashTokens covenant tx undershoots chipnet's relay floor. Use 2500–3000 bits for mint, 3000+ bits for swap (with 2 covenant + P2PKH composite). Add a 1.5×–2× safety multiplier to byte-based fee estimates in production.

What this ADR does NOT resolve:

  • The 10 Stage 1 regtest cases (snapshot 0004) still need rewriting against the CashTokens contract surface. Pre-Q4 they were "valid as design exercises"; post-Q4 they should be exercised against chipnet/regtest with the new contract.
  • src/swapMath.ts and src/swapBuilder.ts still encode the OP_RETURN-era assumptions. The math is fine, but the builder needs a CashTokens rewrite informed by the working chipnet scripts.
  • The wiki/index.md "Known spec/code divergence" callout narrows but doesn't fully resolve until the spec text is amended per the corrections above.

Alternatives considered and rejected:

  • Mint-NFT-creates-FTs model. Would require a BCH consensus change (not in scope).
  • Per-mint fresh-genesis NFTs as token units (one NFT per token unit). Wasteful at any scale; defeats fungibility.
  • OP_RETURN-tagged plain-BCH UTXOs (the ADR-0004 path). Already superseded by ADR-0006; the chipnet validation reinforces the choice.

Rationale: Chipnet evidence is the gating criterion the user set on day one ("first task is to validate the protocol swap on chipnet"). Now that evidence exists, the architecture proceeds. The reservoir correction is a real fact about CashTokens, not a Deviant design choice — every implementer hits the same wall — so documenting it here protects future contributors.


ADR-0008 — Trust-decision policy: the wallet verifies (Tier A), and Deviant never becomes the authority (Tier B)

Date: 2026-05-14 Status: Accepted — implementation pending

Context: Snapshots 0018 (T-001, non-canonical contract rug) and 0019 (T-002, B-model definition poisoning) each closed with the same open follow-on: how does the wallet decide what to trust? For Tier A the question is the source-of-truth for the canonical contract bytecode hash (snapshot 0018, D-3). For Tier B it is the definition format and registry policy (snapshot 0019, D-8, Q-T5, Q-T7, Q-T8). They are combined into one ADR because they share a single rationale, drawn from the snapshot 0019 conceptual frame: Architecture A needs a verifier; Architecture B needs an authority — and Deviant must be the verifier's toolmaker, never the authority itself.

Decision A — Tier A: embedded canonical bytecode constant; the wallet verifies.

The canonical DeviantOpenMintFT contract bytecode hash ships as an embedded constant in the wallet release, versioned with the wallet. On adding any Architecture A token, the wallet fetches the minting-NFT-holding contract's bytecode, normalises constructor-parameter positions, hashes, and compares against the embedded constant (this is D-1 from snapshot 0018). Mismatch → the token is rendered "non-standard, peg not guaranteed": no protocol-swap eligibility, no peg display.

Clarification — what "verified" is (added 2026-05-15): "verified / truly-pegged" is a computed predicate, not a curated list and not an on-chain service.

  • Not a list of assets. The wallet maintains no allow-list of blessed token IDs. A token is pegged-100% iff its reservoir contract bytecode matches a canonical hash — the canonical bytecode is what enforces the peg. This makes "verified" permissionless (anyone deploying the canonical contract is automatically verified), complete (no listing lag), and authority-free. A curated list would reintroduce the discretion-is-authority this ADR rejects.
  • Not an on-chain meta-contract. "One contract that verifies all the others" does not map onto BCH: a CashScript covenant is a passive spending condition that introspects only its own transaction — it cannot scan other tokens, read global state, or emit warnings. The protocol-level guarantee is content-addressing itself — a P2SH32 address is the hash of its code, so checking the address checks the code. Verification is therefore an irreducibly client-side hash check; the "warning" is a wallet-side D-1/D-2 output, never an on-chain annotation.

Both points are the authority/verifier split (snapshot 0019; architecture.md trust model) applied to tooling: the chain enforces the rules, the wallet verifies which rules apply.

Decision B — Tier B: open definition format, no blessed registry, optional user-rooted issuer signatures.

  1. Deviant ships no canonical registry. It publishes an open, documented B-token definition format and may ship a reference registry implementation (code) but never a blessed registry instance (data). (D-8.)
  2. The definition format includes an optional issuer-signature field from v1 — adding it after definitions are in circulation is a migration problem (Q-T5).
  3. The wallet surfaces the content-addressed B-token ID (sha256 of immutable genesis params, per ADR-0006) so a user can compare a received definition against an out-of-band-trusted hash (Q-T8).
  4. All B definitions are unverified by default. "Verified by <issuer>" is shown only on a valid signature against a key the user has chosen to trust. Deviant ships no blessed issuer-key list — a blessed list is an authority, and would reintroduce into Tier B the exact thing the tier was designed not to need.

Alternatives considered:

  • Tier A — BCMR manifest signed by a Deviant release key. Rejected: adds a trusted signer and a network dependency, and makes Deviant a soft authority for a tier whose entire point is that it needs none — only a verifier.
  • Tier A — curated registry of audited tokens. Rejected, more strongly: curation is discretion, and discretion is authority. A model replaced the authority with a verifier (snapshot 0019, Part 2); a curated registry would walk that back.
  • Tier B — ship a canonical/reference registry instance. Rejected: per the snapshot 0019 authority analysis, a de-facto-canonical registry is precisely how soft power (default-trust) hardens into hard power (lock-in). Shipping "the" registry would make Deviant the central bank of its own B tier.
  • Tier B — Deviant-blessed issuer-key list. Rejected: the same hardening mechanism, one layer up.
  • One ADR per tier. Rejected: the shared rationale is the decision. Splitting it hides the point.

Rationale: The snapshot 0019 frame yields a clean rule. Tier A's trust decision is mechanical verification against on-chain fact — so the wallet should carry the smallest possible verification root (an embedded constant) and check everything else itself, introducing no new trusted third party. Tier B's trust decision cannot be mechanical — there is no on-chain anchor — so the only safe posture is to keep the unavoidable authority plural, minimal, and user-rooted, and to ensure Deviant never occupies the authority seat itself. "Never become the authority" is the throughline: for Tier A it is free (the tier needs a verifier, not an authority); for Tier B it is a discipline that must be actively held, because the convenient move — ship the wallet, ship the registry, ship the key list — is exactly the move that builds the trap.

What this ADR does NOT resolve:

  • The exact wire format of the B-token definition schema and its signature field — to be specified when the Phase 2 metadata DB is built (snapshot 0019, Next item 3).
  • Constructor-parameter normalisation details for the Tier A bytecode comparison (snapshot 0018, Q-T2) — an implementation question for the D-1 module.
  • Whether the wallet hard-blocks (vs. only warns on) sends of unverified B tokens above a raw-BCH threshold (snapshot 0019, Q-T6) — a UX decision deferred to the Phase 2 send-path build.

Implementation pointers: D-1/D-3 land with the UTXO classification engine (spec §6 Phase 2 step 3). The B-format decisions land with the metadata DB (Phase 2 step 2) and the Architecture B display/send layers (Phase 2 steps 4–5). See the "Next" sections of snapshots 0018 and 0019.

Update (2026-05-22): the authority-free registry alternative this ADR called for is specified in ADR-0020 — mechanical D-1 safety (canonical vs risk-flagged) kept strictly separate from honestly-labelled paid promotion; the two are never merged, so Deviant sells visibility, never truth.


ADR-0009 — openMint is inert metadata in Tier B; design does not depend on which spec reading wins

Date: 2026-05-14 Status: Accepted — flags a spec ambiguity for resolution in spec v0.2

Context: The Token detail screen design (wiki/wallet-ui-design.md) needs to render every token across the tier × tokenType × openMint space. Working through the variants surfaced a spec-internal ambiguity:

  • Spec §2.1 defines openMint entirely in terms of the minting NFT mechanism: true = "anyone can mint via minting NFT mechanism", false = "only founder holds minting NFT".
  • The minting NFT is a CashTokens primitive. It exists only in tier: "hard" (Architecture A). Spec §3.1 is explicit that Tier B involves "no CashTokens", and §3.3 derives a B balance purely as utxoSatoshis / satsPerUnit — anyone holding BCH plus the definition can label their own UTXO.
  • Therefore in tier: "soft" (B) there is no mint gate at all — open or closed. openMint has no mechanism to refer to. "Closed-mint B" is, in the spec's own terms, close to a category error.

A second clarification fell out of the same analysis: openMint (boolean) and tokenType (standard | stablecoin | governance) are independent fields, not one axis. Earlier UI drafts conflated them into a single "type chip" and omitted governance entirely.

Decision:

  1. For Tier B tokens, the wallet treats openMint as inert metadata — not a capability, not an action-row entry, not a prominent classifier. It appears only in the Token detail "Token info" section, labelled as advisory and explicitly not enforced in soft tier.
  2. tokenType is the prominent classifier for Tier B tokens. The openMint open/closed distinction only sub-divides tier: "hard" rows in the variant model.
  3. The design intentionally does not depend on which spec reading wins. Two non-broken readings of openMint-on-B exist (below); "inert metadata, surfaced honestly in Token info" is correct under both, so UI work proceeds now without waiting for the spec.

The two readings (for spec v0.2 to choose between):

  • Escalation intent — openMint declares what the token becomes when escalated B→A (architecture.md escalation path). Dormant in B, activated on escalation.
  • Advisory authority claim — openMint: false is the definition author asserting sole-issuer status, with zero enforcement. Aligns with the architecture.md frame that B is the authority tier (and with ADR-0008's posture that such claims must be shown as unverified by default).

Alternatives considered:

  • Present [B] open-mint / [B] closed-mint as parallel capabilities to their Tier A counterparts. Rejected: implies an enforcement guarantee B structurally cannot provide; manufactures phantom variants.
  • Drop openMint from B definitions entirely. Rejected: it is still meaningful metadata under the escalation-intent reading, and stripping it pre-empts the spec's choice.
  • Block UI work until the spec disambiguates. Rejected: unnecessary — see Decision point 3.

What this ADR does NOT resolve:

  • Which of the two readings spec v0.2 adopts — and, if escalation-intent is chosen, the exact semantics of how openMint activates on B→A escalation.
  • The related-but-separate question of whether a tier: "hard" closed-mint token exposes a redeem path at all (a contract-surface question for DeviantOpenMintFT or a closed-mint sibling, not a metadata-semantics question). The Token detail action row hides the Redeem button when no redeem path exists. Resolved — see Update (2026-05-15) below.

Implementation pointers: Lands with the Token detail screen and the variant model in wiki/wallet-ui-design.md. The "inert metadata" rendering belongs to the Architecture B display layer (spec §6 Phase 2 step 4) and the metadata DB schema (Phase 2 step 2).

Update (2026-05-15) — closed-mint-A redeem path resolved as a genesis-time variant choice; scarcity-vs-gating framing corrected.

The open item above — whether a tier: "hard" closed-mint token exposes a redeem path — is resolved not by choosing one answer but by recognising it is a deliberate decision the founder makes at genesis. Closed-mint A has two valid product variants:

  • Variant 1 — bare issuer token. Founder holds the minting NFT; no permissionless redeem covenant. A provably-scarce, issuer-gated CashToken with no BCH redemption endpoint. Right for tickets, membership, governance, credentials.
  • Variant 2 — closed mint, open redeem. mint() is gated to the founder; redeem() stays permissionless in the covenant. The founder controls issuance; holders retain a guaranteed BCH exit at the peg. Right for issuer-gated but holder-protective assets.

Design consequence: the Token detail action row's Redeem button for a closed-mint A token is driven by the token's genesis-time variant — a metadata/contract-surface fact knowable at UTXO-classification time — not by a runtime "does the contract happen to support it" probe. The variant is a first-class decision in the Create-token (genesis) flow.

Framing correction carried in from the same design discussion: the open/closed-mint distinction is about issuer-gated issuance, not scarcity. Open-mint pegged tokens are already scarce — every unit is fully BCH-collateralised at the peg under the reservoir model (ADR-0007), inheriting base-layer BCH scarcity. What open-mint cannot do is gate who issues. Closed-mint A exists for tokens that are claims on off-chain value the contract cannot custody (fiat, equity, goods), where permissionless minting would create an unfunded issuer liability. This corrects an imprecise "open-mint cannot enforce scarcity" statement made earlier in design discussion. The genesis-flow framing of the open/closed choice is therefore "is your token a BCH-denominated unit, or a claim on something off-chain you must fund?" — never "do you want it to be scarce?"

Update (2026-05-15, evening) — Variant 1 / Variant 2 fork SUPERSEDED by spec V2.1; the redeem-path question is resolved by self-backed UTXOs.

Spec V2.1 (Deviant_Technical_Specification_v2.1.md) supersedes the Variant 1 / Variant 2 framing in the previous update. The framing assumed closed-mint Architecture A might or might not expose a permissionless redeem() covenant, and that this was a genesis-time choice. V2 makes both branches obsolete:

  • Redemption is not a contract path. V2 §5.2 ("No fee on redemption") and §5.4 ("Self-Backed FT UTXOs") define redemption as a standard P2PKH spend of the FT UTXO by the holder. The backing BCH lives inside each minted FT UTXO; the holder unlocks it with their own key. There is no contract redeem() to expose or withhold.
  • Closed-mint tokens are always redeemable. Because redemption is just spending a self-backed UTXO, even a DMA holder retains a guaranteed BCH exit at the genesis rate. The "Variant 1 bare — no redeem path" branch is a category error in the V2 model.
  • The asset taxonomy replaces the variant framing. V2 §4.2 enumerates four MBA types via mint rule × supply (DEA / DEA-L / DMA / DMA-L); a fifth class Token has no fixed rate. This taxonomy is the canonical description of closed-mint Architecture A; "Variant 1/2" is not used.

Consequences for prior UI design work:

  • The Token-detail rule "Variant 1 bare closed-mint tokens show no floor" was incorrect. All MBAs show a Protocol Net Value (V2 §5.4 — units × backingRateSats) because all are self-backed. Only the Token class (no fixed rate) shows literal UTXO BCH with no peg display (V2 §4.4).
  • The Avalanche (Genesis) Mint-mode step's Variant 1 / Variant 2 sub-choice is replaced by the V2 §4.2 asset-type picker.
  • The Token-detail action row's "shown only if contract exposes a redeem path" qualifier is gone — redemption is available on every MBA.

The original "openMint inert in Tier B" conclusion stands in spirit: V2 §5.1 no longer has an openMint constructor field at all — the mint rule is encoded in the asset taxonomy. For Tier B, the asset type is wallet-convention only; D-4 / D-6 (V2 §8.2, §15) require it to be visually marked as non-authoritative. The ADR's design rule — the wallet must not surface open/closed-mint as an enforceable capability on B tokens — is therefore correct, but its mechanism is now the verification axis and the asset-type chip, not an openMint field.

Status: ADR-0009 original decision and first update retained for context; this update is the operative correction. ADR-0010 is unaffected and remains aligned to V2 §12 (registry architecture).


ADR-0010 — Tier B definition propagation: out-of-band, wallet-to-wallet, recipient-derived addresses

Date: 2026-05-15 Status: Accepted — wire format deferred to Phase 2 metadata DB

Context: The Tier B model (spec §3) places the entire token-identity layer in wallet metadata: "Token identity exists only in wallet metadata" (§3.1), "Local SQLite database on device" (§3.3), "no special transaction structure required" (§3.3), "chain sees nothing unusual" (§3.1). What the spec does not specify is how a recipient's wallet associates an incoming BCH UTXO with a token definition it holds — and therefore what a Deviant wallet displays on receipt of a B asset.

The Send-flow design (wiki/wallet-ui-design.md) surfaced this as a concrete gap: A sends a "wrapped" B asset to B; on-chain it is just BCH; what does B's wallet show? Without an answer the Tier B Send branch and the Receive flow are under-specified, and wallet behaviour on incoming UTXOs is undefined.

This ADR also makes two architectural facts explicit that fall out of the spec but were never stated outright: there is no relay for Tier B (the spec §6 Phase 5 relay-server item is a closed-mint-coordination future feature, not a definition-propagation layer), and there is no shared state between wallets — each wallet's definition store is purely local.

Decision:

  1. Tier B definition propagation is out-of-band, wallet-to-wallet — never via chain, never via a Deviant-operated service. This clarifies the spec's architectural intent so the wallet can rely on it.
  2. The v1 signaling mechanism is recipient-derived addresses bound to held definitions. A B-token definition carries a derivation path (or equivalent key/branch identifier); the recipient — who holds the definition — derives a receive address from their own keys under that path and shares it with the sender like any BCH address. The recipient's wallet classifies incoming UTXOs at those derived addresses as the token. This is what spec §3.3's "Check if address matches known Architecture B token definition" implies; this ADR adopts it as the explicit v1 mechanism.
  3. The sender does not need to hold the definition to send a B asset to a recipient who does. The sender just sees a BCH address; sender-side complexity collapses to zero, consistent with §3.1.
  4. The recipient must hold the definition to display the incoming UTXO as a token. Without it, the wallet displays the UTXO as plain BCH — there is no on-chain marker to indicate otherwise, and inventing one would violate §3.1.
  5. Definition sharing is a separate, one-time, out-of-band act (QR, link, file, third-party registry per ADR-0008). It is not bundled into every transfer. Once shared, all subsequent transfers under that definition need no per-transaction signaling.
  6. Trust posture is governed by ADR-0008. An imported definition is unverified by default. The Token-detail tier line, the Send-flow recipient-receives block, and the Receive-flow definition picker must surface that status. A signed definition matched against a user-trusted issuer key may be shown as "verified by ."

Wallet behaviour table:

Scenario Recipient wallet behaviour
Recipient holds the definition; sender sends to a recipient-derived B-token address Display incoming UTXO as the B token (subject to ADR-0008 trust posture)
Recipient does not hold the definition; sender sends to any of their addresses Display incoming UTXO as plain BCH — no token, no badge, no hint
Recipient holds the definition; UTXO is at a non-derived address Display as plain BCH — UTXO is not classifiable as the token under v1
Sender does not hold the definition; sends to a B-token receive address the recipient provided Sender shows a plain BCH send; recipient classifies on receipt; both views are correct
Recipient imports the definition after the relevant UTXO has been received Wallet re-classifies retroactively (Activity row updates)

Alternatives considered:

  • Per-transaction out-of-band payment message (BIP21-style URI, paymail, etc.) carrying a definition reference. Rejected for v1: adds a synchronous coordination requirement to every send; requires both sides to implement the same payment protocol; does not compose with cold-storage or generic BCH wallets. Address derivation degrades gracefully — the recipient sees BCH if the definition is missing — where a payment-protocol-only design fails harder. Worth re-examining as an optional enhancement, not the default.
  • On-chain marker (OP_RETURN tag, vout pattern). Rejected: violates §3.1 "chain sees nothing unusual" and §3.3 "no special transaction structure required"; reintroduces the carriage question ADR-0006 already settled for Tier A.
  • Deviant-operated relay or canonical registry. Rejected as a hard line by ADR-0008 ("Deviant never becomes the authority").
  • Heuristic classification (bits divisible by a known satsPerUnit → guess the token). Rejected: ambiguous, fragile, security-hostile.

Wallet-UI consequences:

  • Send flow, Tier B branch — the existing recipient-receives copy ("their wallet needs the ZCoin definition to display it — without it they just see plain BCH") and the [ share definition ] action are ratified as correct behaviour by this ADR, not placeholders.
  • Receive flow (to be designed) — must include an Import definition path (paste / scan QR / file / registry lookup), gated by ADR-0008's unverified-by-default posture, plus a Generate B-token receive address affordance once a definition is held.
  • Activity — incoming UTXO at a recognised B-token address is shown as a token receive; the same UTXO before the definition was imported would have shown as plain BCH and must re-classify retroactively on import.

What this ADR does NOT resolve:

  • Wire format of a B-token definition (the derivation-path field, optional issuer signature, serialisation). Belongs to the Phase 2 metadata DB schema (spec §6 Phase 2 step 2), already flagged in ADR-0008.
  • Whether to additionally support an optional payment-protocol message as a richer alternative. v1 ships address-derivation only.
  • Retroactive-reclassification UX details (Activity row animation, notifications) — UI polish for Receive / Activity screens.

Implementation pointers: Lands with the Phase 2 metadata DB schema, the UTXO classification engine (spec §6 Phase 2 step 3), and the Receive / Activity flows. The Send-flow Tier B branch in wiki/wallet-ui-design.md is already aligned to this ADR.


ADR-0011 — V1 two-knob fee model retained; V2 §5.2 / §3.2 to be amended to re-admit updateFeeRate

Date: 2026-05-15 Status: Accepted — V2 spec amendment required

Context: V1 §2.1 defined two fee fields:

  • feeRate — 0–5%, adjustable by founder
  • maxFeeRate — set at genesis, immutable, hard cap

The two-knob model gave the founder room to lower fees over time as adoption grows (or to raise within the cap in response to genuine economic conditions) while binding them at genesis to a holder-protection ceiling they can never exceed.

V2 §5.1 simplified this to a single feeBps (0–500), with the contract declared fully immutable in §3.2 ("no upgrade path, no selfdestruct, no admin function") and claim() declared the contract's only function in §5.2.

A design review (2026-05-15) found that the V2 simplification removed two properties the project intends to preserve: founder fee adjustability, and the commitment-versus-flexibility distinction the V1 split expresses. The wallet UI in wiki/wallet-ui-design.md had been edited to reflect V2's single-knob model; that revision is being reverted to V1's two-knob.

Decision:

  1. Retain V1 §2.1's two-knob fee model. feeRate is the current founder fee (0–5%), adjustable. maxFeeRate is the immutable hard cap set at genesis; the contract enforces feeRate <= maxFeeRate on every fee-update call.
  2. feeAddress remains adjustable (V1 §2.1) — founder can update the payout destination without affecting the rate.
  3. No redeem fee. Redemption is a P2PKH spend of a self-backed UTXO (V2 §5.4) — no contract path, so there is no surface to attach a fee to. The V2 architectural simplification of redemption is not affected by this ADR; only the mint-side fee mutability is restored.
  4. Permanence marks in the wallet UI distinguish 🔒 immutable (categoryId, backingRateSats, mint rule, supply, maxFeeRate) from ✎ adjustable (feeRate within cap, feeAddress).
  5. The Avalanche genesis wizard's Fees step is a two-knob slider — current feeRate (✎) and maxFeeRate (🔒) — with the cap required ≥ the current rate.
  6. A "Manage fee rate" screen lives in Founder tools for post-genesis adjustments of feeRate (always ≤ maxFeeRate) and feeAddress.

Spec amendment required:

  • V2 §5.1 — add maxFeeRate constructor parameter (immutable bound) alongside feeRate (replacing the single feeBps). The cap is set at genesis; the rate is the current value.
  • V2 §3.2 — revise "no upgrade path, no selfdestruct, no admin function". The founder-gated updateFeeRate function is neither an upgrade path nor a selfdestruct; it is a narrow admin function bounded by a hard cap set at genesis. The text should specify only the categorically dangerous admin functions (selfdestruct, mint-without-payment, address-substitution) are absent — while permitting bounded fee adjustment.
  • V2 §5.2 — add updateFeeRate(newRate, founderSig) as a second contract function:
    require(checkSig(founderSig, founderPk));
    require(newRate >= 0);
    require(newRate <= maxFeeRate);
    

Alternatives considered:

  • Keep V2's single-knob model and add a feeAddress-only update path. Rejected: loses fee adjustability entirely — a real-world need as markets shift, costs change, and adoption phases pass.
  • No fee-update at all (full V2 immutability). Rejected: too inflexible; locks the founder into their day-one judgement forever. A hard-cap-bounded fee adjustment is a much weaker form of mutability than a contract upgrade — it changes one number within a known-at-genesis envelope, not arbitrary code.
  • Allow maxFeeRate to be raised post-genesis. Rejected outright: would break the holder-protection promise. The cap must be one-way at genesis.

Wallet-UI consequences (recorded in wallet-ui-design.md):

  • Status callout names the V1 fee retention as an explicit project decision overriding V2.
  • Sitemap shows Manage fee rate under Founder tools.
  • Avalanche fee step is the two-knob slider.
  • Cross-cutting primitives table's fee-breakdown row reflects feeRate ✎ within maxFeeRate 🔒.
  • The permanence mark primitive is dual: 🔒 / ✎.

What this ADR does NOT resolve:

  • The exact CashScript signature of the re-introduced updateFeeRate function. Belongs to the V2 spec amendment and the contract repo (contracts/DeviantOpenMintFT.cash) which need a corresponding change.
  • Whether the wallet should rate-limit fee changes (e.g., one update per N blocks) — a UX decision deferred to the Manage-fee-rate screen build.

Implementation pointers: Spec amendment to V2 §5.1 / §5.2 / §3.2. Contract amendment to DeviantOpenMintFT.cash re-introducing updateFeeRate. UI work already reflected in wiki/wallet-ui-design.md v3.


ADR-0012 — B-token definition schema includes issuerSignature field from v1 (resolves V2 §14 Q-T5)

Date: 2026-05-15 Status: Accepted

Context: V2 §14 Q-T5 asks whether the B-token portable definition schema should include the optional issuer-signature field (D-7) from v1, or add it later. ADR-0008 Decision B point 2 already declares the intent — "the definition format includes an optional issuer-signature field from v1" — because adding it after definitions circulate is a migration problem. This ADR formalises that intent so implementation has the explicit on-the-record decision to point at.

Decision:

  1. The B-token portable definition schema (V2 §12.1 PortableToken) carries an optional issuerSignature field from v1. Schema field name: issuerSignature (camelCase to match the rest of the V2 portable format).
  2. Shape: { pubkey: hex, signature: hex, scheme: enum } — pubkey is the issuer's public key; signature is the signature over the definition's immutable genesis params (the same canonical byte ordering used to compute the content-addressed tokenId per ADR-0003); scheme enumerates the signing scheme to allow future additions.
  3. Default state: definitions without the field are valid and render as Unverified definition (ADR-0008 default).
  4. Verified state: definitions with a valid signature against a key the user has explicitly trusted (managed in Settings → Registries → Trusted issuer keys, ADR-0008 D-7) surface as Verified by <label> in the verification badge.

Alternatives considered:

  • Add later, in a v2 schema. Rejected: migration cost. Old wallets won't parse new fields; new wallets must handle two formats; existing definitions circulating without the field create permanent ambiguity. Schema changes are cheap now, expensive after circulation.
  • Make the field required. Rejected: forces every founder to maintain signing infrastructure — including small-scale founders, closed-group founders, and experimental users where signed issuance has no value.
  • Carry signatures out-of-band (separate from definitions). Rejected: definitions are already passed around via QR / file / registry; coupling the signature to the definition is the natural place. Splitting them creates a synchronisation burden for no gain.

Implementation pointers: src/token/portable.ts schema; src/registry/server.ts serialisation; Receive-flow Import-definition review (already designed to render the signature state in wallet-ui-design.md); verification-badge logic in src/verify/.

What this ADR does NOT resolve:

  • The specific signing scheme (Schnorr / ECDSA / PGP-style). Deferred to schema implementation; the scheme enum's initial value will be set when that work lands.
  • Exact wire format and canonical byte ordering for the signature payload. Deferred to the implementation, aligned to ADR-0003's existing byte-ordering rules for content-addressed IDs.

ADR-0013 — Friction-only graduated sends for unsigned B tokens; no absolute hard guard (resolves V2 §14 Q-T6)

Date: 2026-05-15 Status: Accepted

Context: V2 §14 Q-T6 asks whether the wallet should hard-block or only add friction to sends of unsigned B tokens above a BCH threshold. The concern is T-002 attack pattern A-2: a poisoned definition with an inflated satsPerUnit causes the victim to send far more BCH than intended (snapshot 0019). The Send-flow design (wiki/wallet-ui-design.md) already enforces D-4 (raw-BCH co-display on every B-token figure) and D-5 (raw-BCH send confirmation at review). This ADR decides whether to add a third layer above some BCH threshold.

Decision: Graduated friction, never an absolute hard guard.

A four-tier escalating-friction ladder, keyed on the literal BCH amount the send would move:

BCH moved Friction layer
< 100,000 bit D-4 + D-5 baseline (raw-BCH always visible at review)
100,000 bit — 0.1 BCH + Type the BCH amount in literal bits to confirm
0.1 — 1 BCH + Acknowledge an unverified-transfer warning explicitly
> 1 BCH + 10-second confirmation delay before broadcast; + type the issuer label (if any) or the word UNVERIFIED

There is no BCH threshold above which the send becomes impossible. The user remains the authority for B-tier definitions (ADR-0008); the wallet's role is to maximise the visibility of what is actually happening, not to override the user's decision.

Thresholds are calibrated to consequence (at a reference $500/BCH, 100k bit ≈ $0.50; 0.1 BCH ≈ $50; 1 BCH ≈ $500) and are user-tunable in Settings → Security so individual risk tolerance is respected.

Rationale: Hard-blocking violates ADR-0008's load-bearing principle that the user is the authority for B-tier trust decisions. There are legitimate reasons to send large amounts via unsigned B definitions — closed groups, bulk distribution before signing infrastructure exists, deliberate sweeping to a contained address. Friction matches consequence: small sends shouldn't require typing math; large sends shouldn't be one-tap. The wallet's job is to make a mistake really hard to make, not to make a choice impossible.

Alternatives considered:

  • Hard guard above 0.1 BCH (or any threshold). Rejected — user-as-authority violation.
  • Friction-only with a single uniform level. Rejected — too coarse. Small sends shouldn't require typing the literal BCH amount; large sends shouldn't be one-tap. The ladder distributes friction by consequence.
  • Per-issuer-trust thresholds (lower friction for definitions signed by a key the user trusts). Considered; deferred to a future ADR. Once ADR-0012's issuerSignature field is in wide use and D-7 verification is working, this becomes a natural extension. For v1, friction is keyed purely on BCH magnitude.
  • Apply the ladder to receives as well. Rejected. Receives cannot move user BCH; the threat in T-002 A-1 (misvaluation on receive) is addressed by D-4's passive defence — raw-BCH always co-displayed — not by friction at receive time.

Implementation pointers: Send-flow review screen (wallet-ui-design.md); src/wallet/send.ts for the threshold logic; Settings → Security for user-tunable thresholds.

What this ADR does NOT resolve:

  • Whether MBA-Unverified A-tier sends (T-001 surface) also get a friction ladder. Different threat — defense there is D-1 / D-2 / verification badge, not friction. Treated as separate; current Send-flow design warns the sender pre-broadcast but does not add a ladder.
  • Whether the ladder applies inside swap flows where one side is an unsigned B token (the WALLET CALC path indicator already names the unenforced exchange; whether to add a friction ladder there is a follow-on UX question).

ADR-0014 — Content-addressed B-token ID surfaced as the user's verification anchor (resolves V2 §14 Q-T8)

Date: 2026-05-15 Status: Accepted

Context: V2 §14 Q-T8 asks whether the content-addressed B-token ID (sha256 of immutable genesis params, per ADR-0003 / ADR-0008 D-B-3) should be surfaced in the wallet so users can compare it against an out-of-band-trusted value. The Receive-flow Import-definition review already displays it; this ADR extends and formalises the surface across the rest of the wallet.

Decision: The B-token definition's content-addressed ID is surfaced in three places, formatted short-form by default with copy-to-full:

  1. Receive flow — Import-definition review (already designed in wallet-ui-design.md). Shown prominently with a copy affordance, inviting out-of-band comparison before commit.
  2. Token detail — Asset info region (⑦), Tier B only. For every held B-tier asset, the content-addressed ID is shown alongside the asset's other identity fields, available for ongoing verification.
  3. Settings → Registries → per-registry detail. Each B-token entry shows its content-addressed ID — useful for cross-registry comparison: "does my-friends.cash list the same ZCoin definition as registry.zcoin.io? Compare the hashes."

Format: 9c2f…a410 — first 4 hex / ellipsis / last 4 hex. Tap-to-expand reveals the full 64-character sha256; [copy] affordance copies the full hex.

Rationale: The content-addressed ID is the only verification anchor a user has for trusting that a B-token definition matches what they intended to import. ADR-0008 makes this explicit — out-of-band trust is the only B trust path until D-7 signing matures (ADR-0012). Surfacing the hash everywhere a B-token identity is shown gives the user the tool to do the comparison whenever they need to.

Alternatives considered:

  • Surface only on import (the existing design). Insufficient — the user may need to re-verify later ("did my-friends.cash overwrite my ZCoin definition with a different one?"), or compare across registries.
  • Full hash always shown. Rejected — 64 hex characters in every B-token row is noise; users will not compare character-by-character without a copy affordance. Short-form-with-copy is the right ergonomic trade.
  • Checksum instead of full hash. Rejected — checksums lack the collision resistance the sha256 hash provides. Anything weaker than a cryptographic hash is unsafe as a verification anchor.

Implementation pointers: src/token/portable.ts (compute on import; persist alongside definition); wallet-ui-design.md Token detail region ⑦; Receive-flow Import review; Registries per-registry detail.

What this ADR does NOT resolve:

  • The format of the out-of-band-trusted value the user compares against. Could be an issuer's website, social-media bio, signed announcement, printed business card — outside the wallet's scope. The wallet's job is to expose its own computed hash; the source of truth is up to the user.

ADR-0015 — Dynamic-rate (bonding-curve) assets as a recognised premium DEA variant; ASM stays functional via execution-time rate quotes

Date: 2026-05-16 Status: Accepted — design direction; contract template + wallet surface pending

Context: Snapshots 0025/0026 demonstrated that a founder can write a contract whose claim() rate is a deterministic function of state (bonding curve) rather than a single constant. As an attack (T-003), it works by masquerading as fixed-rate — D-1 catches it because the bytecode is non-canonical. But the same mechanism is a legitimate, widely-used pricing primitive (token launches, dynamic pricing, presale curves). The user asked whether escalating rate could be offered as a sanctioned premium feature rather than only treated as a threat.

Two questions had to be settled before saying yes:

  1. Does it break D-1? No — but it means D-1 would reject a legitimate bonding-curve token, since its bytecode differs from the single canonical fixed-rate template.
  2. Does it break the ASM (atomic swap mechanism)? The V2.7 §swap math uses UER = rateA / rateB with both rates constant. A bonding-curve token has no single rate. Initial assumption (recorded then overturned) was that this made dynamic-rate assets ASM-ineligible.

Decision:

  1. Recognise a dynamic-rate DEA variant (working name DRA — Dynamic-Rate Asset; a permissionless, BCH-backed asset whose per-unit rate is a published deterministic function of state, not a constant). It is a backed asset — every unit is fully BCH-collateralised at its mint-time rate — so it belongs to the MBA family, not the Token family.

  2. D-1 accepts a registry of canonical templates, not a single template. Ship the DeviantBondingCurveFT template bytecode as a second embedded canonical constant alongside DeviantOpenMintFT. D-1 verifies a token's on-chain contract against whichever canonical template its published assetClass declares. A token claiming class: DRA is checked against the DRA template instantiated with its published curve params; mismatch → "non-standard, peg not guaranteed," exactly as today. (This generalises Q-T3 — "contract version field for multiple canonical templates" — from the V2 open-questions table.)

  3. Transparency is mandatory. The fraud in T-003 was masquerade — publishing a single rate while charging a curve. A sanctioned DRA must publish its curve parameters in BCMR (base rate, increment, curve type, escalation granularity), and the wallet must render the curve openly: current rate, next-unit rate, and the formula — never a single fake peg. A DRA that publishes a single rate is not a DRA; it is a T-003 attack and D-1 flags it.

  4. ASM stays functional via execution-time rate quotes. Validated empirically on chipnet (snapshot 0027): a swap from a fixed-rate source into a bonding-curve target executed atomically, minting at the target's current state-derived rate. Two swaps of identical 120,000-bit source backing minted 12 then 7 target units, because the curve escalated between them (rate 10,000 → 16,000). The swap mechanism is rate-function-agnostic: the source side is a P2PKH spend that releases whatever BCH the UTXO carries; the target side mints at whatever rate its contract computes from current state. A fixed rate is just the special case where that function returns a constant.

    The cost is that the exchange rate becomes a quote at execution time rather than a constant known in advance — front-runnable, like AMM slippage. So:

    • The wallet computes the quote from current on-chain state and shows it with a slippage caveat.
    • A swap may fail if the target's state changes between quote and broadcast (someone else mints first, moving the rate). This is expected and benign — the tx simply doesn't validate; no funds move.
    • UER remains exact and front-run-free only between two fixed-rate assets. Any swap involving a DRA is a dynamic quote.

Alternatives considered:

  • Keep dynamic rate banned (T-003 only). Rejected — bonding curves are a legitimate primitive; the problem was always masquerade, not escalation. Transparency + a recognised template resolves it without banning the mechanism.
  • Make DRA ASM-ineligible (like managed MBAs / stablecoins). Rejected — the chipnet test (snapshot 0027) proves ASM works with dynamic targets. Excluding them would forgo a real capability for no reason. (Documented here because the prior turn's analysis wrongly assumed ineligibility; the test overturned it.)
  • Single canonical template forever; force all variation into BCMR metadata. Rejected — a bonding curve is contract logic, not metadata. It must live in bytecode, which means a second template and a multi-template D-1.

What this ADR does NOT resolve:

  • The exact DeviantBondingCurveFT contract surface — which curve families to support (linear-per-FT, per-N-FT step, per-claim counter, time-based, geometric), per-batch vs per-unit (integral) pricing, and how curve params are encoded as constructor args. To be specified when the template is built. Snapshots 0025/0026 enumerate the design space.
  • The BCMR schema extension for curve parameters (class: DRA, base rate, increment, curve type, granularity).
  • Front-run/slippage UX — tolerance bands, quote expiry, retry behaviour — to be designed with the ASM swap UI.
  • Whether a DRA can itself be a swap source with rate-aware redemption (the chipnet test used a fixed-rate source into a DRA target; the reverse — and DRA→DRA — are mechanically the same P2PKH-release-on-source, but should be tested explicitly).

Implementation pointers: second canonical constant alongside CANONICAL_BYTECODE_HASH in src/verify/; assetClass-aware D-1 in verifyContractCanonical(); curve-rendering in wallet-ui-design.md Token detail; chipnet evidence and the DEA/DRA swap distinction in snapshots 0025–0027. Cross-references: wiki/protocol-vs-wallet-enforcement.md (T-003, D-1-is-load-bearing), ADR-0008 (D-1/D-3 policy), wiki/risks.md R-013.

Update (2026-05-22) — implemented (snapshot 0037). The core landed in the library:

  • contracts/DeviantBondingCurveFT.cash — canonical DRA template. Curve family resolved to the chipnet-validated v1: linear-per-atomic-FT, per-batch within a claim. Constructor (tokenCategory, initialReservoirFtSupply, baseRateSats, rateIncrementPerUnit, founderFeeBps, founderLockingBytecode).
  • Multi-template D-1 — BONDING_CURVE_ARTIFACT + BONDING_CURVE_BYTECODE_HASH in verify/canonical.ts; verifyContractCanonical takes an optional curve input and reconstructs the bonding-curve address from the published curve params, returning template: 'dynamic'. The T-003 masquerade guard is unit-tested: a DRA whose curve is unpublished or whose increment is wrong verifies non-canonical.
  • DRA added to the assetClass taxonomy (MBA family, permissionless, ASM full); TokenTierA.curve + portable serialisation; display labels a 'dynamic' match as DRA. 7 unit tests. Still open (deferred to follow-ups): the genesis/issuance flow + curve UI, the BCMR curve schema, slippage/quote-expiry UX, and explicit DRA-as-source / DRA→DRA swap tests (chipnet-validated mechanically in snapshot 0036, not yet covered by the new template's own scripts).

ADR-0016 — Market integration: pluggable MarketProvider, no first-party AMM, route by asset class

Date: 2026-05-21 Status: Accepted — design direction; MarketProvider interface + Cauldron adapter pending

Context: The user asked whether to (a) integrate the existing CashTokens market into the wallet, (b) build a new market, or (c) let users join any market — and how. A web investigation of the live ecosystem (2026-05-21) found:

  • Cauldron (cauldron.quest, indexer by Riften Labs) is the dominant CashTokens DEX. Pools are on-chain single-LP "micro-pool" covenants; pool creation is permissionless and free (only the BCH miner fee); the 0.3% swap fee accrues entirely to the LP, not the platform. There is no pay-to-list.
  • Display/verification is a separate, lightly-curated layer: either OpenTokenRegistry (otr.cash, a community BCMR registry shared across BCH wallets) — preferred — or a Cauldron-only "Token Claim" (requires public token-ID publication, min 0.1 BCH liquidity, no impersonation; submitted via Telegram with Riften approval discretion; no fee). BCMR drives name/icon/description.
  • The market is small: ~$1.2M TVL, ~$2.28M 30-day volume, ~$11k 24h volume, ranked ~252nd in DeFi (DefiLlama, 2026-05-21). The 2026-05-15 BCH upgrade is expected to grow stablecoin/DEX activity, but liquidity is currently thin.
  • A public, no-auth indexer API exists at indexer.riften.net/cauldron/ — active pools, token list/search, current/historical prices, candlesticks, volume, APY, latest txs. Connection protocols: WalletConnect and Riften's WizardConnect (HD-aware). No published wallet-dev SDK for transaction construction; in-wallet swaps would require building covenant-spend txs against their (referenced but unconfirmed) open-source covenant spec.

Decision:

  1. No first-party AMM. Deviant does not build a DEX/AMM/LP venue. This reaffirms spec §7 ("not building an AMM or liquidity pool DEX"). Building one into a ~$1.2M market is off-thesis and would reintroduce exactly the counterparty / LP-withdrawal / soft-rug surface Deviant exists to remove.

  2. Route by asset class.

    • MBAs (fixed-rate, BCH-backed: DEA / PLA / MCA / MTA / DRA) → the protocol swap (ASM), counterparty-free. No external market. Putting a pegged asset into an AMM adds slippage and LP-rug risk to something that already has a consensus-enforced floor — strictly worse.
    • Token classes (no rate: PTU / PTL / MTU / MTL) → these have no peg and genuinely need market price discovery, so route them to external markets. Consistent with V2.7 Rule 2 (non-protocol-swappable assets are routed to third-party mechanisms).
  3. Pluggable MarketProvider interface. A thin abstraction — roughly listPools(token), getPrice(token), getQuote(...), and either buildSwapTx(...) or handoffUrl(...). Never hardwire a single venue; support multiple adapters and let the user choose/add one. This is the same plurality principle as ADR-0008 — a single de-facto market venue is the same authority-hardening we reject for registries (venue lock-in = soft power hardening into hard).

  4. Cauldron is the first adapter, read-first. Use the public indexer API (indexer.riften.net/cauldron/) for discovery, pricing, and charts immediately. Execute swaps via WalletConnect / WizardConnect handoff initially; in-wallet covenant-spend is a later enhancement gated on confirming Cauldron's open-source covenant spec (see wiki/risks.md R-014).

  5. Venue tier-transparency (Rule 6 applied to venues, not just tiers). A swap must be visibly one of two kinds — "Protocol swap" (peg, counterparty-free, no slippage) vs "Market swap" (external AMM, market price, slippage + LP/rug risk). A market swap must never be rendered to look like a protocol swap.

  6. Listing posture for Deviant's own tokens: prefer OpenTokenRegistry (community BCMR verification) over the single-gatekeeper Cauldron "Claim." Consistent with ADR-0008 — choose the plural/community path over the discretionary-authority path.

Alternatives considered:

  • Build a first-party AMM. Rejected — off-thesis (spec §7), tiny addressable market, and it rebuilds the LP/rug surface Deviant removes.
  • Hardwire Cauldron as the only market. Rejected — violates the plurality principle (ADR-0008); venue lock-in is the authority-hardening failure mode in another costume.
  • Ignore external markets entirely. Rejected — the no-rate Token classes (PTU/PTL/MTU/MTL) have no peg and need price discovery somewhere; users will expect to trade them, and the existing (thin) liquidity is on Cauldron.
  • Full in-wallet covenant swaps from day one. Deferred — depends on Cauldron's covenant spec being open, documented, and stable (R-014). Start read-first + handoff, which carries no covenant-spec dependency.

What this ADR does NOT resolve:

  • The exact MarketProvider method signatures and the discovery/quote data model.
  • Whether to support in-wallet covenant-spend execution vs handoff-only in the long run (gated on R-014).
  • Which additional venues (e.g. TangoSwap) to write adapters for, and when.
  • Slippage / quote-expiry / retry UX for market swaps — overlaps the DRA execution-time-quote UX from ADR-0015; design them together.
  • Whether any MBA should ever be optionally marketable (current decision routes all MBAs to ASM); revisit only if a concrete use case appears.

Implementation pointers: MarketProvider interface + Cauldron adapter (wrapping indexer.riften.net/cauldron/) in a new src/markets/ module; route in the swap path by assetClass; venue tier labels in wallet-ui-design.md; R-014 tracks the covenant-spec dependency for in-wallet execution. Cross-references: spec §7 (not building an AMM), Rule 2 (routing of non-swappable assets), Rule 6 (tier transparency), ADR-0008 (plurality / verifier-not-authority), ADR-0015 (execution-time quote + slippage UX).


ADR-0017 — Wallet service-fee model: a service fee is not a counterparty cost; peg-preserving and soft by default

Date: 2026-05-22 Status: Accepted — design direction; fee-output construction + disclosure UX pending

Context: The user asked whether a 0.3% wallet fee can be charged on mint, swap, and asset creation. An initial framing wrongly suggested that a fee on the MBA protocol swap "undercuts the counterparty-free thesis." That conflated two different things and is corrected here.

Counterparty-freeness is a property of market structure — no one must take the other side of a trade for it to clear — not a claim that no fee exists. A service fee is a charge for facilitation (the wallet, tx construction, the UI); the payee is not a counterparty — they provide no liquidity, take no opposite position, and are not required for the swap to clear. The design already proves the two coexist: the founder feeBps is a service/issuer fee, and the MBA swap remains counterparty-free with it in force. So "counterparty-free" never implied "fee-free."

Decision:

  1. Fee taxonomy (canonical for the project). Four distinct fee types, not to be conflated:

    • Counterparty cost (spread / slippage / LP fee) — paid to the other side for bearing the trade / providing liquidity. Eliminated on MBA swaps; this is the structural win.
    • Service fee — paid to a facilitator (wallet/dev) for building/broadcasting the tx. Orthogonal to market structure. This is the wallet fee under discussion.
    • Issuer fee (feeBps) — paid to the token founder on mint/redeem; contract-enforced.
    • Miner fee — paid to the network for inclusion.
  2. The service fee is peg-preserving. It is taken as a surcharge from the user's free BCH, as an additional output — never skimmed from the backing flow. On a swap the full backing must reach the target (the target contract's value-equality require), so the fee rides alongside, funded by the user's spare BCH; on mint/creation it is a surcharge on top of the backing. Per-unit backing always remains exactly N × rate in every case. A fee skimmed from backing would underback the target and break the peg — explicitly disallowed.

  3. Soft (avoidable) by default. The fee is appended by Deviant's wallet tx-builder, not baked into contract bytecode. It is therefore avoidable by using a different wallet — which is correct: paying a provider you chose is a service market, not an authority. A consensus-baked, unavoidable fee is rejected — unavoidability is what would turn a service charge into a rent-extracting authority (ADR-0008), and it would also change the canonical bytecode (D-1).

  4. Disclosure is mandatory. The service fee is shown as a separate line item in raw BCH at confirmation — never hidden inside the token unit or folded into the swap rate. Consistent with Rule 6 (tier transparency) and the raw-BCH co-display discipline (D-4). Where a founder feeBps also applies, the UI shows both fees distinctly so the user can see who receives what.

  5. Scope and config. Applies to the genesis/creation, mint/claim, and ASM swap transactions the wallet constructs. Configurable rate (working default 0.3%) and a single dev/fee address (multisig permitted). Spec §4.4 already reserved the slot: "Output: platform swap fee (optional, wallet-added)."

Alternatives considered:

  • Treat the wallet fee as incompatible with the counterparty-free thesis (the prior-turn framing). Corrected/rejected — it conflated market structure with fee absence; the founder feeBps already disproves the incompatibility.
  • Consensus-baked, unavoidable fee (a contract require). Rejected — unavoidability creates a rent-authority (ADR-0008) and forks the canonical bytecode (D-1). The soft, avoidable fee preserves both principles.
  • Skim the fee from the backing flow. Rejected — underbacks the target / breaks the peg. The fee must come from the user's free BCH.

What this ADR does NOT resolve:

  • The default rate, and whether it varies by surface (mint vs swap vs creation) or by asset class.
  • Whether to waive or reduce the fee on MBA protocol swaps to preserve a "no extra cost" UX advantage as a product/marketing choice — this is now a product call, not a structural constraint (the thesis permits the fee either way).
  • Fee-address governance (single key vs multisig vs rotating).
  • The exact stacked-fee confirmation UI when a founder feeBps and the wallet service fee both apply.

Implementation pointers: fee-output insertion in the genesis, claim/mint, and ASM swap builders in the wallet core; a disclosure line-item in the confirmation UI (wallet-ui-design.md); config constants for rate + fee address. The ADR-0016 market layer is the natural home for service fees on Token-class market swaps specifically. Cross-references: ADR-0016 (market integration), ADR-0008 (avoidable ≠ authority), spec §4.4 (reserved wallet-fee output), founder feeBps (V2.7 §4.1), D-4 raw-BCH co-display (snapshot 0019).


ADR-0021 — Referral revenue-share via a baked-in founder-fee split

(Renumbered from 0018 → 0021 on 2026-05-22 to resolve a collision; remains in its original physical position, above ADR-0019/0020.)

Date: 2026-05-22 Status: Accepted — design direction; contract params + wallet capture-at-genesis pending

Context: Proposed referral model — user A invites user B; B creates an MBA whose founder feeBps generates fees on every swap; a fixed share (e.g. 10% of the founder fee) is routed to A. This builds on ADR-0017's taxonomy — it is a split of the issuer fee, not a new fee — and it resolves the attribution gap noted while surveying BCH referral programs (Zapit etc.): non-custodial rails have no accounts to attribute referrals to. Asset creation breaks that deadlock, because genesis is a one-time event at which a referrer address can be captured and then made permanent.

Decision:

  1. Referral reward = a baked-in split of the founder fee, not a new fee. The canonical DeviantOpenMintFT template gains two constructor parameters: referrerLockingBytecode and referrerShareBps (a fraction of the founder fee — e.g. 1000 = 10% of the fee). On each fee-bearing claim/swap the contract emits two fee outputs: feeBps − referrerShare to the founder, referrerShare to the referrer. referrerShareBps = 0 ⇒ no referrer, a single fee output to the founder.

  2. Consensus-enforced and wallet-portable. Because the referrer address and share live in the contract (and therefore in its P2SH32 address), the split is enforced by consensus on every swap, forever — including after B switches to a non-Deviant wallet. Captured once at genesis, immutable thereafter (Rule 5).

  3. Decentralised attribution via the invite link. A's invite link carries A's address — the referral "code" is A's locking bytecode. B's wallet reads it and passes it into the genesis constructor. No registry, no account system, no central lookup. This is precisely why the model works on non-custodial rails where ongoing-activity referral attribution does not.

  4. D-1 parameterisation, not a new template. referrerLockingBytecode + referrerShareBps are standard constructor params of the canonical template; the bytecode shape is unchanged, so D-1 still recognises canonical (referrerShareBps = 0 is the no-referrer default). Same parameterisation principle as ADR-0015. A referral mechanism that altered the bytecode would be flagged non-canonical, as intended.

  5. Deviant takes nothing. The split is a peer founder→referrer arrangement configured by B at genesis. Deviant inserts no mandatory cut for itself — doing so would be the rent-authority violation ADR-0008 forbids. Deviant's own revenue stays the soft, avoidable service fee of ADR-0017.

  6. Not farmable (the key property). The reward redistributes an existing fee rather than minting new value (contrast Zapit's per-signup ZRP). Self-referral is an economic no-op — paying your own fee to your own address. A earns only from B's real swap volume. There is no inflationary or Sybil attack surface, because no new units are created per referral.

Constraints / rules:

  • referrerShareBps ≤ feeBps — the referrer slice can never exceed or invert the fee (contract require).
  • Dust: the referrer output must be ≥ 546 bits OR 0; below the threshold the whole fee goes to the founder. Mirrors R-002 and is doubled here (two fee outputs). Tracked as R-015.
  • Peg unaffected: the split only redirects part of the founder's existing cut; per-unit backing and the swapper's economics are unchanged (ADR-0017 reasoning).
  • Capture requires a referral-aware wallet at genesis; tokens minted via non-Deviant tools simply carry no referrer.

Alternatives considered:

  • Wallet-forward soft split (fee to founder, wallet forwards 10% to A). Rejected as the primary mechanism — bypassable (B uses another wallet and keeps 100%). Retained only as a fallback where on-chain enforcement is not wanted (e.g. a revocable or expiring referral).
  • Reward-token referral (Zapit/ZRP — mint a discount token per referral). Rejected for issuer referrals — farmable via fake signups, inflationary, needs an app-layer token economy. May still suit user-acquisition referrals where there is no founder fee to split; out of scope here.
  • Deviant-level mandatory referral cut. Rejected — rent-authority (ADR-0008).
  • Mutable/revocable referrer on the baked-in path. Rejected — conflicts with Rule 5 immutability. If revocation is required, use the soft wallet-forward fallback instead.

What this ADR does NOT resolve:

  • Whether referrerShareBps is fixed by protocol convention or chosen by the founder at genesis, and any cap on it.
  • Expiry/revocation semantics if a non-permanent referral is ever wanted (forces the soft model).
  • Multi-level referral (A→B→C) — explicitly out of scope; the baked-in model supports exactly one referrer per token. Multi-level needs app-layer accounting and reintroduces farming/Sybil concerns.
  • The invite-link URI scheme for encoding A's address, and the "you were referred by <addr>" consent screen B sees before genesis.
  • Whether referral also applies to Token-class market-swap service fees (ADR-0016/0017) — that would be a different, app-layer attribution problem, not this on-chain one.

Implementation pointers: add referrerLockingBytecode + referrerShareBps to the DeviantOpenMintFT constructor and the two-output fee logic in claim()/swap; param-aware D-1 in verifyContractCanonical(); invite-link parse + genesis capture + consent screen in the wallet; dust/cap requires in-contract. R-015 tracks dust-stranding. Cross-references: ADR-0017 (fee taxonomy — this is an issuer-fee split), ADR-0008 (Deviant takes no cut), ADR-0015 (canonical-template parameterisation / multi-template D-1), Rule 5 (genesis immutability), R-002 (founder-fee dust), spec Phase 5 (voluntary marketing revenue share — this is its concrete mechanism).


ADR-0018 — Managed-mint contract template (MCA / MTA) + two-template verifier

Date: 2026-05-22 Status: Accepted — implemented (contract compiled, verifier + display wired, tests green)

Context: The eight-class taxonomy (V2.7 §2.4) has four MBA classes split by mint rule: permissionless (DEA / PLA) and managed (MCA / MTA). Only the permissionless contract (DeviantOpenMintFT) existed, so the wallet could derive DEA / PLA but never MCA / MTA. For MBAs the mint rule gates ASM eligibility, so it must be verifier-sourced (which canonical template the on-chain contract matches), never a trusted registry field — otherwise a mislabel could wrongly enable/disable Orbital swaps.

Decision:

  1. New canonical template contracts/DeviantManagedMintFT.cash. Identical reservoir + self-backed-UTXO + full-peg model as DeviantOpenMintFT, with one difference: mint() requires checkSig(founderSig, founderPubKey) — only the founder may issue. It adds a 5th constructor parameter, founderPubKey (33-byte compressed). It is not a backdoor: every issued UTXO is fully backed (output[1].value == tokenAmount * backingRateSats), so D-2 holds. (Distinct from the malicious _Whitelist test artifact, whose privileged paths drop the peg.) Anyone may still redeem (P2PKH spend of a self-backed UTXO); only issuance is gated.
  2. Two-template verifier. verifyContractCanonical (D-1, address-equality) and discoverContract (D-1+, redeem-script byte cross-check) now try the permissionless template first, then — when a founderPubKey is published — the managed template. Each reports template and mintRule ('permissionless' | 'managed' | null). The matched template is the verifier-sourced mint rule.
  3. founderPubKey is a published, verifier-validated parameter, not a trusted claim. A wrong value yields a non-matching reconstructed address → non-canonical. Added to TokenTierA as optional founderPubKeyHex (present only for managed tokens).
  4. Display derives the class from the verifier-reported mint rule. Canonical + permissionless → DEA/PLA (by supply); canonical + managed → MCA/MTA (by supply); non-canonical → "MBA — Unverified", no class, no ASM. MCA/MTA are never ASM-eligible (V2.7 §2.4).

Alternatives considered:

  • Trust a registry mintRule field for MBAs. Rejected — verifier-vs-authority regression; mint rule gates ASM, so it must be proven against on-chain bytecode, not asserted.
  • One parameterised contract with a "managed" flag. Rejected — a flag that disables the founder gate is exactly a backdoor surface; two distinct bytecode templates give two distinct canonical addresses and a clean binary D-1 verdict.
  • Reuse founderLockingBytecode for the mint gate. Rejected — checkSig needs the 33-byte pubkey; the P2PKH hash can't be reversed to it. Hence the extra published param.

What this ADR does NOT resolve:

  • The managed-mint transaction flow (the founder-issuance builder + signature path) — classification/verification only here; the mint(founderSig, …) tx builder is follow-on wallet work.
  • MTA hard-cap enforcement at the contract level — supply (DEA/MCA unlimited vs PLA/MTA limited) is currently a reservoir-size display signal; a consensus-enforced cap is a separate contract concern.
  • Stablecoin (MCA) off-chain dollar-peg surfacing — an issuer claim layered on the BCH PNV (see architecture.md worked example); UI work.

Implementation pointers: contracts/DeviantManagedMintFT.cash (+ dist/ artifact via npm run compile:contracts); src/verify/canonical.ts (MANAGED_ARTIFACT, MANAGED_BYTECODE_HASH); src/verify/contract.ts + src/verify/discover.ts (two-template, template/mintRule); src/token/types.ts (founderPubKeyHex); src/wallet/display.ts (verifier-sourced mint rule → deriveTierAClass). Tests: test/unit/verify/contract.test.ts, discover.test.ts, test/unit/wallet/display.test.ts. Full suite green (249 / 240 pass / 9 network-skipped).


Numbering note (2026-05-22, resolved): the earlier ADR-0018 collision is resolved — the referral revenue-share ADR was renumbered 0018 → 0021 (it sits in its original physical position, above ADR-0019); the managed-mint ADR (immediately above) keeps 0018 because it has code cross-references (MANAGED_BYTECODE_HASH etc.). All references now point to ADR-0021 for the referral/marketing stream.


ADR-0019 — Premium feature tier: DRA via a configurable one-time unlock + optional elevated service fee

Date: 2026-05-22 Status: Accepted — design direction; unlock-flag + pricing config pending

Context: Consolidating the business model (wiki/revenue-model.md). Premium features — DRA (Dynamic-Rate Asset, ADR-0015) first — are offered as a paid tier. The chosen pricing is configurable: a higher one-time BCH unlock, or a small one-time unlock plus an elevated DRA service fee (0.5% → 1%), or a tuned blend. Builds on ADR-0017 (service-fee taxonomy, soft + peg-preserving) and the two-revenue-shapes framing (throughput-based vs user-based).

Decision:

  1. Premium = paid access to advanced issuance tooling, not permission. The wallet builds/validates the DRA genesis + curve UI for paying users; the on-chain DRA template (ADR-0015, D-1-verified) is unchanged and anyone can construct a DRA with raw tools. Gate the tooling, not the capability (ADR-0008) — pay for convenience/support, not for the right to issue.

  2. Two pricing knobs — fee is binary (0.5% or 1%), unlock is quantity-based. Every DRA carries either the 0.5% or the 1% service fee — not a continuous band — and the amount paid upfront selects which: pay more upfront → 0.5%; pay less upfront → 1%. The unlock unit is quantity, not time: per-DRA-creation, a bundle of N creations, or unlimited. Illustrative: a 5-DRA bundle at $5 → 1% or $10 → 0.5%; an unlimited unlock at a higher one-time price → 0.5%.

    • Pay-more plans (higher upfront / unlimited) → 0.5% — revenue decoupled from throughput (captured at setup).
    • Pay-less plans (small / bundle) → 1% — splits value capture into usage.
    • Time-boxed / daily / weekly passes dropped: quantity bundles avoid the recurring-billing question entirely (each is a one-time prepaid payment).
  3. Paid in BCH; no accounts or subscriptions. Recurring subscription was rejected (off-thesis, needs billing rails, and recurring BCH auto-debit is clunky on UTXO).

  4. The elevated DRA fee follows ADR-0017 mechanics — a surcharge from the user's free BCH, peg-preserving, disclosed as a line item. It stacks independently with the founder fee and (if set) the referral split on the founder side.

  5. Soft/avoidable, like all Deviant revenue. The unlock is a wallet-honored flag; a fork could strip it. Revenue rests on most users valuing the official build and support — consistent with the whole soft-revenue posture (ADR-0017).

Alternatives considered:

  • Pure recurring subscription. Rejected — billing rails/accounts (custodial-ish), UX friction, least on-thesis; recurring BCH auto-debit is clunky on UTXO.
  • Pure extra-service-fee, no unlock. Rejected as the sole model — re-couples all premium revenue to throughput, forfeiting the decoupling/robustness benefit. Retained as one knob position (the "usage" plan).
  • Gate the on-chain capability (only premium users may issue DRAs). Rejected — permission-gating is authority (ADR-0008), and unenforceable anyway (raw tools / forks).
  • Single fixed price. Rejected — founders value upfront vs usage differently; a configurable spectrum mirrors the self-regulating founder-fee market.

What this ADR does NOT resolve:

  • The actual unlock prices and bundle sizes (per-creation / bundle-of-N / unlimited); the fee itself is now settled as a binary 0.5% / 1% choice tied to the amount paid.
  • Which premium features beyond DRA join the tier (advanced analytics, batch issuance, multisig issuance, …).
  • How the unlock is represented/verified (local wallet flag vs a small on-chain proof-of-payment the wallet checks) — and that, being soft, it is forkable.
  • Whether the elevated DRA fee and referral split ever interact specially (default: they stack independently on the founder side).

Implementation pointers: premium-unlock flag + pricing config in wallet core; DRA genesis builder behind the flag; the elevated-fee path reuses the ADR-0017 service-fee output logic with a per-asset-class rate; disclosure line item in the confirmation UI. Consolidated model + sizing in wiki/revenue-model.md and wiki/revenue-model.xlsx. Cross-references: ADR-0015 (DRA template / D-1), ADR-0017 (service-fee mechanics — soft, peg-preserving), ADR-0021 (referral revenue-share — the marketing stream), ADR-0008 (gate tooling, not capability), spec Phase 5 (monetization surfaces).


ADR-0020 — Hosted/B2B revenue surfaces; "promoted" (not "verified") registry; external-DEX swaps are 0% retention

Date: 2026-05-22 Status: Accepted — design direction; pricing/UX specifics pending

Context: Extending the business model (wiki/revenue-model.md) beyond the soft on-chain service fee, and resolving the registry tension ADR-0008 left open (it rejected a curated "verified" registry but did not specify the alternative). Also settles the external-DEX fee question from ADR-0016/0017.

Decision:

  1. Soft vs hard revenue surfaces — name the split. Soft surfaces are on-chain and forkable (service fee, ADR-0017; premium unlock, ADR-0019) — frictionless and on-thesis but bypassable; measure by take-rate × throughput. Hard surfaces are server-side or contractual — Deviant controls the server/license, so they are enforceable and recurring (real MRR + lock-in). The hard/B2B layer below is where durable revenue and the moat live.

  2. Hosted relay / SaaS. Optional infrastructure Deviant hosts and meters: managed-mint (MCA/MTA) signing coordination, Tier-B definition propagation (ADR-0010/0012), a hosted indexer API (free tier + paid high-volume), and referral/analytics tracking for the tier ladder. Server-gated → genuinely enforceable billing.

  3. Registry: a three-list model — "promoted / standard / risk-flagged" — replaces "verified / unverified". "Verified" conflated three things and made Deviant the arbiter of legitimacy (the ADR-0008 trap) while implying a guarantee curation can't give. Split them; never merge:

    • Safety = mechanical D-1, free, not for sale. Canonical bytecode → "peg consensus-enforced," eligible for the canonical list. Non-canonical → excluded from the canonical list, cannot be promoted, and is placed in a separate risk-flagged list with an explicit peg-not-guaranteed warning.
    • Promotion = a paid "promoted" list, canonical-only. Honestly labelled as advertising/featuring; never a safety signal. Restricting it to canonical tokens is an ad-quality bar, not a truth-claim.
    • Identity = BCMR metadata (name/icon/description; Deviant deviant namespace). Rule: the D-1 safety status is always shown truthfully and visually dominant; promotion can never override or imply it. Plurality is preserved — Deviant's registry is one of several, and non-promoted canonical tokens are fully usable, visible, and verifiable. Deviant sells visibility, never truth → still a verifier, not an authority.

    (BCMR = Bitcoin Cash Metadata Registries: the CHIP standard for authenticated JSON registries mapping a CashToken category-ID to human-readable metadata, on-chain-authenticated so wallets can trust it came from the issuer.)

  4. The promoted list IS the visibility arm of the voluntary marketing revenue share (ADR-0021). Promotion and the marketing tier ladder are one growth layer: higher marketing tier → promoted placement. Promotion pricing uses the same upfront-vs-usage trade-off as DRA: either a higher % fee to Deviant (e.g. 1%) with no upfront, or an upfront payment keeping the standard 0.5%.

  5. White-label issuance. License the verified MBA/DRA contract templates + wallet tooling + D-1 verification to third parties issuing under their own brand (loyalty, remittance, games, fintech): setup fee + per-issuance + revenue share. B2B, enforceable, recurring; leverages the canonical/verified rails as the product.

  6. External-DEX-routed swaps are 0% (a retention feature), not a revenue surface. Routing a no-rate Token-class swap to an external venue (Cauldron, ADR-0016) carries no Deviant fee (0%), or a thin 0.1% maximum. Cauldron already charges 0.3%; any premium over going to Cauldron directly just teaches users to bypass the soft fee. The integration's value is retention/funnel — keep users in the wallet where Deviant-native monetization (MBA mint/swaps, premium) lives. The full 0.5% is reserved for Deviant-native MBA events.

Alternatives considered:

  • "Verified / unverified" registry. Rejected — conflates safety with curation (authority, ADR-0008) and is a dishonest badge.
  • Selling promotion to non-canonical tokens. Rejected — would let scams buy visibility; promotion is canonical-only, risk-flagged tokens excluded.
  • Full 0.5% on external-DEX swaps. Rejected — self-defeating; drives bypass, captures nothing, loses the session.
  • A single canonical Deviant registry as source of truth. Rejected — authority-hardening; truth stays mechanical (D-1), the registry is one of several.

What this ADR does NOT resolve:

  • Pricing specifics for relay, registry/promotion, and white-label.
  • Promoted-list ranking mechanics (flat fee vs auction) and exactly how it bundles into the ADR-0021 tier ladder.
  • Whether external-DEX is exactly 0% or a thin 0.1% (start 0%, revisit on data).
  • Risk-flagged list UX (warning copy; shown-with-warning vs hidden-by-default).
  • Operational/legal aspects of running hosted infra and offering a white-label licence.

Implementation pointers: D-1 status and promotion rendered as separate, never-merged UI signals (wallet-ui-design.md); the promoted list wires into the ADR-0021 tier ladder; hosted relay + indexer API as server-side services; external-DEX 0% in the ADR-0016 MarketProvider routing. Cross-references: ADR-0008 (authority-free registry — alternative now specified here), ADR-0016 (market routing / external-DEX), ADR-0017 (soft service fee), ADR-0021 (marketing revenue share = the promoted list's payment arm), ADR-0019 (premium tier), wiki/revenue-model.md.

ADR-0022 — React Native shell runs the core in an on-device nodejs-mobile process, not in Hermes

Date: 2026-05-24 Status: SUPERSEDED by ADR-0023 (same day). The WASM finding here stands, but its decision (embed nodejs-mobile) was reversed: a build-time noble shim makes the core run directly in Hermes/JSC, removing the on-device Node process, the bridge, the bundling problem, and — decisively — the iOS jitless-WASM open risk that gated this ADR. ADR-0022's option (c) (the shelved @scure/@noble pure-JS path) became the chosen direction in a more complete form (a full byte-for-byte libauth crypto shim, not a second divergent derivation stack). Retained for the WASM evidence and the rejected alternatives.

Context: Phase 4 forks three platform shells (RN mobile, Electron desktop, React PWA — wiki/wallet-ui-design.md §Design constraints) over the same shared TypeScript core and the cross-shell KeyStore seam (ADR/snapshot 0040, src/wallet/keystore.ts). The mobile shell raised a make-or-break runtime question that gates every screen: does the core's crypto stack run under the React Native JS engine (Hermes on both platforms by default; JavaScriptCore as the legacy fallback)?

Evidence gathered from the installed packages (not from a device run — see open risk):

  • @bitauth/libauth 3.1.0-next.8 ships its secp256k1, sha256, sha512, ripemd160 and sha1 primitives as WebAssembly only — node_modules/@bitauth/libauth/build/lib/bin/**/*.wasm plus crypto/secp256k1.js instantiating them. There is no pure-JS fallback and no injection seam to substitute the crypto primitives from outside the library.
  • mainnet-js 3.1.7 depends transitively on libauth (@bitauth/libauth ^3.1.0-next.8) and on @electrum-cash/network for connectivity, so it inherits the same WASM requirement plus a WebSocket networking assumption.
  • Hermes has no WebAssembly support, and RN's JSC build does not expose it either. Therefore the core, run directly inside the RN JS runtime, would fail at instantiateSecp256k1() / the hash instantiators — i.e. it cannot sign, hash, or talk to Electrum.

Decision:

  1. Do not run the core in Hermes/JSC. Embed it in an on-device nodejs-mobile process. nodejs-mobile-react-native runs a real Node.js (V8) runtime inside the app. libauth's WASM and mainnet-js's networking run there unmodified, so the ~231-test core ships byte-for-byte identical across CLI, web, desktop and mobile — no fork of the trust-critical code, which is the entire point of the cross-shell seam.
  2. The RN/Hermes layer does no blockchain crypto. It is UI + secure key custody + a thin bridge client. All derivation, signing, tx-building, D-1/D-2 verification and Electrum I/O happen in the Node process. This is consistent with the cross-shell goal: one crypto implementation, not a second divergent one in Hermes.
  3. The RN↔Node bridge mirrors the src/web/api.ts request/response surface. The Node entry builds a RunContext exactly as src/web/server.ts does (InMemoryTokenRegistry + connectWallet(...) → { wallet, registry, ownTokenAddress }) and dispatches over the bridge to the same handleBalance / handleTokens / handleCommand (→ runCommand) / handleRegister / handleFavorite / handleForget handlers the web shell already uses. The mobile client is "an HTTP-style client over the core API" — just over the nodejs-mobile channel instead of TCP, so the core never leaves the device (non-custodial preserved, spec §3.2).
  4. KeyChainKeyStore seals the seed in iOS Keychain / Android Keystore (biometric/PIN-gated); derivation runs in Node and honours the keystore.ts wiring contract. On create/restore/unlock the (biometric-decrypted) mnemonic crosses the on-device bridge once; the Node side derives the key, calls connectWallet, holds the live DeviantWallet for the session, and returns the derived signing key to the RN layer so UnlockedAccount.exportPrivateKey() returns real bytes — i.e. the contract's documented connectWallet({ privateKey: await account.exportPrivateKey(), ... }) wiring works verbatim. The mnemonic never leaves the Keychain; the derived key is held in RN memory only while unlocked and zeroed on lock(). (This revises an earlier draft of this ADR that had exportPrivateKey() throw not-exportable — overridden so the cross-shell KeyStore contract is honoured identically on every platform; the never-in-heap posture was a nice-to-have, not a requirement, and conflicted with the contract.) The non-secret AccountIdentity (addresses + 33-byte pubkey) is cached in plaintext app storage so getPublicIdentity() serves receive/balance views while locked, per the contract.

Alternatives considered:

  • Native secp256k1/hash shim into Hermes (run the core directly in RN). Rejected — highest risk. libauth exposes no seam to replace its WASM crypto, so this means forking libauth and re-implementing mainnet-js's Electrum networking for RN — forking the trust-critical layer is exactly what the cross-shell seam exists to prevent.
  • Remote HTTP API, RN as a pure thin client (core on a server). Rejected. Either the seed leaves the device (custodial — violates spec §3.2) or the device signs locally, which re-introduces the Hermes WASM blocker for the signing step and needs core changes to split build-unsigned/sign/broadcast. nodejs-mobile gives the same "client over the core API" ergonomics while keeping everything on-device.
  • A second, Hermes-safe crypto stack in RN (@scure/bip39 + @scure/bip32 + @noble/secp256k1) for derivation only. Rejected for v1. Pure-JS and Hermes-compatible, but it creates a second derivation/address-encoding implementation that must be kept bit-for-bit in parity with libauth — the divergence risk the shared core is meant to eliminate. Kept on the shelf as the iOS fallback if the open risk below proves fatal.

What this ADR does NOT resolve — the load-bearing open risk:

  • iOS jitless WASM. Apple forbids JIT for third-party apps, so nodejs-mobile runs V8 in --jitless mode on iOS. V8's jitless mode has historically disabled WebAssembly compilation (WASM needs codegen). If that still holds for the bundled V8, libauth's WASM crypto will fail on iOS even inside nodejs-mobile — the one thing this whole decision was meant to secure. This is unverified here (this was decided on a Windows box with no Mac/iOS/device toolchain; the finding above is package-evidence + engine docs, not a device run). Before the iOS shell can ship, this must be device-verified. If WASM is unavailable under jitless iOS V8, the fallbacks, in preference order, are: (a) ship Android first while the iOS path is resolved; (b) swap libauth's WASM crypto for a native secp256k1/hash addon compiled into the nodejs-mobile iOS build; or (c) adopt the shelved @scure/@noble Hermes-side derivation+signing path for iOS only (accepting the parity-test burden). Android (V8 with JIT) is expected to work and should be validated first.
  • Bundling the core into nodejs-assets/nodejs-project (which build artifact ships — dist/ vs a bundler step) and the WASM asset-packaging for both platforms.
  • Bridge backpressure / large-payload handling and reconnect semantics on the nodejs-mobile channel.
  • Everything above the seam — screens, the four V2.7 §9 interfaces, QR/deep-linking, app-store packaging — is deliberately not started; per the scope decision of 2026-05-24 it is worthless until the runtime path (esp. the iOS open risk) is device-verified.

Implementation pointers: shells/mobile/ skeleton landed alongside this ADR (snapshot 0041): src/core-bridge/ (protocol + NodeCoreClient), src/keystore/ (KeyChainKeyStore + SecureStore/Keychain impl + identity cache), nodejs-assets/nodejs-project/main.js (the Node-side bridge that rebuilds the src/web/server.ts RunContext). Cross-references: src/wallet/keystore.ts (the contract), src/web/api.ts + src/web/server.ts (the surface the bridge mirrors), src/wallet/runtime.ts (connectWallet), snapshot 0040 (KeyStore seam), wiki/wallet-ui-design.md §Design constraints.


ADR-0023 — Noble-backed crypto shim replaces libauth's WASM via a build-time alias (one core on every shell)

Date: 2026-05-24 Status: Accepted — implemented and proven (231-equivalent unit suite green; byte-for-byte parity vs libauth; one live chipnet sign+broadcast confirmed). Supersedes ADR-0022's decision (not its WASM finding).

Context: Phase 4 ships three shells (React Native, Electron, web) over the same TypeScript core (mainnet-js + cashscript + @bitauth/libauth). ADR-0022 established the blocking finding: libauth sources its 5 crypto primitives (secp256k1 / sha256 / sha512 / ripemd160 / sha1) from build/lib/crypto/default-crypto-instances.js, which instantiates them as WebAssembly via a top-level await at import. Hermes/JSC (React Native) has no WASM, so import '@bitauth/libauth' throws on device. ADR-0022 resolved this by embedding a nodejs-mobile (real V8) process and keeping Hermes a thin client — but that left a load-bearing, unverified iOS open risk (V8 --jitless on iOS historically disables WASM, which would break libauth's crypto even inside nodejs-mobile), plus a Node-bundling problem and a whole RN↔Node bridge to maintain.

The key realisation that re-opened the decision: that default-crypto-instances.js module is a clean injection seam. libauth re-exports its crypto from there and consumes it internally (combinations.js, hmac.js, the pure-JS BIP32 deriveHdPath*), all via that single module. Replacing only that module swaps the entire crypto backend with no fork.

Decision (Option B): replace libauth's WASM crypto with a pure-JS @noble-backed shim, injected at BUILD time via a one-line bundler alias per shell. One shared core + one crypto implementation runs in-process on every shell — including directly in Hermes/JSC. No fork of libauth / mainnet-js / cashscript; they stay on published versions (@bitauth/libauth 3.1.0-next.8, cashscript 0.12.1, mainnet-js 3.1.7).

  • The shim is src/crypto/ (index.ts = the alias target, export shape byte-identical to default-crypto-instances.js; hashes.ts; secp256k1.ts — the full ~30-fn Secp256k1 surface incl. the tweak fns that back libauth's BIP32; schnorr-bch.ts). Backed by @noble/curves@1.6.0 + @noble/hashes@1.5.0. No top-level await, no WASM — import-safe on every engine.
  • Each shell aliases the resolved path @bitauth/libauth/.../default-crypto-instances.js → the shim. Metro / webpack / vite snippets and the Node ESM-loader equivalent are documented in src/crypto/README.md. (Config only; the shell sessions apply them.)
  • node:crypto createHash was also removed from src/tokenId.ts, src/verify/canonical.ts, src/verify/discover.ts (non-portable to Hermes/browser) and replaced with @noble/hashes sha256.

The Schnorr-vs-ECDSA resolution. cashscript's SignatureTemplate defaults to SignatureAlgorithm.SCHNORR, and BCH Schnorr is the pre-BIP340 variant — not @noble/curves' BIP340 schnorr; they are not interchangeable. A grep of all contracts/*.cash confirmed every covenant unlocks with algorithm-agnostic checkSig(sig, pk) — none hardcodes a 64-byte Schnorr length, none uses checkDataSig. Therefore: shell-runtime.ts now signs with SignatureAlgorithm.ECDSA (one-line override). noble's ECDSA is RFC6979-deterministic, lower-S, and proven byte-identical to libauth — the recommended low-risk path. BCH Schnorr is nonetheless fully implemented in schnorr-bch.ts (libsecp256k1 RFC6979 nonce with the 16-byte "Schnorr+SHA256 " algo tag; the quadratic-residue-Y convention; plain-SHA256 challenge) and proven byte-for-byte against libauth, so the shim stays a faithful drop-in for any caller (e.g. legacy chipnet scripts) that still signs Schnorr.

Alternatives considered:

  • Option A — embed Node on device (nodejs-mobile). Rejected (was ADR-0022's choice). Heaviest option, no wallet precedent, mobile-only (doesn't help unify with web/desktop), and carries the unresolved iOS jitless-WASM risk that could break libauth's crypto on iOS regardless. The shim removes the risk entirely instead of betting against it.
  • Option C — fork libauth (or mainnet-js/cashscript) to add a pure-JS crypto backend. Rejected. Forking the trust-critical layer is exactly what the cross-shell seam exists to prevent; it also creates a permanent maintenance burden against upstream. The build-time alias achieves the same effect with zero fork.
  • A second, RN-only derivation/signing stack (@scure+@noble, derivation only). Rejected (ADR-0022 had shelved this). It creates a divergent second implementation to keep in parity. Option B generalises it into a single libauth-API-faithful shim proven byte-for-byte, so there is no second behaviour to drift.

Proof (the shim is not trusted without this):

  • Byte-for-byte parity against real libauth (which runs in Node, where WASM works) over thousands of random vectors + edge cases — every secp256k1 function (ECDSA DER/compact/recoverable, BCH Schnorr, lenient + LowS verify, recovery, format conversion, normalize/malleate, add/mul private+public tweaks incl. tweak ≥ n) and every hash (hash + streaming init/update/final). See test/unit/crypto/parity.test.ts.
  • Full unit suite green: 358 pass / 0 fail (9 skipped) via node --test. (The 6 test/regtest/* failures are a pre-existing, unrelated cashscript export error in test/regtest/setup.ts — randomTxId — needing a live regtest node; untouched by this work.)
  • Live chipnet sign + broadcast through the shim: with libauth's crypto aliased to the shim (WASM bypassed, asserted at runtime), a P2PKH self-send built via the exact cashscript TransactionBuilder + SignatureTemplate(priv, _, ECDSA).unlockP2PKH() path that shell-runtime.ts uses was accepted on chipnet — txid f32be4cb83496a5f31d0233c8e03de60e93fbb0c21196f1a3682785e4af0da47 (scripts/live-noble-send.mjs, run via scripts/crypto-shim-register.mjs).

Caveats / deferred:

  • The shells must each apply the documented alias (the configs are intentionally not committed here — that's the shell sessions' job). Until a shell aliases the module, it still loads WASM.
  • validatePrivateKey is intentionally stricter than libauth: it rejects any non-32-byte key (returns false) instead of libauth's WASM behaviour (zero-pads short keys; throws on >32 bytes). Real keys are always 32 bytes; parity is contracted for well-formed inputs.
  • BCH Schnorr is bespoke crypto. It is proven equal to libauth here, but the live broadcast deliberately used ECDSA (the sanctioned low-risk path); a Schnorr covenant spend through the shim has not been broadcast.

Implementation pointers: src/crypto/* (shim) + src/crypto/README.md (alias snippets), src/wallet/shell-runtime.ts (ECDSA override), src/tokenId.ts / src/verify/canonical.ts / src/verify/discover.ts (node:crypto→noble), test/unit/crypto/parity.test.ts (proof), scripts/crypto-shim-{hook,register}.mjs + scripts/live-shim-entry.mjs + scripts/live-noble-send.mjs (Node loader + live proof). Cross-references: ADR-0022 (the WASM finding it builds on), snapshot 0041 (RN shell slice).


ADR-0024 — Oracle-settled event prediction + pari-mutuel tote as recognised Deviant contract families; result-oracle accountability model

Date: 2026-05-25 Status: Accepted — Families B & C chipnet-validated. Snapshot 0045: EventPredictionEscrow + fixed-roster 3-party ParimutuelTote (resolveOracle via checkDataSig, pro-rata split on live consensus). Snapshot 0046: 4-party fixed-roster ParimutuelTote4 (two winners per side). Snapshot 0047: open-entry NFT-free tote OpenTote.cash (Tier 1) — peg-enforced bet() accrual, oracle-gated + operator-co-signed settle(), timelocked refund(); forged-oracle settle rejected on-chain; D-1 verifier verifyOpenToteCanonical added so the wallet can label an on-chain open-tote pool. Tier 2 (covenant-enforced trustless pro-rata) is chipnet-validated (snapshot 0048, contracts/ParimutuelToteOpen.cash): open-entry bet() mutates a state-NFT ledger + mints per-bet mutable receipts; attest (oracle checkDataSig) freezes totals; claim pays covenant-computed floor pro-rata with the last winner draining the residual + burning the state NFT; double-claim rejected on-chain (cash:113, require(rcClaimed==0)) and loser rejected on-chain (cash:114, require(rcSide==outcome)). Built in plain BCH (single state-NFT pool UTXO). The MBA-denominated variant (ParimutuelToteOpenMBA.cash) is also chipnet-validated (snapshot 0049): pool = two co-spent UTXOs (state-NFT + peg-enforced MBA vault); stakes/payouts are a backed Deviant asset (ADR-0025); same on-chain double-claim/loser rejections (cash:127/cash:128). NFTs are permitted as covenant machinery per the same-day revision (Addendum 2). D-1: verifyParimutuelToteOpenCanonical + verifyParimutuelToteOpenMBACanonical. Operator rake (peg-preserving, ADR-0017) folded into both canonical templates (snapshot 0052): attest takes rake = grossPool*rakeBps/10000 off the top, winners split netPool; rakeBps+rakePkh are constructor params (default 0 = no rake), surfaced by D-1 as "house take = X%". BCH validated (snapshot 0051, byte-identical) and MBA validated (snapshot 0053, rake paid as a peg-correct FT). Full design in wiki/event-prediction-and-tote.md.

Context: The checkers integration (integrations/checkers-dea.md) settles a 2-party wager with a trusted arbiter (GamePoolEscrow.resolveArbiter — checkSig(arbiterSig, arbiterPk), the game server signs the winner). A design conversation asked why a price prediction can be oracle-settled but a game result cannot. Working through it corrected the trust framing and surfaced two new product families (spectator event-prediction; pari-mutuel tote). This ADR records the corrected model and adopts the families as recognised Deviant contract types.

The framing correction (load-bearing):

  1. A game result is a fact, like a price. "Fact vs non-fact" was the wrong axis.
  2. Nothing needs to be on-chain. A result-oracle signs the outcome off-chain; only the signed attestation is referenced at settlement via checkDataSig. Two distinct ways to remove an arbiter were previously conflated: (a) oracle path (accountable party signs the result off-chain; cheap; works for games) vs (b) full-replay path (commit all inputs on-chain; trustless; expensive). They are not the same; (a) applies to games exactly as to price.
  3. "Disinterested" is the wrong property; "accountable" is right. The guarantee is not the oracle's purity but that misreporting is detectable (cross-checkable against many independent public sources) and costly (the oracle stakes its whole franchise — every contract trusting its key — on not lying). A publicly-observed tournament satisfies both and is therefore as oracle-settleable as a price; the only hard case is a private, unobserved 1-on-1 match (sole interested witness), which is itself a design choice.

The arbiter/result-oracle distinction, made precise: an arbiter signs a decision specific to one contract (checkSig over the spending tx; only the parties can check it); a result-oracle signs a reusable fact (checkDataSig over a standalone message, consumed by many contracts, publicly cross-checkable, franchise-staked). Rule: arbiter when the only witness is an interested party and committing the input is too expensive; result-oracle when the outcome is publicly observable or a reputable named oracle attests it.

Decision:

  1. Recognise three contract families, all MBA-denominated + non-custodial + peg-preserving: A. player-staked match (ships — GamePoolEscrow, arbiter/hashlock); B. spectator event-prediction (new — resolveOracle via checkDataSig; bettors don't control the outcome, so settlement must be an accountable oracle); C. pari-mutuel tote (new — pooled, pro-rata split).
  2. Result-oracle = a published, named oraclePubKey embedded in the covenant and surfaced by the wallet ("settles via oracle X on event E at block N"). Deviant surfaces which oracle, never is the oracle — same verifier-not-authority posture as ADR-0008/0020.
  3. Multi-template D-1. Each new template ships as a canonical bytecode constant; the wallet verifies an on-chain pool against published params before a bettor co-signs. Same mechanism as ADR-0015 (DRA) / ADR-0018 (managed-mint).
  4. Tote has three trust tiers (event-prediction-and-tote.md §3): Tier 0 k-of-n multisig + oracle co-sign + off-chain pro-rata (semi-trusted, minimal code); Tier 1 covenant+oracle gate, off-chain amounts; Tier 2 covenant-enforced pro-rata via a state-NFT sequential-claim pattern (fully trustless; novel covenant). The UTXO constraint — a covenant introspects only its own tx, not betting history — is what every tier works around.
  5. Build sequence (agreed): design doc + this ADR → Family B resolveOracle escrow (1-v-1 first) → tote Tier 0 → tote Tier 2; each chipnet-validated, mirroring scripts/chipnet-escrow-test.mjs.

Alternatives considered:

  • Settle games only via the arbiter (status quo). Rejected as a general rule — it conflates "private unobserved match" (genuinely arbiter-only) with "publicly-observed tournament" (oracle-settleable). Spectator markets need the oracle path.
  • Treat the result-oracle as a per-bet anonymous signer. Rejected — kills accountability (no franchise at stake, not cross-checkable). The oracle must be named and reused.
  • Skip the tote; offer only matched 1-v-1 prediction. Deferred, not rejected — Family B 1-v-1 is the first build; the tote follows. Both are wanted.
  • Deviant operates the oracle. Rejected — authority capture (ADR-0008). Deviant verifies and surfaces the oracle; trust is the user's.

What this ADR does NOT resolve:

  • The exact resolveOracle data-message format ({eventId, outcome, timestamp}) and oracle signing scheme.
  • Tier 2 specifics: NFT-commitment size vs state needed (inline totals vs merkle root of bets), pro-rata rounding/dust distribution, unclaimed-winning-share sweep, griefing deadlines (open questions listed in event-prediction-and-tote.md §3.1). Live again for the Tier-2 build — see Addendum 2 (the same-day NFT-policy revision).
  • Commit-reveal for hidden predictions in Family B (Guru hides the first prediction until match).
  • Regulatory posture per jurisdiction (R-016) — blocking for any real-money mainnet pilot.

Implementation pointers: new contracts/ templates (a resolveOracle escrow sibling to GamePoolEscrow; a tote covenant for Tier 2) + contracts/dist/ artifacts; multi-template entries in src/verify/canonical.ts; chipnet scripts mirroring scripts/chipnet-escrow-test.mjs; connector co-sign via wc2-bch-bcr (pending connector ADR). Cross-references: wiki/event-prediction-and-tote.md (full design), integrations/checkers-dea.md, contracts/GamePoolEscrow.cash, ADR-0008 (verifier-not-authority), ADR-0015 / ADR-0018 (multi-template D-1), ADR-0016 (oracle/market), ADR-0017 (peg-preserving fee), R-016 (regulatory + oracle-trust + dust).

Addendum (2026-05-25) — no-NFT scope decision ⇒ open tote ships as Tier 1, Tier 2 deferred. The project is MBA/DEA-only; NFTs are out of scope. Decision-4's Tier 2 derives its trustlessness from a state NFT (aggregate {totalPool, totalWinningStake}) + per-bet receipt NFTs (double-claim guard). A bare fungible UTXO carries only an amount, so without NFTs a single-tx covenant cannot verify the split or block double-claims, and CLTV bounds time only from below (no on-chain "betting closed"). Therefore the open-entry tote was built as Tier 1 (contracts/OpenTote.cash, chipnet-validated snapshot 0047): open-entry bet() folds with the full peg enforced; settle() binds release to the named oracle's checkDataSig for the event (outcome trustless) + the operator's checkSig; refund() is the timelocked backstop. The pro-rata amounts and winner set are operator-computed and trusted-but-publicly-checkable (the §1 "accountable, not disinterested" posture). Tier 2 (covenant-enforced pro-rata, receipt double-claim prevention) remains specified in §3.1 but is deferred unless/until NFTs are brought back into scope — at which point the §3.1 open questions become live again. The fixed-roster totes (ParimutuelTote, ParimutuelTote4) bake the roster into the contract, so they enforce pro-rata without NFTs but are not open-entry.

Addendum 2 (2026-05-25, same day) — NFT scope REVISED: NFTs allowed as covenant machinery ⇒ Tier 2 un-deferred. The no-NFT decision above was revised the same day: NFTs are permitted when they function as covenant machinery — a state commitment the covenant reads/rewrites, or a per-bettor receipt — but not as collectibles/speculative artifacts. Rationale: the objection was to NFTs-as-product, not NFTs-as-plumbing; the tote's state/receipt NFTs are pure accounting (an on-chain ledger the covenant maintains), which is the only way to get open-entry + covenant-enforced pro-rata together. Consequence: Tier 2 is now in progress (ParimutuelToteOpen or similar); the §3.1 open questions (commitment packing ≤40 bytes, rounding/dust per claim, unclaimed-share sweep, griefing deadlines) are live again, not moot. Tier 1 (OpenTote) remains the shipped no-NFT / operator-trusted-split option. The D-1 verifier gained verifyOpenToteCanonical (Tier 1); a Tier-2 template entry follows when that contract lands.

ADR-0025 — An MBA is an obligor-free, self-enforcing backed instrument (not a "token"); denomination scales with unit value

Date: 2026-05-25 Status: Accepted — definitional. Sharpens the asset taxonomy (wiki/asset-landscape.md Layer 2b, "MBA Standard"); binding guidance for every high-value MBA and for downstream integer-math consumers (swaps, pari-mutuel tote).

Context: A design conversation (2026-05-25) pressed two points: (1) calling an MBA a "token" is a loose convention inherited from CashTokens; (2) decimals/denomination are not cosmetic for high-value MBAs.

Decision / definitions:

  1. An MBA is a covenant-enforced, self-backed, OBLIGOR-FREE bearer instrument. Its nearest traditional analogues — promissory note, redeemable note, collateralized/asset-backed currency — all share the one thing an MBA lacks: a third-party obligor whose promise to honor redemption is the source of value (and thus carries counterparty/credit risk, a redemption gate, and failure under institutional stress). An MBA has no obligor:

    • the backing bits are physically present inside the instrument's own UTXO (value == amount × rate, enforced per output at consensus), and
    • redemption is the holder's own plain spend releasing those bits — no issuer, no permission, no counterparty. The "promise" is therefore not a party's commitment that can be broken; it is a consensus invariant. Backing is intrinsic and present, not owed. This is Layer 2b carried to its limit: not merely strict enforcement of a claim on an issuer, but removal of the issuer from the redemption path entirely. (Issuance may be gated — managed MCA/MTA — but redemption never is; the obligor-free property holds for every MBA class.)
    • Naming. Use MBA / backed instrument, not "token." "Token" names the CashTokens container; "MBA" names the asset class. The lack of an established label for "obligor-free backed bearer instrument" is not a reason to collapse it into "token."
  2. Denomination scales with unit value — divisibility is a genesis-time choice, not cosmetic. A CashTokens FT amount is an integer; an MBA's divisibility is fixed at genesis by the base-unit rate (bits backing per integer unit), and BCMR decimals only display that granularity. The smallest holdable/transactable increment is one base unit = rate bits. Therefore:

    • High-value MBAs MUST be finely denominated — small per-base-unit rate + display decimals — so the smallest increment is reasonable (≈ cent-scale) regardless of the headline unit's value. A coarse 10-BCH MBA (rate = 10 BCH, 0 decimals) is indivisible below 10 BCH.
    • Integer-math consumers inherit the granularity. Pari-mutuel pro-rata floors each payout; the residual is bounded by one base unit — materially large for a coarse high-value MBA, negligible when finely denominated. Same for swap remainders (snapshots 0036/0037).
    • Guidance: choose rate so one base unit is a small fixed value (cent-scale) and express the headline unit via decimals. Penny-value MBAs already satisfy this.
    • Concrete schedule (scale decimals with unit magnitude; BCH supports up to 8): roughly track display decimals to the per-unit value's order of magnitude — ~2 decimals at unit-value ≈100, 3 at ≈1000, 4 at ≈10 000, … The higher the per-unit value, the finer the subdivision should be, so the smallest representable increment stays ~constant (cent-scale) rather than ballooning with the headline value. (BCH's native 8-decimal divisibility is the ceiling; a 10-BCH MBA wanting cent-scale increments needs ~8 decimals.)

Consequences: the MBA-denominated tote (and any high-value-MBA feature) assumes a finely-denominated MBA, stated explicitly rather than left implicit. Docs/UI prefer "MBA / backed instrument" over "token" for Layer 2b assets.

Alternatives considered:

  • Treat MBA as a token (convention). Rejected — flattens the obligor-free / intrinsic-backing distinction that is the whole point of Layer 2b.
  • Frame MBA as a digital promissory note. Rejected as the primary frame — a promissory note is an obligor's promise; an MBA removes the obligor. Useful only as an on-ramp analogy.
  • Leave denomination to issuers ad hoc. Rejected for high-value MBAs — coarse denomination silently degrades divisibility and downstream integer-math accuracy.

Cross-references: wiki/asset-landscape.md (Layer 2b / MBA Standard), spec V2.7 §2.4, ADR-0006 (reservoir + self-backed UTXOs), src/verify/backing.ts (D-2 per-UTXO backing), snapshots 0036/0037 (divisibility/denomination), ADR-0024 + event-prediction-and-tote.md (the pari-mutuel consumer whose accuracy depends on denomination).

ADR-0026 — Wallet UI: the CosmoGenesis four-tab shell, automated risk scale, dual-protocol Apps

Date: 2026-05-25 Status: Accepted — design. Supersedes the v5 navigation model in wallet-ui-design.md; binding for the Phase-4 shell. Mockups in design/mockups/, tokens in design/cosmo-genesis.css, vocabulary in wiki/glossary.md.

Context: A design session (2026-05-25) built the wallet's visual IA from the project's own assets — the "CosmoGenesis" visual language defined in the project website (Knowledgebase/08_historical_drafts/deviant_website.html) — informed by Cashonize's functional model and BCH-community input on connection protocols. Every decision was validated as a live HTML mockup before being recorded.

Decision:

  1. CosmoGenesis is the wallet's design system. Dark "sky" gradient + starfield, acid-lime (#d6ff3d) accent with glow, Fraunces (display) + JetBrains Mono (all numeric/technical data). Extracted verbatim from the project website; lives in design/cosmo-genesis.css + design/starfield.ts. External refs (Cashonize, Exodus, Figma kits) informed layout/polish only — the project's own language is authoritative.

  2. Four-tab shell + central action. Bottom bar: Main · Assets · Avalanche · Apps, with a central acid FAB (Send / Receive / Scan QR). Main's icon is the Bitcoin Cash mark (BCH-green, distinct from acid). Settings demoted to a gear. Supersedes v5's Home/Activity/Swap/Settings.

    • Assets is a two-level hub: family tabs Assets | Tokens, each with sub-tabs — My Assets | Orbital Network and My Tokens | Market. The single swap engine is unchanged underneath; the split is navigational and trust-expressive (Orbital = counterparty-free ASM; Market = routed external DEX, ⚠).
    • Orbital Network lists all existing DEAs from the user's registries + on-chain discovery (no blessed catalog — ADR-0008).
    • Avalanche lands on the eight-class taxonomy as a two-column matrix (MBA left / Token right; rows = mint×supply). Founder dashboard lives in Assets → My Assets.
  3. Automated risk scale replaces the Verified/Unverified UI binary. Three tiers — 🟢 canonical/standard (acid), 🟠 risky (orange), 🔴 high-risk (red) — computed automatically from the existing D-1/D-2/D-3 checks (spec §7; checks unchanged). The canonical case is calm (class chip only, no "Verified" badge); only anomalies are flagged. Headline high-risk pattern: an issuer who free-mints to self and sells it as a "backed" currency (claims a peg the UTXOs don't carry — T-001, caught by D-2). Acid glow is reserved for canonical + actions, so it never marks a risky asset. The orange-vs-red severity split is a mapping layer over D-1/D-2/D-3.

  4. Apps supports both WalletConnect and CashConnect. Auto-detect from the URI; one honest transaction-preview either way; a network switcher (Mainnet/Chipnet/Testnet); a user-extensible BCH-app directory (no blessed list — ADR-0008). Rationale: WalletConnect = broad reach; CashConnect = frontier apps (BCH Guru) and currently Cashonize-only → implementing both is a genuine differentiator.

Consequences: wallet-ui-design.md gains a v6 navigation section (v5 retained as history); glossary.md gains the risk scale + connection-protocol terms. Implementation: the bottom bar becomes one React TabBar (icons defined once); the risk-tier mapping is a layer over src/verify + src/token/classify.ts; CashConnect + WalletConnect are new Phase-4 surfaces. The MBA price-display discipline is preserved (PNV on Assets; market price only on Tokens — ADR-0025).

Alternatives considered:

  • Keep v5 Home/Activity/Swap/Settings. Rejected — the family split (Assets/Tokens) and the Orbital DEA directory express the trust model better and match the Cashonize mental model users already know.
  • Keep "Verified/Unverified" labels. Rejected — badging every normal asset "Verified" reads as marketing; flag-the-anomaly is more honest, and a gradient conveys severity.
  • WalletConnect only. Rejected — excludes CashConnect-native apps (BCH Guru); supporting both is the differentiator.
  • Adopt an external template's look (Figma kits / Exodus) as the base. Rejected — CosmoGenesis (the project's own language) is authoritative; externals informed layout/polish only.

Cross-references: wiki/wallet-ui-design.md (v6 nav), design/ (tokens, mockups, README), wiki/glossary.md, ADR-0008 (no blessed registry/list), ADR-0011 (two-knob fee), ADR-0025 (MBA denomination + price discipline), ADR-0023 (crypto shim — the shell runtime), spec V2.7 §2.4/§7/§9, snapshot 0044 (Capacitor GO).

ADR-0027 — Asset-family naming (NBA) and the direct-value vs backed-claim distinction

Date: 2026-05-25 Status: Accepted — taxonomy / naming. Refines ADR-0026 and the eight-class taxonomy; supersedes the spec V2.7 §2.4 "PLA real-world peer = gold/silver" statement and the "Token" family label in user-facing surfaces. Aligns the class framing with wiki/asset-landscape.md (Layer 1 / Layer 2).

Context: A design conversation (2026-05-25) refining the Avalanche taxonomy surfaced several confusions in the "Token" framing. Grounded against Knowledgebase/02_structural_taxonomy/...mba-standard-asset-taxonomy... and asset-landscape.md. This is the corrected, authoritative version.

Decisions:

  1. Family name: NBA (Non-Backed Assets), parallel to MBA (Mathematically Backed Assets). "NBA" replaces "Token" as the family label in user-facing surfaces (Avalanche, Main, Assets) and the glossary. The four class codes keep their canonical letters — PTU / PTL / MTU / MTL (the "T" is historical: token); DEA / PLA / MCA / MTA unchanged.

  2. Direct value vs backed claim — the load-bearing distinction.

    • Direct-value (Layer-1) assets — gold, silver, barter goods, BTC, BCH, PoW coins — are not backed; they are value. Nothing backs them; there is no redemption claim. On-chain they are NBA: permissionless + unbacked → PTU (uncapped) / PTL (capped, e.g. Bitcoin's 21M).
    • Backed claims — gold certificate, promissory note, USDT/USDC — are derivative claims redeemable for an underlying. These are the MBA category. A Deviant MBA is the strict, consensus-enforced form: MBA : BCH ∷ gold-certificate : gold. The backed thing is always the note / certificate, never the metal.
    • Correction: spec §2.4's "PLA peer = gold/silver" conflates Layer-1 gold (unbacked) with the PLA backed claim. PLA shares gold's issuance pattern (permissionless extraction, finite) but adds a BCH redemption floor gold lacks; PLA's true peer is a capped certificate backed by a reserve, not raw metal. Raw metal → PTL.
  3. Memecoins are managed in the common case → MTU / MTL. A memecoin is normally issuer-minted (a creator/team mints the supply exclusively) → managed. Only open-mint (community-mintable, no exclusive issuer) memecoins are PTU / PTL. (Corrects the loose "memecoin = PTU" reading; the corpus's "open-mint memecoins" under PTU is specifically the open-mint variety.)

  4. PoW coins are the real-world peer for the permissionless NBA classes (the corpus gave no token-side peer): permissionless issuance + unbacked → PTU (uncapped emission) / PTL (capped, Bitcoin-style).

Consequences: glossary.md, wallet-ui-design.md and the Avalanche mockup use NBA + the direct-value / backed-claim framing. Spec V2.7 §2.4 PLA-peer line and the corpus §2.4 are to be updated in their next revision (flagged here as superseded). No contract/code change — purely classification, naming and UX copy.

Alternatives considered:

  • Keep "Token" family name. Rejected — "NBA" parallels MBA and reads as the honest "non-backed" counterpart; tokens are one member alongside PoW coins and direct-value commodities.
  • Classify gold as PLA (per spec §2.4). Rejected — gold is Layer-1 direct value (unbacked); PLA is a backed claim. Same issuance pattern, different backing.
  • Leave memecoins in PTU. Rejected — most are issuer-minted (managed) → MTU/MTL; only open-mint ones are PTU/PTL.

Cross-references: ADR-0026 (the UI), wiki/asset-landscape.md (Layer 1/2), spec V2.7 §2.4, Knowledgebase/02_structural_taxonomy, design/mockups/avalanche-tab.html (detail sheets encoding this).

ADR-0028 — Real-world peer labels for the eight classes; the consensus-backing test moves extant stablecoins/fiat to NBA

Date: 2026-05-25 Status: Accepted — taxonomy / naming. Extends ADR-0027. Amends spec V2.7 §2.4 (MCA "stablecoin home") and §8 (stablecoin examples) in framing only — the eight-class axes (backing × mint × supply) are unchanged. Drives the Avalanche detail sheets in design/mockups/avalanche-tab.html.

Context: A 2026-05-25 design pass gave each of the eight classes a concise consumer-facing real-world peer label (shown as the Avalanche card subtitle; fuller framing in the clickable detail sheet). Two substantive findings emerged beyond labelling.

Decisions:

  1. Per-class real-world peer labels (card subtitle · fuller framing in detail):

    Class Label Peer / framing
    DEA Hard-Backed Self-collateralised currency — each unit carries its own redeemable BCH; the hardest backing any currency has carried (no custodian, no market peg). Novel — no traditional peer.
    PLA Commodity Capped, BCH-backed certificate (shares gold's issuance pattern, adds a redemption floor metal lacks).
    MCA Bond Rolling / perpetual backed issuance program / reserve note (uncapped).
    MTA Tranche Single capped backed slice — tranche, fixed bond series, Treasury notes & bills, closed-end fund share.
    PTU Game currency Non-Convertible Virtual Currency — open-mint, earned by anyone (play / tap / faucet), inflationary, ~non-convertible. Peers: MMORPG gold (RuneScape, WoW), arcade tickets, faucet / tap-to-earn tokens.
    PTL Mineable Coin Capped, permissionlessly-mined direct-value coin — Bitcoin, gold, silver.
    MTU Meme / Tokens Issuer-minted, uncapped, convertible — memecoins (SHIB / PEPE), governance / utility tokens, and real-world fiat money & "stablecoins" (see #3).
    MTL NFT / Collectibles Issuer-minted, capped — NFT collections, numbered editions, tickets, memberships.
  2. NBA convertibility split. A consumer-facing convertibility lens (FinCEN's convertible vs non-convertible virtual currency) separates PTU — non-convertible, closed-loop, earned from MTU — convertible, traded. It is layered on the mint axis: centrally-issued non-convertible currencies (e.g. V-Bucks) are managed → MTU, not PTU; PTU is the open-mint, anyone-can-earn equivalent.

  3. The consensus-backing test — extant stablecoins & fiat are NBA, not MBA. MBA membership requires the claim-to-BCH relationship to be a consensus invariant (D-2, enforced per-UTXO). Real-world "stablecoins" (USDT / USDC) and fiat assert backing that sits off-chain and unenforceable — the on-chain mint input is ~zero (a tx fee) while the claim can be worth billions; whether the reserve exists is disputable. By the test they are non-backed → NBA (MTU), regardless of issuer assertion.

    • MCA remains the home for a protocol-backed stablecoin — one whose reserve is enforced at consensus (Layer 2b, "the category empty in the existing world", asset-landscape.md). Its real-world peer is therefore a bond program / reserve note, not USDT/USDC.
    • Consequence: the USDT/USDC examples move from MCA → MTU. A Deviant stablecoin is still an A-tier managed MCA; existing stablecoins are NBA peers.

Consequences: glossary.md, wallet-ui-design.md, spec-amendments.md and the Avalanche mockup updated. No contract/code change — classification, naming and UX copy only. UI also adopted the Avalanche nav icon △ and an acid-coloured initial-capitals treatment that reads the class code straight out of the formal name (Permissionless Token Unlimited → PTU).

Alternatives considered:

  • Keep USDT/USDC as MCA examples. Rejected — their backing is not a consensus invariant; listing them as MBA peers re-blurs the MBA↔NBA line ADR-0027 drew.
  • Label PTU "Memecoin". Rejected — memecoins are usually issuer-minted → MTU (ADR-0027). PTU is the open-mint, earned-by-activity non-convertible currency.
  • Put V-Bucks in PTU. Rejected — V-Bucks is centrally issued (managed → MTU); only the open-mint equivalent is PTU.

Cross-references: ADR-0026 (UI), ADR-0027 (NBA naming / direct-value vs backed), asset-landscape.md (Layer 2a soft-backed vs 2b strict), spec V2.7 §2.4 + §8, design/mockups/avalanche-tab.html.


ADR-0029 — Escalating founder fee as an opt-in, pre-committed, capped schedule (supply-triggered); the "franchise" variant

Date: 2026-05-26 Status: Accepted — feasibility validated on chipnet (snapshot 0058), confirmed by the user 2026-05-26. Supply trigger is canonical; escalating + decreasing sibling templates registered in D-1 (snapshot 0059). Spec amendment pending alongside ADR-0011's.

Context: The flat immutable founderFeeBps in DeviantOpenMintFT and the discretionary updateFeeRate of ADR-0011 are the only two fee shapes today. The user asked whether a founder fee could escalate with adoption — framed as a franchising model: a low entry fee, rising as the asset grows, "and if the founder wants more income from growth, that's their own business."

Snapshot 0058 tested three escalation triggers on live chipnet (fork of DeviantOpenMintFT, modelled on the depletion read proven by DeviantBondingCurveFT / ADR-0015):

Trigger Mechanism Result
1 — Supply feeBps = min(baseFeeBps + (issuedSoFar / supplyStepSize)·feeStepBps, maxFeeBps) ✅ compiled, 5 mints stepped 10→50 bps, zero new Script primitives
2 — Tx-count counter in mutable NFT commitment; int(commitment) / bytes8(n) ✅ works (commitment mutation does not change the P2SH32 address), but escalation is strictly serial through the one reservoir UTXO
3 — Block-height tx.time arithmetic ⚠️ direct tx.time - genesisHeight does not compile (tx.time is not a readable int); the require(tx.time >= claimedHeight) workaround compiles and ran clean on chipnet (V2). Caveat: it binds a lower bound only, so a hostile self-minter can pick the minimum height — fine for cooperative/honest-path mints, not as a guard against an adversarial claimer.

Two findings reframe the economics:

  • The franchising analogy half-holds. "Later entrants pay more to join a proven, de-risked system" is exactly what supply escalation produces and is economically defensible (early claimers are paid for bearing launch risk). But "costs rise with growth" does not hold mechanically — a covenant has ~zero marginal cost per claim. The honest justification is value-capture on proven success, on terms fixed and disclosed at genesis — not cost-recovery.
  • Escalating the fee is materially safer than escalating the rate (the DRA / ADR-0015 case). The supply trigger keeps backingRateSats a flat constant, so every UTXO is still fully pegged at one rate and the ASM stays exact and front-run-free (UER = rateA/rateB is unchanged — a fee output is orthogonal to the rate). A DRA, by contrast, turns every swap into a dynamic quote with slippage. The escalating-fee asset remains a fixed-rate MBA.

Decision:

  1. Sanction an opt-in escalating-fee variant; the founder chooses the fee shape at genesis. Three mutually exclusive shapes, all bounded by the same immutable hard cap:

    • Flat — DeviantOpenMintFT (one number forever).
    • Adjustable — ADR-0011 updateFeeRate (discretionary, post-hoc, within maxFeeRate).
    • Escalating — this ADR (pre-committed deterministic schedule, within maxFeeBps). The escalating shape is more holder-protective than discretionary adjustment in one axis (the entire future fee path is known and capped at genesis — no surprises) and less flexible in another (it cannot react to changed conditions). The founder picks the trade-off. This does not retract ADR-0011; it adds a third option beside it.
  2. Canonical mechanism = SUPPLY trigger (DeviantEscFeeSupply); all three triggers are technically viable. Supply is the recommended default — it maps directly to the user's "adoption and quantity" intent, reuses the chipnet-proven depletion read, adds no new Script primitives, keeps the asset a fixed-rate MBA (ASM-exact), and is fully enforceable against any claimer. Constructor: (tokenCategory, backingRateSats, initialReservoirFtSupply, baseFeeBps, feeStepBps, supplyStepSize, maxFeeBps, founderLockingBytecode). The other two triggers are available, not rejected (see point 7).

    Decreasing sibling (DeviantDescFeeSupply). The supply-stepping mechanism is sign-agnostic: the decreasing variant SUBTRACTS feeStepBps per step, clamped at an immutable floor minFeeBps, so a founder can pre-commit at genesis to lowering the fee as adoption grows. This is the startup / launch-incentive shape (early adopters bear the highest fee while the asset is unproven; it ratchets down as supply is taken) and is the deterministic, transparent realisation of ADR-0011's "lower fees over time" intent. Constructor swaps maxFeeBps → minFeeBps; baseFeeBps is the immutable START (highest) fee, bound ≤ the 5% ceiling at genesis. Shipped as a second canonical template beside the escalating one.

  3. "Fee stays low" is enforced by the cap, not by trust. The protocol enforces only 0 ≤ feeBps ≤ maxFeeBps ≤ 500 (the existing spec §2.1 / ADR-0011 5% ceiling, untouched). An escalating fee can therefore never exceed what a flat-fee founder could already charge from day one — escalation is a path to the existing ceiling, not a new ceiling. Recommended (non-enforced) defaults bias low: small baseFeeBps, gentle feeStepBps, generous supplyStepSize, maxFeeBps well under 500. The schedule's steepness is the founder's business (ADR-0008: the wallet verifies and discloses; the market decides) — the ceiling is the holder's protection.

  4. Transparency is mandatory (inherit ADR-0015 / T-003). The only fraud here is masquerade — publishing a single low fee while charging a rising one. A sanctioned escalating-fee asset MUST publish its full schedule (baseFeeBps, feeStepBps, supplyStepSize, maxFeeBps) in BCMR, and the wallet MUST render current fee, next-step fee, and the formula — never a single fake number. An escalating-fee contract that publishes a flat fee is a T-003-class attack and D-1 flags it non-canonical.

  5. D-1 multi-template, not a metadata field. Register DeviantEscFeeSupply bytecode as a new canonical template constant (same pattern ADR-0015 used for DeviantBondingCurveFT). assetClass-aware D-1 reconstructs the P2SH32 address from the published fee-schedule params; mismatch → "non-standard, fee schedule not guaranteed." The asset stays in the DEA / MBA family (flat backing rate) — this is a fee-policy template variant, orthogonal to the rate class.

  6. Deviant takes nothing extra. Like ADR-0021, this is a founder-configured parameterisation; Deviant inserts no mandatory cut (ADR-0008 anti-rent-authority). The escalating fee is the founder's income, by their own pre-committed schedule.

  7. All three triggers are available; choose by enforcement model, not by capability. Each compiled and (for supply/tx-count/block-height-V2) ran on chipnet (snapshot 0058):

    • Supply — recommended default; fully enforceable, ASM-exact, simplest. Canonical template.
    • Tx-count (NFT commitment) — fully enforceable; escalates per claim event rather than per unit, so it suits a "per-participant" semantic. Cost: mutable-commitment surface and strictly serial claims through the one reservoir UTXO. Available as a registered variant when that semantic is wanted.
    • Block-height (tx.time, V2 form) — escalates with elapsed blocks (calendar/"time-since-launch" semantic). Works for cooperative or founder-operated mints, but require(tx.time >= claimedHeight) is a lower bound only, so a hostile self-minter can underpay by passing the minimum height. Use only where the claimer is not the adversary; surface the caveat. tx.time also remains valid for one-shot time gates regardless.

Alternatives considered:

  • Leave fees flat/adjustable only. Rejected — forecloses a legitimate, now-validated pricing primitive the user wants; discretionary updateFeeRate can already raise fees post-hoc but without the genesis-time transparency and pre-commitment an escalating schedule gives holders.
  • Escalate the rate instead of the fee (reuse the DRA). Rejected for this purpose — escalating the rate breaks ASM exactness (turns swaps into slippage-bearing quotes) and changes the peg; the user's goal is founder income, which is the fee, and escalating the fee leaves the peg and ASM untouched.
  • Tx-count (NFT commitment) as the canonical mechanism. Deferred — it works, but it adds mutable-commitment surface and a strict serial-claim ordering for no benefit over supply when the goal is "fee rises with quantity/growth."
  • Block-height escalation. Rejected — not adversarially enforceable on BCH (see Context, trigger 3).
  • Uncapped escalation ("founder's business" taken literally). Rejected — the hard cap is the holder-protection invariant (ADR-0011 point 3, one-way at genesis); founder autonomy governs the schedule within the cap, never the cap itself.

What this ADR does NOT resolve:

  • Registering the tx-count and block-height-V2 templates in D-1 (only supply + its decreasing sibling are wired in snapshot 0059). Add them if/when a use-case selects those semantics.
  • The BCMR schema extension for the fee schedule (feeSchedule: { base, step, stepSize, max }) and the wallet's fee-curve rendering UI (pairs with the DRA curve UI from ADR-0015 — design together).
  • Whether the Avalanche genesis wizard exposes escalating-fee as a third Fees-step mode (ADR-0011 currently specifies the two-knob slider).
  • Interaction with the ADR-0021 referral split (escalating fee × referrer share — mechanically a larger fee output to split; confirm output layout when both are present).

Implementation pointers: contracts/DeviantEscFeeSupply.cash + contracts/DeviantDescFeeSupply.cash promoted to canonical templates; bytecode-hash constants (ESC_FEE_SUPPLY_*, DESC_FEE_SUPPLY_*) + feeSchedule-driven reconstruction added to src/verify/canonical.ts / verifyContractCanonical() (mirrors the BONDING_CURVE_* wiring from ADR-0015), with new CanonicalTemplate values 'escalating-fee' / 'decreasing-fee'; artifacts embedded via scripts/compile-contracts.mjs's EMBEDDED set; spec amendment alongside ADR-0011's §5.1/§5.2 changes (see wiki/spec-amendments.md). Chipnet evidence: snapshot 0058 (escalating, scripts/chipnet-escfee-supply-test.mjs) + snapshot 0059 (decreasing, scripts/chipnet-descfee-supply-test.mjs). Cross-references: ADR-0011 (fee model — third shape added beside it), ADR-0015 (multi-template D-1, mandatory transparency, T-003 masquerade), ADR-0008 (verify-not-authority, plurality), ADR-0021 (founder-fee parameterisation / referral split), wiki/protocol-vs-wallet-enforcement.md (D-1 load-bearing).

ADR-0030 — Collateralized binary share market (the "Polymarket model") as Family D; split/merge mechanism, collateral as a constructor parameter

Date: 2026-05-27 Status: Accepted — design only. No contract shipped. Full design in wiki/event-prediction-and-tote.md §2.3; build roadmap item 10. Recognises a fourth event-market contract family beside A/B/C (ADR-0024).

Context: A design conversation (2026-05-27) examined how real blockchain prediction platforms operate and whether Polymarket is "just a tote." It is not. Polymarket is a CLOB trading fixed-value outcome shares, not a pari-mutuel pool: each share has a terminal value of 1 C, fixed at trade time and freely tradable, with no dilution from later entrants. Three findings drove this ADR:

  1. The split/merge collateral mechanic answers "where does the winners' money come from / who is the counterparty": from the losers, pre-locked at mint. 1 C splits into 1 YES + 1 NO (collateral locked); merge reverses; YES + NO ≡ 1 C by consensus invariant. Winners redeem 1:1 for C; losers get 0. Fully collateralized, zero-sum, non-custodial — no house bankroll. The counterparty to a YES holder is the NO holders; trades are peer-to-peer.
  2. Early exit is a consequence of fixed-terminal-value, not a tote add-on. Pricing a tote position for resale requires fixing what it pays; fixing that = split/merge = this family. So "a tote with proper exit" is Family D — tote (C) and share market (D) are two points on one axis (when/whether a position's terms freeze), not variants of each other.
  3. No new ASM swap shape. Buying YES with BCH is redeem bits → mint MBA → MBA⇄YES; the core swap is always MBA↔MBA, so BCH-collateral markets trade through existing ASM. Volatility is explicitly rejected as a collateral-comparison axis — it is bidirectional and numéraire-relative; "stable" means relative to the bettor's own unit, and Deviant's audience is BCH-native.

Decision:

  1. Recognise Family D — collateralized binary share market — alongside A (player match), B (event-prediction book), C (pari-mutuel tote). MBA-denominated-or-BCH, non-custodial, oracle-settled (checkDataSig, reusing Family B's message format + the ADR-0024 accountable-named-oracle model). Family D is the CLOB/share-market generalization of B.
  2. Mechanism = split / merge + oracle-gated redeem. The covenant is the sole minter of the YES/NO categories so the YES+NO ≡ C invariant holds; redemption is 1:1 (hence no pro-rata integer-division residual — a structural win over the tote's floor/dust handling).
  3. Collateral is a constructor parameter; three configs to chipnet-validate — (1) pure BCH (bare CashToken shares, no MBA — proves the mechanic, one category/single vault, mirrors snapshot 0048); (2) BCH+MBA (BCH backing, MBA-class shares); (3) MBA-only (mirrors snapshot 0049, two co-spent UTXOs). Build pure-BCH first, MBA as production default (ASM-native, winnings stay in-economy) — the same 0048→0049 order the totes used.
  4. D-1 multi-template + mandatory transparency. Each shipped config registers its canonical bytecode (ADR-0015/0024 pattern); the wallet verifies an on-chain market against published params (collateral asset, oracle pubkey, event, close height) before a trader co-signs. Fee, if any, is a peg-preserving slice on split/redeem (ADR-0017); zero-rake is both the cleanest legal posture and the most trustless — recommended default.
  5. Regulatory bucket distinct from the tote. A fixed-price tradable binary claim reads as an event contract / derivative (the Polymarket/Kalshi regulated-exchange framing), not pari-mutuel gambling (Family C). Still R-016-blocking for a mainnet pilot, still counsel-required — but a different sub-bucket. (Polymarket is no longer zero-fee as of 2026-03-30 Fee Structure V2.)

Alternatives considered:

  • Add early exit to the pari-mutuel tote instead of a new family. Rejected as a mischaracterization — done properly it converges on split/merge, i.e. it is Family D; done improperly (stake refund / unrealized provisional value) it breaks the pool. Better recognised as its own family.
  • Mandate USD-pegged-stablecoin collateral (copy Polymarket's USDC). Rejected — installs USD as numéraire for a BCH-native audience; "stable" is relative to the bettor's unit. BCH/MBA collateral is the default; a stable DEA is an opt-in for USD-scored markets only.
  • BCH-only (skip MBA). Rejected as the default but kept as the proving build — MBA is ASM-tradable and keeps value in-economy; BCH is the minimal, issuer-free first step.
  • State-NFT accounting (as the totes use). Likely unnecessary — YES+NO=C is an invariant, not stored state, so pure-FT accounting may suffice; to confirm at build.

What this ADR does NOT resolve: the share-token genesis + sole-minter covenant construction; whether any state NFT is needed; partial split/merge semantics; the exact vault layout for each of the three collateral configs; secondary-market price discovery (off-chain order book design); and the same resolveOracle message-format / signing details left open by ADR-0024.

Implementation pointers (when built): new contracts/ split/merge + redeem templates (one per collateral config) + contracts/dist/ artifacts; canonical entries in src/verify/canonical.ts; chipnet scripts mirroring scripts/chipnet-tote2-test.mjs (BCH) / chipnet-tote2-mba-test.mjs (MBA); trading via ASM + the WalletConnect/CashConnect connector co-sign (ADR-0024 connector note). Cross-references: ADR-0024 (families A/B/C + result-oracle accountability), ADR-0025 (obligor-free MBA / denomination), ADR-0015 + ADR-0018 (multi-template D-1), ADR-0017 (peg-preserving fee), ADR-0008 (verifier-not-authority), R-016 (regulatory), wiki/event-prediction-and-tote.md §2.3.


ADR-0031 — Non-custodial synthetic-USD stablecoin as the continuous-oracle generalization of Family D (the "Deviant hedge" / AnyHedge-class instrument)

Date: 2026-05-27 Status: Accepted — design; settlement core chipnet-validated (snapshot 0065). Generalizes ADR-0030 (Family D) from a binary settlement oracle to a continuous price oracle. Recognises a distinct asset instrument; does not add a new MBA class (see Decision 6). contracts/OraclePricedVault.cash (npm run chipnet:oracle-vault) proved the oracle-priced open/settle mechanism on live chipnet in a simplified one-sided value-lock vault form (LP reserve as counterparty): open margin=10000 @ p0=100 → nominal 100; forged-oracle settle rejected on-chain (:64, checkDataSig); settle @ p1=150 paid nominal*p1 = 15000 (value-locked, +5000), reserve 30000→15000, position burned. Still design-only for the production form: the full bilateral STABLE/LONG split (Decision 1), the mandatory published protection-range cap (Decision 2), and an attestation freshness bound (the spike has none — a stale favorable p0 is replayable, same class as snapshot 0058's block-height freshness gap) are not yet built. Not production-ready.

Context: A design conversation (2026-05-27) asked whether the project's primitives can produce a USD stablecoin. Working through stepped fees (ADR-0029), the DRA bonding curve (ADR-0015), oracle-indexed rates, and Family D (ADR-0030) surfaced one unifying constraint and one clean answer.

The unifying constraint — minting price ≠ maintained peg. Every "set the price at mint" idea (a USD-floating fee, a USD-driven bonding curve, an oracle-indexed mint rate) founders on the same fact: in the self-backed MBA model the backing bits are frozen inside the UTXO at mint. Controlling entry price sets the opening exchange rate; it does nothing to hold a peg afterward, because the minted unit's USD value drifts the instant BCH/USD moves. Maintaining a USD peg post-mint requires continuously rebalancing volatile collateral against a fixed target — and that is, by definition, either liquidation (overcollateralized/Maker model) or a counterparty taking the other side of the volatility (the hedge model). There is no third mechanism.

The trilemma. {USD peg · no custodial obligor (trustless) · no liquidation} — you may have at most two:

  • MBA (this project's flagship): drops the USD peg (pegs to BCH instead) → keeps trustless + no-liquidation. The Layer 2b position (asset-landscape.md).
  • Custodial MCA: keeps USD peg + no liquidation → reintroduces a soft obligor.
  • This ADR: keeps USD peg + no custodial obligor → pays with an oracle, a counterparty, a bounded protection range, and a maturity.

The key insight — Family D is a stablecoin generator. Family D (ADR-0030) locks collateral C and splits it into complementary claims (1 C → 1 YES + 1 NO, YES + NO ≡ C), oracle-settled, winners redeem 1:1, losers pre-locked at mint — zero-sum, fully collateralized, non-custodial, no house. Replace the binary event oracle with a continuous BCH/USD price oracle and split C (BCH or a BCH-MBA) into two asymmetric legs instead of two symmetric outcome shares:

  • a capped STABLE (hedge) leg — entitled at settlement to a fixed USD-amount's worth of BCH ($X / price_settle, capped by total locked collateral); and
  • a residual LONG leg — takes whatever BCH remains (leveraged BCH exposure).

At settlement the oracle reads BCH/USD; the covenant pays the STABLE leg min($X / price_settle, C) and the LONG leg the remainder. The STABLE-leg holder held a USD-stable instrument for the term, fully collateralized and non-custodial. This is exactly the AnyHedge construction (General Protocols). Family D's split/merge with a binary oracle and the hedge/long split with a continuous oracle are the same covenant family — the project already designed the more general primitive in ADR-0030.

Decision:

  1. Recognise the synthetic-USD (hedge-class) instrument as the continuous-oracle generalization of Family D. Mechanism: a covenant that is the sole minter of a complementary STABLE/LONG category pair, locking collateral C at mint so STABLE_payout + LONG_payout ≡ C always holds; an oracle-gated settle distributes C by the settlement price. It is non-custodial (collateral lives in the covenant, no issuer obligor) — the obligor-free property (ADR-0025) is preserved structurally, though via a counterparty rather than intrinsic per-unit backing.
  2. A bounded protection range is mandatory and must be a published, verified parameter. Unlike Family D's binary outcomes (payout bounded at 1 C), a price is unbounded, so the STABLE leg's $X / price claim can exceed the locked C if BCH falls far enough. The covenant caps the STABLE payout at the available collateral; below the implied floor price the peg breaks on the downside. The leverage/range parameter (equivalently the LONG leg's low-liquidation price) is set at mint, published in BCMR, and rendered openly by the wallet — never hidden behind a "$1.00" display. Concealing the range is a T-003-class masquerade and D-1 must flag it (same posture as the DRA curve, ADR-0015, and the Family-D params, ADR-0030).
  3. Settlement model: fixed-maturity first; perpetual rollover deferred. v1 contracts settle at a close height/time via checkDataSig on the ADR-0024 accountable-named-oracle message (reuse Family B's format verbatim). Optional early settlement at the range boundary (a liquidation analog that protects the STABLE leg before collateral is exhausted) and perpetual rollover (re-hedging into a fresh term) are deferred to a follow-on ADR — they add keeper/oracle-liveness assumptions that the fixed-maturity form avoids.
  4. Counterparty matching is off-chain; the chain only enforces the covenant. A STABLE leg exists only when a LONG counterparty funds the other side (peer-to-peer, like Family D's YES↔NO). Order-book / matching design is out of scope here (shared with ADR-0030's secondary-market item). The covenant neither needs nor provides a matching service — keeping Deviant the verifier, never the authority (ADR-0008).
  5. D-1 multi-template + mandatory transparency. Each shipped config registers canonical bytecode (ADR-0015/0024/0030 pattern). Before a trader co-signs, the wallet verifies the on-chain market against published params: collateral asset, oracle pubkey, peg target ($ amount), protection range / leverage, and settlement height. Any divergence, or a single fake "$1.00 peg" display without the range, → non-canonical.
  6. This is NOT a new MBA class, and the STABLE leg is NOT an MBA. Its value rests on an oracle + a counterparty + a bounded range, not on intrinsic consensus-enforced per-unit backing. Under the V2.7 taxonomy and asset-landscape.md, the MBA family is the obligor-free Layer 2b position; the hedge-class STABLE leg is a distinct, oracle-dependent instrument that must be labelled as such (its own asset-class chip + verification axis). Calling it an MBA would relaunder the exact soft/strict distinction ADR-0025 and ADR-0028 drew. C, the collateral, may be a BCH-MBA; the STABLE leg minted against it is not.
  7. Fee posture. If any, a peg-preserving slice on split/settle (ADR-0017). Zero-rake is the cleanest legal and most trustless default — recommended, mirroring ADR-0030.

Alternatives considered:

  • USD-floating founder fee (ADR-0029 reframed). Rejected — the fee is a supply-triggered skim, orthogonal to unit value; floating it cannot create or maintain a peg.

  • USD-driven bonding curve (ADR-0015 reframed) / oracle-indexed mint rate. Rejected as a stablecoin mechanism — sets entry price only; minted units freeze their BCH and drift, and DRA units are vintage-stamped (not fungible at one $-rate). Useful for fair entry pricing, not for a maintained peg.

  • Overcollateralized vault + keeper liquidation (Maker/DAI model). Viable but rejected as the v1 default — continuous margin-call liquidation needs a live keeper market and an always-on oracle; the fixed-maturity hedge form delivers the peg with weaker liveness assumptions and no keeper.

  • Custodial MCA USD stablecoin. Already available (ADR-0021 stablecoin frame); kept as the soft-peg option. This ADR is the non-custodial alternative, not a replacement.

  • Treat the STABLE leg as an MBA / stable DEA. Rejected — see Decision 6; it is oracle- and counterparty-dependent, categorically Layer 2a-strict-enforced, not obligor-free Layer 2b.

  • Oracle-indexed high-frequency re-minting MBA (2026-05-28 design probe). Proposal: mint each unit at the current oracle rate bits-per-$1, with a tolerance band (e.g. ±5%) that triggers burn-and-re-mint when crossed. The probe collapses into two coherent sub-designs, neither of which dominates any corner of the four-option rail matrix (ADR-0032 / wiki/usd-rail.md):

    • Variant A — drop the recalibration ("oracle-stamped transit receipt"). No burn-and-re-mint; the "USD" label is a historical price stamp at issuance, not a maintained property. Honest if labelled. Not a stablecoin — it is BCH with a price tag and drifts exactly like any fixed-backing MBA. Has a narrow legitimate niche (audit-trail-bearing transit receipts where the dollar-value-at-send must be cryptographically recorded), captured as Option D in ADR-0035 — not as a peg instrument.
    • Variant B — keep the recalibration with a passive reservoir. The recalibration delta (bits injected when BCH falls, extracted when it rises) must be funded by someone; tracing where the BCH comes from yields exactly one answer — a pool of participants implicitly short USD / long BCH. This is the AnyHedge long leg smeared across a passive reservoir, i.e. structurally a Frax / Maker-PSM-shape stablecoin with the well-documented failure modes of those designs (reservoir drain in sustained downtrends; redemption restriction / stability-fee hikes / socialized losses as defensive measures). Strictly weaker than the disciplined bilateral hedge of Decision 1 above (no per-position long-side accountability; pool dynamics rather than explicit counterparty matching) and than a clean custodial MCA (operationally simpler for the same UX). Rejected as a peg mechanism.

    The unifying reason both sub-designs fail: the MBA's freeze-backing-at-mint invariant means minting price ≠ maintained peg. Setting the rate at mint calibrates the opening exchange rate; recalibrating after the fact requires external BCH flow, which is either holder self-funding (no stability — a pure relabel of the holder's own BCH) or counterparty/pool funding (= the hedge model, already Decision 1). The trilemma {USD peg · no obligor · no liquidation} does not yield to clever framing. Recorded explicitly so the door is closed for future readers — the probe is productive (it forces out the trilemma from a new angle and validates Decision 1), but it does not produce a new instrument. The taxonomic placement of Variant B follows ADR-0033's CDP analysis: the reservoir's position would be a secured liability (rung 2), the circulating "stable" unit a market-priced PTU (rung 3) — not an MBA.

What this ADR does NOT resolve: the exact split/settle covenant construction and whether a state NFT is needed (shared with ADR-0030); the protection-range/leverage encoding in constructor args + BCMR; partial-fill and counterparty-matching / order-book design; early-settlement and perpetual-rollover semantics (deferred follow-on ADR); oracle liveness + dispute handling beyond ADR-0024's accountable-named-oracle model; and the regulatory bucket (a non-custodial synthetic-USD derivative is R-016-blocking and almost certainly a more sensitive sub-bucket than Family D — counsel-required before any mainnet pilot).

Implementation pointers (when built): generalize the ADR-0030 split/merge templates to an asymmetric STABLE/LONG split with a continuous-oracle settle; new contracts/ + contracts/dist/ artifacts; canonical entries in src/verify/canonical.ts with the published-range transparency guard; chipnet driver mirroring the tote/Family-D oracle-settlement scripts; co-sign via the WalletConnect/CashConnect connector (ADR-0024/snapshot 0061). Recommended build order mirrors 0048→0049/ADR-0030: pure-BCH collateral first to prove the asymmetric split + price-settlement, then BCH-MBA collateral as the production default (ASM-native, value stays in-economy). Cross-references: ADR-0030 (Family D split/merge — the binary special case), ADR-0024 (accountable-named-oracle + checkDataSig message format), ADR-0025 / ADR-0028 (obligor-free MBA vs soft-backed; why the STABLE leg is not an MBA), ADR-0021 (custodial-stablecoin frame, the soft-peg sibling), ADR-0015 / ADR-0018 (multi-template D-1 + transparency-or-masquerade), ADR-0017 (peg-preserving fee), ADR-0008 (verifier-not-authority), R-016 (regulatory), wiki/asset-landscape.md (Layer 2a/2b), wiki/event-prediction-and-tote.md §2.3 (Family D).

ADR-0032 — Expiring / maturing MBA: a time-boxed self-backed instrument that reverts to base BCH at maturity

Date: 2026-05-27 Status: Accepted — chipnet-validated spike (snapshot 0066). contracts/ExpiringMBA.cash (npm run chipnet:expiring-mba). A minimal feasibility build; a finely-denominated / MBA-vault-collateralized production form and a transfer path are follow-ons.

Context: A design conversation (2026-05-27) asked for an MBA/DEA that expires after a period, automatically swapping back to base units. Two facts shaped the answer.

  1. Nothing is automatic on a UTXO. A UTXO is inert until spent; a covenant cannot self-execute at a deadline. "Automatic expiry" can only mean a time gate (tx.time >= expiryHeight, OP_CHECKLOCKTIMEVERIFY) plus a permissionless conversion: after maturity anyone may trigger the revert, and the covenant fixes the destination (the owner). Deterministic and unstoppable, but a party (holder / issuer / watcher bot) submits the tx and pays the fee. This is the same "no scheduler" reality as ADR-0029's block-height trigger.
  2. A plain-address MBA cannot be made to expire. A claimed Deviant MBA is a bare FT UTXO at the holder's P2PKH (ADR-0025: redemption is the holder's own plain spend) — no covenant, so nothing can force maturity. An expiring MBA must therefore be held under a maturity covenant (a per-holder address), trading the plain-spend redemption model for enforceable expiry.

Decision:

  1. Recognise the expiring MBA as a covenant-held, time-boxed self-backed instrument. Backing is intrinsic (the amount * rate bits live in the covenant UTXO). Two paths: redeem (owner checkSig, available anytime) releases the backing to the owner and burns the FT; expire (permissionless, gated by tx.time >= expiryHeight) reverts the backing to base BCH always to the fixed owner, so anyone may trigger maturity without being able to redirect the funds.
  2. "Automatic" = permissionlessly triggerable, not self-executing. Documented as such; a watcher/wallet sweep is the practical trigger. Validated on chipnet: a FAR-expiry position rejected a premature expire on-chain (ExpiringMBA.cash:39, CLTV) and was redeemed early by the owner; a MATURE position was expired by a third party (not the owner) with the backing landing at the owner — the permissionless auto-revert.
  3. The destination is fixed at mint. expire pays only ownerPkh; a triggerer cannot capture the funds (they only pay the fee). This is the "revert to the holder" semantic (a self-dissolving wrapper). An issuer-reclaim-on-expiry voucher (use-it-or-lose-it) is the mirror variant (expire → issuer) and a documented follow-on.

Alternatives considered:

  • Make a plain-address MBA expire. Impossible — no covenant on a bare FT UTXO to enforce the gate (point 2 above).
  • Truly automatic on-chain expiry (a self-spending UTXO). Impossible on BCH — no scheduler; reframed as permissionless triggering.
  • Issuer reclaims unredeemed backing at expiry (voucher model). Valid and useful, but a different product (expire → issuer, with use-it-or-lose-it semantics); recorded as the mirror variant, not built.

What this ADR does NOT resolve: a transfer path (re-locking to a new owner's maturity covenant — the current spike is single-owner); fine denomination (ADR-0025) for high-value units; collateralizing in a BCH-MBA vault rather than raw bits (ADR-0030 config (c) pattern) so the reverted value is itself a backed asset; early-expiry/rollover semantics; freshness is a non-issue here (CLTV is consensus time, unlike the oracle-price freshness gap of ADR-0031); D-1 canonical template + verifier; regulatory (R-016 — a bearer instrument with a maturity).

Implementation pointers: contracts/ExpiringMBA.cash + contracts/dist/; driver scripts/chipnet-expiring-mba-test.mjs; the tx.time >= expiryHeight gate mirrors the tote betDeadline/sweepDeadline CLTV usage; FT-burn-to-BCH via allowImplicitFungibleTokenBurn. Cross-references: ADR-0025 (obligor-free MBA, intrinsic backing, plain-spend redemption — the model this time-boxes), ADR-0030/0031 (covenant-held collateral + oracle/settlement families), ADR-0029 (the "no scheduler" / block-height reality), ADR-0024 (CLTV deadline patterns in the tote), ADR-0008 (verifier-not-authority), R-016 (regulatory).

ADR-0033 — CDP / collateralized stablecoin: the collateral backs the DEBT, not the token; the circulating token is an unbacked PTU (split from custodial MTU)

Date: 2026-05-27 Status: Accepted — taxonomy refinement. Extends ADR-0027 (NBA/MBA, direct-value vs backed-claim), ADR-0028 (the consensus-backing test; stablecoins → MTU) and ADR-0031 (oracle-dependent synthetics). No contract/code change — classification + the structural test only.

Context: A 2026-05-27 conversation worked through how a collateralized-debt-position (CDP) stablecoin (MakerDAO/DAI-class) maps onto the eight-class taxonomy. CDP = the vault where a borrower locks collateral and draws debt; the "stablecoin" is that debt made transferable. It shares the permissionless-mint surface of an MBA ("bring backing, mint an asset"), which invites mis-classification as a backed asset. Pressing on what is actually backed dissolves that.

Decisions:

  1. The collateral backs the DEBT POSITION, not the circulating token. The locked collateral secures the borrower's obligation (repay or be liquidated). A holder of the token has no redemption claim on that collateral — in normal operation 1 unit entitles you only to (a) repay a debt position, or (b) sell it on a market at a market-determined price. So a CDP system is really two instruments: a collateralized debt (the vault — a liability) and an unbacked, market-priced token (the "stablecoin") floating on top. The collateral supports the token's price only indirectly (solvency → supply → arbitrage), never as a per-token claim.

  2. The circulating token is NBA · Permissionless · Unlimited → PTU — taxonomically a virtual currency, not a backed asset. It fails the consensus-backing test (no per-unit invariant, ADR-0028) and confers no holder-side redemption, so it is unbacked in substance; its "stability" is an emergent, breakable market outcome (empirical record: UST → $0.20, asset-landscape.md). The only backing that ever reaches a holder is a system-level, emergency-only, pro-rata claim (e.g. Maker's Emergency Shutdown) — categorically unlike MBA per-unit always-on redemption, and not enough to confer backed-asset status.

  3. Mint-axis split: CDP stablecoin (PTU) ≠ custodial stablecoin (MTU). ADR-0028 filed "extant stablecoins" into MTU; that holds only for the managed/custodial kind (USDC/USDT — exclusive issuer mints). A CDP stablecoin is permissionless-mint (anyone opens a vault) → PTU. "Stablecoin" therefore spans two NBA cells, separated by the mint axis:

    • USDC / USDT — managed, off-chain reserve → MTU.
    • DAI / CDP — permissionless, on-chain debt-collateral → PTU.
  4. The soft-backing (Layer 2a) attaches to the DEBT, not the token. An interim framing in the same conversation loosely called a CDP stablecoin a "soft-backed PTU." Corrected: the soft, conditional, on-chain collateralization is a property of the vault (a collateralized liability), not of the token. The vault is Layer-2a soft-backed debt; the token is unbacked (PTU). Do not attribute the collateral's backing to the circulating unit.

  5. Add the unilateral-redemption discriminator to the structural test (asset-landscape.md). Beyond "is the rate a consensus invariant?", ask: can the holder realize value in base units unilaterally — by their own action, with no market, oracle, or counterparty? MBA → yes (spend the UTXO). CDP token → no (sell on a market, or be the borrower repaying). A "no" places the asset on the soft/NBA side regardless of any collateral elsewhere in the system.

  6. The three-rung backing ladder (refines the MBA/NBA binary):

    • Rung 1 — intrinsic / unconditional: MBA (Layer 2b). Backing in your own UTXO, per-unit consensus invariant, self-redeems to base.
    • Rung 2 — collateralized debt (soft, conditional): the CDP vault (Layer 2a). On-chain collateral securing a liability — not a circulating asset's backing.
    • Rung 3 — unbacked / market-priced: the CDP token (PTU), custodial stablecoins (MTU), faucet/game currencies (PTU). Value is demand/solvency-determined and breakable. The token sits at rung 3; the debt sits at rung 2. The consensus-backing binary collapses rungs 2–3, which is why the debt-vs-token split must be stated explicitly.
  7. The volatility paradox (risk exposure inverts the marketing). A consequence worth stating because it inverts the intuition the "stablecoin" label invites: a CDP stablecoin is overcollateralized AND the most volatility-exposed of the three rungs. An MBA makes no USD-peg claim, so there is no peg to break — only honest BCH exposure the holder always saw; a CDP token does claim a peg, and that peg is exactly the property a crash threatens (Black Thursday, TerraUSD). Overcollateralization protects the system's solvency, not the holder's promised price — and the holder paradoxically carries the most volatility risk at the moments the stablecoin label would suggest otherwise. This compounds with Decision 5: the holder cannot exit to base units without a market, and the market is exactly where the volatility lives.

  8. "Secured" is not "backed", and the two sides are decoupled. Refining Decisions 1 and 4: the collateral is attached to the borrower's debt obligation to enable liquidation on default — it secures the enforcement of a liability, not a redeemable unit. "Backing" in the MBA sense means the unit is the value (per-unit, consensus-enforced, redeemable). A secured loan and a backed asset are categorically different shapes. And there is no per-unit thread between any token and any vault: a CDP-stablecoin unit carries no claim path to the borrower whose vault minted it; the token circulates independently of the debt that birthed it. What links them is only aggregate (total supply ≤ collateralization-ratio × sum of debts). Per-unit, neither side is backed: the token floats unbacked (rung 3) and the debt is secured but not backed (rung 2). The three-rung label "collateralized debt (soft)" is therefore more precisely read as secured liability — not a backed asset.

  9. Leverage and the value-flow asymmetry (the systemic critique). A dynamic consequence of Decisions 7–8 worth recording for risk framing: CDPs are leverage instruments. Each vault is a leveraged-long position on the collateral; the aggregate is a large levered position whose forced unwinds (liquidations) amplify drops into cascades — CDPs raise the volatility of the very asset they depend on. Under that volatility, value flows asymmetrically across three actors and the holder never captures any of it: borrowers capture collateral upside while solvent and bear the full downside; keepers buy liquidated collateral at a discount + penalty and capture the recovery in a drop-then-recover sequence; stablecoin holders capture nothing from collateral volatility and absorb the tail (peg break). The collateral never lands in the holder's possession (Decision 8 — decoupling). In every scenario the upside flows past the holder — the asset's "stability" is achieved by routing volatility elsewhere, not by eliminating it.

Alternatives considered:

  • Classify a CDP stablecoin as MBA (collateral is on-chain). Rejected — the collateral backs the debt, not the token; no per-unit invariant; no holder redemption. Fails every backing test.
  • Classify it identically to USDC (MTU). Rejected — the mint axis differs (permissionless vs managed): DAI → PTU, USDC → MTU.
  • Call the token a "soft-backed PTU." Rejected as imprecise — the soft-backing is the vault's; the token is unbacked.

Consequences: asset-landscape.md gains a Layer-2a "collateralized-debt (CDP) stablecoin" note (the debt-vs-token split) and the unilateral-redemption discriminator in the structural test; glossary / Avalanche labels treat a permissionless CDP stablecoin as PTU ("permissionless debt-token, market-priced"), distinct from custodial MTU. No contract/code change.

Cross-references: ADR-0027 (NBA/MBA, direct-value vs backed-claim), ADR-0028 (consensus-backing test; stablecoins → MTU — this splits CDP → PTU), ADR-0031 (oracle-dependent synthetics; the AnyHedge STABLE leg is the counterparty-collateralized sibling of the CDP's debt-collateralized vault), ADR-0025 (obligor-free MBA — the rung-1 contrast), asset-landscape.md (Layer 2a/2b, the structural test).


ADR-0034 — Buyer-funded point-of-sale cashback is a payment-request + wallet-split feature, not a covenant

Date: 2026-05-27 Status: Accepted — implemented + chipnet-validated (snapshot 0060). src/wallet/cashback.ts, 10 unit tests, live split confirmed on chipnet.

Context: The user asked for a "cashback system contract": a merchant advertises e.g. 10% cashback; a customer buys a $1 coffee; the payment splits 0.90 → merchant, 0.10 → customer. A design conversation worked through what shape this actually takes on BCH. Two realisations settled it:

  1. A buyer-funded split cannot be enforced by a covenant in a single transaction — and that is a fundamental BCH property, not an implementation gap. A locking script (an address/contract) only executes when its own UTXO is spent; paying to an address imposes no constraint on the payer. So there is no contract you can place on the merchant's receiving address that forces "whoever pays me must also pay 10% back to themselves." The only way to make a covenant enforce the split is to route funds through an escrow UTXO and spend them out in a second transaction — which adds latency and a settlement hop for zero benefit at a coffee counter.
  2. In a normal BCH point-of-sale flow the customer's wallet builds the transaction. The merchant's POS app emits a payment request (a QR / URI); the customer's wallet reads it and constructs the outputs. So "merchant adds cashback in their POS app" naturally resolves to one transaction with two outputs, built by the payer. Enforcement lives where it can live — in the payer's wallet, the same place that already decides every output of a payment it signs.

The user also rejected the pool-funded alternative (customer pays full price; cashback comes from a pre-funded rewards pool) as circular: the pool is filled by income collected from merchants, so it cannot bootstrap the cashback it is meant to fund. Buyer-funded is direct — the money for the rebate is already in the customer's hand at purchase.

Decision:

  1. Buyer-funded cashback ships as a payment-protocol + wallet feature, not a contract. No covenant. The customer's wallet builds a single transaction:

    • [0] merchantSats = amount − cashback → merchant
    • [1] cashbackSats = floor(amount × cashbackBps / 10000) → customer (a distinct, labelled output — the verifiable rebate, kept separate from generic change)
    • [2] change → customer (if ≥ dust)
    • optional OP_RETURN receipt (DEVCB:v1) so the cashback is an auditable on-chain record. merchant + cashback ≡ amount exactly (integer-floored, remainder to merchant — no bits created or lost).
  2. Honest economics: a buyer-funded rebate is a trustless, advertised discount. The customer nets paying the merchant merchantSats; the cashback returns their own money. The system's value is not "free money" — it is that the merchant's advertised rate becomes a verifiable commitment (distinct output + receipt + optional signed attestation) rather than a marketing promise, and that the split is computed and shown by the payer's own wallet (which cannot be short-changed by the merchant).

  3. Cashback payment-request format (BIP21-style). bitcoincash:<addr>?amount=<BCH>&cashbackBps=<int>[&label=<text>][&attest=<pubkeyHex>.<sigHex>]. amount is BCH-decimal; cashbackBps is integer basis points, 0 ≤ bps < 10000 (100% would make the purchase free). Parsed + validated by parseCashbackUri.

  4. Optional merchant attestation = the only place a "contract"-like trust layer helps (verify-not-authority, ADR-0008). The merchant may sign DEVCB:v1:<merchantCashaddr>:<cashbackBps> (ECDSA/DER over its sha256), binding the rate to their receiving address so a signature cannot be replayed for a different rate or merchant. A wallet that verifies it renders "✓ verified N% cashback merchant"; an absent/invalid attestation still produces a valid split (enforced wallet-side) but is surfaced as unverified. Nobody is required to custody anything, and no registry is the authority — the wallet verifies.

  5. Dust + validation guardrails. merchantSats must be ≥ dust (else the price is too low for the rate); a non-zero cashbackSats below dust is rejected (can't emit a sub-dust standard output); sub-dust change is absorbed into the miner fee. Surfaced as typed CashbackError codes.

Alternatives considered:

  • Covenant escrow, merchant-settled. Rejected for PoS — funds park at a covenant and a second tx settles the split; two transactions and settlement latency for a coffee. (The covenant can enforce the split trustlessly, but the cost isn't worth it when the payer's wallet already builds the tx.)
  • Pool / sponsor-funded cashback. Rejected per the user — circular bootstrap (the pool is funded by the income cashback is meant to create) and it needs a funded pool covenant. A covenant genuinely earns its place only here (true added cashback from a third party), and even then it can be a single atomic co-spend — revisit if a sponsor-funded program is ever wanted.
  • Force it into a covenant anyway. Rejected — would re-introduce the escrow/2-tx detour the direct model exists to avoid; there is no single-tx covenant that constrains an incoming payment.

What this ADR does NOT resolve:

  • The OP_RETURN receipt's exact byte layout and whether a loyalty indexer consumes it (the module leaves attaching it to the shell; the chipnet test proved the split without it).
  • Wallet/PoS UX: how the merchant app generates the request QR, and how the wallet renders the verified/unverified badge + the net-cost breakdown (the plan already produces a confirmation string).
  • A signed merchant directory (mapping merchants → trusted keys) — the attestation verifies a key, but trusting which key is the merchant is the same user-rooted trust problem as ADR-0008 / ADR-0012 (issuerSignature); reuse that machinery rather than inventing a cashback-specific one.
  • Tier-A (token) cashback — out of scope; this is plain-BCH PoS.

Implementation pointers: src/wallet/cashback.ts (pure: parseCashbackUri, bchToSats, computeCashbackSplit, verifyMerchantAttestation, prepareCashbackPayment → CashbackPaymentPlan), mirroring the plan-builder style of src/wallet/send.ts. Tests: test/unit/wallet/cashback.test.ts (10). Chipnet evidence: snapshot 0060 (scripts/chipnet-cashback-test.mjs, TXID 383dbd37…b411930c — 90/10 split confirmed on chain). Cross-references: ADR-0008 (verify-not-authority; the attestation trust layer), ADR-0012 (issuerSignature — reuse for a merchant key directory), ADR-0017 (service-fee taxonomy — cashback is a buyer-side discount, not a Deviant fee), src/wallet/send.ts (plan-builder pattern).


ADR-0035 — BCH as a self-custodied USD rail: the four-option matrix; the synthetic-USD + covenant-protected-transfer + ASM-exit-at-receipt assembly as Deviant's distinguishing position

Date: 2026-05-28 Status: Accepted — design only. No code shipped this ADR. Synthesizes ADR-0031 (synthetic USD), ADR-0033 (CDP/custodial taxonomy), ADR-0030 (Family D), ADR-0025 (obligor-free MBA), the existing ASM, and CashScript covenant primitives into a product framing. Full discussion in wiki/usd-rail.md.

Context: A 2026-05-27/28 design conversation worked through whether BCH can serve as a self-custodied USD payment rail, what stops CashScript from issuing 1:1 locked assets from other networks or fiat, and whether the bonding-curve / oracle-indexed-rate / stepped-fee / Family-D primitives already in the project assemble into a coherent rail product. Two things settled the question.

  1. The bridge trust problem is universal, not BCH-specific. A CashScript covenant introspects only its own transaction; checkDataSig verifies that a key signed something, not that the claim is true. Every "1:1 backed by external value" instrument therefore reduces to trusting an attesting entity — single custodian, federation, oracle, or SPV relay. This is a property of any closed system referencing an external one, not a CashScript limitation. The right question is not "how do we eliminate the bridge" but "what is the best bridge trust model BCH primitives can support, and what does the rest of the rail look like once the bridge is paid for?"
  2. "Why not LN?" is a category error in the BCH context. LN exists to climb a wall BCH walked around: BTC's small-block, slow-and-expensive base layer made an off-chain settlement layer necessary. BCH made the opposite choice (bigger blocks, sub-cent fees, double-spend-proof messaging for retail-grade 0-conf safety in 1–2 seconds). For stablecoin transfer specifically — "send $X to address Y, possibly with a condition" — there is no settlement deficit to trade away in exchange for LN's channel-state complexity, routing constraints, always-online liveness, and custodial creep. The rail belongs on the base layer.

Decision:

  1. Recognise four realistic USD-rail deployments on BCH — the four-option matrix — and reject the false-economy fifth (reservoir-recalibrated MBA, ADR-0031 rejected alternative):

    Option Asset on the wire Custody USD-stable in transit? Counterparty during transit Best fit
    A. Raw BCH + ASM swap at endpoints BCH (no new asset) Self n/a (no transit token) None Fast transit; receiver chooses what to hold via ASM exit
    B. Synthetic-USD hedge leg (ADR-0031) Hedge-leg CashToken Self Yes, within published range, until maturity Long leg, per position Non-custodial USD holding over hours/days during transit or rest
    C. Custodial USD-on-BCH (CashTokens stablecoin) Issuer-minted CashToken Custodial (issuer = obligor) Yes (soft) The issuer Bank-fiat-to-bank-fiat with simple operational story; the conventional rail
    D. Oracle-stamped transit receipt (ADR-0031 Variant A) MBA + price stamp at mint Self No — labelled, not maintained None Niche: audit-trail / regulated-reporting transit where the dollar-value-at-send must be cryptographically recorded

    Per ADR-0033, the taxonomic placement: A = base unit (Layer 1); B = oracle/counterparty-dependent synthetic (Layer 2a-strict, not MBA); C = MTU (managed/custodial NBA); D = MBA with attached oracle-stamped BCMR (intrinsic backing, the "USD" is metadata, not invariant). None of A–D is an MBA dollar-peg in the rung-1 sense — none exist, by the ADR-0031 trilemma.

  2. The Deviant-distinguishing assembly is B + covenant-protected transfer + ASM exit at receipt. None of the three components is individually novel (synthetic USD: AnyHedge; CashScript covenants: extensively used; ASM: this project's flagship swap), but the combination is not being built elsewhere and is the realistic product direction the rail use case selects:

    • Non-custodial transit medium (B): no issuer obligor on the wire.
    • Covenant-protected transfer: escrow / conditional release / multisig / time-lock / split / refund-on-non-delivery, expressed as CashScript covenants on the transfer itself — primitives LN's HTLC/PTLC script set cannot match.
    • ASM exit at receipt: the receiver swaps the synthetic USD into their preferred unit (BCH, an MBA, raw stablecoin, or hold) in the same flow, without leaving self-custody. The Cosmogenesis web/PWA shell + the CashConnect/WalletConnect connector (snapshot 0061) + the existing ASM + the ADR-0031 hedge primitive are the four pieces; this ADR names the assembly so the product target is on the record.
  3. Honest prior-art positioning. Custodial stablecoins on BCH are not new (Tether issued on BCH via SLP historically; CashTokens makes the pattern natively first-class). AnyHedge (General Protocols) is the realised, validated synthetic-USD primitive on BCH. The contribution of this ADR is neither — it is the assembly (Decision 2) and the explicit four-option matrix as a decision framework, so a deployer can pick a rail flavour against their custody/stability/counterparty/regulatory constraints without re-deriving the trilemma each time.

  4. The bridge trust model is a per-deployment choice, not a rail-level decision. The matrix is agnostic to which bridge funds the on/off-ramp (single custodian, federation, oracle+collateral, SPV-attested, or hybrid). A real deployment pays the bridge cost once at each endpoint of the rail and operates self-custodied + programmable + final-settlement in between. The bridge taxonomy + per-pattern failure history is recorded in wiki/usd-rail.md; this ADR does not pre-commit to one.

  5. LN comparison is part of the rail's preamble, not its alternatives. A "why not LN" section belongs upstream of the matrix because every reader will ask it first; rejected there with the structural argument above (LN solves a BTC-shaped problem; BCH's base layer is the rail). LN is not an option on the matrix because it is not a BCH-side deployment.

Alternatives considered:

  • Option E — reservoir-recalibrated MBA stablecoin (the ADR-0031-rejected dynamic-rate experiment). Rejected: dominated by B (cleaner discipline, explicit per-position long counterparty) and by C (operationally simpler for the same UX). Inherits Frax/Maker-PSM failure modes (reservoir drain in sustained downtrends, redemption restriction, socialised losses). Recorded so the door is closed (ADR-0031, plus the ADR-0033 PTU placement).
  • LN as the rail. Rejected per Decision 5 / the preamble. The argument is structural, not preference-based: LN's "instant settlement" exists because the BTC base layer is slow and expensive. The BCH base layer is neither, and adds primitives (CashTokens, full covenants, no routing constraint) LN structurally cannot offer.
  • A single-option opinionated rail (pick one of A/B/C/D and ship it). Rejected: deployers' constraints (regulatory bucket, counterparty availability, custody appetite, transit duration, audit requirements) genuinely select different cells. The matrix is the right primitive; the assembly (Decision 2) is the recommended default.
  • Build a new CashScript primitive to make 1:1 bridging trustless. Rejected as out of scope and likely impossible without a major addition (a zk-verifier or SPV light-client opcode set). The bridge trust problem is fundamental to closed-system referencing of external state.

What this ADR does NOT resolve:

  • The specific CashScript patterns for "covenant-protected stablecoin transfer" — escrow, conditional release, multi-party splits, refund-on-non-delivery, etc. To be drafted as a follow-on technical note when the assembly is built.
  • The choice of bridge for any specific deployment (custodial vs federation vs synthetic-only). Per-deployment.
  • The regulatory analysis per option — A/B/C/D occupy materially different buckets (and Option B is the R-016-sensitive non-custodial-synthetic sub-bucket already flagged in ADR-0031). Counsel-required per deployment.
  • The on/off-ramp design (banking partners, KYC posture, etc.) for any bank-fiat-touching variant.
  • Wallet UX for the rail (Cosmogenesis Apps-tab placement, send/receive flow for B with covenant attachments, ASM-at-receipt UX).
  • Any chipnet validation — this ADR is design-only.

Implementation pointers (when built): the rail's transit-medium and ASM components reuse ADR-0031 (hedge leg) and existing ASM/src/wallet/swap.ts; covenant-protected transfer is new contracts/ work patterned after the tote CLTV/escrow scripts (ADR-0024) and the cashback split-payment plan-builder (ADR-0034); CashConnect/WalletConnect co-sign per snapshot 0061; D-1 entries for any new transit-covenant template per the ADR-0015/0018 multi-template pattern. Cross-references: ADR-0031 (synthetic USD — the transit medium of choice for B; and the source of the Option-D niche), ADR-0033 (CDP/custodial taxonomy — why B is Layer-2a-strict synthetic and C is MTU, not MBA), ADR-0030 (Family D — the covenant family the hedge leg generalises), ADR-0025 (obligor-free MBA — the rung-1 contrast and why none of A–D is one), ADR-0034 (payment-request + wallet-split — pattern for "USD send with conditions"), ADR-0024 (oracle + CLTV deadline patterns), ADR-0017 (peg-preserving fee), ADR-0008 (verifier-not-authority — the bridge-trust posture), snapshot 0061 (CashConnect adapter), wiki/usd-rail.md (full matrix, LN preamble, bridge taxonomy, prior art).


ADR-0036 — BCH on-ramp: third-party acquisition providers in Apps → Discover; transparency-ranked, never Deviant-endorsed; read-only API depth only

Date: 2026-05-31 Status: Accepted — implemented end-to-end (snapshots 0085–0086). src/onramp/ (types, four provider adapters, transparency ranking + registry, jurisdiction gating, kycnot.me read-only reputation client) + web API handlers/routes (/api/onramp[/handoff|/quote|/rate], ?journey=&country=) + the Buy BCH Discover tab in the web shell (public/). Tier 1 (referral handoff) + Tier 2 (read-only quote) for all providers; Tier 3 (in-wallet API shift) added for SideShift — see the 2026-05-31 amendment below. Full suite 478 pass / 0 fail. Live-API verification done (2026-05-31): corrected kycnot.me (kycLevel 0–4, key-gated) and Trocador (/anonpay/ handoff, key-gated quote); a few provider deep-link param names remain UNCONFIRMED (commented in code) and need a live re-check before mainnet, along with real per-provider country lists for the (currently global-seeded) jurisdiction gating.

Context: The user asked for a "fiat on-ramp, non-KYC exchange" — a way to acquire BCH from inside the wallet. A 2026-05-31 web investigation (two research passes) plus a design conversation settled three things that shape the decision:

  1. A truly no-KYC fiat→BCH on-ramp does not exist in 2026. The honest ceiling is low-KYC (e.g. Guardarian, verification triggered ~€700/tx). The dedicated no-KYC P2P fiat routes (Bisq, RoboSats, HodlHodl) are BTC-only and/or unembeddable; LocalCoinSwap delisted BCH. So the "demand" splits into two journeys: (A) has fiat, wants BCH — only low-KYC providers fit; (B) has other crypto, wants BCH — genuinely no-KYC crypto→BCH swaps exist.
  2. The wallet has no accounts and no KYC ("seed is the identity," wallet-ui-design.md). This is load-bearing for the rating design: no identity ⇒ no sybil resistance, so a Deviant-hosted aggregate user-rating backend would be farmable — a scam on-ramp could buy itself a 5-star score, the exact failure ADR-0020 exists to prevent.
  3. "Recommended by Deviant" is the ADR-0008/0020 authority trap. Deviant "sells visibility, never truth"; it may surface facts and plural reputation, but must not become the arbiter of which on-ramp is legitimate.

Decision:

  1. Home: Apps → Discover, a new "Buy / On-ramp" category. Consistent with the Discover tab being "a user-extensible BCH-app directory (no blessed list — ADR-0008)" (wallet-ui-design.md). These are external acquisition providers, distinct from the DEX/Market surface (Assets → NBA → Market, ADR-0016/0028) and from the dApp connection manager (Connected). Acquisition ≠ trading ≠ connection.

  2. Launch provider set — four, covering both journeys:

    Slot Provider Journey Honest label shown to user
    Fiat entry Guardarian A (fiat→BCH) Low-KYC (~€700/tx threshold); delivers to user address
    No-KYC swap (primary) Trocador.app B (crypto→BCH) No-KYC aggregator; surfaces its own per-partner KYC-risk rating
    No-KYC swap (embed-grade) SideShift.ai B No-KYC by default; clean rep since 2019
    Alternative Changelly A + B Big brand / liquidity; crypto-swap is its own engine (no-KYC, AML-freeze risk); fiat leg is a full-KYC multi-processor marketplace (MoonPay/Simplex/Banxa/etc.)

    Excluded, with reasons on record:

    • SimpleSwap — UK FCA public warning (Mar 2025) + AML-freeze pattern + opaque jurisdiction. A verifiable regulator fact (the on-ramp analogue of a D-1 risk flag) → excluded from launch; if ever added, it goes in the risk-flagged tier, never featured.
    • THORChain — BCH is a nominally supported chain but THORChain's granular per-chain halting makes BCH intermittently "not available." An on-ramp that randomly shows a dead button reads as the wallet being broken. Revisit only as an optional "decentralized; availability varies" entry with a live status check — not worth it for v1.
    • Exolix / StealthEX — fine no-KYC fallbacks, held in reserve; not needed alongside Trocador + SideShift.
  3. Ranking is transparency-driven, never a Deviant endorsement (the operative reconciliation of the user's "recommended" intent with ADR-0008/0020). Three never-merged signals, each attributed to its source:

    • Objective facts — KYC posture and regulator warnings (e.g. the FCA notice). Verifiable; Deviant reports, does not author. Highest integrity. A regulator flag excludes from featured (the SimpleSwap rule).
    • External plural reputation — kycnot.me trust score (free open API, crypto-specific; also a discovery source). Surfaced, not owned — Deviant stays a verifier, not the rater-of-record.
    • Local, per-device user rating — the user may rate/favourite on their own device (reuse the existing favourite mechanism, handleFavorite). Private, no backend, no sybil surface. This satisfies "users rate each one" without building a gameable authority.
    • Rejected: a Deviant-hosted aggregate community rating (sybil-gameable without accounts; a hosted moderation/authority surface). Rejected: Trustpilot as a ranking input — gameable, commercial, not crypto-aware (reflects support complaints, not custody/KYC/freeze risk), and paywalled API. May appear only as a labelled secondary sentiment number if ever wanted, never as a ranker.
  4. Integration depth: read-only only (Tier 1 + Tier 2); Tier 3 deferred. Mirrors the ADR-0016 read-first via public API, execution via handoff posture.

    • Tier 1 — referral handoff link-out (baseline). Open the provider's page/widget pre-filled with the user's BCH address + amount + a referral ID. No API keys, near-zero maintenance, zero custody. A complete feature on its own.
    • Tier 2 — read-only quote + reputation API (the differentiator). Pull live quotes (SideShift REST, Trocador cross-partner quotes, Guardarian) + kycnot.me reputation to power an honest comparison view ("for €100 / 0.1 BTC ≈ X BCH," each with KYC label + score) before the user picks and hands off. Read-only ⇒ no custody, no liability.
    • Tier 3 — transactional API (create order / manage deposit / track in-wallet): deferred. Drags the provider's compliance/AML obligations partly onto Deviant, adds key-management + status infra + ongoing maintenance, and pulls toward quasi-broker (off-thesis). Revisit only on demand, same gated posture as in-wallet covenant swaps (ADR-0016).
    • Prefer external link-out over embedded iframes/SDKs. In a non-custodial wallet, a third-party iframe near key-access context is a real attack surface; opening externally is safer. Any embedded widget must be strictly sandboxed and key-isolated.
  5. Revenue posture: retention/funnel, not a skim. External-routed acquisition carries no Deviant swap fee (ADR-0020 §6, external routing is 0%). Referral IDs are permitted (partner commission, ADR-0021) and ride in the Tier-1 handoff URL — but a referral relationship must never influence ranking or be shown as a safety signal. If a paid "Promoted" placement is ever introduced, it follows ADR-0020: honestly labelled as advertising, visually separate from the safety/reputation signals, never overriding them.

  6. Tier-transparency (Rule 6) applies to acquisition, not just swaps. The user must always see which journey they are in (fiat→BCH vs crypto→BCH), the provider's KYC posture, and that they are leaving to a third party Deviant does not control or vouch for. An acquisition link must never be rendered to imply a Deviant guarantee.

Alternatives considered:

  • Editorial "Deviant recommends Guardarian." Rejected — the ADR-0008/0020 authority trap; an endorsement Deviant can't honestly back.
  • Deviant-hosted aggregate user ratings. Rejected — no accounts ⇒ no sybil resistance ⇒ farmable; plus a hosted moderation/authority surface. Local-per-device rating + external (kycnot.me) reputation gives the user-rating value without the trap.
  • Trustpilot as a reputation/ranking source. Rejected — gameable, commercial, category-agnostic (not crypto-risk-aware), paywalled API.
  • Include SimpleSwap as "recommended but warned." Rejected as self-contradictory under ADR-0020: a regulator-flagged service is risk-flagged, listable-with-warning at most, never featured. Dropped entirely for launch.
  • THORChain as a headline option. Rejected — intermittent per-chain halts make BCH availability undependable; reads as wallet breakage.
  • Deep (Tier 3) transactional integration / embedded widgets in-shell. Deferred / discouraged — custody-adjacent liability, compliance bleed, maintenance, and an iframe key-access attack surface, for marginal UX gain over handoff + read-only quotes.
  • Build a first-party on-ramp. Rejected — custody, liquidity, and money-transmission exposure squarely against the non-custodial / authority-free thesis (and wiki/integration-guide.md's money-transmission caution).

What this ADR does NOT resolve:

  • The exact Discover "Buy / On-ramp" card layout, comparison-view UX, and how the three signals (facts / kycnot.me / local rating) render side-by-side without merging.
  • The referral-URL parameter schemas per provider, and the referral-revenue accounting.
  • Whether to ship Tier 2 (live-quote comparison) in v1 or fast-follow Tier 1.
  • The kycnot.me API integration shape (poll/cache cadence, offline fallback) and resilience when a provider/reputation API is down.
  • Per-jurisdiction availability gating (providers have differing country lists) and the regulatory analysis of surfacing acquisition links (counsel-required, cf. integration-guide.md).
  • Whether/when to admit a risk-flagged tier (and its warning copy) — none ships at launch since the launch set carries no regulator flag.
  • The on/off-banking ramp design — explicitly out of scope here; this ADR covers third-party provider link-outs, complementing the open banking-ramp item in ADR-0035.

Implementation pointers (when built): a new src/onramp/ module with a thin OnrampProvider adapter interface (buildHandoffUrl(addr, amount, fromAsset?) + optional read-only getQuote(...) / getReputation()), patterned on the ADR-0016 MarketProvider plurality (multiple adapters, user choice, no hardwired venue); kycnot.me read-only reputation client; local rating reuses the favourite store / handleFavorite path; surfaced in the Cosmogenesis Apps → Discover "Buy / On-ramp" category (wallet-ui-design.md); referral IDs in handoff URLs per ADR-0021. Cross-references: ADR-0008 (verifier-not-authority — no blessed list, surface facts), ADR-0020 (sells visibility never truth; safety vs promotion never merged; external routing 0%), ADR-0016 (pluggable provider, read-first + handoff), ADR-0021 (referral commission), ADR-0026/0028 (Apps tab IA; Discover ≠ Market), ADR-0035 (open banking on/off-ramp item this complements), wiki/integration-guide.md (money-transmission caution), kycnot.me (external reputation source), memory: WizardConnect (sibling deferred Apps-tab integration).

Amendment (2026-05-31) — Tier-3 admitted for API swap providers; SideShift in-wallet shift shipped

Status: Accepted — implemented (snapshot 0099). Reverses, in a bounded way, Decision 4's deferral of Tier 3.

Context: Decision 4 deferred Tier 3 ("create order / manage deposit / track in-wallet") as custody-adjacent. On building it for SideShift, the custody concern proved not to apply to a non-custodial swap provider: the user sends crypto directly to a SideShift deposit address and BCH settles to the user's own address — the wallet operator never holds, routes, or can divert funds. What actually remains of the deferral rationale is narrower: (a) the wallet now holds an API secret, (b) it accepts the provider's integrator ToS / AML expectations, (c) maintenance of the API surface, (d) it is mainnet-only. Those are real but acceptable for a single, user-opted-in provider — so Tier 3 is admitted narrowly, not generally.

Decision:

  1. Admit Tier 3 only for non-custodial API swap providers — those where funds never touch the wallet operator (user → provider deposit address → user's own settle address). SideShift is the first and only such integration. Fiat on-ramps (Guardarian, Changelly) and the aggregator (Trocador) stay link-out (Tier 1/2) — a fiat processor's flow is custodial/KYC and does not qualify.
  2. The provider secret is server-side only. SIDESHIFT_SECRET is read from env by the local server; it is never sent to the browser. The browser calls /api/onramp/shift; the server attaches x-sideshift-secret. Only non-secret shift data (deposit address, min/max, status) is returned. (Asserted by a test: the secret never appears in the response.)
  3. Opt-in and degrades cleanly. Tier 3 is off unless SIDESHIFT_SECRET is set (/api/onramp/config → shiftEnabled). Absent ⇒ SideShift falls back to the same Tier-1 link-out as everyone else. The other transparency rules (ranking, warnings, no endorsement) are unchanged.
  4. Self-custody guardrails. settleAddress is always the user's own BCH address (default = wallet cashaddr). Mainnet-only: SideShift settles mainnet BCH, so a bchtest:/bchreg: settle address is rejected with a clear "switch to mainnet" message (link-out still works for testnet testing).
  5. Variable-rate shift is the v1 shape (POST /v2/shifts/variable): no amount lock, user sends any amount in the returned min/max range, rate set on deposit. Fixed-rate (quote-locked) shifts are a possible later enhancement.

Alternatives considered:

  • Keep Tier 3 deferred / link-out only. Rejected for SideShift specifically — the link-out can't prefill the settle address (no documented URL param), so the in-wallet API flow is materially better UX and keeps self-custody, with only the bounded costs above.
  • Generalise Tier 3 to all providers. Rejected — fiat processors are custodial/KYC and don't share the "funds never touch us" property; admitting them would reintroduce exactly the liability Decision 4 guarded against.
  • Embed SideShift's widget iframe instead of the API. Rejected — the in-wallet-iframe key-surface concern from Decision 4 stands; a server-side API call with the secret never reaching the browser is safer.

What this does NOT change: the link-out + read-only-quote model for all other providers; the transparency-ranking/no-endorsement rules; the 0%/retention revenue posture (SideShift commission via the affiliate id is the same affiliate revenue, not a Deviant skim).

Implementation pointers: src/onramp/sideshift-shift.ts (createVariableShift / getShiftStatus / verifySideShiftSecret, injected fetch); web handlers handleOnrampShift / handleOnrampShiftStatus / onrampShiftEnabled + routes GET /api/onramp/config, POST /api/onramp/shift, GET /api/onramp/shift?id=; OnrampContext.sideshiftSecret / sideshiftAffiliate from SIDESHIFT_SECRET / SIDESHIFT_AFFILIATE env; in-wallet shift UI in public/ (deposit address + range + status poll). Live-verified: the configured secret authenticates against GET /v2/account. Tests: test/unit/onramp/sideshift-shift.test.ts + shift route tests (full suite 478 pass).


ADR-0037 — Supply semantics across the taxonomy: max-reservoir for circulating MBAs; Token-family supply is not a scarcity guarantee

Date: 2026-05-31 Status: Accepted — documents two facts that fall out of CashTokens consensus + the asset taxonomy. No code change required for the second; the first is a recommended genesis parameter pattern, validated on chipnet (snapshot 0086).

Context: Two observations surfaced while building the GLD/SLV/CUP decreasing-fee metal MBAs (snapshot 0086) and discussing the non-MBA [?] sweep. Both are about what supply means in Deviant, and both follow from a single CashTokens consensus fact (ADR-0007): a category's fungible-token supply is permanently fixed at the genesis transaction; the minting NFT can mint more NFTs, never more FTs. "Mint-on-demand" is impossible at L1.

Decision A — For non-consumable / perpetually-circulating MBAs, seed the reservoir at the consensus ceiling (2⁶³−1).

The reservoir model (ADR-0007) — pre-mint the whole FT supply at genesis, park it at the contract, hand it out via claim() — is the only mechanism CashTokens allows for a token users acquire over time. For a consumable / bounded asset (a fixed-edition run, a depleting voucher pool) a finite reservoir is meaningful. For a circulating asset (a "metal" meant to change hands indefinitely), reservoir depletion is an artifact of the workaround, not a desired property — and redemption burns circulating FTs (removes them from recognised supply) rather than returning them to the reservoir, so the reservoir is strictly monotonic-decreasing once seeded.

Resolution: seed the reservoir at 2⁶³−1 (the signed-int64 FT cap). At ~9.2 quintillion units, "depletion" is a non-event for any realistic circulation. Critically, this does not distort any supply-stepped fee schedule (DeviantEscFee*/DeviantDescFeeSupply, ADR-0029), because those step off issuedSoFar = initialReservoirFtSupply − currentReservoir (cumulative claims), not reservoir size — at genesis issuedSoFar = 0 regardless of how large the reservoir is. Validated live: GLD/SLV/CUP genesis'd at 2⁶³−1 with the 5%→0.25% ladder intact (snapshot 0086). Recommended pattern: "max-reservoir = effectively-unlimited supply" is the default for circulating MBAs; a bounded reservoir is the deliberate exception for genuinely scarce/consumable editions.

Decision B — Token-family (non-MBA, backing: 'token') supply is descriptive metadata, never a scarcity or value guarantee.

For any non-MBA Token (PTU / PTL / MTU / MTL) there is no backing BCH bound per unit. Therefore:

  • Minting an unbacked unit only attaches a free label to a UTXO; burning only spends that UTXO. No value is created by mint or destroyed by burn, because none was ever bound to the unit.
  • The full circulating supply can be minted and then burned in seconds, whether mint is permissionless (any address) or managed (founder key). A managed gate controls who issues, not whether supply is a meaningful scarcity claim — it isn't, in either case.
  • Consequently the wallet must treat Token-family supplyMode / mintRule as low-stakes descriptive metadata (already so in token/types.ts), grant no Protocol Net Value, and refuse redeem (no peg to redeem against — redeem.ts not-mba). The only BCH recoverable from an unbacked token UTXO is the raw bits sitting on it — handled by the unpegged sweep (the non-MBA [?] normalization, task 11), which must be labelled explicitly as "no peg — you receive only the on-UTXO satoshis," distinct from MBA redeem/redeem-all.

This is the security rationale behind the MBA/Token split and the verification model (ADR-0008, ADR-0025): consensus enforces backing only for MBAs (self-backed UTXOs), so only MBA supply/PNV is a consensus-grade claim. An unbacked token's supply number is a UI convention with no on-chain weight.

Alternatives considered:

  • (A) Finite reservoir sized to "expected demand" for circulating assets. Rejected as the default — it bakes an arbitrary ceiling into an asset meant to circulate forever, and conflates the consensus workaround with an economic choice. Keep finite reservoirs for genuinely bounded editions only.
  • (A) Lobby for a consensus change to allow post-genesis FT minting. Out of scope; not a Deviant decision. Max-reservoir achieves the same user-visible behaviour without it.
  • (B) Display unbacked-token supply with the same prominence as MBA supply. Rejected — implies a scarcity guarantee the chain does not provide; precisely the misrepresentation the verification model exists to prevent.
  • (B) Allow redeem on Token-family at "whatever bits are on the UTXO." Rejected as redeem semantics — redeem means "burn at the protocol peg." The unpegged sweep is the correct, separately-labelled primitive for recovering raw bits.

What this ADR does NOT resolve:

  • The unpegged-sweep UX and warning copy (task 11) — to be designed when that feature lands.
  • Whether a spec amendment should state the max-reservoir recommendation explicitly in the reservoir-model section (track in spec-amendments.md).

Cross-references: ADR-0007 (reservoir model; FT supply fixed at genesis), ADR-0025 (MBA is an obligor-free self-backed instrument, not a "token"), ADR-0029 (supply-stepped fee schedules read issuedSoFar, not reservoir size), ADR-0008 (verifier model — consensus claims vs UI conventions), token/types.ts (isMbaBacked, Token-family fields are descriptive), snapshot 0086 (GLD/SLV/CUP max-reservoir validation), snapshot 0084 (redeem-all; non-MBA exclusion).

ADR-0038 — Operation-surface taxonomy: split the B→A enforcement change ("Transition") from the dynamic rate/fee schedule ("Elevation"); dynamic mechanics are genesis-sealed and counterparty-free by the self-backed floor

Date: 2026-05-31 Status: Accepted — naming + framing decision; public rollout phased (Static MBA first, Dynamic Rate later).

Context: The wallet exposes operation surfaces (Avalanche/create, Spectrum/view, Orbital Network/swap, and historically Elevation for the Architecture B→A enforcement escalation). Two things converged:

  1. The B↔A move is a change of enforcement form (soft convention → hard consensus; identity preserved), which reads more naturally as a transformation than an "elevation." "Elevation" implies a single upward step the operation doesn't really have, and the name is needed elsewhere.
  2. ADR-0015 established the DRA (dynamic-rate asset; per-unit rate a deterministic function of state); a dynamic fee schedule (ADR-0029) is the same shape on the cost axis. Both genuinely escalate — and "Elevation" fits that far better than it fits the B→A tier change. Lumping enforcement-change and rate/fee-escalation under one surface conflates a trust-model operation with an economic one.

Decision:

  1. One verb per surface:

    • Avalanche — create (genesis).
    • Spectrum — view.
    • Transition — change the enforcement form (Architecture B → A; same tokenId, same backing rate, soft convention → hard consensus, identity preserved). Renamed from "Elevation." Effectively one-way (B→A); A→B is not meaningful — an on-chain-enforced asset cannot be un-ossified.
    • Elevation — operate a genesis-sealed dynamic schedule: dynamic rate and dynamic fee. The elevating-rate instrument on this surface is the ERA — Elevating Rate Asset (the monotonic-up subset of the general DRA — Dynamic-Rate Asset, ADR-0015); fee schedules per ADR-0029. The economic-dynamics surface.
    • Orbital Network — swap (counterparty-free exchange; UER for fixed-rate pairs, execution-time quote where a DRA is involved).
  2. "Dynamic" means deterministically scheduled, never discretionary. The schedule and its bounds are Core Parameters (fixed, invariant at genesis); only the current value along the schedule is the Operational dimension Elevation surfaces. No admin turns a dial; the curve/step function executes mechanically. This preserves "deterministic, not probabilistic — there is no approximately." A schedule changeable by discretion is not an Elevation feature; it is the mutability the MBA Standard rejects.

  3. Floor preservation under any schedule (the determinism guarantee). A dynamic rate never threatens the floor, because the floor is the BCH locked at mint time, carried inside the unit's own UTXO, redeemable by a plain P2PKH spend. The rate module governs only the conversion/mint price going forward, which ASM applies at execution time (ADR-0015; chipnet snapshot 0027). The schedule changes future pricing, never existing backing. A DRA therefore stays redeemable, deterministic, and counterparty-free under any schedule — a counterparty would add discretion, not safety. This is the design test dynamic mechanics must pass, and it is already passed and validated (ADR-0015, snapshot 0027).

  4. DRA placement resolved (README pending-item #1) → property modifier (reading β). Dynamic rate/fee is not a separate fifth asset class and not a parallel quadrant; it is a modifier applied via Elevation to assets in the existing eight-class taxonomy (DEA/PLA/MCA/MTA · PTU/PTL/MTU/MTL). "Dynamic" is an Elevation property, not a new cell; a DRA is a permissionless MBA carrying a dynamic-rate schedule.

  5. Phased public rollout. Public launch presents Static MBA only (fixed-rate DEA + the static surfaces, including Transition for B→A enforcement). Elevation (dynamic rate/fee) is held out of public-facing copy and presented in a future public version. The webpage and positioning four-surfaces are updated to rename Elevation → Transition now; "Elevation" is reserved for the later dynamics reveal.

Alternatives considered:

  • Keep "Elevation" for B→A and name dynamics something else. Rejected — "Elevation" semantically belongs to the thing that escalates (rate/fee); "Transition" describes the form-change (B→A) better.
  • Lump enforcement + rate + fee under one "Elevation" surface (the earlier framing). Superseded — conflates the trust axis with the economic axis; the split keeps the model legible.
  • Introduce a literal fifth public surface now. Rejected for launch — the phased rollout keeps the public set at four (Avalanche/Spectrum/Transition/Orbital), Elevation being the future addition.

What this ADR does NOT resolve:

  • The Elevation UI surface itself (creating/displaying/operating dynamic schedules) — to be designed with the dynamics reveal; ADR-0015 holds the DRA curve-rendering and slippage-UX pointers.
  • Dynamic fee schedules beyond ADR-0029 (e.g. time-based) — to be specified when built.
  • Whether "Transition" is the final public word vs. an in-voice alternative (Crystallization / Hardening / Anchoring). "Transition" is adopted as the working term.
  • Propagation of the Elevation→Transition rename across the spec, architecture.md, the ecosystem overview, and the Strategic-Intelligence mindmap (which use "Elevation" for B→A) — flagged as a terminology item; treated as equivalent (cf. the open-mint ≡ permissionless-mint note in glossary.md) rather than swept now.

Cross-references: ADR-0015 (DRA; ASM execution-time quotes; snapshot 0027 floor / dynamic-target validation), ADR-0029 (supply-stepped fee schedules read issuedSoFar), ADR-0006 (Architecture B→A composite; identity-preserving escalation), ADR-0008 (verifier model), spec §Core-vs-Operational parameters, wiki/wallet-ui-design.md (surface descriptions), README pending-item #1 (DRA placement — now resolved).


ADR-0039 — MBAs are divisible by default; the create flow derives the densest dust-safe denomination from the founder's whole-unit rate

Date: 2026-05-31 Status: Accepted — implemented (snapshot 0098). defaultDenomination() in tokenMetadata.ts + GenesisIntent.decimals + the CLI create flow; 6 unit tests; chipnet-previewed across rate tiers.

Context: The metals work (snapshots 0086–0093) exposed that MBAs were created whole-unit-only by default (decimals absent ⇒ 0), so GLD was un-fractionable until we hand-built divisible variants (GLDd/SLVd) and learned the governing constraint: a divisible MBA's smallest atomic FT output must clear the 675-bit CashTokens dust floor, and an MBA's FT-output value equals its backing exactly (peg invariant), so the atomic backing rate must stay ≥ ~675 (we use the ADR-0006 1000-bit floor). Divisibility depth is log₁₀(wholeRate / 1000). Hand-deriving this per asset is error-prone; it should be the default.

Decision:

  1. MBAs are divisible by default. The founder specifies the whole-unit backing rate (bits per whole coin). The wallet auto-assigns the densest dust-safe denomination: the largest decimals such that wholeRate divides cleanly by 10^decimals and the per-atomic rate stays ≥ ATOMIC_RATE_FLOOR (1000). The contract deploys at that atomic rate; decimals is display metadata (not a contract param, not in the category id).
    100000 → atomic 1000, decimals 2 (0.01, GLD)   10000 → atomic 1000, decimals 1 (0.1, SLV)
      1000 → atomic 1000, decimals 0 (whole, CUP)   50000 → atomic 5000, decimals 1
    
  2. Whole-unit-only is an explicit opt-out (create --whole-only) — and the natural result when the rate is at/below the floor or not cleanly divisible (1500 → 0 dp, 12345 → 0 dp).
  3. --decimals <n> forces a depth and may go sub-floor — the caveated path (divisible CUP, snapshot 0093). Allowed with a sub-floor warning naming the dust-bounded minimum; the covenant enforces no rate floor, so it's honest opt-in, not a guarantee.
  4. Genesis keeps atomic-rate semantics. prepareGenesis still takes the per-atomic rateSats (trust-critical address derivation + all existing tests unchanged); the whole→atomic+decimals derivation lives in the create/CLI layer. GenesisIntent.decimals carries the chosen denomination onto the registry entry + confirmation.

Alternatives considered:

  • Fixed default decimals = 2. Rejected — wrong for low-value assets (a 1000-bit coin can't carry 2 dp without sub-dust atoms) and arbitrary for high-value ones.
  • Whole-only default, divisibility opt-in. Rejected — the metals work showed divisibility is the common want; flip the default and make founders opt out, not in.
  • Change prepareGenesis to take the whole-unit rate. Rejected — would change the atomic rate fed to the contract and break address-derivation tests; keep the consensus-facing layer atomic, derive in the create layer.
  • Allow sub-floor atomic by default. Rejected — produces un-sendable sub-dust outputs; sub-floor is --decimals-only, with a warning.

What this ADR does NOT resolve:

  • --mint / --reservoir stay in atomic units (advanced flags); only the headline rate positional is whole-unit.
  • The CLI create path deploys the canonical flat-fee contract; stepped-fee (Esc/Desc) MBAs still go through dedicated scripts. The denomination rule applies to both (display-only).

Cross-references: ADR-0006 (1000-bit rate floor), ADR-0037 (dust floor / supply semantics), tokenMetadata.ts (defaultDenomination, atomicRateForUnit, wholeUnitRate, formatUnits), snapshots 0090/0093 (hand-built divisible metals that motivated the rule), token/types.ts (decimals is display-only).

Update (2026-06-01) — default is uniform 2 dp; the dust floor becomes a wallet-side transaction threshold, not a creation constraint. The original decision derived the densest dust-safe denomination, giving low-value coins fewer decimals (SLV → 1 dp, CUP → 0 dp). Per founder direction the default is now DEFAULT_DECIMALS (2) applied uniformly (clamped down only when the whole rate isn't cleanly divisible by 10^d, to keep the atomic rate a whole bit count). A 1000-bit coin is now as fractionable as a 100,000-bit one. Consequence: low-value assets get a sub-floor atomic rate (CUP-class atomic 10), which is allowed — the indivisibility constraint moves from genesis to transaction time. minSendableUnits(atomicRate, decimals) computes the dust-bounded minimum, surfaced as a friendly pre-flight error (prepareTierASend → sub-dust-recipient: "Minimum sendable is 0.7 CUPd") and via the genesis confirmation's existing Minimum viable claim line. --whole-only opts out; --decimals <n> forces a depth. Validated: create CUPX 1000 → 2 dp / atomic 10 + sub-floor warning; a 0.5-CUPd send refused pre-flight naming the 0.7 minimum. New: minSendableUnits (tokenMetadata.ts), sub-dust-recipient (send-tier-a.ts). The densest-dust-safe phrasing above is retained as history.

ADR-0040 — On-chain voting as a recognised Deviant contract family: three primitives, three trust profiles, and the plutocracy / hashwar lesson

Date: 2026-05-28 Status: Accepted — all three primitives chipnet-validated (snapshots 0105, 0106). Recognises voting as a Deviant contract family alongside the event-prediction A–C (ADR-0024), share-market D (ADR-0030) and AnyHedge-class hedge (ADR-0031). Full design in wiki/voting-systems.md.

Context: A 2026-05-28 design conversation asked how to do on-chain voting on BCH using Deviant primitives. The naive instinct ("use a DEA token, one vote per token") collapses to plutocracy and re-introduces sybil; the right answer depends on which kind of vote the system is asking. Working it through produced three covenants and three composition shapes, each occupying a different point on the trust spectrum — and surfaced a load-bearing precedent (the BTC/BCH hashwar) on capital-capture of apparently-meritocratic voting systems.

Decisions:

  1. Recognise three voting primitives as Deviant contract family members, with distinct sybil/trust profiles:

    • PollCovenant — per-voter allowlisted token (one-token-one-vote, MCA/MTA-style issuer allowlist). Sybil resistance lives in the issuer's identity rigor, not in the covenant.
    • TallyState — stateful on-chain tally (counters in the NFT commitment, permissionless close(), a readOnly() co-spend path for downstream markets to read the outcome trustlessly). No sybil resistance in itself; composes with PollCovenant.
    • StakeVoteTote — stake-weighted vote via pool comparison. Variant of ParimutuelToteOpen with the oracle stripped: settle() picks the bigger pool as winner. Plutocratic by design.
  2. Recognise three composition profiles wiring voting into Family-D markets — three points on the trust spectrum:

    • Option 1 — poll-as-oracle: PollCovenant tallies; a named signer (ADR-0024 accountable-named-oracle) reads the public tally and signs {eventId, outcome} via checkDataSig; BinaryShareVaultBCH attests on the signature. Trust = the signer (constrained to honestly read a public tally; reputation-staked). No new covenants.
    • Option 2 — tally-as-oracle (trustless): TallyState + BinaryShareVaultPoll (new). The market's attest() co-spends the closed tally as a read-only input and reads the outcome from its commitment. No oracle, no signer. The covenant chain is the whole trust path.
    • Option 3 — stake-weighted (no oracle, plutocratic): StakeVoteTote settles via pool comparison. The vote IS capital; the outcome IS pool composition.
  3. Sybil resistance is upstream of the signer and upstream of the tally — it lives in the allowlist issuer. A "named signer" is who attests an outcome, not who is allowed to vote. The named-signer ≠ identity distinction is load-bearing and is the most common confusion this work surfaces. On a pseudonymous chain, universal one-person-one-vote is impossible without an external personhood attestation; the rigor of a one-person-one-vote poll is exactly the rigor of its upstream identity attestation, never more.

  4. "Co-spend as oracle" is a general primitive, not just a voting trick. TallyState.readOnly() allows any external covenant to read its state via co-spend, with the source covenant enforcing its own preservation. This pattern is broader than voting — any on-chain measured quantity can serve as an oracle for any other covenant. Recorded as a reusable Deviant idiom.

  5. Plutocracy is right for some votes and wrong for others; the wallet must label which. StakeVoteTote is plutocratic by design — appropriate for stakeholder votes (DAO treasuries, prediction-market consensus, skin-in-the-game decisions) and inappropriate for universal-suffrage questions. ADR-0008 (verifier-not-authority) extends to voting: don't hide the trust model — surface it. A wallet that shows "Vote on Proposal X" without labelling it "stake-weighted — capital decides" or "allowlisted by Issuer Y — sybil rigor depends on Y" misrepresents what is actually happening.

  6. Record the BTC/BCH hashwar as the canonical case study for capital-capture of apparently-meritocratic voting. PoW looks meritocratic (energy = vote) but the upstream eligibility (who can afford the merit) is plutocratic, and in 2018 was further distorted by Tether-printed liquidity (an NBA/MTU asset by the consensus-backing test of ADR-0028 — non-backed but treated as money) flowing into hashpower rental. The plutocracy attack and the soft-backing failure are the same shape from different angles. Any voting system anchored in a purchasable resource ultimately ranks by capital, however meritocratic the surface looks. Voting systems on Deviant must label this honestly.

Alternatives considered:

  • Choose one primitive (e.g., stake-weighted only) as "the" Deviant voting model. Rejected — voting questions are heterogeneous; one model would mis-serve most polls. Three primitives + clear labelling is the right discipline.
  • Treat PollCovenant (allowlisted) as sufficient for one-person-one-vote. Rejected — the covenant only enforces "one token, one vote"; sybil rigor depends entirely on the issuer's identity attestation. The covenant cannot promise what the issuer doesn't enforce upstream.
  • Build a single "perfect" voting covenant with all features. Rejected — composition (PollCovenant + TallyState, StakeVoteTote + snapshot-at-height) is the production path; each primitive carries one concern cleanly.

What this ADR does NOT resolve:

  • The production PollCovenant + TallyState composition (allowlisted and trustless tally) is described in voting-systems.md §7 but not yet built.
  • Snapshot-at-height for stake-weighted votes (flash-loan governance defence).
  • Multi-outcome (K > 2) variants of all three primitives.
  • Wallet-side rendering of trust-model labels.
  • Identity-oracle integration for true one-person-one-vote (proof-of-personhood).
  • K-of-N attestor pools to mitigate single-named-signer franchise risk.

Implementation pointers: contracts/PollCovenant.cash, contracts/TallyState.cash, contracts/BinaryShareVaultPoll.cash, contracts/StakeVoteTote.cash (+ contracts/dist/); drivers scripts/chipnet-poll-test.mjs, scripts/chipnet-poll-as-oracle-test.mjs, scripts/chipnet-tally-as-oracle-test.mjs, scripts/chipnet-stake-vote-test.mjs. Cross-references: wiki/voting-systems.md (full design), ADR-0008 (verifier-not-authority — the labelling discipline), ADR-0024 (accountable-named-oracle model), ADR-0028 (the consensus-backing test that places Tether as NBA — the hashwar's soft-money tool), ADR-0030/0031 (Family D / hedge — the markets these polls settle), ADR-0033 (CDP placement + the volatility paradox — same shape as the hashwar capital-capture, different angle), wiki/asset-landscape.md (the three-rung backing ladder).

Addendum (2026-05-28) — the design is now mechanism-complete; the constructive answer to the hashwar critique is recorded in voting-systems.md §6. The conversation continued past the initial three-primitive recognition into a full system design: bicameral 15+15 admission (regular vouching + impartial qualification), sortition of the delegated chamber (random selection, with streaming juror replacement on no-show), a 29-attempt cap with 15-no fail-fast (probabilistic majority filter, with a strict 21-attempt mode for special events), pay-to-launch + paid-regardless-of-vote (40 / 60 reward split favouring Delegated members, anti-bribery by design), adaptive bootstrap (linear threshold escalation per 100 members from founder-grants up to full 15+15, fee escalation alongside), periodic recertification (anti-slow-infiltration), commit-reveal voting (public who, private how), and Elder + permanent founder tiers for crisis-response. Eleven nested mechanisms in total; voting-systems.md §6.14 enumerates them. Two deliberate departures from earlier framing worth flagging in the record:

  • Founder permanence — the founder's special privilege is retained by default, revocable by the standard demotion process, rather than sunsetting on a schedule. Rationale: "look what happened to Bitcoin when Satoshi left" — having a trusted crisis-response actor is valuable, and the symmetric demotion path keeps the role accountable. Trade-off accepted; recorded honestly. A future ADR may revisit if the founder role becomes contentious.
  • Single-tier citizenship — admission is one substantial constitutional event; voting is unlimited thereafter (no "cheap citizenship + expensive delegated" two-tier split). The 30-voucher cost amortises over a participatory lifetime; the asymmetry between high-admission-cost and low-per-vote-cost is the strength. Per-vote cost is a per-poll parameter (zero for routine polls, substantial for high-stakes ones), not a system-wide constant.

A staged implementation roadmap (voting-systems.md §6.15) sequences the build from CitizenToken (Phase A) through Elder + lazy-voter detection (Phase F) to D-1 + regulatory pass (Phase G). No code shipped for this composition yet — the three primitives in contracts/ are the validated building blocks; the full assembly is the substantial future build.

Addendum 2 (2026-06-02) — three-stage gating + eight-layer founder-capture mitigation. Two further refinements consolidated into voting-systems.md §6.2 and §6.7.1:

  • Three-stage architecture recovers the founder's original ~2019 design that pre-dated this thread by ~6–7 years. The interim "bicameral with candidate-chosen friend chamber" of §6.2 was a simplification; the recovered original is structurally stronger: (1) pre-gate of 15 off-chain signed recommendations from existing Citizens (social-existence proof — not a vote), (2) Stage 1 chamber of 15-of-29 from random Citizens (sortition), (3) Stage 2 chamber of 15-of-29 from random Delegated Members (sortition). Neither voting chamber is candidate-controlled — friends only prove social existence, both votes are sortitioned. Three independent filters, not two. §7.3's capture analysis updated to reflect three filters in the sybil-flood scenario.
  • Founder-capture mitigation stack (§6.7.1) — seven layered defences bounding the persistent-founder trade-off recorded in Decision (founder-permanence): (1) multi-sig K-of-N founder + voluntary key rotation, (2) bounded action scope enumerated in contract, (3) pre-action announcement delay, (4) Elder freeze quorum, (5) elevated demotion path (§6.7.1.1) — strict 21-attempt mode in both random chambers plus Elder unanimous consent confirmation; routine 15+15 is insufficient for constitutional roles, (6) confession requirement (on-chain reasoning field), (7) on-chain action log. Two residuals are named precisely: an action-time residual (a captured founder during the announcement window with Elders also unresponsive can do bounded damage on one bounded-scope action before elevated demotion catches up) and a dormant-key residual (the absence of a dead-man's switch — deliberate, the founder is reserve / crisis-response and should not be forced to participate routinely — leaves a long-unused-key threat upper-bounded by the multi-sig threshold). Both qualitatively different from "unbounded permanent backdoor."

Two corrections from earlier framing on 2026-06-02:

  • Dead-man's switch removed. The initial mitigation stack included a heartbeat requirement, which conflicts with the founder's reserve / hands-off role. Removed; dormant-key threat is bounded by multi-sig + voluntary key rotation instead.
  • Routine demotion is insufficient for top-tier roles. Standard three-stage 15+15 applies to Citizens and Delegated Members; founder and Elder demotion use an elevated path — strict mode (21-attempt cap, ~71% support required) in both random chambers, plus an Elder unanimous (3/3 — every Elder confirms) as a final step. The Elder demotion path excludes the accused Elder from the final confirmation. Symmetric principle preserved: roles hard to earn are hard to lose, but never permanent beyond contest.

Addendum 3 (2026-06-02) — concrete bootstrap schedule + operational playbook. The §6.6 abstract "1+1 at N<100, etc." is replaced by a formula-driven 15-phase schedule:

  • conf = min(15, ceil(memberCount / 100)); cap = max(2, 2·conf − 1); pre-gate = conf; fee = floor(conf · baseFee).
  • This gives 1/2 (50%) at Phase 1, 2/3 (~67%) at Phase 2, 3/5 (60%) at Phase 3, ... settling at 15/29 (52%) at Phase 15 (mature). Strictest while the network is most vulnerable; honest-majority filter at maturity where multi-dimensional defences carry the load.
  • Phase 0 (0–99) uses founder + first-class-citizen single-sig grants (viral). At N=100 the bicameral process activates automatically; everyone admitted in Phase 0 is grandfathered.
  • Elder admissions open at N=300 with 3+3 + 1 founder confirmation (the founder substitutes because no Elders exist yet). As the Elder cohort grows the founder's role phases out: 1–4 Elders → founder + all Elders unanimous; 5+ Elders → founder + Elder unanimous (3/3 — every Elder confirms); mature → Elder unanimous consent alone, founder optional.

A new wiki doc, wiki/voting-systems-bootstrap-playbook.md, specifies the operational guide for launching: founder-cohort selection criteria, the publishable founding documents (founder roster, first-class-citizen cohort, schedule, bounded scope, action log, governance norms) that make the genesis-phase trust assumption externally evaluable, Phase 0 operational discipline, transition to Phase 1, Elder emergence at N=300, bootstrap risk register, and what the playbook does not guarantee (honest residuals — genesis cohort capture is an external sociotechnical risk that no code-level mechanism can eliminate). §7.5 of the design doc is updated to point to the playbook as the external-validation answer to the bootstrap-trust gap.

Addendum 4 (2026-06-02) — removal-bond economics: capture cost scales with the network. The procedural defences of Addendum 2 (elevated demotion paths, 21-attempt strict mode, Elder unanimous consent) raise the coordination cost of capture but not its monetary cost. A faction lucky with sortition could still trigger an essentially-free removal attempt, wasting community attention. The eighth layer of the founder-capture mitigation stack closes that gap economically and — critically — is the only layer whose deterrent strength scales with N. Recorded as voting-systems.md §6.7.1.3.

The mechanism:

  • Bond formula: bond(rank, N) = base · N · log₂(N) · rankMultiplier, with base denominated in Static-MBA units, N read from RegistryState at bond-post time, and rankMultiplier ∈ {1×, 10×, 100×, 1000×} for Citizen / Delegated / Elder / Founder.
  • Curve choice — N · log₂(N) not linear or quadratic. Linear undershoots at scale (a deep-pocketed clique can still afford 1M·base at N = 1M). Quadratic overshoots (1M²·base eliminates legitimate recall at maturity → capture by inertia, the bad outcome from another direction). N · log₂(N) keeps cost meaningful but tractable; reads cleanly as the bits-of-coordination cost of organising a faction of size N.
  • Worked example at maturity (N = 1M, base = 1 Static-MBA unit): Citizen = 20M, Delegated = 200M, Elder = 2B, Founder = 20B units. Crowdfunding affordable per-capita; solo-attacker pricing prohibitive.
  • Two-stage bond: 1 % posted at proposal (forfeited if pre-gate signatures fail) + 99 % at vote-initiation (forfeited on vote failure). Defence in depth — frivolous proposals burn the 1 %; capture attacks pay full freight.
  • Crowdfunded escrow (RemovalBondEscrow.cash, future). Any Citizen contributes; refunds proportionally on success; slashes proportionally on failure. Spreads cost across genuine supporters, spreads loss across coordinated attackers, and prices in community judgment before attestation runs.
  • Distribution on failure (revised 2026-06-02 — supersedes the burn proposal): 30 % to the defender (compensation for the asymmetric defensive cost they bore — bounded, not income, because attack frequency is geometrically self-extinguishing via §6.7.1.3 cooldown + doubling), 70 % to the protocol treasury (TreasuryVault.cash, future — running costs, audit bounties, founding-document infrastructure, Elder compensation, wallet trust-label maintenance). On legitimate-recall success, the bond is fully refunded to escrow contributors. The earlier burn proposal is dropped: at the magnitudes the formula produces (millions to billions of Static-MBA per event), burning discards usable working capital and the "defender solicits fake attacks for income" vector is self-extinguishing by attack cost alone — no rational attacker funds a 20B-unit attack to give 30 % to a defender they're colluding with. Treasury outflow governance is deferred to treasury-governance.md (not yet written); inflows accumulate in the vault until that is specified, and the same procedural defences that protect rank protect treasury custody (founder/Elders cannot drain unilaterally).
  • Cooldown + repeat-attempt doubling. A failed removal triggers a cooldown window (~max(1000, N) blocks) blocking re-attempts against the same target; subsequent attempts inside an escalation window (10 · cooldown) pay a doubled bond (capped at 8× base). Prevents the "keep retrying until sortition is lucky" attack — persistent harassment is geometrically self-destructive.
  • Denomination = Static MBA. Predictable (fixed BCH-rate by definition), aligned with the public-facing rollout primitive, decoupled from the Dynamic-Rate / Elevation surface. A future option (post-Elevation public) is to switch to Dynamic-Rate denomination so cost auto-rises with network activity; recorded but not adopted, since it couples this constitutional mechanism to a deferred surface.

What this does NOT solve:

  • A state-level attacker with ~20 B Static-MBA of burn-tolerance can still force removal at maturity. The bond raises cost; the procedural layers (Elder unanimous, strict mode) remain the last line. The bond is the first line that scales with N — necessary, not sufficient.
  • Defender does not post a bond — risks only rank, not capital. Asymmetric by design (attacker pays).
  • Cross-target aggregation: a coordinated multi-target attack (e.g., demote one Elder to weaken the cohort, then attack the founder) pays bonds independently per target; the 8× doubling cap is per-target. Aggregation across coordinated attacks is recorded as a future refinement.

Implementation pointer: RemovalBondEscrow.cash (new, future) + extension of RegistryState.cash to track cooldownUntil[target] and attemptCount[target] fields read by the escrow at bond-post time. Mechanism is fully specified in §6.7.1.3; the contract is not yet built. Cross-references: ADR-0025 (Static MBA — what the bond is denominated in), ADR-0038 (Transition/Elevation split — why Static-MBA denomination over Dynamic-Rate at first), voting-systems.md §6.14 (full nine-mechanism stack), voting-systems-bootstrap-playbook.md §10.5 (operational worked examples at bootstrap N).

Addendum 5 (2026-06-02) — per-vote participation cost: capture-cost scaling on the voter side too. Addendum 4's removal-bond raises the proposer's monetary cost but leaves the voter's cost at zero. A coordinated faction lucky with sortition can still cast 30 yes votes essentially free — only the proposer (or escrow underwriters) bears the bond risk. This leaves capture-by-mobilisation-of-many affordable even when capture-by-lone-attacker is not. Layer 9 of the founder-capture mitigation stack closes that. Recorded as voting-systems.md §6.7.1.4.

The mechanism:

  • Per-vote fee on elevated paths only (Founder + Elder demotion). Routine admission / demotion paths retain the §6.5 paid-regardless-of-vote reward; only constitutional-grade votes invert it.
  • Formula: voteCost(rank, N) = voteBase · √N · log₂(N) · rankMultiplier, with voteBase denominated in Static-MBA units, N read from RegistryState at commit time, rankMultiplier = 1× for Elder and 10× for Founder. The gentler √N · log₂(N) curve (versus the bond's N · log₂(N)) reflects that the fee is paid by each individual juror — many of whom are sortitioned, not self-selected — and per-juror cost must stay bounded.
  • Worked example at maturity (N = 1M, voteBase = 1): Elder vote = 20 000 units per juror; Founder vote = 200 000 units per juror. With 30 voters total on a Founder removal: 6 million units extracted from voters, meaningfully on top of the 20 billion bond, and specifically priced against mobilisation rather than against the proposer alone.
  • Treasury-routed on participation, not refundable (revised 2026-06-02 — supersedes the burn proposal). 100 % of the fee flows to TreasuryVault.cash at commit time, regardless of vote direction. Refunding voters on the winning side would re-introduce vote-with-the-predicted-winner gaming, collapsing voting into majority-prediction rather than independent judgment. Routing to treasury at commit preserves the secret-ballot property (§6.9) — the fee is paid for the act of voting, not the direction. The seriousness filter is preserved (still a real cost to participate); the value is captured productively rather than discarded. At maturity, a single Founder vote extracts ~6 M Static-MBA from the voter set — meaningful capital that funds audit, infrastructure, and Elder compensation.
  • No exemption for sortitioned voters. A sortitioned juror unwilling to pay simply declines (no commit); the streaming juror replacement (§6.3) draws the next-up juror. This is the filter against reflex voting at the highest stakes — the cost is the seriousness signal. The §6.11 lazy-voter problem is solved at the constitutional layer by economics rather than detection.
  • Inverts §6.5 explicitly for elevated paths. Routine voters are paid; constitutional-vote voters pay. Both have anti-bribery properties through opposite mechanisms — routine via paid-regardless-of-vote (no marginal income from voting either way), elevated via burn-on-participation (no marginal income, full marginal cost).

Why the bond AND the per-vote fee — they attack different threat models:

  • Bond = defends against lone-attacker or small-faction-with-deep-pockets. Cost concentrated on proposer / escrow.
  • Per-vote fee = defends against mobilisation-of-many. Cost distributed across voters.
  • Together they ensure both organisational shapes of capture (one party funds it; many parties split it) face N-scaling friction.

What this does NOT solve:

  • A genuine community consensus that the founder should be removed pays the fees and proceeds — by design. The per-vote fee filters unserious mobilisation, not serious mobilisation; serious mobilisation by an honest community is the legitimate use case.
  • State-level wealth can pay for proxies — same residual as the bond.
  • Sortition-selection capture would compound attacker cost but not block them — defence-in-depth, not silver-bullet.

Implementation pointer: Extension of CommitRevealVote.cash to escrow + burn the per-vote fee at commit (refund only if the juror declines and is replaced). Reads N from RegistryState.cash (already required by §6.7.1.3). Cross-references: §6.5 (the routine paid-regardless-of-vote pattern this inverts), §6.7.1.3 (the proposer-side bond this complements), §6.9 (commit-reveal — preserves the secret-ballot property during fee collection), §6.11 (the lazy-voter problem this solves at the constitutional layer), ADR-0025 (Static MBA denomination), voting-systems.md §6.14 (full nine-mechanism stack).

Addendum 6 (2026-06-02) — three-layer vote-integrity framing: qualification, counting, presentation. A subsequent design conversation introduced an organising frame that makes the whole §2–§6 design easier to read and easier to compare against historical voting systems. The frame splits voting into three independent attack surfaces, each of which can be strong or broken regardless of the others. Recorded as voting-systems.md §1.5.

The three layers:

  1. Qualification — who is allowed to vote? A captured registrar admits illegitimate voters, allows double-voting, lets sybils participate, or excludes eligible voters.
  2. Counting — who counts the votes? A captured counter miscounts — by error, by fraud, by selective inclusion / exclusion, by stopping the count early, by recounting until a target result emerges.
  3. Presentation — who displays the results? A captured presenter misreports — calls the election early, announces false totals, refuses detailed disclosure, presents data selectively.

The layers nest. Layer 3 can only honestly present what Layer 2 correctly counted; Layer 2 can only correctly count what Layer 1 legitimately qualified. A break at any layer poisons the layers above it. A voting system is integral only when all three are removed from any trusted intermediary, not just one.

Mapping to the existing design:

  • Layer 1 (Qualification) — the hard sociotechnical layer. All of §6 — three-stage gating, sortition, ranks, Elder consent, adaptive bootstrap, recertification, removal bond + per-vote fee. This is where ALL of Deviant's substantive Layer-1 work lives. Layer 1 cannot be made fully trustless on a pseudonymous chain — personhood proof is irreducibly external — but it can be distributed across many independent attestation acts (the §6 design) so no single attestor is load-bearing. The §7.5 honest-limits residual (bootstrap-cohort capture) is precisely a Layer-1 residual.
  • Layer 2 (Counting) — trustless by construction on a public blockchain. The covenant is the counter. CashScript bytecode verified by every full node enforces — at every transaction — that each admitted ballot increments the tally exactly once, no out-of-set ballots are counted, no in-set ballots are dropped. No human, institution, or off-chain aggregator participates. Mechanisms: §2.2 TallyState, §6.7.1.4 CommitRevealVote, §6.13 ApplicationCovenant, the co-spend-as-oracle pattern (decision 4 of this ADR).
  • Layer 3 (Presentation) — trustless by construction on a public blockchain. Chain state is the canonical surface. Any wallet, explorer, or observer retrieves the same bytes from the same UTXO. ADR-0008 (verifier-not-authority) is precisely a Layer-3 discipline — the wallet surfaces the result and its trust labels, never asserts a framed conclusion. Decision 5 of this ADR ("plutocracy is right for some votes and wrong for others; the wallet must label which") is the Layer-3 standing instruction.

Why this framing is more than a relabelling:

  1. It pins down what the §6 design IS. §6 is a Layer-1 design — the substantive contribution. Layers 2 and 3 are addressed at the covenant level (§2 primitives) and at the wallet-discipline level (ADR-0008) respectively; §6 does not duplicate them. Earlier readings of §6 sometimes conflated qualification with counting (e.g., "won't the founder cheat the count?" — no, the contract counts, the founder cannot affect Layer 2; the §6 concern is whether the founder can cheat who is allowed to be in the qualified set).
  2. It pins down what the plutocracy / sybil critique IS. Decision 3 ("sybil resistance is upstream of the signer and upstream of the tally — it lives in the allowlist issuer") and decision 6 (the hashwar lesson) are precisely Layer-1 critiques. The hashwar's failure was not at Layer 2 (PoW counted honestly) or Layer 3 (the chain announced honestly); the failure was at Layer 1 — who got to vote (anyone who could rent hashpower), and the upstream rental market was capturable by USDT-printed off-chain capital. This frame lets future analysis classify capture vectors precisely instead of arguing in mixed-layer terms.
  3. It gives a precise vocabulary for historical comparison. Most historical voting failures are one-layer breaks. The §1.5 examples — US 2000 punch cards (Layer 2 break), US election-night TV calls (Layer 3 break), one-party states (all three captured), Estonia e-voting (Layer 2 contested), BTC/BCH hashwar (Layer 1 break) — make this immediately operational for analysts comparing the Deviant design to existing systems.

No code or contract change. This is a documentation and framing addition. The mechanisms are unchanged; the organisation of how they're explained is sharpened. Earlier sections (§2–§7) continue to function as the substantive mechanism record; the new §1.5 frames where each piece fits in the qualification / counting / presentation taxonomy.

Cross-references: ADR-0008 (verifier-not-authority — Layer-3 discipline), voting-systems.md §1.5 (the framing itself), §5 (plutocracy / sybil — Layer-1 critiques recast), §6 (the substantive Layer-1 design), §7 (historical comparison — now readable as cross-layer comparison), Knowledgebase/03_historical_and_political_economic/deviant_voting-systems_FOR-sophisticated-readers-and-political-economic-analysts.html (public reference doc updated with the framing).


ADR-0041 — Wallet-side WalletConnect v2 responder for the Apps tab; live wiring on top of the validated proposer + scaffolded UI

Date: 2026-06-01 Status: Accepted — design direction; live wiring + device validation pending. Full design in wiki/connector.md. Driven by the 2-device Checkers integration test.

Context: The proposer (dApp) side of the connector is complete and on-device validated against Cashonize (snapshot 0057, mainnet bch_signTransaction co-sign) — src/connect/proposer.ts + spike/wc-connect/. The wallet (responder) side has the UI shipped in the Apps tab of shells/app/ (slice 9, snapshot 0060) — Connect → URI auto-detect → app preview → SigningPreview modal — but per BETA_TESTING.md §5 it is explicitly "a UI simulation — no live dApp sessions." The handlers in AppsScreen.tsx are setSessions(...)/setSigningReq(...) with hardcoded mock data; no @walletconnect/* packages are installed in shells/app/; no shells/app/src/connect/ directory exists. A 2-device test against the Checkers game (this session 2026-06-08) is the concrete driver: each player runs the Deviant chipnet beta, pairs with the game over WalletConnect, co-signs only their own stake input into GamePoolEscrowFlat.

Decision: Wire a live WalletConnect v2 responder into the existing Apps-tab UI, mirroring the namespace + methods + wire format the proposer (and the rest of the BCH WC ecosystem) already speak.

  1. Add the SDK. @walletconnect/web3wallet + @walletconnect/core (the responder side of WC v2) as dependencies of shells/app/. The proposer keeps using @walletconnect/sign-client.
  2. New module shells/app/src/connect/ with three files:
    • responder.ts — init(projectId), pair(uri), event surface for the UI (session_proposal, session_request, session_delete), method-dispatch table, response/reject paths. Bridges to the CoreSession already carried in App.tsx.
    • signer.ts — signCoSign({tx, sourceOutputs, ownPubkeyHash, ownPubkey, privateKey, network}) returning {signedTransaction (hex), signedTransactionHash (hex)}. Uses libauth: decodeTransaction → find inputs whose sourceOutputs[i].lockingBytecode == p2pkh(ownPubkeyHash) → generateSigningSerializationBch → sha256 → secp256k1.signMessageHashDER → append sighash byte → write unlocking bytecode → encodeTransaction.
    • index.ts — barrel.
  3. Mirror the proposer's wire spec EXACTLY (snapshot 0057 was load-bearing):
    • Namespace bch; chains bch:bchtest (chipnet) and bch:bitcoincash (mainnet — exposed in shell builds where ALLOWED_NETWORKS permits; the chipnet beta is chipnet-only by design).
    • Methods bch_getAddresses, bch_signMessage, bch_signTransaction.
    • Account string bch:<chain>:<cashaddr>.
    • Params/response encoded via libauth stringify (Uint8Array → "<Uint8Array: 0x..>", bigint → "<bigint: ..n>"); the responder reverses via libauth parseExtendedJson. Plain hex / decimal / raw arrays are rejected — proven against Cashonize in 0057.
  4. Replace AppsScreen.tsx mock handlers with real ones. Thread session: CoreSession | null from App.tsx into AppsScreen. openConnect → responder.pair(uri). Real session_proposal populates app metadata; Allow approves with the user's address(es). Real session_request populates the SigningPreview from the parsed request — and for bch_signTransaction over a Deviant covenant, re-derives the contract address from published params and renders "✓ canonical <template>" via the existing D-1 multi-template verifier (ADR-0008/0015/0018 — verifier-not-authority at signing time).
  5. Signing discipline (structural non-custody): the signer only signs an input whose source-output lockingBytecode equals our own P2PKH. Inputs the dApp built with another player's placeholderP2PKHUnlocker are untouched. A rejected request returns the standard getSdkError('USER_REJECTED').
  6. CashConnect responder is deferred to a follow-up ADR. The CashRPC differences (snapshot 0061 — _V0-suffixed methods, SignTransactionV0Params[] shape, cc: URI scheme, internal stringify/parseExtendedJson) need a parallel adapter on the wallet side, and validation against Cashonize / BCH Guru.

Alternatives considered:

  • Do nothing; tell players to use Paytaca/Cashonize. Rejected as the long-term answer — the Deviant wallet is the project's flagship surface and the integration with Deviant covenants (D-1 contract recognition at signing) is uniquely valuable. Paytaca/Cashonize remain valid stopgaps while the responder is wired.
  • Wire a full CashConnect responder first (richer contract-template recognition). Deferred. WC v2 is the proven-against-Cashonize path (snapshot 0057), strictly simpler, and the entire BCH dApp ecosystem already speaks it; CashConnect follows as an enrichment once WC is live.
  • Run the responder in the server rather than the shell. Rejected — that would require funneling the user's private key to a server (custodial) or building an additional bridge for each sign call. The shell already holds the key (WebKeyStore/CapacitorKeyStore), so the responder lives where the key is. Non-custody preserved.
  • Build a Deviant-proprietary transport instead of WC. Rejected — ADR-0008/0016 plurality; the BCH ecosystem has settled on wc2-bch-bcr and ~10 dApps + 4 wallets speak it (bitcoincashresearch.org/t/wallet-connect-v2-support-for-bitcoincash).

What this ADR does NOT resolve:

  • Exact sighash type (default SIGHASH_ALL | SIGHASH_FORKID; covenants that require a different sighash must surface it in the request).
  • bch_signMessage signature algorithm — BIP-322 vs. simple ecdsa(sha256(msg)). Mirror what Cashonize uses; verify against the proposer.
  • WC v2 projectId management — env var in dev (WC_PROJECT_ID), build-time inject in prod; key rotation policy.
  • The CashConnect responder (deferred — separate ADR).
  • WizardConnect — probe whether it's WC v2 under the hood (then "free") or a distinct SDK requiring its own adapter (per snapshot 0057 "Next").

Implementation pointers:

  • shells/app/src/connect/{responder,signer,index}.ts (new).
  • shells/app/src/screens/AppsScreen.tsx (replace mock handlers; add session prop).
  • shells/app/src/App.tsx (pass session to AppsScreen).
  • shells/app/package.json (add @walletconnect/web3wallet, @walletconnect/core).
  • Cross-references: wiki/connector.md (full design, both halves), src/connect/proposer.ts (the mirror image), spike/wc-connect/connect.mjs (the namespace/chain/methods the responder must accept), integrations/checkers-dea.md (the driver — 2-device test), snapshot 0057 (the proposer co-sign proof, including the libauth stringify wire-format requirement), BETA_TESTING.md §5 (the limitation this ADR closes), ADR-0008/0015/0018 (D-1 multi-template verification at signing time), ADR-0016 (the WC routing decision this fulfills).

ADR-0042 — MBA shares: a BCH-backed equity instrument that pays holders a split of transaction fees

Date: 2026-06-10 Status: Accepted — design finalized at the property level; collection-side chipnet prototype pending. Full design record in Parameters/ASM_Native/MBA_Shares/mba-shares_complete-design_FOR-design-record.md.

Context: The oracle-priced MBA (DeviantOracleMBA), the fee path to a recipient locking bytecode (founder fee / EURm 5% / descfee), the voting + sortition primitives (PollCovenant, SortitionBeacon, CitizenToken), and the oracle-integrity stack (designed for USDm) are all proven or designed. The open question was whether an equity instrument — shares with dividends and shareholder rights — fits the MBA category, and if so what its properties are. The existing BinaryShareVault* / SharePairAMM contracts are prediction-market shares (collateralized binary outcomes), not company equity; this ADR concerns a genuinely new instrument. Driven by the design thread on a "shares asset with dividends for shareholders" (e.g. a Hertz payment-coin whose fees flow to holders).

Decision: Define the MBA share as a distinct category — not a re-implementation of a stock certificate — with the following finalized properties.

  1. Price = backing is an identity, not an estimate. value == amount × rate at consensus. Canonical equity machinery that exists to cope with price ≠ backing (valuation/DCF/P/E, residual-claimant risk + liquidation waterfall, issuer solvency/credit risk, disclosure-as-protection, illiquidity) has no referent here. See the design record Part II for the obsolete / transformed / remaining table.
  2. Value is BCH-shaped and enterprise-shaped, reflexively coupled (corrects the earlier "unbundled axes" framing): value(fiat) = Q_sats × P_bch, where Q_sats (the consensus floor) ratchets up with retained enterprise revenue (enterprise-driven) and P_bch is exogenous BCH price. Multiplicative and reflexive (enterprise success is stored as BCH; BCH appreciation amplifies enterprise value). The mechanisms are unbundled (explicit floor / income / vote); the value stays coupled, but now legible and floored.
  3. Base is a DEA (unlimited, permissionless mint). Dilution is irrelevant: new units cannot dilute the floor (each is independently BCH-backed — you cannot print backing); unlimited mint dilutes only yield, which self-corrects when fee revenue scales with supply (yield ≈ activity ÷ supply). Anti-dilution / pre-emptive rights / authorized-share ceilings therefore have no referent.
  4. The dividend is a split of transaction fees, in the fee event itself: companyBps → company treasury (direct, like the proven founder fee) + shareholderBps → a distribution covenant. shareholderBps is the company's cost of capital (shareholders supply the locked BCH backing; the fee share is their return); companyBps is operating margin. The split is immutable at genesis by default (constitutional guarantee), optionally a genesis-fixed band, and if governed must be bicameral (pure shareholder governance carries the vote-themselves-100% conflict).
  5. Distribution is oracle-attributed, time-weighted, with no claimable pool. Fees are attributed to whoever holds at the moment each fee occurs, proportional to (time-weighted) share. This defeats the mint-then-redeem "drain" by construction: to earn you must hold while transactions happen (real locked capital); a flash mint-redeem captures nothing and in fact donates part of the minter's own fee to existing holders. Time-weighting erases the single-large-payment sandwich.
  6. Output-count, not a pool → attribute-on-every-fee, settle-in-batches. Since a payment tx cannot emit an output to every holder, the oracle credits each holder per fee event and settles periodically; the in-flight amount is earmarked per holder (a transit buffer), not a drainable pot.
  7. Buffer security: consensus-locked, not custodial. The shareholderBps stream routes to a distribution covenant whose only valid output is oracle-attested payouts to holders — the company cannot take or freeze it. Bounded residual risks: oracle compromise ≤ one settlement period's fees (mitigated by the integrity stack); liveness via a Merkle-snapshot timeout backstop. Period length is the security/cost dial (daily…monthly) → a genesis parameter or bounded governance choice. Verdict: good enough iff covenant-locked + integrity stack + liveness backstop; max loss = one period's shareholder-fees.
  8. Single-asset vs dual-asset is open-shareholding vs scarce-equity — the choice is a capital strategy, not a default (revised from the earlier "dual recommended"). The oracle handles holder-counting/timing/time-weighting in either; an elastic DEA supply dissolves the old liquidity/conflation objections (savers hold → others mint more to transact). The fundamental, structurally-irreducible difference is supply policy: single (unlimited) = open shareholding — anyone owns by holding, dividends are a broad diluted yield F / N_growing (growth absorbed by new entrants); dual (capped shares) = scarce equity — F / N_fixed scales 1:1 with revenue, existing holders capture the growth (EPS property), shares carry a scarcity premium. You cannot have unlimited currency AND scarce equity in one token (contradictory supply policies) — dual decouples the two supply curves, which is the only way a fixed shareholder set captures an unlimited-volume network's growth. Hoard-vs-spend in single-asset is a tunable, self-correcting parameter, not a flaw (yield is funded by velocity → homeostatic). Recommendation: single-asset for alignment/inclusion (the default for a customer-facing payment coin — "money that earns while remaining money"); dual+capped for concentrated investor upside / fundraising; dual-with-shares-earned-through-usage to get alignment + scarce-equity together.
  9. Securities posture — extraction is the front-half driver; prong (4) is decisive (honest, not legal advice; corrects an earlier draft). Howey needs all four of (1) investment of money, (2) common enterprise, (3) expectation of profit, (4) efforts of others. (a) Extraction (the issuance split) establishes prongs (1)+(3) — handing the company a non-recoverable slice to earn income is, almost verbatim, "investment of money in expectation of profit." (b) Extraction does NOT create prong (2) (an earlier draft wrongly said it "introduces the pool"): the extracted slice is the company's revenue, not a commingled managed fund; dividends come from a separate automated fee stream distributed pro-rata by oracle — revenue-sharing, not a pool → horizontal commonality weak by construction. (c) Automation lowers but only relocates prong (4): no manager decides to pay (oracle does), no custodian holds principal (floor is in the holder's UTXO) — but the fee revenue still depends on an operator running the business. (d) Prong (4) — whose efforts generate the fees — is therefore the decisive axis: a centralized operator (Hertz) → likely a security even with no pool + full automation; operation-level decentralization (not merely issuance/custody) → "others" dissolve → (4) weakens. (e) The extraction dial = the fundraise dial = the security dial: more extraction → more capital raised → more clearly a security; the pure revenue-share end (0% extraction, 100% non-custodial redeemable floor) is least security-like — arguably no "investment of money" (unilateral redemption) and no pool — which is the project's own unilateral-redemption discriminator (ADR-0033) / obligor-free MBA (ADR-0025) predicting less-security from security. Securities law governs raising capital from investors; the pure revenue-share escapes the front half because it raises nothing. Not legal advice; Howey is fact-specific; "weaker case" ≠ "not a security."
  10. The instrument has three independent dials, not one fee: (i) issuance split / extraction extractBps — at mint, extractBps·V → company treasury (raised capital), (1−extractBps)·V → redeemable floor; the downside/fundraising dial (0% = pure yield-note raising nothing; 40% = real raise with a 60% consensus floor; 100% = classical at-risk equity). Makes the share a capped-downside instrument (max loss = the extracted fraction; floor unilaterally redeemable, better than a wipeable liquidation preference); thesis = break-even when cumulative dividends ≥ extracted fraction. (ii) fee split companyBps/shareholderBps — the coupon, any ratio, neither side zero. (iii) dividend horizon T — the term.
  11. Dividend horizon (term dial) turns the share into a bond, and the three dials span the equity↔debt spectrum. The dividend right may expire after T (hard expiry / cumulative-payout cap / decay), clock from issuance not acquisition (per-holder reset is gameable; per-share is transfer-safe, like bond maturity). Floor (permanent) + expiring coupon = a secured term note, not perpetual equity; it re-prices cleanly as floor + PV(remaining dividends); rolling tranches mean no perpetual rent. In the oracle model the limit is one line (weight 0 once age > T); grounded in ExpiringMBA (snapshot 0066). Composed, floor × coupon × term = one covenant family spanning perpetual yield-note → floored perpetual equity → secured term note → unsecured note; the time-limit is what unlocks the debt half. Taxonomy flag: expiry + floor + bounded coupon is categorically a bond/note, not equity (and the Howey/Reves note analysis differs).

Alternatives considered:

  • Shares as a fixed-supply (PLA/MTA) asset with board-authorized issuance and anti-dilution rights. Rejected as the base — dilution is structurally irrelevant on an MBA (Decision 3), and unlimited mint is correct for a currency. Bounded supply re-enters only as the ShareAsset leg of the dual model (Decision 8), for a defined cap table.
  • Accumulating dividend pool with a rebasing redemption rate (u × pool/supply). Rejected — drainable (mint a huge M, redeem, capture M × pool/(supply+M)). Replaced by the no-pool, attribute-at-fee-time, time-weighted model (Decisions 5–6).
  • Company-custodied buffer paying out each period. Rejected — full custody risk (company could abscond with or freeze up to a period's accrual). Replaced by the consensus-locked distribution covenant (Decision 7).
  • Registered-NFT shares (per-holder accumulator checkpoint) as the primary model. Deferred to the trustless-community variant. The operator-run oracle attribution is simpler (no global-state contention) and bounded by the pool cap + integrity stack; the NFT-accumulator remains the fully-trustless option.
  • Merkle-snapshot drop as the primary distribution. Retained only as the liveness backstop; oracle attribution is the hot path because the project already operates the oracle-integrity stack.

What this ADR does NOT resolve:

  • The exact covenant encoding of the distribution buffer (output structure, how oracle attestations name recipients and bound totals to ≤ pool).
  • The settlement period default (a genesis parameter / governance choice — daily…monthly).
  • The earn-shares-through-usage drip rate and its anti-gaming bounds.
  • Regulatory positioning per deployment (protocol-toolkit vs operator-issued) — a positioning decision, deployment-specific.
  • Whether the fee split is ever governed (and the exact bicameral mechanism if so).

Implementation pointers:

  • Parameters/ASM_Native/MBA_Shares/mba-shares_complete-design_FOR-design-record.md (full design).
  • Collection side (first step): fork the proven fee path (DeviantOracleMBA / DeviantOpenMintFT fee output) to split companyBps (direct) + shareholderBps (→ distribution covenant).
  • New DistributionCovenant.cash (consensus-locked; oracle-attested payouts; period; Merkle backstop) — reuses the checkDataSig pattern proven in DeviantOracleMBA and PegState.
  • Oracle accountant: time-weighted holdings attribution per fee event, batched settlement, integrity-stack governance (Parameters/ASM_Native/FIATm/fiatm_complete-design_FOR-design-record.md).
  • Cross-references: ADR-0027/0028/0033 (token vs asset; secured ≠ backed; unilateral-redemption discriminator), wiki/asset-landscape.md (MTA / closed-end-fund-share peer), wiki/voting-systems.md (bicameral + sortition reused by the split and oracle), snapshots 0136–0141 (oracle MBA + fee mechanics this builds on).

ADR-0043 — Multi-protocol wallet responder: WalletConnect + CashConnect over one WC2 client, dispatched by shape

Date: 2026-06-13 Status: Accepted — runtime built in shells/app/src/connect/, tested headlessly, device pairing pending. Full design in wiki/connector.md. Extends ADR-0041 (WC-only origin) and closes the CashConnect-deferral ADR-0041 §6 flagged.

Context: ADR-0041 wired a WalletConnect-only responder and explicitly deferred CashConnect to a follow-up ADR ("CashConnect responder is deferred to a follow-up ADR. The CashRPC differences need a parallel adapter on the wallet side"). Two findings from snapshots 0061 + 0062 made the follow-up tractable:

  1. CashConnect rides the same WC2 relay infrastructure (@reown/walletkit under the hood). Both protocols share the relay, the bch namespace name, and the libauth stringify wire encoding — they diverge only in proposal shape (CC adds protocol: 'CashRPC:0'), method names (_V0-suffixed for CC), payload encoding for request.params (CC sends one EJSON-string blob; WC sends a tree of EJSON-marker strings), and the URI scheme prefix (cc: vs wc: — purely a UI hint).
  2. CashConnect V0's bch_signTransaction_V0 is wallet-orchestrated — not a dApp-built pass-through. The wallet builds the tx from instructions, auto-adds its own unspents and change, and runs a libauth VM verify before signing. This means standard sends (BCH / CashTokens) work cleanly over CC, but Deviant covenant txs (tote bet(), GamePoolEscrowFlat.fund()) route through WalletConnect's true pass-through bch_signTransaction (snapshot 0062 Finding 3). Multi-protocol wallets like Cashonize speak both, so users never see the routing decision.

These two findings unlock a single-client multi-protocol architecture: one @reown/walletkit instance, one relay socket, with the dispatcher routing each proposal/request to the right handler by shape.

Decision: Implement the multi-protocol responder in the wallet shell (shells/app/) as one shared client with shape-based dispatch.

  1. One WalletKit instance per shell, dispatched by proposal/request shape.

    • classifyProposal(p) → 'wc' | 'cc' — 'cc' iff p.requiredNamespaces.bch.protocol === 'CashRPC:0', else 'wc'.
    • classifyRequest(method) → 'wc' | 'cc' — 'cc' iff method matches /_V\d+$/ or equals 'wc_authRequest', else 'wc'.
    • classifyUri(uri) → 'wc' | 'cc' | null — 'cc' for cc: / cashconnect: / bchconnect: prefixes; 'wc' for wc:; null falls back to proposal-shape classification once the proposal arrives.
    • All classifiers are pure and unit-tested (test/connect-classifier.test.ts, 16 tests).
  2. Per-protocol approval shapes at session_proposal.

    • WC approval: namespaces.bch = { chains: [chain], methods: WC_METHODS, events: [], accounts: ['bch:<net>:<cashaddr>'] }.
    • CC approval: namespaces.bch = { chains: SupportedChains, methods: CC_METHODS, events: ['balancesChanged'], accounts: ['bch:<net>:<pubkeyHex>'] }. The account is a public key, hex-encoded — CashConnect has no bch_getAddresses (snapshot 0062 Finding 1).
  3. Per-protocol request dispatch.

    • WC bch_getAddresses / bch_signMessage / bch_signTransaction → wc-handler.ts (delegates to signer.ts for the libauth pass-through co-sign).
    • CC bch_*_V0 / wc_authRequest → per-session CashRPC_V0 instance from the cashconnect package, constructed in cc-handler.ts. We use only the CashRPC_V0 standalone class (it's exported); we do NOT use CashConnectWallet because it would instantiate its own competing WalletKit.
  4. Signing discipline preserved from ADR-0041 §5 (structural non-custody). signer.coSignTransaction only signs inputs whose sourceOutputs[i].lockingBytecode equals our own P2PKH. Never any other input, even if asked. BCH_SIGHASH_ALL_FORKID = 0x41 is the default; covenants needing a different sighash will surface it explicitly in a future protocol extension.

  5. Routing rule (the only thing dApps see):

    Flow Protocol
    User outbound BCH send / token send WC or CC (both fine)
    dApp covenant co-sign (tote, escrow, checkers) WC only (CC V0 can't carry covenant unlockers)
    Authentication / bch_signMessage either
  6. Dependency injection so the runtime stays testable: the shell provides the WalletKit instance (already initialised against the relay with its projectId), a KeyProvider (sealed access to the active KeyStore's private key + identity + cached pubkey hash), and a NetworkProvider (getSourceOutput, optional getUnspents). The responder owns no secrets, no relay lifecycle, no network access.

  7. Bundle patch persisted in shell. shells/app/patches/apply-cashconnect-fix.mjs rewrites the cashconnect@0.0.25 ESM bundle's import Client from '@walletconnect/sign-client' → import { SignClient as Client } to work around sign-client's ESM default-export being the package namespace rather than the class (snapshot 0062 Finding 1). Wired as postinstall; idempotent.

Alternatives considered:

  • Two side-by-side stacks (CashConnectWallet from the cashconnect package + a plain WalletKit for WC) over the same projectId. Rejected — burns two relay sockets per device and risks double-handling of session events. The package's CashConnectWallet doesn't allow injecting an outer WalletKit, so we'd have no way to deconflict. The standalone CashRPC_V0 class (also exported) gives us all the request-processing without the relay-owning plumbing.
  • Pin protocol per session at the UI / require the user to pick "WC or CC" before pairing. Rejected — the URI prefix already conveys this (cc: is the explicit CashConnect rewrite per the cashconnect bundle), and even when it doesn't, the proposal namespace shape does. Forcing a user-visible toggle leaks plumbing detail with no benefit.
  • Extend CashConnect V0 to carry dApp-supplied covenant unlockers (i.e. fix it upstream). Out of scope. The BchNamespace.template field technically lets the dApp supply a libauth WalletTemplate, but the wallet auto-orchestrates UTXO selection / change / VM-verify after parsing, breaking covenant input structure in practice (snapshot 0062 Finding 3). Worth tracking for CashConnect V1+; not load-bearing for shipping the responder.
  • Move the responder to a server. Re-rejected (ADR-0041 alternative #3) — would require funneling the user's private key to a server. Non-custody is the entire reason the responder lives in the shell next to the KeyStore.

Wire format (matches the proposer, snapshot 0057):

  • libauth stringify for outgoing payloads (Uint8Array → "<Uint8Array: 0x..>", bigint → "<bigint: ..n>").
  • A local reviveExtendedJson (libauth 3.1 doesn't export a parseExtendedJson) walks the parsed JSON tree to revive the markers. Patterns are unambiguous; the responder treats decoded params as untrusted regardless.
  • CC's request.params arrives as a single EJSON-string blob (per the cashconnect RequestEvent.params: string type) and is parsed before revival.
  • WC's request.params arrives as a JSON tree of EJSON-marker strings (the proposer's toWcParams shape) and is revived directly.

What this ADR does NOT resolve:

  • Wiring AppsScreen.tsx's mock handlers to the live Responder. Separate UI replacement; the runtime is the load-bearing piece this ADR ships.
  • bch_signMessage exact wire format (BIP-322 vs simple ecdsa-DER(sha256(msg))) — ADR-0041 already deferred this; on-device validation against Cashonize will confirm.
  • BCH WC chain id for testnet4 — the spec has no canonical chain there; the shell currently maps testnet4 → bch:bchtest for transport (networkToChain) and documents this.
  • CashConnect signTransaction for standard sends — implemented (cc-handler.ts routes through CashRPC_V0.signTransaction) but on-device behavior is unverified; covenant flows route through WC per the structural finding and need no further work here.
  • Device pairing validation. The runtime headlessly type-checks, unit-tests pure pieces (31/31), and builds via vite build (19s); the WalletKit loop is exercised on-device against Cashonize, BCH Guru, and the tote — separate session, separate snapshot.

Implementation pointers:

  • shells/app/src/connect/{bch-namespace,classifier,signer,wc-handler,cc-handler,responder,index}.ts (new this ADR; the typed surface in index.ts extends ADR-0041's WC-only surface to multi-protocol).
  • shells/app/test/connect-{classifier,signer}.test.ts — 31 unit tests, all green.
  • shells/app/package.json — adds @reown/walletkit, @walletconnect/core, @walletconnect/sign-client@2.17.0 (pinned for the cashconnect ESM-default workaround), cashconnect@0.0.25, @bitauth/libauth@3.1.0-next.8. Plus a postinstall running the cashconnect bundle patch.
  • shells/app/patches/apply-cashconnect-fix.mjs — the bundle patch (idempotent; same as spike/wc-connect/patches/).
  • Cross-references: wiki/connector.md (full design, this ADR's deep-dive), src/connect/proposer.ts (the dApp-side mirror, with its own header note updated this session to reflect the WC vs CC split), snapshot 0061 (CashConnect adapter pinned to installed API), snapshot 0062 (sign-client bundle patch + structural finding), snapshot 0152 (this implementation), ADR-0041 (the WC-only predecessor this extends).

ADR-0044 — BCH-native non-custodial P2P fiat market (covenant escrow + off-chain fiat leg)

Date: 2026-06-14 Status: Proposed (design only — no contract or UI shipped). Captures a market-gap thesis and a primitive-mapping so a future build has a spec to argue with. Builds on the on-ramp aggregator (ADR-0036) as the zero-friction fallback, and reuses the escrow / oracle / order-book families (ADR-0024 EventPredictionEscrow, ADR-0030 ShareLimitOrderBCH, the DeviantOracleMBA / EURm oracle).

Context — the gap (verified 2026-06): there is no non-custodial, on-chain P2P fiat↔BCH market. The landscape splits cleanly and leaves one cell empty:

Layer BTC ETH BCH
Custodial multi-fiat P2P Binance / Bybit / OKX P2P ✓ ✓ (custodial only)
Non-custodial on-chain P2P fiat Bisq, Hodl Hodl, Peach, LocalCoinSwap LocalCoinSwap — empty —
Non-custodial P2P fiat on L2 RoboSats (Lightning hold-invoices) — —
Non-custodial on-chain DEX (crypto/crypto) — Uniswap Cauldron, TangoSwap
Non-custodial on-chain derivatives / fiat hedge — — BCH Bull / AnyHedge

Key facts behind the table:

  • LocalCoinSwap proves the multi-fiat + non-custodial + on-chain model is viable and (largely) survives regulatory pressure — but it is BTC (L1 multisig) + ETH (contract) only; it delisted BCH in Aug 2019 and never re-added it.
  • Bisq / Hodl Hodl / Peach are on-chain but BTC-only, stuck on 2-of-3 multisig escrow — which is why they never expanded the conditional logic past simple release.
  • RoboSats is non-custodial but pushed escrow onto Lightning hold-invoices (HTLC), not L1 — precisely because L1 BTC Script can't express the conditional/timeout escrow it wanted.
  • AgoraDesk / LocalMonero (the last multi-coin non-custodial P2P with BCH reach) wound down by Nov 2024 — regulatory pressure + the irreducible fiat-leg dispute load.
  • BCH-native apps fill the crypto/crypto and fiat-hedge cells (Cauldron/TangoSwap DEX; BCH Bull synthetic fiat exposure via AnyHedge) — but none is a P2P fiat market: BCH Bull gives oracle-pegged price exposure settled in BCH, never a real-EUR counterparty trade.

The refined thesis: incumbents had to choose on-chain XOR programmable (Bisq's rigid L1 multisig, or RoboSats' programmable-but-L2 Lightning). BCH CashScript covenants give both — L1 settlement with L2-grade conditionality (timelocks, oracle-conditioned release, arbiter co-sign, escalating bonds) in a single transaction, no second layer. The empty cell is BCH's to take, on the chain whose escrow is strictly more expressive than the BTC/ETH approaches the proven platforms settled on.

The central open problem — the fiat leg is an oracle problem, not a contract problem. The covenant makes the crypto side trustless (BCH locked, released, or refunded — all chain-visible). It is blind to the bank: "did €200 arrive in IBAN X?" is an off-chain fact the covenant cannot read, so someone must attest it. That attestor is the single source of every dispute. Two mirror attacks define the threat model:

  1. Seller hostage — buyer pays EUR; seller refuses to confirm ("never received it"). Chain can't contradict.
  2. Buyer fraud / reversal — buyer claims/forces release without paying, or pays then reverses (SEPA recall, card chargeback, PayPal) after the irreversible BCH has moved. The irreversible-crypto / reversible-fiat asymmetry is the deep structural wound; no covenant closes it.

Decision (design): model the market as a bilateral escrow covenant + off-chain order book + a layered dispute apparatus, mapping each piece onto an existing Deviant primitive rather than inventing new contracts:

  1. Escrow covenant = generalized EventPredictionEscrow (ADR-0024). That contract is already a bilateral lock with (a) an oracle-signed resolution branch (resolveOracle — checkDataSig over {id ++ outcome}) and (b) a refund-on-timeout safety net (refund after deadline). A fiat trade is the same shape: BCH (+ bonds) locked; "fiat arrived" is the attested outcome that releases to the buyer; timeout refunds the seller. The result-oracle slot becomes the fiat attestor (seller signature in the happy path; arbiter or banking-oracle in dispute).
  2. Order book = ShareLimitOrderBCH (ADR-0030) + the CLOB experiments (chipnet:clob-*). A resting "sell BCH for EUR at rate R, SEPA only" offer is an ASM swap-offer parked in a covenant UTXO with partial-fill + cancel — exactly ShareLimitOrderBCH's mechanic, generalized from shares↔bits to BCH↔(fiat-claim). Matching can stay off-chain (relay) initially; the on-chain resting-order leg already exists if/when wanted.
  3. Pricing / peg = the DeviantOracleMBA (EURm/USDm) oracle. The same 16-byte AnyHedge-format signed price message (src/oracleUsdm.ts) gives the reference rate for offers and for valuing bonds across the BCHm fiat family (EURm, GBPm, JPYm, INRm) — multi-currency falls out of the existing family, no per-currency contract.
  4. Dispute apparatus — make cheating expensive, slow, unprofitable (you do not eliminate the trust gap, you price it):
    • (a) Bilateral security bonds locked in the same covenant — grief/vanish forfeits the bond (Bisq's core deterrent; turns "refuse to confirm" from free to costly). Bond size can scale with payment-method reversibility (see (d)) using the escalating-NFT bond machinery already in-tree (EscalatingPriceNFT family).
    • (b) Timeout default via the refund branch — funds never freeze forever; the default direction is a tuned parameter, never the sole resolution.
    • (c) Arbiter as 2-of-3 co-signer — happy path is buyer+seller; on dispute the arbiter reviews off-chain evidence and co-signs with the honest side. The arbiter can break ties but never steal (always needs a second sig). Arbiter selection can later be randomized/rotated from a pool via SortitionBeacon (commit-reveal) to resist arbiter capture/collusion.
    • (d) Payment-method tiering — reversible rails (card, PayPal, standard SEPA recall window) carry larger bonds or are blocked; irreversible/instant rails (SEPA Instant, regional push rails) get cheap bonds. Policy, not contract.
    • (e) Reputation — generalize src/onramp/reputation.ts to per-device + plural-external signals; the long-game deterrent for identity-continuous makers.
  5. Front-end = one shared API, two thin clients. Reuse src/web/{api,server}.ts and shells/app; add a web Mini-App page and a Telegram bot / Telegram Mini App over the same endpoints (matching ADR-0036's surface style). The wallet/escrow logic is shared, never duplicated per channel.
  6. Custody / regulatory posture (the reason to do B not C): funds live only in covenants; neither the operator nor any server ever holds user BCH or user fiat (fiat moves bank-to-bank between users). This keeps the platform a matchmaker + escrow-enforcer, the Bisq/Hodl Hodl/AgoraDesk class, not a money transmitter / CASP — the line that forces Binance-style licensing. Crossing into holding fiat or BCH inventory is model C (a licensed exchange) and is explicitly out of scope here.

Alternatives considered:

  • Custodial P2P (Binance/Bybit model). Rejected — it's model C: requires custody, KYC/AML apparatus, and (EU) a MiCA CASP license + a banking partner that tolerates crypto. Heavy legal+capital, contradicts the repo's non-custodial / on-chain-enforcement philosophy.
  • Stay aggregator-only (extend ADR-0036 with more fiat→BCH adapters). Not rejected — kept as the complementary fallback. It's the right "I just want BCH now" path, but it's dependent on third-party fiat doors (today only Guardarian is a genuine EUR door, ~€700/tx) and never gives a peer market. B and A coexist.
  • Lightning-style L2 escrow (RoboSats path). Rejected for BCH — the entire BCH advantage is that L1 covenants already give the conditionality RoboSats needed L2 for; adding a second layer would discard it and import channel-liquidity/routing fragility.
  • Pure on-chain order book / fully trustless matching. Deferred, not rejected — ShareLimitOrderBCH proves the resting-order leg works on-chain, but off-chain relay matching is cheaper to ship first; on-chain matching is an upgrade, not a prerequisite.
  • Banking-API ("open banking") fiat oracle that auto-attests EUR arrival. The holy grail — would collapse disputes back into automation and is the natural home for the existing DeviantOracleMBA machinery. Deferred: bank-API coverage, user privacy, and the new trust-in-the-oracle surface make it a phase-2 research item, not a launch dependency. Disputes exist today precisely because this oracle does not.

What this ADR does NOT resolve (open problems for the build):

  • The dispute/arbitration policy itself — evidence standards, arbiter incentive/payment, arbiter slashing for bad rulings, and whether arbiters are named (Hodl Hodl style) or sortition-rotated (SortitionBeacon). This is the load-bearing human-design problem; the contract is the easy part.
  • Chargeback / fiat-reversal handling — even a confirmed SEPA/card payment can reverse days later, after irreversible BCH release. Mitigations (bond hold windows, method tiering, settlement delays) are policy choices not yet specified.
  • Regulatory mapping per jurisdiction — "non-custodial matchmaker" reduces but does not universally eliminate licensing exposure; src/onramp/jurisdiction.ts is the seed but a real legal read is required before mainnet, especially given what wore down AgoraDesk/LocalMonero.
  • Generalizing EventPredictionEscrow from "MBA stake on a binary outcome" to "BCH↔fiat-claim trade with bonds" — concrete contract parameters, output ordering, and bond accounting are unspecified here.
  • Whether matching is off-chain-relay or on-chain (ShareLimitOrderBCH) at launch, and the maker/taker fee model.

Implementation pointers (existing primitives this would build on):

  • contracts/EventPredictionEscrow.cash (ADR-0024) — bilateral oracle-settled escrow + refund-timeout; the escrow template to generalize.
  • contracts/ShareLimitOrderBCH.cash (ADR-0030) + scripts/chipnet-clob-* — the resting limit-order / order-book leg.
  • contracts/DeviantOracleMBA.cash + src/oracleUsdm.ts — EURm/USDm/GBPm/JPYm/INRm signed-price oracle for rates and bond valuation.
  • contracts/SortitionBeacon.cash — commit-reveal randomness for future arbiter-pool rotation.
  • contracts/EscalatingPriceNFT.cash family — escalating-bond mechanics for payment-method risk tiering.
  • src/onramp/{reputation,jurisdiction}.ts (ADR-0036) — reputation + jurisdiction scaffolding to generalize; the aggregator stays as the complementary fallback.
  • src/web/{api,server}.ts, shells/app/ — shared backend + React shell; add a web Mini-App page and a Telegram bot/Mini App over the same API.
  • Cross-references: ADR-0036 (on-ramp aggregator — the A-model fallback), ADR-0024 (event-prediction/tote escrow family), ADR-0030 (on-chain share order book), wiki/event-prediction-and-tote.md.

ADR-0045 — P2P fiat market dispute resolution: launch design (lean)

Date: 2026-06-14 Status: Proposed (design only). Refines the "central open problem" ADR-0044 left open — dispute/arbitration — by scoping the launch mechanism and explicitly deferring the advanced models. Reuses ADR-0024 (escrow), ADR-0030 (order book), ADR-0036 (reputation/jurisdiction).

Context. ADR-0044 mapped the market and flagged dispute resolution as the one unsolved piece, because the fiat leg is off-chain and no covenant can see a bank. A long design exploration (this session) worked through sortition juries, an Athenae-Politeia citizen court (the CitizenToken family), Kleros-style stake + coherence-slashing, graduated quorum, and the dark-DAO / P+ε bribery threat model. The conclusion is the reason this ADR exists:

Every one of those mechanisms addresses mature-state, high-value, high-volume threats. None is needed at launch. No citizen pool exists yet; dispute volume is ~0; the bribery economics never pencil out at small scale. Launching with them would be premature complexity. The exploration's payoff is therefore a conclusion (it is safe to stay simple), not a spec.

Two principles the exploration established and this ADR rests on:

  • Fair exchange of an on-chain asset for an off-chain payment is impossible without a trusted third party (Pagnia–Gärtner). The chain makes the crypto leg trustless; the fiat leg always needs a trusted attestor. Launch plants that foundation in the cheapest honest place: a small, curated, revocable arbiter set.
  • Prevention beats adjudication. Bonds make lying negative-EV, so most disputes never reach an arbiter; a handful of people can handle the residue.

Decision (the launch mechanism — the only thing built):

  1. On-chain escrow, fiat off-chain. BCH (+ bonds) locked in a CashScript covenant generalized from EventPredictionEscrow (ADR-0024): a happy-path co-signed release plus a refund-on-timeout safety net. Fiat moves bank-to-bank between users; the platform never touches fiat → matchmaker, not money transmitter (keeps it out of model C / custodial-exchange licensing).
  2. MAD escrow + seller bond ≥ trade value. Seller locks trade amount + a dispute reserve; lying is negative-EV, so most trades self-resolve. (Buyer-side skin is the known gap — see Deferred / open.)
  3. Evidence-hardening — the unique payment reference. The buyer must include a server-issued nonce in the fiat transfer memo; the seller's proof must show that exact nonce. Kills screenshot-replay and binds a payment to this trade. This is the launch substitute for a fiat oracle — cheap, zero bank integration.
  4. Curated arbiter set via a soulbound, revocable NFT. A CashToken NFT grants arbiter rights: non-transferable (can't be sold), revocable by the issuer (the founding authority) if an arbiter misbehaves or goes inactive. Granted to vetted, willing workers. This is the embryonic form of the deferred ElderFreezeAction / CitizenToken.
  5. Automatic, on-verdict payment. The covenant releases the arbiter fee on the arbiter's signed ruling; funded from the dispute reserve at launch (loser-pays funding refined later).
  6. requiredConfirmations = m-of-n, scaled by stake, starts at 1 — the graceful-growth hinge. Small trades → 1 arbiter; larger trades → 2-of-3 from the set, so no single arbiter rules alone above a threshold. The same covenant carries from a 3-person team to a sortition court by changing m, n, and (later) how the n are selected — no rewrite.
  7. Value caps at launch. Bound single-trade size so worst-case exposure (skinless-buyer grief, single-arbiter error/collusion) stays small. Caps rise as confirmations and funding mature.
  8. Honest trust disclosure. Escrow is trustless; dispute resolution is a known, vetted, permissioned team. Stated to users — for a non-custodial product this is a feature, and it sets up the decentralization roadmap.

Two scaling axes kept distinct (for the future spec, so they aren't conflated):

  • Network size → availability (can you even field n confirmers?).
  • Stake at risk → security (how many do you need?).
  • Mature rule: requiredConfirmations = f(stake), capped by networkSize. At launch both are small.

Alternatives considered:

  • Custodial P2P (Binance/Bybit model). Rejected — model C: custody + KYC/AML + (EU) MiCA CASP licensing + a banking partner. Contradicts the non-custodial ethos.
  • Launch with sortition / citizen court. Rejected as premature — no citizen pool, ~0 dispute volume, bribery economics don't pencil at small scale. It is the floor of this same design anyway (n small, manual selection), so deferring loses nothing and avoids maintaining unused machinery.
  • Receipt-free / anti-bribery voting at launch. Rejected — not needed (a trusted set removes the bribery threat outright) and not natively buildable on BCH; at maturity, citizenship-slashing subsumes vote-unprovability.
  • Fiat oracle at launch. Deferred — an aggregator (Plaid/TrueLayer) is one integration and DKIM/ZK-email verifies signatures banks already produce, but both are phase-2; the unique-payment-reference covers launch.

Deferred — "do not build at launch; upgrade path only" (the documented destination, conclusions only — derivations are in the ADR-0044 design session):

  • Sortition juries (SortitionBeacon commit-reveal) — capture-resistant random panels for high-value disputes once a juror pool exists. Bribery defense matrix: targeted → pseudonymity + ~10-min vote window + auto-replace; public/trustless (dark-DAO) → self-incrimination + citizenship-slashing > bribe. Both are high-value, mature-state concerns only.
  • Athenae-Politeia citizen court (CitizenToken / EndorsementCovenant / ApplicationCovenant / BallotCovenant / TallyState / ElderFreezeAction / ComplaintCovenant) — permissioned-but-decentralized polity as the juror body; the launch NFT (Decision 4) is its embryo.
  • Kleros-style stake + coherence-slashing — juror stake ≥ prize ÷ majority; slash incoherent votes; franchise loss subsumes vote-unprovability.
  • Graduated quorum — confirmations scaling ~1 → 15-of-29 with network size + stake (the model already sketched for governance).
  • Fiat oracle (aggregator, or DKIM/ZK-email verified via checkDataSig) — the eventual escape that lets the chain see the bank and collapses disputes back to automation; pairs with two-sided loser-pays bonds + an insurance pool for the buyer-skin gap.

Deferred / open problems (acknowledged, not solved at launch):

  • Buyer-side skin gap — a fiat-only buyer posts no on-chain bond, so "loser-pays" can't reach them; launch leans on value caps + reputation + the seller reserve. Real fix: two-sided bonds / insurance pool / fiat oracle (all deferred).
  • Chargeback / fiat reversal after release — mitigated by a payment-method allowlist (irreversible rails) + the reference nonce; not fully closed without an oracle.
  • Per-jurisdiction regulatory posture — "non-custodial matchmaker" reduces but doesn't universally eliminate licensing exposure; needs a legal read before mainnet (cf. what wore down AgoraDesk/LocalMonero). src/onramp/jurisdiction.ts is the seed.

Implementation pointers:

  • Generalize contracts/EventPredictionEscrow.cash (ADR-0024) → BCH↔fiat-claim escrow with bonds + arbiter-release + refund-timeout.
  • Arbiter NFT: a soulbound, revocable CashToken (covenant-locked to the holder key + an issuer-revoke path) — pattern kin to CitizenToken / ElderFreezeAction.
  • Matching: ShareLimitOrderBCH (ADR-0030) + scripts/chipnet-clob-*; off-chain relay at launch.
  • Pricing/peg: DeviantOracleMBA / src/oracleUsdm.ts (EURm/USDm/GBPm/JPYm/INRm).
  • Front-end: src/web/{api,server}.ts + shells/app/ — one shared API behind a web Mini-App page and a Telegram bot/Mini App.
  • Cross-references: ADR-0044 (market gap + primitives; this refines its open dispute problem), ADR-0024 (escrow family), ADR-0030 (order book), ADR-0036 (on-ramp aggregator fallback + reputation/jurisdiction).

ADR-0046 — BTC↔BCH: T6 ("Bitcoin Cash as a better Lightning") + the bonded BTC-IOU trust floor

Date: 2026-06-27 Status: Accepted (validated end-to-end on BCH chipnet + BTC signet, real coins). Consolidates the design record Parameters/ASM_CrossChain/T6_BitcoinCash_LN/ (complete-record, model-explainer, why-demonstration) and the live tracker Parameters/ASM_CrossChain/T6_BitcoinCash_LN/btc-on-bch-rails_test-program.md. Snapshots 0172–0178.

Context. A captured BTC audience (Vol.13/14; auto-memory demonstration-over-reasoning-captured-audience) cannot be argued into Bitcoin Cash — only shown. The deliverable is therefore a runnable demonstration that BCH does what BTC+Lightning do, better, meeting LN users inside their own model (lock BTC, channel, stay in BTC). Four turns of protocol mechanics produced one governing constraint:

The VM wall. BTC has no covenants — it can verify signatures, hashlocks, timelocks, nothing more. So BTC cannot read another chain or bind a release to foreign state. Consequence (the clerk/bailiff split): BCH can record, judge, and watch a BTC channel (the clerk), but only BTC script can move the BTC (the bailiff). BCH verdicts are invisible to BTC. This single fact shapes every model below.

Decision — ship a three-model menu, two axes (technical trustlessness × adoption), let users choose:

  • Model 2 — Swap (T1/T4/T5/RT). Trustless HTLC atomic swap BTC↔BCH, then transact natively on-chain. Wins the technical axis; costs leaving BTC. Validated on real coins (T4, btc-bch-htlc-swap.mjs): one sha256 hashlock over a signet BTC HTLC + a chipnet AtomicSwapHTLC; claim path + BTC refund safety net.
  • Model 3 — BTC-IOU on BCH. Lock BTC → mint a bearer IOU CashToken → transact on-chain (no LN/node/hub), backing publicly verifiable. Wins the adoption axis (BTC stays "locked and yours"). Settlement of the locked BTC is trust-minimized, not trustless — see the trust floor below.
  • Model 1 / T6 — Lock + BCH board. Keep LN's model (2-of-2 lock, off-chain commitments, BTC settlement) but replace LN's private, must-not-lose, must-stay-online node with a public, permanent, permissionless BCH on-chain record (the "board"). The LN-faithful "better Lightning."

T6 architecture (the headline). A T6 channel and an LN channel are the same object — a BTC 2-of-2 with co-signed commitments settling on BTC — differing only in where the state record lives: private LN node vs. public BCH board. That one difference cascades into every advantage: no lost-state (the chain remembers), permissionless watchtower (anyone reads BCH), no must-stay-online-to-defend. The user runs two light, self-custodial wallets instead of (or instead of trusting) a node. One secp256k1 key signs both the BTC commitment and the BCH board update.

Validated layers (real coins, chipnet + signet):

  • T6a — BCH board (BchChannelBoard.cash): both-signed, strictly-monotonic on-chain state; a stale state can never overwrite a newer one (the board is its own watchtower). Scaled to 3 concurrent channels, permissionless read.
  • T6b — BTC 2-of-2 (signet): funding + cooperative close.
  • T6c — unilateral exit, both branches (signet): the penalty/IF branch (a stale commitment is seized via the revocation key, b35186…) and the honest CSV-delayed self-close/ELSE branch (a8d95644…, rejected before the delay, swept after). The trustless unilateral-exit pair is complete.
  • T6r — LN→T6 routing (chipnet + signet): a routed payment received into a T6 channel; one preimage settles both chains — revealed on the BCH board (19bcd25b…), then read off-chain by the bridge to claim the upstream BTC HTLC (65256a63…). So a T6 channel that implements LN's HTLC/routing is an LN channel that uses BCH as its watchtower; LN payments route into it off-chain.

The "penalty is a BTC tax" — why the residual friction is BTC's, not BCH's. T6 needs a penalty + a watchtower only because the funds stay on BTC, which cannot deny a stale commitment (a revoked state is a perfectly valid BTC tx; BTC can't read the board to know it is old). So BTC's only option is let-it-through-and-punish. On a BCH-native channel a covenant simply reads the latest on-chain state and denies the stale settlement — no penalty, no watchtower, no timelock race. The penalty, the watchtower, sign-to-receive, and routing are the last of the "BTC tax"; they dissolve the moment value settles on BCH. T6 shows BCH makes a better LN; the on-chain models show BCH does not need one — and T6 is the door an LN user can actually walk through.

The BTC-IOU trust floor — bonded M-of-N redemption (M3-bond). A wrapped/locked BTC peg is necessarily trust-minimized, never trustless, because of the VM wall. The honest floor (tBTC-v2-style) is validated on both sides:

  • BTC release threshold (signet, btc-signet-mofn-release.mjs): an M-of-N (2-of-3) multisig releases the reserve; a single signature is rejected by consensus. BTC enforces the release.
  • BCH slashable bond (chipnet, contracts/BondedSignerSlash.cash): each signer posts a bond on BCH; equivocation — two valid signatures over the same redeemId with conflicting payloads — is a self-contained cryptographic fraud proof anyone can submit to slash the bond. BCH enforces the punishment BTC cannot. (5/5 checks: slash on conflict; reject identical/different-id proofs; honest withdraw after the unbond height; reject early withdraw.)
  • Together: collusion is possible but bonded and slashable, so a rational signer never equivocates. This is what makes Model-3's "trust the signers" into "trust-minimized," with publicly-verifiable backing on top.

Alternatives considered:

  • Trustless wrapped BTC (a circulating BTC token with no bonded signers). Impossible — an HTLC is reclaimable, so it cannot back a persistent token; and BTC has no covenant to bind a lock to a burn. The floor for any BTC peg is a bonded signer set. Rejected as unbuildable, not undesirable.
  • Argue the case (whitepaper, block-size history, trust-model proofs). Rejected for the primary audience: a conditioned reader does not integrate disconfirming facts (motivated reasoning). Lead with demonstration; keep the reasoning available for the few who want it.
  • Treat streaming micropayments as LN's permanent moat. Rejected — BCH batching gives near-zero amortized per-payment cost and real per-batch finality, while an LN per-update is not settlement (finality is deferred to the BTC channel close).

Engineering record (for future cross-chain work):

  • BTC side via bitcoinjs-lib v7; the HTLC/spend lib scripts/lib/btc-htlc.mjs needs bigint amounts in hashForWitnessV0 (a Number→BigInt fix; the lib's first real run was T6r/T4).
  • Fund covenants via TransactionBuilder from the live provider.getUtxos() view — mainnet-js's wallet.send() UTXO cache goes stale under heavy key reuse and can emit a txid for a tx the network silently drops.
  • checkDataSig(sig, msg, compressedPk) verifies a Schnorr signature over sha256(msg).
  • BCH-side gotchas (carried): no ternary ?:; CSV is this.age, CLTV is tx.time; consume unused split() vars with .length invariants; covenant hash160 checks need publicKeyCompressed.

Observability & privacy. Moving the channel state onto a public chain gives T6 a property LN structurally lacks: off-chain BTC flow becomes auditable. Every update is on the BCH board, so anyone can read per channel the balance split, the full state history, the update count and per-update deltas — and aggregate them across all boards for total BTC locked in T6 channels, balance evolution, and gross in-channel flow. LN keeps channel state private/bilateral and can expose only capacity (announced channels) + open/close events — never balances, payments, or volume. Scope honestly: it covers the T6 subset only, and intra-channel flow is reallocation, not settled throughput (finality is at the BTC close).

Whether this costs privacy is baseline-dependent, and on-chain data is pseudonymous (addresses, not identities) — deanonymization needs a separate linking event (KYC anchor, IP, address reuse) plus clustering, expensive at scale:

  • vs custodial LN (LN's trilemma funnels most users here): a strict win — T6 is self-custodial + no-KYC with only pseudonymous on-chain data, where custodial LN is trusted custody + full custodian visibility + usually KYC bound to real ID.
  • vs non-custodial LN (run a node): identical on identity (pseudonymous; none unless self-linked) and custody (self). The only delta is flow observability — LN hides the channel relationship/flow, T6 publishes it pseudonymously. That delta is what buys the auditability; it is mitigable (fresh keys per channel, no reuse) and is partly offset because a public LN routing node leaks an IP in gossip (bound to its pubkey + channels) whereas T6 runs no always-online node (smaller passive identity surface).

So the accurate statement is not "T6 trades privacy for auditability" but: T6 adds transaction transparency + auditability while keeping users self-custodial and no-KYC — a combination (no-KYC + self-custodial + auditable) neither LN tier offers. Full treatment: Parameters/ASM_CrossChain/T6_BitcoinCash_LN/Observability-and-privacy.md.

Hub topology & the always-on requirement (precisely). Pure T6 is bilateral — reach requires hubs (route between users) and bridging hubs (connect the T6 network to the existing LN network). Topology: T6 users → T6 hubs → bridging hubs (run LN + T6) → LN network. This centralization is not LN-centralization-with-extra-steps, for three reasons:

  • Non-custodial at every layer. Routing is HTLC-enforced (forward-or-don't, atomically) — a hub can never steal in-flight funds. (True of LN routing nodes too — so this isn't the differentiator.)
  • The real differentiator is user-side. LN's custody problem emerges at the wallet layer: the node burden (liveness + lost-state + watch-duty) drives end users onto custodial wallets. T6 removes that burden, so users stay self-custodial — the custodial drift never starts.
  • Hub-dependence is soft (availability), not hard (safety). A T6 hub is a board-protected router: no lost-state, no watch-to-defend duty, and it survives going offline (durable board state + HTLC timelock windows let pending payments resolve on its return, where an LN node risks failure/fund-loss). Users are safe regardless of any hub's liveness. Bridging hubs are asymmetric: their LN side keeps LN's failure modes (the service's problem), their T6 side and all users are clean; they link via the T6r mechanism (one preimage settles an LN HTLC + a T6 board settle).

The one failure mode T6 does not remove is online-to-transact — and it collapses to "a normal app," not infrastructure:

  • Holding funds needs zero uptime — a settled balance sits safely on the board indefinitely.
  • Receiving a new payment needs you online only within the HTLC timelock window, not at the instant of send — a hub/LSP holds the pending HTLC and you claim it when your app next wakes. Only a pending receive times out if you stay dark past the window; the settled balance is never at risk.
  • The wallet is stateless between sessions — restore from seed + the board (no lost-state), which is what makes a light background mobile/web app genuinely safe in T6 where it isn't in LN.
  • Operationally, "background online" is push-triggered wake; the push relay is not a node, holds no funds, knows no secrets — disposable and trustless.

Net: T6 turns "online to receive" from run a persistent stateful node into a stateless app that wakes to sign and restores from seed — infrastructure → app — which is the concrete reason T6 keeps users self-custodial. Build implication: three distinct programs — the user wallet (BTC+BCH light + T6 engine, self-custodial), a T6 hub (router: channel engine + forwarding, non-custodial, board-watched), and a bridging hub (the hub + an LN node + T6r linking).

Product shape, BOLT-native interop & the board as the state/watchtower/recovery layer (design dialogue, 2026-06-28→30). A long design conversation settled the product architecture and corrected an earlier over-engineering. Key conclusions:

  • T6 is not a competing channel protocol — it is a BOLT channel + a BCH board as the state layer. The board is orthogonal to the channel. Implement BOLT and you interoperate with LN directly; the board rides along.
  • The board is unilateral public persistence of your own channel copy — it needs nothing from the counterparty. LN has no shared channel state and no consensus; each party persists its own copy however it likes, and where you store yours (disk / server / BCH) is a purely local choice the counterparty can't object to. (This supersedes any earlier "the board must be co-signed / verify the counterparty's signature / two board flavors" wording — that was unnecessary.) The both-signed board (BchChannelBoard, validated) is the optional T6↔T6 variant where both want a single mutually-agreed record.
  • Reading state — LN vs the board. LN nodes never read each other's storage; they exchange live BOLT wire messages over a direct connection (both must be online). The board is async and public — read anytime via a BCH light client, no counterparty connection. Consulting it just needs a BCH-reading module (a vanilla LN node lacks one — so "LN reads the board" = add that module, not a change to LN itself).
  • Automatic backup for any counterparty. Channeling with a T6-aware peer auto-records the channel state on BCH on every update, carrying your own signatures — a free, verifiable, public-good backup. The LN side does nothing for the record to exist; it only reads BCH (a one-off lookup) if/when it needs to recover.
  • Watchtower ≠ recovery (LN has both, separately) — the board unifies and beats both. An LN watchtower is a blind penalty-enforcer (stores encrypted, revoked-txid-keyed justice blobs; knows nothing of your state; delivers penalties). SCB/DLP is the separate, weaker recovery (needs the counterparty online + honest, balance only). The board does both, better: a permissionless watchtower (publish the same txid-keyed blobs to BCH) and counterparty-free, verifiable recovery (publish current state seed-key-encrypted; restore from seed → read BCH → recover the full state without the counterparty). Board recovery beats DLP (always-online, verifiable, no counterparty needed).
  • Clear-vs-encrypted board is a dial. Encrypted → private (watchtower + recovery, matches LN's privacy); clear → the public-auditability/observability property (Observability-and-privacy.md). They can be mixed (encrypted state for recovery + a clear aggregate for auditability).
  • Product = one self-custodial engine spanning both chains (BTC channel logic reused from the LN/BOLT format — the BTC chain is L2-agnostic, but the wallet still needs channel-construction logic a generic wallet lacks — plus the BCH board), one key. Hub and bridge are deployment modes of the same engine (hub = always-on + forwarding; bridge = hub + an LN node + T6r). MVP needs only the wallet engine. A generic BTC wallet + generic BCH wallet cannot compose — the two sides are coupled (same key, board mirrors the BTC commitment state).
  • Island is shared, not T6-specific. Channels island without paths (LN and T6 alike). Bootstrap by plugging a few well-connected BOLT-native T6 hubs into LN's existing graph → inherit LN-wide reach → grow the T6-native graph hub-and-spoke. Liquidity cost is concentrated at the hubs (a service), not users; intra-T6 = full benefit, cross-network inherits LN reliability on the LN legs; the on-chain models (swap/IOU) have no islands at all (universal reach day one).
  • The decision — the BOLT-client fork. Option A BOLT-native (build on LDK + a BCH-board persistence/watchtower module → inherit LN's graph, full interop) vs Option B T6-native (our simpler format → self-contained island + bridges). Direction: MVP = B (fastest, T6↔T6 + informal hubs); strategic build = A on LDK (the real product — plug straight into LN's liquidity with the board as the superpower). Full treatment: complete-record §9.

Cross-references: complete record + model explainer + why-demonstration + observability-and-privacy in Parameters/ASM_CrossChain/T6_BitcoinCash_LN/; tracker Parameters/ASM_CrossChain/T6_BitcoinCash_LN/btc-on-bch-rails_test-program.md (rows T1/T2/T4/T5/RT/T6/T6r/T6c-honest/M3-bond/PathA/B1/B2); snapshots 0172 (T1), 0173 (T2), 0174 (T5/RT), 0175 (T6), 0177 (T6r), 0178 (honest-close/T4/bonded M-of-N), 0182 (Path A + B1), 0183 (B2); auto-memories btc-on-bch-rails-not-channel, demonstration-over-reasoning-captured-audience. ADR-0035 (bridge/oracle trust) for the IOU mint attestation.


ADR-0047 — BCH as a decentralized interchain settlement hub: the USDT(Tron)↔BCH rail (Build B)

Date: 2026-06-28 Status: Accepted (validated end-to-end on Tron Nile + BCH chipnet, real coins). Consolidates Parameters/ASM_CrossChain/Transactions/build-B_usdt-tron-bch_scope.md + cross-chain-send-receive_chain-landscape.md. Snapshot 0181. Generalizes ADR-0046 (BTC↔BCH) to a smart-contract source chain.

Context. The same VM wall as ADR-0046 governs every cross-chain rail: BCH covenants cannot verify a foreign chain's consensus, so a value-preserving peg toward BCH is trust-minimized, never trustless. But the trustless swap (HTLC) is available wherever the foreign chain can do a hashlock+timelock — which is nearly every top chain. The thesis: make BCH a host for other chains — deposit, trade on BCH's native DEX, withdraw home — a decentralized, no-KYC settlement hub (bch-interchain-settlement-hub). USDT-on-Tron is the flagship first rail (~half of all USDT, outside Circle CCTP, and no native USDT on BCH since Tether left SLP Sep 2025).

Decision — ship both deposit flavors per asset, let users choose by trust appetite:

  • List A — trustless atomic swap (DEX·Multichain): value leaves the source; no IOU; end-to-end trustless.
  • List B — lock-and-mint IOU (ASM·Multichain): value stays denominated as a CashToken on BCH; trust-minimized (oracle v1 → bonded M-of-N floor). Backing invariant Σ minted == Σ locked, publicly reconcilable on both chains.

Validated layers (real coins, Nile + chipnet) — B1–B6:

  • B1 contracts/tron/TronHTLC.sol — TRC20 HTLC (sha256, matches AtomicSwapHTLC.cash); claim/refund/wrong-secret.
  • B2 scripts/tron-bch-htlc-swap.mjs — Flavor 1: one sha256 hashlock over a Nile TronHTLC (real test-USDT) + a chipnet AtomicSwapHTLC; Alice's BCH claim reveals S, Bob reads it off-chain and claims the Nile USDT. No oracle/wrapped/custodian.
  • B3/B4/B5 contracts/tron/TronUSDTVault.sol + scripts/tron-bch-lock-mint.mjs — Flavor 2: real Tron lock → 69-byte checkDataSig oracle attestation → DeviantWrapMintIOU mints exactly the locked amount; replay of an attestation is rejected (outpoint-bound); redeem returns the IOU and releases the USDT; Σ τUSDt circulating == USDT locked at every step. Upgrades chipnet-btc-iou-demo from a simulated lock to a real cross-chain reconciliation.
  • B6 contracts/tron/TronUSDTVaultMofN.sol + scripts/tron-vault-mofn.mjs — the trust floor: release() requires M-of-N ecrecover sigs (2-of-3 validated; 1-sig/non-signer/replay all rejected). Pairs with the BCH slashable bond (BondedSignerSlash.cash, ADR-0046/M3-bond): Tron enforces the threshold, BCH enforces the punishment.

Honest trust scope. The trading/deposit/withdraw flow on BCH is fully decentralized & KYC-free. List A is end-to-end trustless. List B is trust-minimized, not trustless — the locked USDT sits behind the oracle/bonded signers (the VM wall). Say "trust-minimized," never "trustless," for the bridged path. The public mint == lock reconciliation + signer bond is what holds it honest until a light client / zk verifier removes the trust ("one checkDataSig away from IBC").

Engineering record (carry to all USDT/EVM rails):

  • Tether returns false on transfer (performs the move, declares returns(bool) without setting it) — a naive require(token.transfer(...)) and even SafeERC20's bool decode wrongly revert and roll back a successful move. Verify by balance delta instead. tether-returns-false-balance-delta.
  • solc for TVM: evmVersion: istanbul (no PUSH0); compute the HTLC swap-id client-side with keccak256(abi.encodePacked(...)) (don't parse logs — the token Transfer event precedes the contract's).

Naming. The cross-chain ASM is a sibling of Potidaea (the native-BCH Orbital Swap brand — Ποτίδαια, "city of Poseidon"), not Potidaea itself (poseidon-orbital-swap-naming). User-facing assets show plain USDc/USDt + a Ledger/Chain selector; the Greek prefix (τ/ε) is backend/docs only (wrapped-asset-source-chain-marker).

Alternatives considered: Universal redemption (one IOU redeemable on any chain) — rejected (needs pooled multi-chain reserves + higher trust, collapses the per-source markers; CCTP doesn't cover BCH); per-source IOUs redeemed on their home chain + value-fungibility via the BCH DEX instead. Oracle as the permanent witness — accepted only as v1; the bonded M-of-N (B6) is the trust floor, a light client/zk verifier the end state.

Cross-references: Parameters/ASM_CrossChain/Transactions/build-B_usdt-tron-bch_scope.md, cross-chain-send-receive_chain-landscape.md, ASM-Multichain.md; snapshot 0181; ADR-0035 (bridge/oracle trust), ADR-0046 (BTC↔BCH trust floor), ADR-0016 (routing); auto-memories bch-interchain-settlement-hub, chipnet-dex-and-cross-chain-wrap-plan, wrapped-asset-source-chain-marker, tether-returns-false-balance-delta.


ADR-0048 — Potidaea business model: free-swap-always, paid listing (in flagship DEA) + Olympic promotion, and ecosystem-wide flagship-DEA fee accrual

Date: 2026-06-28 Status: Accepted — design + brand lock. Consolidates Parameters/ASM_Native/Potidaea_OrbitalSwap/orbital-swap_business-model.md (rev. 2) and Parameters/ASM_Native/Potidaea_OrbitalSwap/potidaea_orbital-swap-asm.md. Builds on ADR-0017/0019/0020/0021/0029; extends ADR-0042 (per-instrument fee distribution → ecosystem-wide flagship accrual). Locks the brand the prior docs flagged as pending.

Context. Potidaea (the native-BCH Orbital Swap / ASM brand) structurally zeroes every classic exchange revenue line — no spread, no slippage, no LP fee, no pay-to-list gate — and redeem has no contract path to fee (ADR-0011 §3). Revenue therefore cannot tax the trade; it must come from service, discovery, and enablement, all soft/avoidable (the ADR-0008 anti-rent stance). This ADR locks how Potidaea monetizes and how value accrues across the MBA ecosystem.

Decision.

  1. Brand lock — Potidaea. ASM = the mechanism (Automated Swap Mechanism); Orbital Swap = the generic protocol (native single-party fixed-rate BCH/MBA swap, like "atomic swap"); Potidaea (Ποτίδαια, "city of Poseidon") = the brand of our build. The cross-chain ASM is a sibling, not Potidaea (ADR-0047). poseidon-orbital-swap-naming.

  2. Swap is always free & permissionless; only the protocol fee + discovery are monetized. Every asset — listed or not — is fully swappable via the ASM. The protocol swap/mint fee ≈ 0.3–0.5% on every mint/swap (any tier), redeem free, Token-class ~0% (ADR-0020), soft + from free BCH (ADR-0017). This fee is SPLIT (PINNED): NOW 0.5% = 0.3% → DEA holders + 0.2% → platform (direct slice, transitional); FUTURE: reduce to 0.3% = 100% → DEA holders (platform takes 0 direct from swaps). Note the cut comes entirely out of the platform's slice — holders earn the same absolute yield (0.3% of volume) before and after, while users pay less. Genesis carries a one-time creation fee in DEA — FREE for the first 100 assets, then 10·floor(A/100) DEA (10 for assets 100-200, 20 for 200-300, 30 for 300-400 …): escalating but slow + low, deliberately far gentler than the Olympia curve so it never deters creation (the free Stoa economy needs assets). Modeled in scripts/potidaea-economics-sim.mjs.

  3. Listing model — a free canonical list, plus a paid tab ("Olympia") gated at 100 created assets. The canonical list (free, permissionless, every canonical/peg-verified asset) is always on and keeps going as-is — it is where assets are discoverable and swappable at no cost. Olympia (the paid featured tab) does not exist until 100 assets have been created; thereafter it is purely optional and may remain empty indefinitely if no founder pays. You pay for featured placement, never the right to swap (and never a safety claim — ADR-0020 safety status stays free + dominant). Naming (resolved). Inside Agora (the marketplace hub): Metroon = the full asset directory (the Athenian state archive/registry — replaces "Spectrum", which is retired here so the sibling product CoinSpectrum is sole owner of that name); Stoa = the free list (the open public colonnade, always on); Olympia = the paid competition list (the Games sanctuary — assets compete via the Olympic ladder, point 6, for the Laurel wreath; flagship DEA ≈ Δίας/Zeus, in whose honour the Games ran); Olympus stays the dApp platform layer (home of the gods — no collision). Five distinct names: Agora · Metroon · Stoa · Olympia · Olympus. (A repo-wide "Spectrum"→"Metroon" sweep of live shells/mockups/spec is a follow-on — excludes snapshots, KnowledgeBase, and Parameters/CoinSpectrum/.)

  4. Olympia listing fee — paid in the flagship DEA: fee = (cA·A + cL·L) · M, with cA = cL = 1. A = total assets created (price rises with every genesis — even while Olympia is empty, 100→200→300…); cL·L adds +1 DEA per existing Olympia listing. The starting fee = 100 DEA (emerges as cA·A = 1×100 at the activation point A=100). M is an UNCAPPED velocity multiplier (plain multiplier; baseline ×1.0; +0.1 per listing — no peak; a lister pays the post-bump rate → 1st ×1.1, 2nd ×1.2 … 5th ×1.5 … 10th ×2.0 …). Cooldown is SEQUENTIAL with dwell ∝ HEIGHT: removing the notch at height h takes cph·h blocks, cph = 144 blocks (= 1 day; BCH targets 10-min blocks → 24h·60/10 = 144/day), removed top-first, each waiting for the previous — so the highest notch lingers longest (slow at the top), a small burst clears fast. A 5-burst peaks ×1.5 and holds it 720 blocks (~5 days; full relax 2160 blocks ~15 days); a 10-burst peaks ×2.0 and holds 1440 blocks (~10 days; full relax cph·Σh = 7920 blocks ~55 days). On-chain the cooldown is block-denominated (CLTV reads block height), so this is exact, not wall-clock. The A/L ratchets only rise; only M decays. Stoa stays FREE (the open colonnade — free swap + free discovery; only Olympia, the spotlight, costs). All explored in scripts/potidaea-economics-sim.mjs. Creation + Olympia fees are PLATFORM revenue (Deviant/Potidaea treasury, point 10) — not holder yield (that comes from mint fees, point 7) and not burned (point 8). Being paid in DEA, they create DEA buy-demand (founders acquire DEA via BCH→DEA claims → more swap volume, point 2).

  5. Listing ≠ promotion — two separate products. Listing (paid in DEA) = shelf placement; promotion = rank within the listed set. List without promoting; cannot promote without listing.

  6. Olympic promotion ladder (separate, voluntary). Free → Bronze → Silver → Gold → Laurel = 0 / 10 / 15 / 20 / 25% founder-fee share (hard cap 50%) — the visibility arm of the voluntary marketing revenue-share (ADR-0021); the tier is baked per referred genesis (ADR-0021 immutability; not farmable). Laurel = the laurel crown (στέφανος). The ladder ranks assets within Olympia (the tab, point 3). Keep three names distinct: Olympia (premium tab) · Olympic (the ladder within it) · Olympus (the dApp platform layer). olympic-promotion-tier-ladder.

  7. Ecosystem-wide flagship-DEA value accrual (extends ADR-0042). The DEA-holder share of the protocol swap/mint fee — 0.3% of the 0.5% NOW (→ 100% of a reduced 0.3% LATER), charged on every mint/swap, any tier (Stoa/Olympia/unlisted) — routes to the holders of the single flagship DEA (Deviant Equilibrium Asset). Distribution is time-weighted, pool-free, consensus-locked (ADR-0042 Decisions 5–7), so a flash mint→redeem captures nothing; the more flagship DEA held, the more earned. Because it's on all swap volume, the base is the whole ecosystem's throughput — making the flagship the market driver / value-accrual asset. (The asset's own founder feeBps, 0–5%, is separate — the founder's income, not this.)

  8. No burn for MBAs. MBA supply is backing-bounded — every unit is 1:1 BCH-backed, inheriting the protocol's unit-of-account/exchange (satoshi) scarcity. Burning only corrects unbacked flood-supply; for a backed MBA there is no oversupply, so value accrues via fee distribution, not supply reduction. mba-no-burn-dea-value-accrual.

  9. The equilibrium flywheel (why "Equilibrium"). Two demand sources, both pointing at the flagship DEA: (a) yield — MBA mint activity → fee slice → flagship-DEA holders (point 7) → holding DEA pays → demand; (b) buy-demand — creation + Olympia fees are paid in DEA → founders must acquire it → demand. Flagship DEA is BCH-backed, so buying it locks more BCH → BCH price support. Because any MBA's success feeds the flagship (via its mint fees), success is redistributed ecosystem-wide: BCH + every MBA rise together (non-zero-sum, the opposite of pair-by-pair memecoin extraction).

  10. Deviant/Potidaea earns non-extractively — and is an active DEA holder. Platform revenue: creation + Olympia listing fees (in DEA — it accumulates DEA) + a transitional 0.2% direct slice of the swap fee (sunsets when the fee → 0.3% / 100%-to-holders, point 2) + premium (DRA/ERA, ADR-0019) + B2B/white-label (ADR-0020). The platform holds the DEA it accumulates and earns the swap-fee yield pro-rata on that balance, like any holder (no privileged skim, ADR-0008) — its share = platformDEA ÷ total DEA. End state: it earns from swaps only as a holder, fully aligned with everyone. The holder yield (point 7) is the larger, shared stream; the platform's direct take shrinks to zero over time.

  11. Terminology (glossary-authoritative). DEA = Direct Emission Asset (the class: permissionless-mint, fixed-rate, unlimited-supply, BCH-backed). Deviant Equilibrium Asset = the project's flagship DEA instance (a named market-driver asset, Δίας/Zeus), never the class label.

Alternatives considered:

  • Burn / buy-back of collected listing fees. Rejected — a non-mechanic for backed MBAs (point 8); route to the holder distribution instead.
  • Per-instrument-only accrual (ADR-0042 as written). Extended — ecosystem-wide flagship accrual is what makes DEA a market driver and produces the equilibrium effect (point 9).
  • Consensus-baked unavoidable platform fee on the swap. Rejected — a rent-authority (ADR-0008) that forks the canonical bytecode (D-1). The listing fee MAY be contract-enforced because it gates a Deviant-run marketplace surface (the ADR-0020 "hard/server-or-contract-gated" category), not the swap.
  • Charging for the swap / a pay-to-list gate. Rejected — Potidaea sells visibility, never function or truth (safety status is free, mechanical, dominant; non-canonical assets cannot be promoted, ADR-0020).

NBA fee schedule (added, snapshot 0234 open item resolved). MBAs are BCH-backed so a fee-anchoring service exists to price around; NBAs are unbacked CashToken FTs, and the free-Stoa economy has to keep the deterrent asymmetric so canonical-safety stays cheap and pure-token speculation carries a real spam-toll. The pinned NBA schedule:

  • Pinax registration (both families). MBA = 1 DEA (~$1 at $1/DEA — the "buy free Stoa listing" one-time toll — nominal, deliberate; not a gate). NBA = 100 DEA ($100 · 100× MBA; matches the Olympia uplift; deters cheap NBA spam without blocking legitimate token issuance).
  • Stoa (both families) = FREE after Pinax registration. Discovery + swap paths stay open forever once registered.
  • Creation (both families). MBA = 10·floor(A/100) DEA (free first 100). NBA = ⌈1.2 × MBA⌉ DEA (~+20% uplift; slight premium; keeps the creation curve near-parallel so onboarding isn't blocked, but NBA-only farms pay more).
  • Olympia (both families). MBA = (1·A + 1·L)·M, start 100 DEA. NBA = 100× MBA Olympia (the spotlight is the primary rev-tier — NBAs pay the full anti-spam multiple there).
  • Rationale. MBAs earn their soft-fee cushion by being backed and ASM-swappable; NBAs get the same permissionless-mint rails but pay a 100× multiplier on the two paid surfaces (Pinax + Olympia) and a modest ×1.2 uplift on creation. Everything is still soft/avoidable (ADR-0008) — a founder who never lists on Olympia and skips Pinax pays 0 to the platform beyond the swap-fee slice, which is class-neutral.

All updated in scripts/potidaea-economics-sim.mjs (creationFeeNBA, pinaxFeeMBA/NBA, listingFeeNBA, NBA_UPLIFT_REGISTRY = 100, NBA_UPLIFT_CREATION = 1.2).

What this ADR does NOT resolve:

  • Fee constants — PINNED (this turn): MBA creation 10·floor(A/100) DEA (free first 100); NBA creation ⌈1.2 × MBA⌉ DEA; Pinax registration 1 DEA (MBA) / 100 DEA (NBA); Olympia (1·A + 1·L)·M, start 100 DEA, uncapped M (+0.1/listing), NBA Olympia = 100× MBA Olympia; sequential cooldown, dwell ∝ height, cph = 144 blocks (= 1 day @ 10-min blocks); Stoa free post-Pinax. Contract-enforced-in-DEA path DEMONSTRATED — see covenant below. (No remaining fee-constant opens.)
  • The Olympia activation gate — confirm 100 created assets is the hard trigger; what counts toward it (canonical-only vs all created); whether it is one-way.
  • The exact DistributionCovenant.cash encoding for ecosystem-wide routing — how shareholderBps from many assets settles into one flagship distribution (a follow-on to ADR-0042's open covenant question).
  • Whether all MBAs route shareholderBps to the flagship by default, or only opt-in assets (the scope dial of point 7).
  • Implementation sweep of the surface rename — "Spectrum" → "Metroon" (+ Stoa/Olympia) across live shells/mockups/spec; excludes snapshots, KnowledgeBase, and Parameters/CoinSpectrum/ (which keeps the name).
  • Final per-tier Olympic shares, and where the shared founder-fee % goes (platform/marketer per ADR-0021, or also flagship-DEA holders).

Implementation pointers:

  • Parameters/ASM_Native/Potidaea_OrbitalSwap/orbital-swap_business-model.md (full model), Parameters/ASM_Native/Potidaea_OrbitalSwap/potidaea_orbital-swap-asm.md (brand), wiki/revenue-model.md (project-wide companion), Parameters/ASM_Native/MBA_Shares/ (ADR-0042 distribution design).
  • Fee-path fork (ADR-0042): companyBps (direct) + shareholderBps (→ DistributionCovenant.cash); reuse the checkDataSig oracle + Merkle-snapshot liveness backstop. The new piece = many-assets → one-flagship routing.
  • Surfaces per glossary (v2.9): Agora (hub) / Metroon (asset directory, renamed from Spectrum) / Stoa (free list) / Olympia (paid competition list) / Orbital Swap (swap UI).
  • Covenants built + validated (local VM, MockNetworkProvider):
    • contracts/PotidaeaOlympiaListing.cash — global state-NFT machine (commitment A|L|notches|lastHeight, 16 bytes LE): list() (DEA fee (A+L)·M to treasury, climb + sequential height-∝ decay, cph=144 blocks) + create() (10·⌊A/100⌋ DEA). 4/4 tests (test/regtest/potidaea-olympia.test.ts): fee/climb, decay-lowers-fee, over-decay rejected, wrong-fee rejected. Key finding: block-height fee escalation is unenforceable on BCH (a cheater claims the min height, DeviantEscFeeBlockHeightV2), but a cooldown that DECAYS the fee IS — more decay ⇒ lower fee ⇒ the spender rationally claims the true (CLTV-capped) height.
    • contracts/DistributionVault.cash — the swap-fee → DEA-holder payout (ADR-0042): oracle-attested (recipient, amount) via checkDataSig, replay-bound to the vault outpoint, operator can't take/redirect; remainder returns to the vault. 4/4 tests (test/regtest/distribution-vault.test.ts): sequential payouts decrement exactly, redirect rejected, tampered-amount rejected, replay rejected.
    • contracts/DeviantShareMBA.cash — the collection side (ADR-0042): a mint fee splits consensus-enforced into companySats → treasury (direct) + shareholderSats → the distribution covenant's locking bytecode (company can't touch it). Full loop wired end-to-end (test/regtest/share-mba-loop.test.ts, 2/2): mint fee 5,000 → company 2,000 + vault 3,000 → oracle pays holder 2,000; tampered split rejected.
    • Contention proven, and TWO fixes proven: test/regtest/potidaea-contention.test.ts (two writers on one state UTXO collide — "UTXO not found for input 0"). test/regtest/potidaea-sharding.test.ts (2 shards → both confirm, global A = A0+A1). test/regtest/potidaea-epoch.test.ts (PotidaeaListingRequest.cash + PotidaeaEpochState.cash): two same-block listings drop independent request UTXOs → one permissionless fold settles both, M climbs by the batch size (×1.0→×1.2), fees to treasury, folder can't skim (each request self-verifies its treasury output at mirror index k↔k). 2/2.
    • Decision — for this workload, EPOCH-BATCH over SHARD. The fee is a global, order-sensitive function ((cA·A + cL·L)·M, with M a marketplace-wide velocity). Sharding fits independent writes; here it forces a choice both bad: per-shard-local counters break the "fee ∝ ecosystem size" curve and are gameable (route to the cheapest shard), while a correct global total needs cross-shard reads = co-spending the siblings = re-serialization. Epoch-batch keeps one global state (M stays correct) and the batch-size-per-epoch becomes a cleaner velocity signal than the CLTV-height trick. Sequencing: single-UTXO at launch (listings are low-frequency), epoch-batch when volume warrants.
  • State-UTXO contention / sharding — CONFIRMED empirically, not theoretical. test/regtest/potidaea-contention.test.ts: two users each build a create() spending the same global state UTXO (unrelated assets — the only shared object is the counter A). Alice's confirms (A 50→51, the state UTXO moves to a new outpoint); Bob's — built against the old outpoint — is rejected with "UTXO not found for input 0" (input 0 = the shared state UTXO, not his own funds); Bob rebuilds on the fresh state and succeeds (A→52). So the conflict is the shared counter, never the asset/name. A single global state UTXO is spendable once per tx → concurrent writers conflict (a global lock). Severity differs by surface: listings are low-frequency (a founder lists once) → contention mild, ship single-UTXO first; swaps are high-frequency → the DistributionVault is deliberately pool-free & sequential-per-outpoint (one payout/tx) so the hot path is the oracle's off-chain accounting, not on-chain contention. Scaling options when needed: (1) shard the state into N lanes (by hash(assetId) mod N; global A/L = sum across shards); (2) epoch-batch (collect off the hot UTXO, fold in once per block window); (3) append-only + oracle aggregation (each write its own UTXO; an indexer sums the counts — funds never trusted, only the count). Recommendation: single-UTXO for launch, epoch-batch or shard the listing state and parallelize the vault (K vaults) if volume warrants.

Cross-references: ADR-0008 (verifier-not-authority), 0011 (no redeem fee), 0015 (DRA) / 0038 (ERA = the monotonic-up DRA subset), 0017 (0.5% service fee), 0019 (premium tier), 0020 (promoted registry + hard/B2B + external-DEX 0%), 0021 (voluntary rev-share ladder), 0029 (supply-stepped fees), 0042 (MBA-shares distribution — extended here), 0047 (cross-chain sibling). Memories: potidaea-oceanic-liquidity, olympic-promotion-tier-ladder, mba-no-burn-dea-value-accrual, poseidon-orbital-swap-naming, anthemus-dex-naming, deviant-labs-initiative.


ADR-0050 — Cross-chain conversion is solvent-by-construction: never mint a currency you don't hold

Date: 2026-07-15 Status: Accepted

Decision: The correspondent system stays a 1:1 same-asset warehouse — reserve currency MUST match liability currency (lock USDC → mint USDC; lock EURC → mint EUR). It must never lock one currency and mint a different one at an oracle rate (e.g. lock USDT, mint EUR at 1.08). Any cross-currency conversion (USD→EUR) is a market swap performed outside the 1:1 leg — on the source chain before locking (e.g. USDT→EURC on a DEX) or on BCH after minting (USDT→EUR on Anthemus). The oracle may set the price; real liquidity must provide the counterparty.

Alternatives considered:

  • Oracle-priced mint (rejected). Lock 5.40 USDT, oracle says 1.08 USD/EUR, mint 5.00 EUR on BCH. Reserve = 5.40 USDT behind a 5.00 EUR liability. EUR rallies to 1.20 → 5.00 EUR = 6.00 USD, reserve holds 5.40 → short 0.60, insolvent. The system is running an unhedged short-EUR/long-USD book funded by user deposits; every adverse move widens the hole → bank-run where last redeemers lose.
  • Settle-the-gap-at-redemption (rejected as a default; allowed only as a deliberate, capitalized FX desk). Keep the USDT-vs-EUR mismatch and top up the shortfall from a fund at redeem time. The FX position still exists between mint and redeem; "rate-agnostic for the user" is only true if the fund is capitalized (and ideally hedged) to survive the losing side. Legitimate business, but it is an FX dealer with real capital + risk management — never something the protocol should drift into by accident.
  • Convert-at-market before/after the 1:1 leg (accepted). Reserve and liability are always the same currency, so the books are always matched → cannot be insolvent, needs no capital to absorb rate moves. The FX is taken once, at market, by liquidity providers (priced into the swap spread).

Rationale: An oracle gives a price, not a counterparty — a price feed cannot take the other side of an FX trade. Currency risk never vanishes; it is transferred to whoever will hold it, and they charge for it (swap spread once, or fund capital + hedge carry continuously). Matching reserve currency to liability currency makes the warehouse solvent by construction and rate-agnostic for free; the UX ("pay USDT, receive EUR") is preserved by a liquidity pool taking the FX side at market, not by an internal oracle mint. This is the concrete rule behind the Phase-C "no USD-peg liability" note (bch-interchain-settlement-hub) and governs Deviant Pay (Parameters/ASM_CrossChain/Transactions/deviant-pay_design.md).

Cross-references: ADR-0035 (bridge/oracle trust), ADR-0047 (interchain hub), ADR-0049 (Anthemus business model — where cross-currency swaps settle). Memories: correspondent-system-and-reserve-covenant, bch-interchain-settlement-hub, fiatm-oracle-as-signaling-machine, anthemus-business-model.


ADR-0051 — The cross-currency FX desk: debit (ships, solvent-by-construction) vs the opt-in credit desk — required capital is the FX drift, not the principal

Date: 2026-07-18 Status: Accepted — design, verified by four seeded simulations over the real 2021–2023 EUR/USD parity stress (1.21 → 0.98 → 1.10). Extends ADR-0050 (does not contradict it): 0050 ruled the 1:1 warehouse solvent-by-construction and allowed an FX desk "only as a deliberate, capitalized" option; this ADR quantifies that option and lands one refinement of 0050's capital caution. Full record + reports: Parameters/ASM_CrossChain/DebitCredit/.

Context. ADR-0050 settled that the correspondent warehouse must match reserve currency to liability currency (lock USDC → mint USDC; lock EURC → mint EUR) and that cross-currency (USD↔EUR) is a market swap — "the oracle may set the price; real liquidity must provide the counterparty." It rejected the oracle-priced mint outright and rejected "settle-the-gap-at- redemption" as a default, permitting it "only as a deliberate, capitalized FX desk." This ADR is the design of that desk for two cross-chain IOUs (εUSDC from a USDC lock, εEURC from an EURC lock — not FIATm, whose USD/EUR share a BCH substrate). It answers "how much capital, who bears it, and what makes the credit version safe," verified rather than asserted.

The one fact everything follows from. A user deposit creates zero headroom (locking 100 EURC mints 100 εEURC to that same user — locked[c] and circ[c] rise together). So a swap needs target-currency collateral that isn't already someone's IOU. Only three sources: (a) operator-prefunded same-currency headroom, (b) an opposite (matched) swap, (c) another user's locked backing. (a)+(b) are debit (move money that is there); (c) is credit creation (front money that isn't).

Decision.

  1. Affirm ADR-0050. Reserve currency = liability currency; never mint a currency you don't hold at an oracle rate. The oracle prices; liquidity (inventory + matched flow) is the counterparty. This ADR adds how that liquidity is organized, not an exception to the rule.

  2. Ship Version A — the DEBIT desk (0050-compliant, solvent by construction). Oracle-priced conversion executed only against real same-currency inventory — prefunded headroom (a) plus matched flow (b) — with a covenant enforcing locked[c] ≥ circulating[c] per currency (a UnifiedUsdtReserve-shaped per-currency ledger). Every εEUR is backed by locked EURC; the oracle only prices the swap between two real inventories, so this is not the 0050-rejected "mint a currency you don't hold." When EUR headroom hits 0 → "temporarily unavailable" → route to the P2P DEX. Verified: 0 insolvent weeks across the entire sweep (sim-anthemus-fx-reserve.mjs). A thin EUR reserve is a risk control, not just a limit (avg drawdown $17.9k thin vs $126.9k thick — trip-to-DEX caps toxic inventory). The desk is a matching engine with an inventory buffer, not a lending book.

  3. Permit Version B — the CREDIT desk — only opt-in, gated, and fully stacked. This is 0050's "deliberate, capitalized FX desk." It fills (c) — over-issue against the reserve — made safe by four verified mechanisms, none optional:

    • Net-imbalance band (−L ≤ netImbalance ≤ +L, L = X% of reserve): caps the net position; reverse-direction flow restores room (self-balancing). Covenant-enforceable off the reserve ledger. Bounds size, so maxLoss ≈ L × maxAdverseMove is computable. 10% is conservative vs real banks' fractional ratios; gate it on at maturity/strong adoption.
    • Per-user drift-margin (NOT a CDP). The user posts no explicit lock; their own converted balance is the implicit buffer. Because it is a swap of an owned asset, not a loan, the only thing at risk is the FX drift on the swapped notional — so the pool stays flat even at 90% utilisation, where naive over-issue swings six figures (sim-anthemus-fx-margin.mjs).
    • Two products on one rail: FX-neutral spending (drift settled from the user's balance, pool flat — the payments case) vs speculative trading (user keeps FX P&L, pool = a band-bounded market-maker). Zero-sum vs the pool; the neutral product gives the user no upside.
    • Controller-smoothed multi-oracle liquidation: the FIATm §12 signaling machine (multi-block confirmation + threshold + multi-oracle median) rejects wick-triggered false liquidations (sim-anthemus-fx-margin.mjs Part 5) — users are not wiped on a flash/thin-liquidity gap.
  4. The refinement of 0050's capital caution — required capital is the DRIFT, not the principal. 0050 correctly noted the FX position "exists between mint and redeem" and needs capital to survive the losing side. Quantified (sim-anthemus-fx-spend.mjs): required buffer = the FX drift over the settlement window — ~2.5% (1wk), ~5.8% (1mo), ~20% held through the full 2022 crash — 7–30× leaner than a CDP's 150%, and not "lock 90% of the swapped value." It is satisfied implicitly by the user's own converted balance (no explicit lock), and it is user-attributable: a user may swap 100% of one asset and spend the whole marked balance; each spend is marked to live backing, so a rate loss eats the spend proportionally (verified — A swaps 100 EUR→USD @1.18, spends all, rate→1.14 → spends 214 not 218; the $4 drift settles pro-rata; A is FX-neutral; pool flat). So the "capitalized FX desk" is drift-capitalized and user-borne, far lighter than 0050's "real capital + risk management" framing implied — while still opt-in and bounded. The pool's only residual exposure is a gap (adverse move > buffer before close), contained by liquidation + the band + rate-smoothing, and irrelevant to on-BCH spending (εUSD/εEUR are fungible bearer tokens; the drift is a band-capped pool inventory position, batch-settled at the source-chain rebalance — never per-swap, ADR-0047 "vaults, not toll roads").

  5. Never drift into B by accident (0050 upheld). Version A (debit) is the default and the only mode enabled by default. The credit modes are explicit, disclosed, per-jurisdiction gated/geofenced (margin-FX is the most regulated rung), and off unless deliberately switched on with the full stack (§3) present. Batch-settle the source-chain vaults on the net imbalance, periodically — never a per-swap collateral swap (that reintroduces Ethereum gas/slippage per trade and collapses the hub thesis; sim-anthemus-fx-ledger.mjs Q3).

  6. The band is a rebalancing WORKING-CAPITAL buffer, not a directional loss — the desk does not bleed. The desk's FX imbalance is a paper mark, realized as a cost only when withdrawals of the short currency exceed its locked reserve — i.e. not until ~90% of that currency is withdrawn off-chain (verified: sim-anthemus-fx-realized.mjs Part 1 — the exact threshold is lockedC / circC; at a 10% over-issue, 90.9%). Below that, every withdrawal is served from locked reserve at zero cost. Because most value stays on BCH (the hub thesis) and two-way flow keeps the net imbalance oscillating near zero (Part 2 — users "find a balanced state"; the desk nets deposit vs withdraw and rebalances at ~no cost), the source-chain boundary — the only place FX is realized — sees only the small net. The residual is a rare, bounded tail (≤ band × move), requiring persistent one-way flow and a >~90% withdrawal of the short currency and a sustained adverse move — and it is defanged by pro-rata redemption (ADR-0031: removes the first-mover run) + the band (caps Δ). So any premium is optional insurance for that tail, not compensation for a continuous expected loss.

  7. The risk-share dial + the hybrid (a 2× band). Who holds the FX drift is a continuous dial, and it sets both how much the user de-risks and how large the band can be (verified: sim-anthemus-fx-market-vs-covenant.mjs). A covenant swap = a market swap minus the spread, plus retained FX risk (oracle mid, no counterparty spread; someone must hold the resulting position). Endpoints: full-buffer (user bears 100% → FX-neutral spending, band user-bounded) and no-buffer (desk bears 100% → full de-risk for the user, band = 10%). The hybrid 50/50 is the sweet spot for a conversion product: the user de-risks half its FX for free, the desk bears half, and because desk risk per position scales with the fraction it bears, the same 10% desk-risk budget covers double the notional — a 20% band. Beyond the band, route to the DEX (market swap) — the two compose: covenant for the first ~20%, market for the tail; users always fill, desk risk stays capped. FX is expected-zero, so the desk's cost is variance/tail (point 6), not an expected loss — a thin premium suffices, far below a full market spread. Honest note: the buffer design leaves FX direction on the user — swapping near a peak and holding underperforms a market swap that locked in (verified: over the 2021-23 decline from a high, the full-buffer self-swap trailed the market swap; "beats by the spread" is a martingale statement, not a guarantee — the spread buys certainty).

  8. Version C — withdrawer-mutualized settlement (desk FX risk = exactly 0, simplest of all). A third way to reach desk-zero-risk (verified: sim-anthemus-fx-mutualized.mjs): the desk never pays out more than it holds — a withdrawal of the short currency is honored pro-rata at available / claims, so the FX shortfall (the drift on Δ) is borne by whoever exits during a shortfall, as a transparent haircut, not by the desk. Solvency is by construction; no inventory, no premium, no per-user margin, no liquidation engine — this is ADR-0031 pro-rata redemption promoted to the default settlement rule. The haircut is ≈ (Δ/circ) × FX-move — ~0 in the normal regime, ~1.5% in a 10%-over-issue / 20%-spike tail, and because most value stays on BCH the aggregate transfer is a fraction of a basis point of the reserve (~0.18% in that stress). The band survives only as a haircut cap for users, not a solvency cap. Three models now reach desk-zero-risk, differing in incidence + peg: B/desk- insurer (desk bears, hard peg, needs capital), B/swapper-margin (the swapper bears via drift-margin, hard peg, needs per-swap accounting + liquidation), C/withdrawer-mutualized (the exiters bear pro-rata, soft peg in the tail, simplest). Costs of C, stated plainly: εEUR becomes a mutualized claim on a possibly-short reserve (soft peg — min(1 EUR, pro-rata share)), not a hard Σminted==Σlocked IOU; it mis-attributes (innocent exiters subsidize others' swaps); it partially re-enters ADR-0050's concern (a liability that can be short) — licit only because the shortfall is band-bounded and on-chain-transparent (published-range, ADR-0031). Run-safe iff the haircut is order-independent (computed from the current global shortfall ratio, equal for all exiters — an order-dependent haircut would trigger a run). Recommendation: offer A (default hard peg) + C (simplest desk-zero-risk, best for a payments product where value lives on BCH and exits are rare — the aggregate cost is a rounding error) + B/swapper-margin (when a hard exit peg + attribution matter). Disclose the peg type per product.

  9. The risk-incidence simplex + the recommended 40/30/30 blend (the "perfect balance"). Points 6–8's three bearers (desk / swapper / withdrawer) are the vertices of a simplex; any interior split (α_desk, α_swapper, α_withdrawer) is valid, and an interior point is better balanced than any corner — no party carries a dangerous share (verified: sim-anthemus-fx-shared.mjs). Worked example — a $100 covenant swap, 5% adverse move, imbalance capped at 10% of pool, at 40/30/30: total drift $5 → swapper $1.50 (1.5% of the swap), withdrawer $1.50 → 0.167% fee spread across the 90% base ($0.17 per $100 withdrawn), desk $2.00 (0.2% of pool, mostly paper). Desk's worst exposure = α_desk × band × maxMove = 0.4 × 10% × 20% = 0.8% of reserve → a near-hard peg on trivial capital. All three are ~0 in the normal two-way regime. Reframe (accepted): "hard vs soft peg" is the wrong axis for a payments product — a bounded, transparent, sub-1%-in-tail fee is not "soft," it is simply a withdrawal fee smaller than any competitor (Ethereum gas alone on a $100 cross-currency swap is $3–20, exceeding our entire 5%-stress all-in ~$1.67; banks 2–4%; normal-regime cost ≈ $0). Users need not know the fee's origin. The fee structure is a gravity well: exit pays the foreign chain's gas + the small risk-share; staying costs ~nothing and unlocks the whole hub — so the "90% stays on-chain" result (point 6) is not just accounting but the product's core incentive, one uniform rail for 100+ fragmented stablecoins on a <$0.001 chain. Honest caveat: the shares are variable (state-dependent), bounded + on-chain-transparent but not constant — disclose the cap; and the swapper retains α_swapper of FX direction (fine for payments, not for a hedger).

Alternatives considered (each mapped to a verified result):

  • Oracle-priced mint of a currency you don't hold — rejected by ADR-0050; our ledger trace (sim-anthemus-fx-ledger.mjs Q1/Q2b) shows it is exactly (c) unbounded → insolvent tail.
  • Naive over-issue (Version B without the stack) — rejected: ~$100k uninsured swing at 90% util, sign is path-luck (sim-anthemus-fx-margin.mjs Part 3).
  • CDP / principal collateral — rejected as the framing: this is not a loan, so principal collateral (150%) is 7–30× more capital than the drift actually at risk (…-spend.mjs Part 1).
  • Per-swap source-chain collateral swap — rejected (§5 / ledger Q3): solvent but breaks the hub.
  • Debit + matched-flow + prefunded headroom (accepted, §2) — solvent by construction, 0050-clean.

What this ADR does NOT resolve.

  • The production covenant: generalizing contracts/UnifiedUsdtReserve.cash (one asset × 4 chains) to multi-currency × the net-imbalance band, and the marked-spend settlement encoding.
  • The liquidation covenant + oracle-freshness bound (reuse checkDataSig + the FIATm controller; the freshness gap noted in DeviantOracleMBA.cash is security-critical for a finite FX reserve).
  • Exact L, buffer %, and the maturity trigger that switches the credit desk on.
  • The regulatory bucket per jurisdiction (margin-FX / CFD framing — counsel-required, R-016-class).
  • Whether marked-spend (pool exactly flat) or bearer-spend (small band-capped pool drift) is the default spend path — both avoid any user lock.

Implementation pointers.

  • Design record + the four raw reports: Parameters/ASM_CrossChain/DebitCredit/README.md + report-{1..4}_*.txt.
  • Verified sims: scripts/sim-anthemus-fx-{ledger,reserve,margin,spend,market-vs-covenant,realized,mutualized,shared}.mjs (reproducible, seeded; eight reports in the DebitCredit folder; pts 6–9 add the working-capital-buffer / realized-vs-paper result (pt 6), the risk-share dial / 2× hybrid band (pt 7), the withdrawer-mutualized desk-zero-risk model (pt 8), and the 40/30/30 risk-incidence simplex + migration/fee thesis (pt 9)). Three accounting bugs were caught and fixed during verification (deposits must mint circulating; margined pool P&L is gap-only; realized loss ≠ paper mark) — recorded so they are not reintroduced.
  • Covenant to generalize: contracts/UnifiedUsdtReserve.cash. Controller: Parameters/ASM_Native/FIATm/ (§12).

Cross-references: ADR-0050 (extended — the 1:1 warehouse rule this operationalizes), ADR-0047 (interchain hub / vaults-not-toll-roads), ADR-0049 (Anthemus business model — the trading floor where the DEX fallback + speculative product live), ADR-0031 (synthetic-USD hedge + published protection range — the not-a-CDP sibling), ADR-0033 (CDP/custodial taxonomy — why this is not a CDP), ADR-0016 (routing: pegged → Potidaea, cross-chain → Anthemus), ADR-0035 (oracle/bridge trust). Memories: crosschain-fx-desk-debit-credit, correspondent-system-and-reserve-covenant, bch-interchain-settlement-hub, fiatm-oracle-as-signaling-machine, anthemus-business-model.


ADR-0052 — Tiered mint fee for a free-floating game currency: a "$1 minimum billable value", oracle-priced but oracle-bounded to the fee

Date: 2026-07-19 Status: Accepted — design + CHIPNET-VALIDATED (5/5, live). Source contracts/DeviantTieredMintOracleFT.cash, test scripts/chipnet-tiered-mint-fee-test.mjs, full record Parameters/ASM_Native/MBA_ingame_currency/.

Context. We want a standalone game currency — a currency in its own right (like BCH or USDT), NOT a betting system — that can be staked into a pari escrow the same way any FT can. Its one distinctive feature is a mint fee stated in dollars: flat $0.01 on mints worth up to $1, and 1% on mints worth more than $1. Two things had to be settled: (1) how to express a USD-denominated fee on a currency that is deliberately free-floating (BCH-backed at a fixed rate, USD ratio floats with the BCH price — 400 units = $1 today, 10 units = $1 if BCH crashes), and (2) how much oracle trust that requires.

The one fact everything follows from. The two rules meet exactly at $1: 1% × $1 = $0.01. So they are not two policies but the two arms of a single continuous function — the percentage fee, billed on at least $1 of notional:

feeSats = feeBps/10000 × max( satsPerUsd , grossSats )      // grossSats = amount × R ; satsPerUsd from oracle

Below $1 the max picks satsPerUsd (bill $1 → the flat floor); above $1 it picks grossSats (bill the real value → the percentage). Continuous at $1, monotone, no kink to game.

Decision.

  1. Ship DeviantTieredMintOracleFT — a fork of DeviantOpenMintFT (fixed backing rate R, self-backed UTXOs, permissionless reservoir mint, free P2PKH redeem) whose only change is the fee arm above. The currency is a plain fixed-rate MBA; D-1 (canonical address) and D-2 (per-UTXO full backing) both hold.

  2. The oracle sizes the fee and nothing else. Because a free-floating coin's USD value isn't knowable from token units, the covenant reads a checkDataSig-signed BCH/USD feed (the 16-byte AnyHedge message DeviantOracleMBA already uses) to get satsPerUsd. It is used ONLY inside the max. The peg, the backing, and redemption never consult it — redeem is a plain P2PKH spend releasing the embedded amount × R, no oracle, no contract path.

  3. Oracle risk is penny-scale by construction — accept it, don't over-engineer freshness. The oracle value only survives the max for sub-$1 mints; above $1 the fee is 1% of grossSats, fully oracle-independent. The feed is signed, so an attacker can at most replay a genuinely-signed stale price. Therefore the worst any oracle manipulation can do is mis-size the ~1¢ sub-$1 floor — it cannot touch the peg, redemption, or the fee on any mint ≥ $1. This is the same bounded-blast-radius "oracle as signaling machine" stance we take elsewhere; here the blast radius is literally a cent, so we ship WITHOUT a CLTV freshness window (unlike price-critical uses) and revisit only if the fee ever grows past penny-scale. A 546-bit dust raise is the hard absolute floor.

  4. Keep the currency and the escrow strictly decoupled. Nothing in the token knows about betting. Any escrow type (the three in Parameters/dApp/Pari_escrow/, or the Pari_P2P winner-take-all) consumes it exactly as it would consume BCH or USDT. The one convenience — minting directly into an escrow address in one tx (Pari_P2P "Case B") — lives on the escrow/wallet side because claim(amount, recipientLB) accepts any recipient locking bytecode; it is not a currency feature.

Why the two arms are the right economics. The $1-minimum is an anti-dust guard (splitting $2 into 200 penny-mints costs 200 × $0.01 = 100%), and it closes a real hole a pure-1% fee has (a $0.10 mint at 1% = $0.001, sub-dust → effectively free). The 1% is clean whale-scaling. Splitting a $2 mint into 2 × $1 costs the same 1% — no arbitrage. Cheap for small real mints, ruinous for spam, linear for whales.

Family placement. Sibling of the supply-triggered DeviantEscFeeSupply / DeviantDescFeeSupply fee schedules (ADR-0029), but triggered by the USD value of the individual mint rather than cumulative supply — hence the oracle. The max()/minimum-billable shape generalizes to any feeBps.

Validation (live chipnet, category 3bb5b9b6…, R=500 bit/unit, feeBps=100, satsPerUsd=200000). SUB-$1 (200 u = $0.50) → 2000 bit = $0.01 floor (89bb07a2…); AT-$1 (400 u = $1.00) → 2000 bit = $0.01, arms equal (3ff7d6ba…); WHALE (4000 u = $10) → 20000 bit = $0.10 = 1% (7bde4687…); NEGATIVE floor-underpay → rejected at the fee require; REDEEM sub-$1 → BCH by P2PKH, no oracle (9c702b28…).

Consequences / open. If this ever becomes a wallet-recognised canonical template, add its JSON to the EMBEDDED set (scripts/compile-contracts.mjs + src/verify/canonical.ts) for D-3 address recognition — not required for the currency to function. Freshness binding remains a documented, deliberately-deferred option (§3 of the design doc).

Cross-references: ADR-0029 (supply-triggered escalating/descending fee — the sibling schedule), ADR-0006 (reservoir mint model), ADR-0015 (DRA — the other oracle-adjacent mint template), ADR-0035 (oracle/bridge trust). Reuses DeviantOracleMBA (16-byte checkDataSig feed) and the Pari_P2P / Pari_escrow consumers. Memories: fiatm-oracle-as-signaling-machine, cashscript-covenant-findings, oracles-cash-bch-oracle-network, mba-no-burn-dea-value-accrual.

ADR-0053 — Uniform Fiat state model: the aggregate-commitment ledger (unlimited cells, quorum-attested caps) supersedes the four-cell UniformReserve

Date: 2026-07-20 Status: Accepted — prototyped + VM-validated (contracts/UniformReserveAggregate.cash, test/regtest/uniform-aggregate.test.ts 5/5, snapshot 0212); this ADR records the decision and governs the migration. Extends ADR-0051 (Uniform Fiat / debit-credit desk); reuses the M5 oracle quorum (ADR-0051 §M5, snapshot 0207) and the FIATm signaling controller (ADR-0051 §M5-2a). Full analysis: Parameters/ASM_CrossChain/UniformFiat/cell-capacity.md.

Context. UniformReserve (ADR-0051 M3) stores the per-(issuer × chain) ledger inside the 40-byte NFT commitment (4 × 8-byte cells ‖ 4-byte buffer ‖ 4 × 1-byte pause). That caps a currency at four cells (snapshot 0211), which the design's own diversification argument cannot live with: report-9 shows a 10-issuer basket turns a 13% single-issuer depeg into a 1.3% dent, and four cells cannot reach ten. Separately, the covenant's redeem script has been scraping BCH's 1650-byte standard-unlocking-bytecode limit — it hit 1666 when fxSwap co-spends two controllers (snapshot 0208), forcing three function merges already.

The fact everything follows from. Of the covenant's jobs, only per-cell caps and targeted writedowns need per-cell data; conservation (Σcells == circ), pro-rata redemption (amount × backing / circ) and FX headroom (backing − circ) are all functions of the TOTAL. Backing is a monoid under addition — a running sum maintained one delta at a time, O(1) state, order-independent. So keep only the sum on-chain and push the per-cell detail off-chain.

Decision.

  1. Adopt the aggregate-commitment model as the production state layout. The 40-byte commitment becomes aggregateBacking (8 LE) ‖ ledgerRoot (32). The per-(issuer × chain) cells live off-chain in services/uniform-registry, keyed by a code, and are unlimited. ledgerRoot is a quorum-attested commitment (a hash of the sorted {code → value} map) to that off-chain ledger. The over-collateralization buffer folds into the aggregate (it is just backing unmatched by circulating); pause moves off-chain (a paused cell is one the oracle won't co-sign a deposit for).

  2. Caps become QUORUM-attested, not covenant-hard (Option 1a). The oracle 2-of-3 quorum already gates and prices every admin op; here it also attests (a) the per-cell cap holds and (b) newRoot correctly reflects the delta. This is a bounded extension of a trust already granted for pricing. The covenant still hard-enforces the things that bound loss: FT conservation (a non-minting NFT), the aggregate arithmetic, and pro-rata redemption from the aggregate (withdraw needs no oracle — the ratio is pure on-chain state). A future Option 1b (a Merkle root with O(log N) proofs) can restore covenant-hard caps without changing this ADR; it is not required for launch.

  3. Reject sharding across controllers. Global invariants (backing, circ in the pro-rata ratio) would span local UTXOs. Every reconciliation fails: co-spending shards overflows the 1650-byte limit at the second shard; a head+shards tree reinvents a Merkle root out of UTXOs with fragile cross-spend binding; independent shards break global pro-rata and the single-fungible-token UX — the two properties that make it "one USD". The in-UTXO aggregate dominates on every axis.

  4. Resolve the withdraw ↔ root reconciliation (open sub-question 1). withdraw reduces the shared aggregateBacking globally and leaves ledgerRoot unchanged (it has no oracle to attest a new one, and must stay permissionless so exit is never gated). The off-chain indexer debits the source cell(s) using a documented, deterministic policy — largest-cell-first (drains the most concentrated exposure, which is also the safest for the cap invariant) — and the next admin op re-attests a fresh root reflecting all interim withdrawals. Between a withdraw and the next admin op the root is a known-stale high-water commitment; the aggregate is always exact. The quorum, when it next signs, is attesting a root computed from aggregate (which the covenant does verify the delta against), so drift cannot accumulate silently.

  5. Resolve code-registry authority (open sub-question 2). A code is keccakless — a stable string id "<ISSUER>.<CHAIN>" (e.g. USDC.Base) assigned by services/uniform-registry, which is the single source of truth for the code set, each code's cap, and its status (integrated | vetting | candidate | paused | retired). A code is append-only and never reused: retiring a dead issuer/chain sets status retired and freezes its value at the writedown result; the code is never deleted and never reassigned to a different (issuer, chain), so a stale off-chain reference can never be silently repurposed. Assignment/retirement is a governance action by the same authority that runs the oracle quorum (disclosed).

  6. Migrate; keep UniformReserve as the reference. UniformReserveAggregate becomes the production covenant; services/uniform-registry gains the off-chain ledger/indexer (map + aggregate

    • root + message construction). UniformReserve (4 cells) is retained as the validated reference implementation and the covenant-hard-cap comparison point, not deleted.

Verified results (snapshot 0212). Unlimited cells (6 codes past the 4-cell ceiling accumulate into one aggregate); withdraw pro-rata from the aggregate alone stays order-independent after a writedown; redeem script 560 B vs 1282 B — 2.3× smaller, so two aggregate controllers co-spend at ~1120 B, comfortably under the 1650-byte limit UniformReserve hit at 1666 — the aggregate model also unblocks the fxSwap co-spend.

Honest trade-offs (carried). (a) Per-cell caps are quorum-enforced, not covenant-hard, until/unless Option 1b ships — a real reduction in the on-chain guarantee, mitigated by the same multi-oracle quorum that already prices everything and by the covenant-hard conservation + pro-rata that actually bound loss. (b) ledgerRoot is a known-stale high-water between admin ops; the aggregate is always exact. (c) The off-chain indexer becomes consensus-critical for cap policy (not for solvency — that stays on the aggregate); it must be reproducible and auditable from the on-chain aggregate + the attested roots.

Alternatives considered. Narrower counters (6-byte cells → 5-6 cells) and fewer reserve decimals (more notional per 8-byte cell) — both are compression stopgaps that do not lift the count meaningfully; kept as backups. Merkle-hard caps (Option 1b) — deferred, not rejected. Sharding — rejected (decision 3).

Cross-references: ADR-0051 (Uniform Fiat / debit-credit — extended), ADR-0050 (1:1 warehouse), ADR-0031 (pro-rata redemption), ADR-0047 (interchain hub). Contract: contracts/UniformReserveAggregate.cash; analysis: Parameters/ASM_CrossChain/UniformFiat/cell-capacity.md; snapshots 0211 (the ceiling), 0212 (the prototype). Memories: crosschain-fx-desk-debit-credit, cashscript-covenant-findings, oracles-cash-bch-oracle-network.


ADR-0049 — Anthemus business model ratified against validated bytecode: book-first engine, maker-0 consensus taker fee, 0.30% curve fee 100%-to-LP

Date: 2026-07-21 · Status: Accepted Source design: Parameters/Anthemus_DEX/anthemus_business-model.md (rev. 1) · Build plan: Parameters/Anthemus_DEX/anthemus_mvp-critical-path.md

Context

The business-model doc (§9) recommended this ADR be written only after the two coupled contract changes were scoped and validated, "so the ADR can reference real bytecode" — the discipline ADR-0048 used. That bar is now met (snapshots 0220, 0222):

  • AnthemusOrderBook.cash — the generalized resting limit order (token↔token, BID side, consensus-enforced taker-fee output). 7/7 MockNetworkProvider + 8/8 live chipnet (place / announce / discover / partial / full / BID / cancel, exact fee deltas).
  • SharePairAmmV2.cash — fractional LP tokens (reservoir treasury; outstanding = supply − treasury), 0.30% fee-on-input invariant so k grows to LPs, treasury-drain guard. 4/4 MockNetworkProvider + 5/5 live chipnet (fee accrual paid out on burn).
  • The relay (services/anthemus-relay) — discovery-only; covenant address = order identity; the pool rendered as a maker on the SAME book (curveLevels).

Decision

  1. Brand. "Anthemus" is locked as the user-facing name of the first-party market venue (Agora ▸ Anthemus). Plain "Anthemus" — never "Anthemus Sea".
  2. Engine order. Book-first, ratifying the critical path: the order book is the engine; the AMM is an automated maker on the same book — one matching engine, one settlement path, one fee surface. The swap UI is the exchange with order-making automated.
  3. ADR-0016 refined (not reversed). "No first-party AMM" was about the thin native CashTokens market — that reasoning stands: native Token-class pairs still route to Cauldron at 0% retention. The refinement: Anthemus pools serve the cross-chain hub pairs Cauldron does not carry (τUSDt, εUSDC, USDμ legs…), a market that did not exist when 0016 was written. No liquidity fork, no double venue.
  4. Book fees. Maker 0% / taker feeBps (default 15 = 0.15%), consensus-enforced in the order covenant — the validated bytecode made the fee hard at launch, going beyond the model's "start soft, harden later". A bits-quote fee below the 546-bit dust floor is waived by the covenant (small BCH fills are fee-free, not un-fillable).
  5. Curve fee. 0.30% on input, 100% to LPs, on every pair (option i). The optional ≤0.05% protocol slice on hub-exclusive pairs is deferred until depth is proven (per §9.2's lean). LP tokens are the claim on the fee-grown k — validated end-to-end (an LP burn paid out more than the feeless share).
  6. Bridge toll. Direction accepted: 0.15% each way, flat-capped, as a published D-1-verified tollBps in the wrap covenant. Implementation pending (the wrap covenants are the cross-chain session's surface); until it lands, the toll is not charged — never a hidden haircut.
  7. USDμ base. USDμ is the canonical base/quote; routing through it is preferred, not forced (§9.4 lean) — the router favors USDμ legs, the user can trade direct pairs where they exist.
  8. No mercenary token. Confirmed. If LP incentives are ever added they route through the existing flagship DEA, not a new emission.
  9. Atomic swap (List A). ~0% protocol fee confirmed; monetized via the book taker fee + funnel only.
  10. Founder-fee split to service provider. Deviant keeps declining its own consensus slice (ADR-0021 Decision 5 stands); revisit only as an explicit, optional founder election alongside white-label work.
  11. Jurisdiction. R-016-class counsel required before any mainnet/fiat-adjacent pilot of the book + bridge; chipnet remains unrestricted.

Consequences

  • The honest-posture test for Anthemus is disclosure + labeling: "Market swap" rendering with price impact and each fee as separate disclosed lines (ADR-0016 Decision 5) — enforced in the shipped UI, not just documented.
  • The liquidity cold-start (the model's #1 risk) stays off the MVP critical path: the book works with zero LPs; pools add continuous liquidity per pair as flow justifies.
  • Next contract work item carrying a fee decision: tollBps in the wrap covenant (§6 above), owner cross-chain session.

Cross-references: ADR-0008/0016/0017/0020/0021/0030/0044/0045/0047/0048. Contracts: contracts/AnthemusOrderBook.cash · contracts/SharePairAmmV2.cash · relay services/anthemus-relay/. Snapshots 0220 (MVP: engine+relay+two UIs), 0222 (SharePairAmmV2). Memories: anthemus-dex-naming, anthemus-business-model, potidaea-oceanic-liquidity (why Potidaea copy never applies here).

Amendment (2026-07-22, user direction). The hub-pair examples in §3 overstated: USD-stablecoin pairs (τUSDt, εUSDC, PYUSD, …) are not listed on Anthemus at all — eliminating that trading-pair matrix is the purpose of unified USD. Stablecoins enter through the Deposit rail (choose asset, choose chain separately → lock on the source chain via the correspondent → unified USD credited at the user's BCH address, market-price disclosure required). Anthemus books/pools trade real pairs against the one USD (EUR/USD, BCH/USD; later non-USD cross-chain assets — BTC, ETH — as they land). This supersedes the business-model §5's "stable-stable pools first" wedge: the wedge remains USDT-Tron deposits, not USDT pairs; the low-IL first pool becomes EUR/USD-class, not stable-stable.


ADR-0054 — NBA issuance on CashTokens: fungible supply is fixed at genesis, so "unlimited" is a reservoir convention, not a mint capability

Date: 2026-07-26 Status: Accepted

Context. The Artemision Create wizard has offered the eight-class taxonomy (ADR-0027/0028) since snapshot 0057, but only the MBA half had a genesis primitive (prepareGenesis → DeviantOpenMintFT). Building the NBA half (PTU/PTL/MTU/MTL) forced a collision with CashTokens consensus that the 2×2 taxonomy had been quietly papering over.

The consensus fact. Under CHIP-2022-02-CashTokens a transaction may create fungible tokens of a new category only in the genesis transaction. No later transaction can raise a category's FT supply. A minting NFT authorises new NFTs of the category — never new FTs. This is the same finding that produced the MBA reservoir model back in snapshot 0008; it applies with equal force to non-backed assets, and it means one axis of the taxonomy does not mean what its label says.

Decisions

  1. The supply axis is renamed in substance, not in label. For a fungible NBA:

    • limited is native and provable — the genesis amount is the cap.
    • unlimited is not a mint capability. It can only mean a reservoir sized so the cap never binds (the same 10^18 convention the MBA reservoir uses). We keep the PTU/PTL/MTU/MTL codes (they are load-bearing across the spec, the display layer and ADR-0027/0028) and instead make the planner state the truth at the point of decision: every uncapped NBA plan carries a warning-severity nba-unlimited-is-a-reservoir note, surfaced on the Review wall before the user types CREATE. Rejected: renaming the axis (churns eight class codes across spec/wiki/code for a distinction the warning already carries), and silently minting 10^18 with no disclosure (the failure mode is a founder telling holders "supply is uncapped" while the chain says otherwise).
  2. Capped ⇒ no minting baton. A limited NBA genesis creates the exact cap and no minting NFT. With no baton the category can never gain another token of any kind, so the cap stops being a promise and becomes a consensus fact anyone can check. An uncapped NBA retains the baton (future NFTs of the category), and the warning says exactly what that baton can and cannot do.

  3. The mint axis is "who is the gate", and the two answers need different machinery.

    • managed (MTU/MTL) deploys no covenant at all. The whole supply is minted to the issuer's own token address; the issuer's key is the gate. A key gate needs no script, and inventing one would be theatre. This is the default CashToken — memecoins, utility/governance tokens, and off-chain-backed "stablecoins" (ADR-0028: those are NBA, not MBA).
    • permissionless (PTU/PTL) deploys DeviantOpenMintNBA.cash (new canonical template), a draw-from-reservoir covenant with three published, immutable terms: pricePerUnitSats (0 = free tap), maxUnitsPerDraw, cooldownBlocks (relative-age gate on the reservoir UTXO, so it rate-limits the reservoir, not the drawer).
  4. The NBA covenant's defining property is a constraint it does NOT have. DeviantOpenMintFT requires outputs[1].value == tokenAmount * backingRateSats — the claimer's BCH is locked inside the token, which is what makes redemption a plain P2PKH spend. DeviantOpenMintNBA requires only outputs[1].value >= 546: the drawer's BCH is paid away to the founder, nothing is locked, nothing is redeemable. That one missing line is the whole MBA/NBA distinction, expressed in script. A free tap with no cooldown is flagged (nba-free-tap-no-cooldown) because it can be emptied by one person paying only miner fees.

  5. Registry treatment: NBAs are tier-A entries with backing: 'token', and validateToken was relaxed to allow it. The rate and the two contract addresses were universal tier-A invariants; they are in fact MBA invariants. A managed NBA has neither (rateSats: 0, empty contract addresses). Demanding them would force callers to invent both — exactly the fiction the registry exists to prevent. Everything downstream already handled backing: 'token' correctly (classify → neutralBacking, redeem/swap-all refuse with not-mba, sweep-tokens includes them, deriveTokenClass labels PTU/PTL/MTU/MTL) — the validator was the only blocker. Rejected: a separate NBA registry (splits every read surface for one field).

Consequences.

  • MAX_FT_SUPPLY (2^63-1) is a hard planner check; an "unlimited" NBA is 10^18, ~9.2× under it.
  • A founder who wants a verifiable number must choose Limited. The wizard says so on the Supply step rather than leaving it to be discovered.
  • DeviantOpenMintNBA is now a canonical D-3 template (embedded artifact + bytecode hash), so a PTU/PTL that publishes its draw terms is address-verifiable the same way a DEA is — a token claiming to be permissionless but sitting at a non-derived address cannot pass.
  • NFT collections (the MTL "fixed collection" reading) are not covered: this slice issues fungible NBAs. A capped fungible MTL is provable; a capped NFT collection is only provable once the baton is burned, which is a later flow.

Cross-references: ADR-0006 (native CashTokens + reservoir), ADR-0008 (no default registries), ADR-0027/0028 (NBA family + peer labels), ADR-0038/0039 (denomination). Contracts: contracts/DeviantOpenMintNBA.cash. Code: src/wallet/genesis-nba.ts, src/token/registry.ts (validateToken), src/verify/canonical.ts (NBA_OPEN_MINT_ARTIFACT). Memories: cashscript-covenant-findings, app-token-registry-and-add-asset.


ADR-0055 — Two-tier registration: your own list is free, the shared Katalogos is paid — and what that amends in ADR-0048

Date: 2026-07-26 (proposed) · 2026-08-31 (amended — v1 shipped on chipnet) Status: Accepted — v1 chipnet-validated (see amendment at end). The oracle-priced $1 covenant KatalogosRegistryV1.cash is deployed, the wallet planner + shell publisher + Publish button are wired, the swap picker reads the shared list; two live entries (CHE + ATD) inscribed on chipnet. The paymaster fallback (v2) remains designed only — deferred until oracle compromise or price policy pressure motivates deploying it.

Context. The v2.10 split (Parameters/Artemision/NAMING_katalogos-pinax.md) separated the asset surface into Pinax (write — inscribe an asset) and Katalogos (read — the directory). That raised a question the old single surface never had to answer: inscribe onto whose register? Today the answer is LocalStorageTokenRegistry — a private list in one browser. The owner asked for registration to become a deliberate, fee-bearing step. A fee on a localStorage write has no payee and nothing to enforce it, so the request necessarily means new shared on-chain state.

Decision

Registration is two tiers, and only one of them costs anything.

Act What it is Cost
Add to my list Local, private, nothing broadcast. Minting in Create does this automatically (executeGenesis / executeNbaGenesis → registry.add), so a founder is never left holding an asset their own wallet cannot name. Free, always
Publish to the shared Katalogos On-chain inscription into state everyone reads — what makes an asset discoverable by other people. Deliberate, separate, optional. Fee

Rationale for the split. Charging for the local list would be charging someone to use their own wallet. ADR-0008 says every registry entry is the user's decision, and a decision you must pay to make is not much of one. Publishing is a different act: it writes state that everybody reads, and the thing worth rate-limiting is entries in a shared list, not private curation.

What this amends in ADR-0048 (which is marked PINNED — read this before implementing)

  1. "Stoa stays FREE … free swap + free discovery; only Olympia, the spotlight, costs." A fee to enter the shared Katalogos charges for exactly what that sentence gives away. Amended: discovery is no longer free at the point of entry. The Stoa/Olympia distinction survives only if Stoa is redefined as "the unranked list you may enter for a small fee" rather than "the free list" — or if the fee is nominal/anti-spam rather than revenue (see Open below).

  2. Fee denomination. ADR-0048 puts creation and Olympia fees in the flagship DEA, deliberately, so every platform fee generates DEA buy-demand (founders acquire DEA via BCH→DEA claims, which is itself swap volume). A USD-denominated, oracle-priced fee removes that effect. Open — see below.

  3. "You pay for featured placement, never the right to swap." — NOT amended, and must not be. The ASM covenant consults no registry: anyone holding a category and its contract address can swap, registered or not. So the paid register may govern what the picker lists, never who may swap. Implementations must keep unregistered assets reachable (paste-a-category path). Gating the swap itself would break a pinned invariant and the protocol's permissionless claim.

Open — must be settled before any covenant is written

  • Fee currency. $1-worth of DEA via oracle (keeps ADR-0048's buy-demand, reuses the chipnet-validated ADR-0052 pattern) · $1-worth of BCH (simplest onboarding, drops the DEA demand) · flat DEA on ADR-0048's existing curve (no oracle, no new trust surface, but not $1).
  • Recipient. Treasury (consistent with ADR-0048 point 4) or burn/miners (makes it anti-spam rather than revenue, and largely dissolves conflict #1).
  • Mutability. Immutable entry forever · issuer may update/withdraw (the founder key is already provable from the published founderLockingBytecode) · immutable plus a separate flag layer.
  • Picker policy. Registered-first with everything still reachable (preserves #3) · registered-only (breaks #3) · registered ∪ my local list.
  • What is stored on-chain. Recommended: the category id only (32 bytes). Everything else in the descriptor is recoverable from the chain — a category id is the funding txid, so the genesis transaction yields the contract address (output 0) and the founder locking bytecode (input 0), after which the fee is a 501-way search over 0..500 bps. Storing the full descriptor would be expensive and redundant.

Reusable, already validated

  • DeviantTieredMintOracleFT.cash — $-denominated, oracle-priced, oracle-bounded fee. 5/5 on chipnet (ADR-0052). The $1 mechanism is solved.
  • PotidaeaListingRequest.cash — per-user request UTXO: zero contention, self-policing fee, refund path. The right shape for registration at scale.
  • PotidaeaOlympiaListing.cash — global state machine + fee curve. Note it commits counts (A | L | notches | lastHeight), not category ids, so it cannot enumerate assets; a registry needs per-entry NFTs whose commitment carries the category.

Consequences. Until this is implemented, the Pinax surface shows the publish action disabled and labelled "not yet built", and the Artemision website carries the amendment openly rather than implying free discovery still holds. ADR-0048's fee-constant table and scripts/potidaea-economics-sim.mjs will both need revisiting once the fee currency is chosen.

Cross-references: ADR-0008 (no default registries), ADR-0048 (Potidaea economics — amended here), ADR-0052 (oracle-priced $1 fee), ADR-0054 (NBA issuance). Naming: Parameters/Artemision/NAMING_katalogos-pinax.md. Open-question record: Parameters/Artemision/REGISTRATION_fee_open-question.md. Surfaces: Parameters/dApp/SURFACES.md.

Amendment 2026-08-31 — v1 shipped on chipnet

Everything below the "Open" list was resolved in favour of the recommended defaults, then built.

Resolved:

  • Fee currency: oracle-priced $1 in DEA (ADR-0052 pattern). Keeps ADR-0048's DEA buy-demand intact; reuses the chipnet-validated oracle codec.
  • Enforcement: covenant-enforced $1 via checkDataSig, NOT UI-only. A raw-tx script cannot underpay by even 1 atomic DEA — the covenant's feeDea >= ceil(satsPerUsd / deaRateSats) runs on-chain.
  • Recipient: platform treasury (consistent with ADR-0048 point 4).
  • Mutability: immutable per-entry NFTs. No withdraw, no edit, no admin. A separate flag/report layer is a possible v2; not built.
  • What's stored on-chain: the category id only (32 bytes, in the NFT's commitment). Everything else in a descriptor is chain-recoverable.
  • Picker policy: the swap picker reads the union of local listTierA() and the shared Katalogos entries — but ONLY intersects with the shared list when it's non-empty on the current network (so nothing regresses pre-deploy). ADR-0048 point 3's "never the right to swap" is preserved: unlisted assets stay swappable at the protocol layer (paste-a-category); what changes is only what the picker offers by default.
  • Versioning: the wallet aggregates entries across a list of covenant addresses (KATALOGOS_VERSIONS). Re-pricing (v2 at $0.50 or $2) or switching to the paymaster fallback = one config line added; discovery unchanged; old entries stay valid forever.

Versioning strategy (why not a paymaster from day one): covenant-in-oracle keeps the fee gate permissionless — no service to run, no signer that can refuse, no availability dependency. Paymaster fits better as an escape hatch (crisis, promo, oracle compromise) than as steady state. When needed, deploying v2 is one line in KATALOGOS_VERSIONS; the shell already reads across versions.

Shipped 2026-08-31 (chipnet):

  • contracts/KatalogosRegistryV1.cash — compiled, embedded as canonical D-3 template.
  • test/regtest/katalogos-registry.test.ts — 7/7 pass on MockNetworkProvider (real bytecode, libauth VM).
  • src/wallet/katalogos.ts — prepareRegister / listRegisteredCategories / deriveKatalogosVersion.
  • test/unit/wallet/katalogos.test.ts — 11/11 pass.
  • shells/app/src/core/katalogos.ts — makeShellKatalogosPublisher, fetchRegisteredCategories, KATALOGOS_VERSIONS (chipnet has one entry, mainnet empty).
  • shells/app/src/core/katalogos-oracle-dev.ts — chipnet-only dev oracle for signing $1 price messages (production uses oracles.cash).
  • shells/app/src/components/TokenImportModal.tsx — Publish button on the local-registration success screen. Costs $1 in DEA; irreversible; UI reflects the two-tier framing.
  • shells/app/src/asm-core/AsmCore.tsx — swap picker filters listTierA() by the Katalogos set when non-empty; legacy behaviour when empty.
  • Live chipnet: registry at bchtest:rwm0e4rnq0htx97g3sllrgld652w7ztxlnqlx7820kd3mhklk6v8vmtth22xs, registry category 04841b26…6f021eea, CHE + ATD inscribed (tx 1ef497a6, [tx …]). Full deploy record: ChipnetTests/.chipnet-katalogos.json.

Known limitations / follow-ups:

  • Mainnet oracle not wired. Production covenant is unbuilt; when deployed, its constructor gets a real oracles.cash pubkey and the publisher's oracle-quote call swaps from the dev signer to a network fetch.
  • Katalogos second-tab paste-address lookup (for browsing an unregistered asset by contract address, without adding it) — not built.
  • The dev oracle key is checked into a gitignored file; if someone with wallet access lifts it, they can sign arbitrary prices for the chipnet covenant only. Blast radius: chipnet registrations pay a wrong-priced fee. Rotated whenever the covenant is redeployed.

Amendment 2026-09-02 — Symbol + Icon extension (KatalogosRegistryV2 · proposed)

Status: Proposed — design locked with the owner; awaiting covenant-author sign-off before contracts/KatalogosRegistryV2.cash is written. Mockup preview shipped in design/potidaea/app.html (commit d98f0a0, live at https://navigate.st/bns/potidaea.asm/). The current V1 covenant is untouched; V2 ships alongside V1 via KATALOGOS_VERSIONS and old entries stay valid forever.

Context. V1's commitment carries the category id only (32 bytes) — deliberately minimal, everything else is chain-recoverable. The wallet's swap picker + Katalogos surface currently paint asset pills using a 1-2 char text glyph the viewer picks locally. Two consequences the owner flagged:

  1. Different viewers see different glyphs. Founder inscribes USDm, wallet A paints $, wallet B paints €, wallet C paints ?. There's no shared identity beyond the ticker.
  2. No path to a real logo image. A founder who wants a branded icon (their SVG, their PNG) has nowhere to put it. Every wallet re-renders text.

Two additions solve this without giving up the "authority lives on the chain, bytes may live anywhere" principle. Neither breaks ADR-0048 point 3 (unlisted assets stay swappable) nor ADR-0008 (no default registries — the picker still shows unregistered assets on request).

Decision

Extend the KatalogosRegistryV1 commitment schema in a new version, KatalogosRegistryV2, that adds three optional fields to the NFT commitment. All three are inscribed as part of the founder-signed genesis; nobody else can inscribe or replace them.

Field Bytes Optional? Purpose
symbol 1–4 (UTF-8) Yes (default 0-len → wallet uses ticker's first 1-2 chars) The currency-glyph — $, ₿, Ξ, Δ. Every wallet paints the same one.
iconSha256 32 or 0 Yes (0 → no icon inscribed) Verifier for the icon bytes.
iconPath ≤ 128 (UTF-8) Yes (0-len when no icon) Sia path hint where the bytes live.

symbol is 1–4 bytes because a single non-BMP glyph (like 𐇑) can take 4 UTF-8 bytes; the wallet decodes as UTF-8 and renders as one grapheme. Keeps the commitment tight.

Terminology (why THIS split — user-directed)

The three concepts are distinct and were being conflated:

  • Ticker (existing, on-chain) — the ISO-code-style string: USDm, BTC, USDC.
  • Symbol (new, on-chain) — the currency-glyph character: $, ₿, Ξ.
  • Icon (new, off-chain bytes + on-chain hash) — the branded logo image.

Standard finance-industry usage aligns with this: $ and USD are not the same thing, and ₿ and BTC are not the same thing. The wizard previously called the glyph field "Symbol" but used it as an icon input; the new split follows the correct semantics.

Icon storage — path-based Sia, moderatable, verified by hash

The bytes live at:

s3://bns/katalogos/<ticker>/logo.<ext>

Path-based, NOT content-addressed. Rejected content-addressing (s3://bns/katalogos/<sha256>.<ext>) because it eliminates the moderation lever: if a founder uploads abuse content — CSAM, targeted harassment, copyright infringement, spam — a content-addressed key has no removal path. Whoever holds the katalogos Sia namespace (currently SilentMode operator's main wallet in Argus/wallets.json) MUST be able to delete files to respond to legitimate takedown demands.

Deletion by the platform is the ONLY thing the platform can do to a founder's icon:

Attack Blocked by Who wins
Silently swap icon bytes iconSha256 verification in every viewer Founder — hash mismatch → wallet rejects → fallback to symbol
Delete an abusive icon (allowed — this IS the moderation lever) Platform — bytes gone, wallets fall back to symbol
Delete a legitimate icon Founder re-uploads identical bytes, or inscribes a new record with a different iconPath Founder — the chain is authority; Sia is a CDN
Founder loses key (same as any wallet) Founder — key hygiene is user's responsibility

The "silently swap" attack is real for pure-path storage; the hash inscribed on-chain neutralises it. Combining path-based storage with content-hash verification gives moderation + tamper-proof without content-addressing's censorship-resistance downside.

Fetch & fallback ladder (wallet-side)

Every viewer that reads a Katalogos entry runs this ladder:

1. Read record → { symbol, iconSha256, iconPath }
2. If iconPath && iconSha256:
     GET iconPath
     if bytes && sha256(bytes) === iconSha256:
       render <img>  ✓
     else if bytes && sha256 mismatch:
       render symbol (or ticker fallback)
       surface a small warning ribbon: "icon tampered on <origin>"
     else (fetch failed / offline / deleted):
       render symbol (or ticker fallback)  — silent
3. Else if symbol:
     render symbol as text glyph
4. Else:
     render first 1–2 chars of ticker (never blank)

The fallback ladder means an icon-less wallet, an offline wallet, a wallet with a broken Sia connection, and a wallet whose iconSha256 mismatches all render something sensible without crashing. iconSha256 is only consulted when it's non-zero and iconPath is set.

Attribution — no indexer, on-chain is enough (user-directed)

No indexer service required. Rejected running a services/katalogos-indexer/ because:

  1. Every V2 record is signed by the founder's key. The founder's identity is signature-recoverable from any record — attribution is native to the chain.
  2. Wallets already scan the registry covenant addresses in KATALOGOS_VERSIONS to build the picker list; adding "who inscribed each entry" costs nothing incremental.
  3. A separate database is another data source to trust and to keep synced. For MVP, the chain is the answer.

If reputation/leaderboard features become useful later (v3+), an indexer can be built on top without amending the on-chain design. The V2 record is complete for attribution today.

What this amendment does NOT change

  • V1 covenant is untouched. No re-write, no migration, no chain fork. V1 entries stay valid. V2 adds a new covenant address to KATALOGOS_VERSIONS; the wallet reads both. Founders who registered under V1 can (optionally) re-register under V2 to gain the symbol/icon fields; V1 entries render with the wallet's fallback ladder (symbol from ticker's first chars, no icon).
  • ADR-0048 point 3 stays honoured. Unlisted assets remain swappable at the protocol layer (paste-a-category path). V2 governs what the picker offers by default; it does not gate the swap.
  • Fee, recipient, mutability, picker policy — all inherited from V1's Amendment 2026-08-31. Oracle-priced $1 in DEA to platform treasury, immutable per-entry NFTs, union-with-local picker filter.

Storage layout on Sia — one shared, moderatable namespace

s3://bns/katalogos/
  ├── USDm/logo.svg
  ├── BTC/logo.png
  ├── SIGMA/logo.svg
  └── …
  • One subfolder per ticker (human-readable, easy to inspect, easy to moderate).
  • SilentMode operator has write access via the existing Argus/src/lib/sia-upload.js setup. A new helper (Argus/src/lib/katalogos-icon-upload.js, follow-on §Shipping) wraps this for the wallet's use.
  • Rejected per-founder subpaths (s3://bns/katalogos-<founder>/…) as unnecessary infrastructure. The hash-verification already prevents substitution; per-founder namespaces would only add deletion-protection at the cost of per-founder Sia allowances (heavy).

Constraints on inscribed values

  • symbol: 1–4 UTF-8 bytes. Wallets truncate at 2 grapheme clusters when rendering. No ASCII-only requirement — Unicode currency signs, Greek letters, mathematical symbols all valid.
  • iconPath: must start with s3://bns/katalogos/ — enforced by the covenant. Prevents a founder from pointing viewers at a URL the platform cannot moderate (e.g., an arbitrary HTTP URL controlled by the founder). If a founder legitimately wants to serve from their own domain, they can — by inscribing an alternate record path in a future v3 (multi-pin), not v2. For v2, one path, in the shared namespace.
  • iconSha256: must be present when iconPath is non-empty (and vice-versa). Covenant requires either both-set or both-unset — no half-inscribed icons.
  • File extension in iconPath (.svg, .png, .jpg, .webp, .gif, .ico) — advisory, not covenant-enforced. Wallets sniff the content-type from the fetch response.

Alternatives considered

  • Extend V1 commitment in place. Rejected — V1 is deployed and immutable per its own design; changing its commitment shape means either a covenant fork or a data-shape break. Cleaner to ship V2 alongside.
  • Content-addressed keys within shared namespace. Rejected — see §Icon storage above. Moderation lever is more important than the marginal deletion-resistance gain.
  • Per-founder Sia subpaths. Rejected — see §Storage layout. Heavy infrastructure for minimal added protection given hash verification.
  • Indexer database service. Rejected — see §Attribution. Not needed for MVP; can be built later without amending the on-chain design.
  • Store icon bytes on-chain directly (like BRC-20 inscriptions). Rejected — 128 KB per icon × N assets is expensive block space with no benefit over Sia + hash.
  • IPFS instead of Sia. Rejected for v2 — Sia is already the project's storage backend (BNS gateway, site deploys). Adding IPFS as an alternate iconPath scheme is a possible v3 addition; not v2.

Shipping (follow-on work — none built yet)

  1. contracts/KatalogosRegistryV2.cash — clone of V1 with the commitment schema extended (category | symbol | iconSha256 | iconPath) and a covenant check enforcing the s3://bns/katalogos/ iconPath prefix + the "both-set or both-unset" invariant for iconSha256/iconPath. Needs covenant-author sign-off before writing.
  2. Argus/src/lib/katalogos-icon-upload.js — wrapper around the existing Sia upload helper. Takes (ticker, file), uploads to bns/katalogos/<ticker>/logo.<ext>, returns { path, sha256, size, mime }. Mirrors Argus/src/lib/sia-upload.js.
  3. Shell wallet extension — src/wallet/katalogos.ts::prepareRegister extended to accept { symbol, iconSha256?, iconPath? }; wizard fields plumbed through. Publish button in TokenImportModal.tsx collects them and calls the extended publisher.
  4. Wallet-side agInner() extension — extend the existing helper (already in design/potidaea/app.html per commit d98f0a0; shell version lives in the asset-pill render path) to fetch iconPath, verify SHA-256, render <img> on match, fall back to symbol on mismatch/deletion.
  5. KATALOGOS_VERSIONS entry — new V2 covenant address added to shells/app/src/core/katalogos.ts; wallet reads V1 + V2 in the picker.

Small-scope amendments after V2 ships: multi-pin (v3 iconPaths[]), alternate storage schemes (IPFS), founder-updatable icons (mutability flag layer per V1's Amendment §Mutability).

Preview live now. The design mockup at https://navigate.st/bns/potidaea.asm/app.html (commit d98f0a0) already shows the split (Symbol grid + Icon uploader with SHA-256

  • Sia path preview) so the UX is testable end-to-end. The on-chain wire is queued for the follow-on session with the covenant author.

Cross-references (added): ADR-0055 base + Amendment 2026-08-31 (this file). Storage backend: Argus/src/lib/sia-upload.js, Argus/wallets.json (the moderation-authority key). BNS gateway: Argus/src/gateway/public-gateway.mjs. Mockup preview: commit d98f0a0 (design/potidaea/app.html glyph picker + icon uploader).


ADR-0056 — Multi-sig wallet: m-of-n via BIP-48 xpubs, transport = Aegis cosign over Nostr

Date: 2026-10-22 Status: Partially shipped — the transport layer (B) is implemented and tests pass against the Aegis spec's §08 test vector. The signing layer (A) and UI (C) are scaffolded with types only; the covenant, PST construction, and flows are the next sessions' work.

Context. Deviant's keystore (ADR-0083 model) ships independent + hd-account single-signer wallets. Adding m-of-n multi-sig has two orthogonal pieces: (A) the signing layer — redeem scripts, address derivation, partial transactions and signature aggregation — and (B) the co-sign transport — how a partially-signed transaction gets from the first device to the second. The Aegis project (sibling of Theseus and Hermes) published a self-contained open spec for B at bns://aegis.x/multisig/, with a test vector that any interoperable implementation can hit byte-for-byte. We adopt that spec unchanged so a Deviant wallet can co-sign with an Aegis wallet (and any other spec implementor) without either side knowing the other exists.

Decision

Layered split, pick each one on its own merits.

(A) Signing layer — per-wallet script form, locked set of design choices

Dimension Choice Rationale
Script form Per-wallet pick at creation: covenant (default) · native (opt-in) covenant = a DeviantMultiSig.cash CashScript — unlimited m-of-n, first-class CashToken support, aligns with the rest of Deviant. native = BCH's OP_CHECKMULTISIG under P2SH-20 — proven for a decade, smaller tx, 15-of-20 cap, limited CashToken story. Both paths visible in src/multisig/types.ts::MultisigScriptForm.
CashTokens Supported for both forms For covenant: baked into the covenant. For native: via P2SH32 variant. Fiat/MBA holdings are the whole point of a Deviant multi-sig; non-negotiable.
PST format on the wire Deviant JSON PST aligned with Aegis spec example shape ({v, chain, network, channelId, unsignedTx, signatures, hint?}) Opaque to the transport, interoperable with any wallet speaking the same shape. Internally the signing layer builds on @bitauth/libauth partial-signing primitives; toJson / fromJson adapters at the session boundary convert.
Keystore model New multisig kind in MultiKeyStore beside independent + hd-account One store, one unlock, one shared PIN — same ergonomics as the rest. Record carries composition: {m, n, xpubs[], mineIndex, scriptForm} + seedId + accountIndex.
HD derivation path BIP-48 — m/48'/145'/<accountIndex>'/2'/0/0 Industry standard for multi-sig; 2' = multi-sig script type. Keeps our cosigning identity on a seed's dedicated branch, separate from hd-account (m/44'/145'/N'/0/0).

(B) Transport — Aegis aegis.cosign.v1 unchanged

  • src/multisig/cosign.ts — HKDF-SHA256 channel derivation over canonical descriptor JSON ({"p","chain","network","m","xpubs":[…sorted…]}); AES-256-GCM envelope with channel id as AAD; three message kinds (request, signed, declined); 24-hour replay guard.
  • src/multisig/cosign-relay.ts — Nostr NIP-59 gift-wrap (kind 1059) transport. Fresh per-session signing key so relays cannot link conversations. Single-filter subscribeMany (array form silently matches nothing — spec warning in §05).
  • Test vector match: channel id 8cca5fb6…, spec envelope round-trip, tamper + replay rejection — 17/17 tests green in test/multisig-cosign.test.ts.
  • MPL-2.0, matching the Aegis reference code.

Open questions (next session owns these)

  1. DeviantMultiSig.cash covenant — needs CashScript design + chipnet validation (both the m-of-n signature verification and the CashToken pass-through branches). Aegis itself ships P2SH-20 for BCH (per their lib/chain-bch.js) so there is no covenant reference to port — we draft from scratch.
  2. PST signing — libauth's partial-transaction API is low-level; the adapter to our JSON format needs careful review (signature re-ordering by xpub index, SIGHASH flag handling for CashToken outputs, SIGHASH_SINGLE edge cases if we ever expose it).
  3. Mine-index authentication — nothing in the cosign layer tells us a signed message came from the xpub whose index it claims. The signing layer verifies the signature against the input's sighash + the claimed xpub's pubkey; if that fails we drop the message. Spec is explicit (§06): the signing layer is the authorisation boundary.
  4. UI — Create flow (paste co-signers' xpubs + QR share own) · Inbox for pending request + signed messages · Approve/decline with tx preview rendered against our own key derivation (never trust hint).
  5. Keystore migration — add kind: 'multisig' to WalletRecord's discriminated union; migrate() leaves existing records untouched; unlockAll opens the multi-sig entry's cosigning branch (not a spending key — the wallet is scripted, not key-locked).

Consequences

  • Any wallet implementing aegis.cosign.v1 can co-sign with a Deviant multi-sig. Opens interop with Aegis (same org), and with any third-party wallet that reads the spec.
  • The 24-hour replay guard means a stale request simply isn't actionable — the user has to re-propose. No long-horizon pending approvals.
  • Nostr relays see "a channel exists" + rough traffic size. Nothing more. The co-signer set is as private as the xpubs themselves (which anyone who knows them could already watch on chain).
  • Per-session fresh Nostr signing keys mean we cannot be rate-limited or reputation-tracked across requests. The relay sees activity, not identity.

Cross-references.

  • Aegis spec: bns://aegis.x/multisig/ (served at navigate.st/bns/aegis.x/multisig/)
  • Aegis source: code.silentmode.st/silentmode/aegis (sibling repo; wallet shell at lib/chain-bch.js)
  • Deviant implementation: shells/app/src/multisig/{cosign,cosign-relay,types}.ts
  • Deviant tests: shells/app/test/multisig-cosign.test.ts (17/17 pass against §08 vector)
  • Keystore host: shells/app/src/keystore/MultiKeyStore.ts (ADR-0083 model; multisig kind to be added next session)
  • Covenant: contracts/DeviantMultiSig.cash (to be written)
  • Memory: multi-account-keystore, chipnet-shared-loader, cashscript-covenant-findings

ADR-0057 — Three-layer metadata split: covenant encodes rules, NFT commitment encodes identity, BCMR carries the rest

Date: 2026-10-22 Status: Partially shipped — the NFT-commitment layer (step 1) is implemented and tested (src/wallet/metadata-commitment.ts, 32/32 unit tests). Steps 2 (Sia mirror in the BCMR OP_RETURN) and 3 (BCMR consumer in the Deviant wallet shell) are scoped for follow-on sessions.

Context. Deviant stores token metadata across three disjoint channels today, in increasing distance from consensus:

  1. Covenant parameters — rateSats, founderFeeBps, founderLockingBytecode are baked into the P2SH32 contract bytecode. Change any of them and you get a different contract address; consensus enforces integrity for free.
  2. BCMR OP_RETURN (CHIP-2023-04) — a tiny on-chain anchor (<'BCMR'> <sha256> <URL>) committing to a JSON file hosted off-chain. The JSON carries name, symbol, decimals, description, uris.icon, uris.web, tags, plus a dvmb extension with our template dispatch params. Already emitted by design/potidaea/src/bcmr.ts on every MBA genesis + publish.
  3. LocalStorage registry — client-side cache of the above.

Three gaps exposed by this split:

  • The NFT commitment is wasted — set to commitment: '' on every minting baton we emit. CashTokens allows 40 bytes there; we store zero.
  • BCMR hosting is single-point — our one URL is on silentmode.st. If the VPS goes dark the metadata URL stops resolving (though the on-chain hash still proves what SHOULD be at the URL).
  • The Deviant wallet shell does not READ BCMR — unknown tokens display as "Unknown token (abcd…ef12)" because we never resolve the chain's BCMR anchors back to the hosted JSON. Only our three hardcoded chipnet seeds (ASM, ASM2, CAP) show tickers; everything else is nameless at the UI even when BCMR fully describes it on chain.

Decision

A three-layer split, each layer answering a specific question with the right redundancy for its criticality.

Layer Pin here Why here
Covenant address (P2SH32 hash) rateSats, founderFeeBps, founderLockingBytecode Already here. These are the consensus-enforced economic rules — must be unchangeable + verifiable by anyone.
NFT commitment (40 B) ticker (12 B ASCII, padded) + decimals (1 B) + assetClass (1 B) + 24 B reserved The irreducible-minimum identity that must survive even when every BCMR URL is dead and the user has no network at all.
BCMR OP_RETURN (~70 B anchor) sha256(json) + URL-primary + URL-sia-mirror (future) Standard interop with every BCH wallet. Two URLs for host-outage resilience.
DVMB OP_RETURN (optional, Deviant-only) templateId, rateSats, founderFeeBps, founderLockingBytecode Deviant wallets reconstruct the covenant off-HTTP for fully-offline resolve of unseen MBAs. Currently inside BCMR JSON extensions.dvmb; promoting to its own on-chain record is a nice-to-have, not critical.
Off-chain BCMR JSON name, description, long-form description, icon URL, website, locale, author, tags, change-log, versioned revisions Unlimited size, mutable via authchain. Icons especially — cannot fit in 40 bytes on-chain anywhere.
LocalStorage registry Client-side cache built from the above sources — never the source of truth What the wallet renders from.

NFT commitment binary layout (ADR-0057 §1)

Pinned here so decoders in other wallets stay interoperable:

byte  0:     magic   = 0x44 ('D' — Deviant metadata commitment)
byte  1:     version = 0x01
byte  2:     decimals (uint8, 0..18)
byte  3:     assetClass byte (ASSET_CLASS_BYTE table — stable, new codes never reuse slots)
bytes 4..15: ticker, 12 UTF-8 bytes right-padded with 0x00 (cap 12 chars, ASCII only)
bytes 16..39: reserved, zero-initialized (24 B for future protocol flags)

Decoders MUST check length === 40 AND magic === 0x44 AND version === 0x01 before interpreting any bytes. Anything else is NOT a Deviant metadata commitment and must be handled per the covenant that produced it.

ASSET_CLASS_BYTE table. High nibble = family (0x1 = MBA, 0x2 = Token). Values stable forever; new classes get new slots.

Class Byte Family
DEA 0x11 MBA
PLA 0x12 MBA
MCA 0x13 MBA
MTA 0x14 MBA
DRA 0x15 MBA (dynamic rate — ADR-0015)
MTU 0x21 Token
MTL 0x22 Token
PTU 0x23 Token
PTL 0x24 Token

Disambiguation vs AdjustableFeeCapped (ADR-0011)

The AdjustableFeeCapped covenant USES the minting-NFT commitment for live feeBps state (ScriptNum-encoded, 1-2 bytes). A naive decoder that just read "the commitment" could mis-interpret an adjustable-fee token as a metadata commitment. The format defends against this at two levels:

  1. Length-first check: metadata commitment is ALWAYS 40 bytes. ScriptNum feeBps 0..500 is at most 2 bytes. No overlap possible.
  2. Magic byte at offset 0: 0x44 ('D'). Even if a future protocol defines a different 40-byte commitment, the magic distinguishes.

The genesis planner enforces this at write time: assetClass is only honoured when adjustableFee is NOT set. The two features are mutually exclusive for the same minting NFT.

Emission (step 1 — this session)

  • New module: src/wallet/metadata-commitment.ts — pure encoder/decoder, no deps outside src/token/assetClass. Exports encodeMetadataCommitment, decodeMetadataCommitment, isMetadataCommitment, format constants.
  • GenesisIntent adds optional assetClass?: AssetClass.
  • prepareGenesis populates GenesisPlan.metadataCommitmentHex when assetClass is set AND adjustableFee is NOT. Invalid encoder inputs (ticker too long, non-ASCII, decimals out of range) become INFO-severity warnings on the plan; the plan still works, just without the commitment sweetener.
  • DeviantWallet.createAsset picks the NFT shape by precedence: (1) adjustable-fee commitment, (2) metadata commitment, (3) empty default — preserving every current path and adding the new one only when requested.
  • Tests: test/unit/wallet/metadata-commitment.test.ts — 32/32 pass. Pins the binary layout, round-trips every asset class, and verifies non-metadata commitments (empty, short, wrong-magic, wrong-version) decode to null.

Deferred to follow-on sessions

Step 2 — Sia mirror in the BCMR OP_RETURN. CHIP-2023-04 allows multiple URL pushes; wallets try them in order. Upload the same canonical BCMR JSON to Sia at s3://bns/bcmr/<network>/<categoryId>.json and emit both URLs on-chain. ~90 extra bytes on-chain, zero ongoing cost, host-outage resilience.

Step 3 — BCMR consumer in shells/app. Biggest UX win. Scan each unknown token's genesis tx for the BCMR OP_RETURN → fetch the referenced JSON (fall back URL-to-URL) → verify SHA-256 → cache as a TierATokenDefinition. Covers every ecosystem token that already follows the CHIP, not just ours. New module likely at shells/app/src/token/bcmr-resolver.ts, hooked into the registry seeding path.

Step 4 (optional) — Separate DVMB OP_RETURN. Promote the DVMB fields out of the BCMR JSON extensions into their own on-chain OP_RETURN so Deviant wallets can reconstruct an unseen MBA covenant without any HTTP hop. Only worth it once we have a fully-offline demo use case.

UI opt-in path

Artemision wizard needs a one-liner change later: pass the already-classified assetClass (which the create flow computes from mint/supply/backing axes anyway via classifyAssetClass()) into prepareGenesis. Zero new UI; purely plumbing in ArtemisionScreen.tsx when it builds the GenesisIntent. Left for the follow-on so this ADR commit is scope-tight.

Consequences

  • Deviant-minted MBAs/NBAs that opt into the metadata commitment are now fully self-describing from UTXO alone — a wallet with no internet, no BCMR hosting, and no local registry can show "ASM (DEA, 2 dp)" for an on-chain amount.
  • BCMR remains the primary rich metadata surface (icon, description, URIs).
  • Existing MBAs with commitment: '' continue to work exactly as before. The isMetadataCommitment guard returns false for them; the decoder returns null; display falls back through BCMR → local registry → "Unknown".
  • The 24 reserved bytes in the commitment leave room for future protocol flags (NFT uniqueness marker, cross-chain pointer, etc.) without reusing the slot.

Cross-references.

  • Spec module: src/wallet/metadata-commitment.ts
  • Spec test: test/unit/wallet/metadata-commitment.test.ts (32/32)
  • Genesis plumbing: src/wallet/genesis.ts (GenesisIntent.assetClass + plan encode), src/wallet/wallet.ts (createAsset NFT precedence)
  • BCMR emitter: design/potidaea/src/bcmr.ts
  • Related ADRs: ADR-0011 (adjustable fee — must not collide), ADR-0038 (decimals/denomination), ADR-0054 (NBA cap-as-consensus — no minting baton), ADR-0055 (Pinax/Katalogos — shared registry for non-BCMR tokens)
  • Memory: app-token-registry-and-add-asset, cashscript-covenant-findings