TSP-0006 FUN20
TSP-0006 proposal for Tondi blockchain
Proposal Number: TSP-0006
Proposal Name: An Inscription-style Fungible Token Standard for Tondi
Category: Applications (A) — pre-Oct 2025 numbering retained as 000x
Status: Review
Author: Avato Labs
Created: 2025-09-04
Revised: 2025-09-21 (P2SH Integration: Updated wire format to support P2SH redeem script mechanism, enhanced MEV resistance through hash-based storage, improved L2/RGB compatibility, added P2SH-specific APIs and validation rules, optimized for compact L1 footprint while maintaining CBOR determinism)
Target: Tondi Frontier → Tondi Mainnet (v2025b)
Scope: On-chain payload schema, validation semantics, DAG ordering, data availability, zk/bridge extensions, governance mechanism
1. Overview
FUN20 establishes a robust and versatile fungible token protocol tailored for the Tondi chain, leveraging an inscription-style approach to on-chain data embedding while incorporating comprehensive zero-knowledge (zk) proofs and bridge functionalities for seamless cross-chain interactions. This protocol is engineered to address the evolving demands of decentralized finance (DeFi), non-fungible tokens (NFTs), and multi-chain ecosystems, drawing inspiration from inscription-based standards like Runes and BRC-20 but advancing beyond them with DAG-optimized determinism and future-proof extensibility. By embedding token logic directly into transaction payloads, FUN20 minimizes reliance on external smart contracts, reducing complexity and gas costs while enhancing auditability and interoperability. The protocol places paramount emphasis on four core principles: determinism (ensuring predictable outcomes across all nodes in a DAG environment), compactness (optimizing for minimal on-chain storage and bandwidth), extensibility (supporting modular upgrades without breaking existing deployments), and ecosystem interoperability (facilitating integration with zk-rollups, EVM-compatible chains, and data availability layers). These principles are manifested through the following key features:
- Compact payloads: Limited to tiered sizes per operation (Transfer/Burn/Mint ≤64 bytes; Issue/Govern ≤96 bytes; Deploy ≤128 bytes), this constraint is meticulously calibrated to achieve a low on-chain footprint while supporting high throughput in Tondi's DAG consensus model. Drawing from efficiency lessons in protocols like Runes, this design reduces network congestion and transaction fees, enabling scalable operations even during peak loads. For instance, a typical transfer operation can fit within 30-40 bytes using array encoding, leaving ample room for optional extensions like proofs or metadata without compromising performance.
- Deterministic replay across DAG consensus via Canonical Resolution Spec (CRS) with MEV resistance: In a directed acyclic graph (DAG) setting, where transactions may arrive out of order, CRS guarantees consistent state resolution by sorting events based on DAA scores, transaction hashes, input indices, payload orders, and event digests. Enhanced with Miner Extractable Value (MEV) resistance through shuffling via DAA-derived seeds (all implementations MUST support; enabled by consensus parameter, default enabled), this mitigates front-running attacks common in high-speed chains. Unlike linear blockchains (e.g., Bitcoin for BRC-20), this approach ensures fair and predictable execution, with built-in de-duplication to prevent duplicate applications.
- Strict safety features: fixed header ("FN20" magic + version) combined with deterministic CBOR encoding (rejecting non-minimal forms, duplicate keys, or indefinite lengths) provide absolute safeguards against parsing ambiguities or exploits. Schema limits (e.g., tick length, decimal precision) and replay protection (via nonce and expiry fields) further fortify the protocol. Formal verification requirements for critical components like CBOR parsers (using tools such as TLA+ or Coq) elevate security to enterprise levels, addressing vulnerabilities seen in less rigorous standards.
- Dual deployment models:
- Deploy-Mint: This open mint model promotes decentralized and fair token launches, enforcing global uniqueness via the token symbol (tick) and namespace hashing. Fair distribution mechanisms, such as per-mint caps (lim) and optional pre-allocation (pre), prevent centralization during supply creation, making it ideal for community-driven tokens where permissionless participation is key.
- Deploy-Issue: In contrast, this issuer-controlled model derives a unique contract address (CA) from deployment data, granting advanced ownership controls like issuance rights, blacklisting, and ownership transfer (chown). It supports unlimited supply (max=0) with rate limits, catering to regulated or enterprise use cases requiring ongoing management.
- Comprehensive operations: The protocol includes a full suite of operations—
deploy(establish token parameters),mint(permissionless supply addition in Mint mode),issue(controlled supply in Issue mode),transfer(balance movement),burn(supply reduction),blacklist(address denylisting in policy layer),chown(ownership transfer), andgovern(parameter proposals via multi-sig or DAO). These are extensible through features arrays, allowing for modular additions like partial chown rights. - zk/bridge readiness: Optional fields for commitments (l1_root), proofs (proof_*), and cross-chain operations (reserved ops like lock/release) enable lightweight data availability (DA) proofs and zk-verified state transitions. EVM compatibility via reserved ops (e.g., evm_call) facilitates DeFi/dApps integrations, such as cross-VM calldata for token swaps or lending, positioning FUN20 for hybrid ecosystems involving Ethereum or other EVM chains.
- Governance lite: Embedded mechanisms allow for secure parameter updates (e.g., adjusting max supply or decimals) through multi-signature approvals or DAO proposals, executed via the
governop with quorum thresholds and approval windows (e.g., 7 DAA cycles). This lite governance model ensures evolution without forking, contrasting with immutable standards, while maintaining decentralization through verifiable on-chain voting.
2. Wire Format
2.1 Header and P2SH Integration
Magic: "FN20" (4 bytes).
A fixed ASCII string encoded as bytes 0x46 0x4E 0x32 0x30 ("F" "N" "2" "0"). The magic is a protocol-family identifier for FUN20-style fungible tokens and enables fast detection by scanners, indexers, and validators. Decoders MUST verify this exact 4-byte sequence at the payload outset; any mismatch ⇒ reject as non-FUN20. Protocol identification and versioning are determined exclusively by the fixed header (Magic + Version); no in-payload protocol tag is used. For compatibility and clarity, map key 1 is reserved and MUST NOT appear in v1 payloads; if present, decoders MUST reject the event as bad-encoding.
Version: 0x01 (1 byte).
The 1-byte version is the sole authority for selecting on-chain semantics. Decoders read version after magic verification and apply the corresponding schema/limits. If the version is unsupported, reject as wrong-version (see Appendix C).
P2SH Redeem Script Structure: The complete FUN20 payload (header + CBOR) serves as the redeem script for P2SH transactions. This design provides significant advantages:
- L1 Storage Optimization: Only the 20-byte BLAKE3-160 hash of the redeem script is stored in UTXO scriptPubKey, reducing L1 footprint by ~70-80%
- MEV Resistance: Hash-based storage hides payload details from miners, enhancing front-running resistance
- L2/RGB Compatibility: P2SH mechanism aligns with Bitcoin's Taproot ecosystem and RGB++ protocols
- Delayed Revelation: Complete redeem script is only revealed during spending, enabling advanced privacy features
Storage Locations:
- L1 On-Chain: UTXO scriptPubKey contains
OP_HASH160 <20-byte-hash> OP_EQUAL(~26 bytes total) - L1 Spending: Complete redeem script (header + CBOR, ~45-128 bytes) stored in scriptSig during transaction spending
- Off-Chain/Indexer: Tondi indexer persists complete redeem scripts in SQLite/Redis (~10 GB/1M events) for API access
CBOR payload follows immediately (deterministic). Directly after the 5-byte header, the CBOR-encoded payload begins without any separators, padding, or additional metadata. This immediate transition maximizes compactness and simplifies parsing, as the decoder can switch seamlessly to CBOR mode post-header validation. The payload MUST conform to the deterministic CBOR rules outlined in §2.2, ensuring that the entire redeem script (header + payload) forms a predictable byte stream for hashing, validation, and replay in the CRS. This design eliminates unnecessary delimiters (e.g., no length prefix for the payload, as the transaction context defines boundaries), reducing overhead in Tondi's inscription model where payloads are embedded in outputs. In practice, this allows for efficient streaming decoders in resource-limited environments, such as embedded devices or light clients, while maintaining full determinism for consensus-critical operations.
2.2 CBOR Encoding Rules
Header-first identification. Protocol identification and versioning are determined exclusively by the fixed header (Magic + Version). Implementations MUST NOT rely on any in-payload string/tag to infer protocol or version. Map key 1 is reserved in v1 and MUST NOT appear; if present, decoders MUST reject as bad-encoding.
Exception to “unknown keys ignored”. While unknown integer keys SHOULD be ignored for forward compatibility, this does not apply to key 1 (reserved/deprecated): its presence is an error.
FUN20 leverages Concise Binary Object Representation (CBOR), as defined in RFC 8949, as its primary serialization format for on-chain payloads. CBOR is selected for its balance of compactness, efficiency, and flexibility, making it ideal for inscription-style protocols where minimizing data footprint is critical to reducing network congestion and transaction costs in a high-throughput DAG environment like Tondi. To ensure absolute determinism, security, and forward-compatibility, FUN20 imposes stringent encoding rules that go beyond standard CBOR implementations. These rules prevent ambiguities in parsing, mitigate potential attacks (e.g., via malformed data), and guarantee consistent behavior across all nodes, wallets, and indexers. All encoders and decoders MUST adhere to these rules; non-compliance results in rejection with a bad-encoding error, ensuring protocol integrity.
The following rules are mandatory for all FUN20 payloads:
- Deterministic encoding only. CBOR payloads MUST follow the deterministic encoding guidelines from RFC 8949 Section 4.2, including preferred serialization for integers (shortest form), canonical map ordering, and avoidance of unnecessary length encodings. This eliminates variability in representation, ensuring that identical logical data produces the exact same byte sequence across implementations. Determinism is crucial for FUN20's event digests (computed via BLAKE3), which rely on byte-precise inputs for domain separation and replay protection. For example, an integer value like 100 MUST be encoded as 0x1864 (short form), not longer variants, to prevent subtle discrepancies that could lead to consensus forks in DAG resolution.
- Integer map keys, strictly ascending order. All map keys MUST be non-negative integers (uint) and appear in strictly ascending numerical order within the payload. This rule enforces canonical structure, simplifying validation and reducing parser complexity. Non-integer keys (e.g., strings or negatives) are prohibited to avoid type confusion attacks. Ascending order ensures predictability during CRS replay, where payload order factors into event sorting. Decoders MUST reject any map with out-of-order or non-integer keys as
bad-encoding, providing a clear audit trail for invalid events. - No indefinite lengths. Indefinite-length encodings (e.g., for arrays or maps starting with 0x9F/0xBF) are explicitly forbidden. All structures MUST use definite lengths to prevent denial-of-service (DoS) attacks via unbounded streaming or memory exhaustion during parsing. This aligns with FUN20's compactness goal, as definite lengths encourage precise sizing and align with the tiered payload limits. In practice, this means arrays and maps must specify their exact element count upfront, enhancing both security and efficiency in resource-constrained environments like mobile wallets.
- Unknown keys ignored (forward compatibility). Decoders MUST silently ignore any unrecognized integer keys in maps, allowing for seamless protocol upgrades without breaking existing implementations. This forward-compatibility mechanism ensures that v1 clients can process future payloads (e.g., v1.1 extensions like new proof fields) by skipping unknown elements, while preserving core semantics. However, ignored keys MUST NOT alter validation of known fields; for instance, an unknown key cannot override a required field like
op. This rule is balanced with security by mandating rejection of malformed structures elsewhere, preventing abuse through extraneous data. - Strict UTF-8, prohibiting control chars; mixed-script homoglyph rejection is mandatory. All text strings (tstr) MUST be valid UTF-8 encoded and normalized to Normalization Form C (NFC) as per Unicode Standard Annex #15. Control characters (e.g., U+0000 to U+001F) are prohibited to prevent injection attacks or rendering issues in UIs. Additionally, mixed-script homoglyph rejection is enforced for name and metadata fields: decoders MUST detect and reject strings with visually similar characters from different scripts (e.g., Cyrillic "а" mimicking Latin "a") that could enable phishing or tick spoofing (tick is restricted to [a-z0-9], no need). This is implemented via Unicode confusables mapping (TR39), ensuring token symbols (tick) and names remain trustworthy. Rejection triggers
bad-encoding, with indexers logging details for auditability. CBOR decoders must reject:- Indefinite lengths. As noted above, this safeguards against streaming exploits and ensures finite resource usage.
- Duplicate keys. Maps with repeated keys (even if values differ) MUST be invalid, preventing ambiguity in field interpretation.
- Non-minimal integers. Integers MUST use the shortest possible encoding; longer forms (e.g., encoding 0 as 0x1A00000000) are rejected to enforce determinism.
- Non-sorted maps. Keys not in ascending order trigger rejection, aligning with canonical requirements. Array Encoding: To further optimize space savings in bandwidth-constrained scenarios, FUN20 v1 explicitly allows array-based encoding as an alternative to maps for payloads. This format represents fields in a fixed, predefined order (detailed in Appendix A), eliminating the overhead of key integers and enabling up to 20-30% size reduction for common operations like transfers. Indexers MUST support both map and array forms transparently, converting as needed during processing to maintain compatibility. Wallets SHOULD prefer array encoding by default to minimize fees and enhance user experience, especially in mobile or low-resource environments. The array format uses fixed field orders per operation type (e.g., for transfer: [op, chain, tick|ca, amt, to?, nonce, ctx_in]), ensuring no loss of semantics. Encoding equivalence is strictly enforced: Any event's semantic determination and validity MUST NOT depend on whether map or array encoding is used—outcomes (e.g., balance updates) MUST be identical regardless of format, with no double application allowed. SDKs MUST handle mutual conversion seamlessly, preserving semantic equivalence while noting that event digests may differ due to byte variations (but CRS sorting accounts for this via digest comparison). Field omission rules further enhance compactness: Optional fields in array encoding MAY be omitted by truncating the tail (e.g., no nonce if not needed), but no mid-array gaps are allowed to avoid parsing errors. This dual-format support provides flexibility without compromising determinism, with test vectors in SDKs covering conversions and edge cases like omitted fields or maximum payloads. To ensure de-duplication across encodings, semantic_digest is computed after normalizing to a canonical map: for arrays, map positions to keys per Appendix A; omitted fields are absent; then encode deterministically with sorted keys.
2.3 Hard Limits
- Redeem script size limits (per op): Transfer, Burn, Mint ≤64 bytes; Issue, Govern ≤96 bytes; Deploy ≤128 bytes (optimized for P2SH scriptSig efficiency and reduced L1 footprint).
- ≤16 FUN20 payloads per transaction.
- ≤2000 FUN20 payloads per DAA cycle (per-block equivalent, to prevent spam).
- P2SH-specific constraints: Redeem script MUST NOT exceed 256 bytes total (including header) to prevent scriptSig inflation and maintain efficient spending.
- Token symbol (
tick):- Length 3–8 chars (extensible to 12 in v1.1).
[a–z0–9], starting with a letter.- MUST be NFC normalized (to prevent homoglyph attacks).
- Decimal precision:
0–18(fixed upon deploy; MUST NOT change post-deploy without governance approval). - Big numbers encoded as minimal big-endian byte strings (zero as
h''; ≤32 bytes for amt, max, lim, pre; exceeding →bad-encoding). - Addresses in raw binary form (20/32-byte hashes or x-only pubkey hashes; addr_kind MAY be omitted if length unambiguous—20B=0x01, 32B=0x02; otherwise MUST prefix).
2.4 Anti-Abuse (Non-Consensus Guidance)
- Suggested max: 4 FUN20 payloads per tx (hard cap 16).
- Per-sender: ≤64 payloads per 60s window.
- Dust avoidance: Wallets SHOULD define min_amt = 1 * 10^(-dec); <min_amt transfers/burns auto-feeized or aggregated. Nodes MAY reject transactions with unknown keys in mempool as an anti-spam policy (mempool-only; MUST NOT affect consensus validation, where unknown keys are ignored).
- Rate-limiting for max=0: Nodes SHOULD enforce dynamic limits based on network load to prevent infinite issuance abuse.
3. Event Digest & Domain Separation
Every FUN20 event has a canonical digest computed from the complete redeem script:
byte_digest = BLAKE3-256("fun20.event.v1.p2sh" || redeem_script_bytes)
semantic_digest = BLAKE3-256("fun20.semantic.v1.p2sh" || normalized_map_cbor)
where redeem_script_bytes is the complete FUN20 payload (header + CBOR) and normalized_map_cbor is the CBOR of the event converted to a standard map form (array converted to map using Appendix A orders; omitted optional fields absent; encoded deterministically with sorted keys).
P2SH Domain Separation:
The addition of .p2sh to domain tags ensures compatibility with non-P2SH FUN20 implementations and prevents cross-protocol conflicts. This domain separation is critical for:
- Hash Collision Prevention: Ensures P2SH and non-P2SH events have distinct digests
- Protocol Evolution: Allows coexistence of different FUN20 variants during transition periods
- Security Isolation: Prevents replay attacks between P2SH and non-P2SH contexts
Domain tags:
fun20.batch.root.v1.p2sh→ batch roots.fun20.addr.ns.v1.p2sh→ address namespace.fun20.ca.v1.p2sh→ contract address derivation.fun20.ns.tick.v1.p2sh→ tick namespace hash.fun20.ns.ca.v1.p2sh→ CA namespace hash.
De-Duplication Enhancement:
Indexers MUST use semantic_digest (based on normalized_map_cbor) to check for duplicates, even when byte_digests differ due to P2SH vs non-P2SH encoding variations. This ensures consistent deduplication across different storage mechanisms.
Address namespace: Addresses MUST use addr_kind (extracted or derived from length during parsing for determinism):
- addr_kind: 0x01 = HASH160 (20B), 0x02 = XOnlyHash (32B), 0x03 = Raw32, etc. For unprefixed 32B addresses, default to 0x02; to use 0x03, MUST prefix with addr_kind byte.
- addr_ns = blake3_160("fun20.addr.ns.v1.p2sh" || addr_kind || addr_bytes). Indexers MUST use addr_ns as keys for balances, nonces, etc. to ensure full fungibility and avoid semi-fungible issues. Test vectors for Rust, Go, and TypeScript SDKs must be published, covering map/array conversions, nonce sequences, pre limits, duplicate events, blacklist separation, addr_kind variants, bstr boundaries (0/h''/max len), features ignoring, CRS duplicates, governance proposals, MEV shuffling scenarios, chain fork migrations, and P2SH redeem script handling.
4. Deployment Models
4.1 Deploy-Mint
- Token identified by unique tick (per-chain uniqueness enforced via chain field and ns_tick = blake3_160("fun20.ns.tick.v1.p2sh" || chain_u8 || tick)).
- Deploy defines:
{tick, max, lim, dec, pre?, to?, metadata?}. lim= per-mint cap (with optional fair distribution via rate-limiting).pre= optional pre-allocation (MUST ≤ max, not subject to lim to maintain deployment compactness).- First valid deploy fixes parameters permanently (no governance changes for Mint model; use Issue for governable tokens).
- Model inference: If
modfield is omitted, presence oftickfield automatically infers Deploy-Mint model.
4.2 Deploy-Issue
- Token identified by deterministic contract address (CA) derived from complete redeem script:
ca = blake3_256("fun20.ca.v1.p2sh" || chain_u8 || 0x01 || txid || redeem_script_bytes) - Deploy defines:
{name, max, dec, pre?, to?, metadata?}. max=0→ unlimited issuance (issue amounts still bounded by 2^256-1; MUST respect node policy rate limits, e.g., ≤10^18 units per DAA cycle; changes to finite max are irreversible in v1 without governance).- Multiple tokens may share the same
name, but CA is unique per redeem script. - CA owner may:
- Issue supply.
- Blacklist addresses.
- Transfer ownership (
chown). - Propose governance updates (e.g., max/dec adjustments).
- Model inference: If
modfield is omitted, presence ofname/cafields automatically infers Deploy-Issue model.
P2SH CA Derivation Benefits:
- Deterministic Uniqueness: CA derived from complete redeem script ensures uniqueness even for identical token names
- L2/RGB Compatibility: P2SH-based CA derivation aligns with Bitcoin's Taproot ecosystem and RGB++ protocols
- Delayed Revelation: CA can be computed from hash before redeem script is revealed, enabling advanced privacy features
- Bridge Integration: P2SH mechanism facilitates cross-chain operations and zk-proof generation
Reserved prefixes (e.g., "fun*", "ton*") MAY be governance-reserved (optional). Chain fork handling: Parameters MUST migrate via governance-approved bridge ops to ensure continuity across upgrades or splits.
5. CBOR Map Specification
All FUN20 payloads MUST be encoded as CBOR deterministic maps or fixed-order arrays (see Appendix A). Every field has a fixed numeric key. Encoders MUST use integer keys only, and keys MUST appear in ascending order.
5.1 Core Fields
| Key | Name | Type | Description |
|---|---|---|---|
| 1 | (reserved) | — | Reserved (deprecated). MUST NOT appear in v1 payloads. If present, decoders MUST reject as bad-encoding to avoid ambiguity with legacy drafts. |
| 2 | op |
uint | Operation code (see §6). REQUIRED. |
| 3 | chain |
uint | Network enum: 0=frontier, 1=mainnet, 2=regtest, … MUST match current chain or event is wrong-chain. |
| 4 | tick |
tstr | Token symbol (Deploy-Mint). Enforced only in Mint model; mismatched model fields MUST trigger wrong-model. |
| 5 | dec |
uint | Decimal precision (Deploy only). MUST be between 0 and 18 inclusive; fixed upon deploy (changes via governance). SDKs MUST normalize to 10^dec for UI; indexers SHOULD provide both integer and human-readable strings. |
| 6 | max |
bstr | Maximum supply (big-endian). REQUIRED for Deploy. |
| 7 | lim |
bstr | Per-mint cap (Deploy-Mint only). |
| 8 | amt |
bstr | Amount (mint, issue, transfer, burn). MUST be >0 (zero as h'' intercepted in wallet pre-check). |
| 9 | nonce |
uint | Sender-scoped monotonic counter (see §6.1 for requirements). |
5.2 Deployment / Ownership Fields
| Key | Name | Type | Description |
|---|---|---|---|
| 11 | mod |
tstr/uint | Deployment mode: "mint" or "issue". If omitted, model is inferred based on presence of tick (Deploy-Mint) or name/ca (Deploy-Issue). |
| 12 | pre |
bstr | Pre-allocated supply amount. MUST NOT exceed max. |
| 13 | to |
bstr | Recipient address (prefixed or length-derived addr_kind). |
| 14 | ctx_in |
uint | Authorizing input index (see §6.1). |
| 15 | name |
tstr | Human-readable token name (Deploy-Issue). |
| 16 | ca |
bstr | Contract address identifier (Deploy-Issue). MUST equal deterministic derivation. |
| 17 | subop |
tstr | Sub-operation for ops like blacklist ("add" or "remove"). |
5.3 Optional / Feature Fields
| Key | Name | Type | Description |
|---|---|---|---|
| 10 | features |
[tstr] | Optional feature flags (≤4 items). Known: "bridge", "zk-proof", "chown-only-owner", "chown-mint-only", "governance", "p2sh-delayed-reveal". Unknown flags MUST be ignored. If a feature requires additional fields, missing fields MUST default to off/no-op. Feature registry in docs; MUST NOT alter core semantics. |
| 21 | batch_id |
bstr | Batch identifier (opaque). |
| 22 | l1_root |
bstr32 | Commitment root for DA/zk proofs. |
| 23 | proof_p2sh |
bstr | P2SH proof field supporting zk verification of redeem script hash (key 24). |
| 24 | proof_* |
various | Additional zk/bridge proof fields. Reserved range 23–24 MUST NOT be reused. |
| 25 | expiry |
uint | Expiration DAA/height. If current DAA ≥ expiry, event is expired. |
| 26 | flags |
uint | Reserved bitmask. All unset bits MUST be 0. |
| 27 | metadata |
[map] | Optional token metadata (≤32 bytes; short keys: "d" (desc), "u" (url), "i" (icon)). For UI/DeFi integration; validated off-chain. |
| 28 | new_params |
[map] | Governance proposal parameters (whitelist keys: 5=dec, 6=max, 27=metadata; others invalid-proposal). |
5.4 Encoding Constraints
- Big Numbers: MUST be encoded as minimal big-endian byte strings. Zero encoded as
h''. Non-minimal encodings MUST be rejected (bad-encoding). - Addresses: MUST be binary raw (20/32 bytes); addr_kind MAY be omitted if length unambiguous (20B=0x01, 32B=0x02); otherwise MUST prefix (parse extracts/derives kind for addr_ns). For 32-byte addresses, default to addr_kind 0x02 (XOnlyHash); to use 0x03 (Raw32), MUST prefix with addr_kind byte.
- Text Strings: MUST be valid UTF-8, normalized to NFC. Invalid strings →
bad-encoding. - Unknown Fields: MUST be ignored for forward compatibility, without altering core semantics.
- Formal Verification: CBOR parsers MUST undergo formal verification (e.g., using tools like TLA+ or Coq) as part of conformance tests.
6. Operations
FUN20 defines a fixed operation set. Each operation is identified by op (uint).
6.1 Common Rules
- Each event MUST include
chainandop. - Each event MUST pass Canonical Resolution Spec (CRS) ordering before evaluation.
- Invalid events MUST be recorded with explicit error codes (see Appendix C).
- Balances and supplies MUST NOT be negative; fully fungible with arbitrary increments (no set-unit restrictions).
- P2SH Validation Requirements: All FUN20 events MUST validate that the redeem script hash matches the UTXO scriptPubKey. The complete redeem script (header + CBOR) MUST be provided in scriptSig during spending. Indexers MUST verify hash consistency:
BLAKE3-160(redeem_script_bytes) == scriptPubKey_hash. Mismatch →invalid-p2sh-hash. - ctx_in Requirements: For state-changing or ownership-requiring ops (transfer, issue, burn, chown, blacklist, govern): MUST be explicitly provided. MUST validate that the referenced input's signature matches the sender address or current CA owner. For mint (open minting): ctx_in MAY be omitted; if provided, used only for sender namespace (nonce) and rate limiting. ctx_in MUST point to a signed input; unsigned or empty witness →
unauthorized. Note: For open minting, since ctx_in may be omitted, mints can be frontrun by bots; wallets should warn users for popular tokens. - nonce Requirements: For transfer, issue, burn: Sender MUST provide nonce. For mint: nonce SHOULD be provided. Nonce MUST be strictly increasing (> last_nonce); not required to be consecutive to accommodate DAG reordering. Regardless of event outcome (Valid/Invalid/No-op), nonce is not consumed unless Valid. If nonce ≤ last_nonce →
bad-nonce(Invalid). Scope: per (addr_ns, tick|ca).
6.2 Core Operations
op=0 — deploy
- Defines a new token.
- Deploy-Mint: Requires
tick, max, lim, metadata?. - Deploy-Issue: Requires
name, max, metadata?; CA derived deterministically. - MUST specify
dec(0–18). preMAY pre-allocate tokens toto(≤ max, not subject to lim).- First valid deploy fixes parameters permanently (changes require governance).
- Model determination: If
modfield is omitted, model is inferred from field presence:tick→ Deploy-Mint,name/ca→ Deploy-Issue.
op=1 — mint
- Deploy-Mint only.
- Requires
tick. - Amount
amtMUST NOT exceed per-mintlim. - Total minted supply MUST NOT exceed
max. - Mint outputs MUST specify recipient via
to. - Errors:
exceed-max,over-limit,wrong-model.
op=2 — transfer
- Both models.
- Token identified by
tick(Mint model) orca(Issue model). - Requires
amt > 0and validtoaddress. - MUST fail if balance insufficient (
insufficient-balance). - MAY include
expiry.
op=3 — issue
- Deploy-Issue only.
- Requires
caandamt. - Increases circulating supply.
- MUST NOT exceed
maxunlessmax=0(bounded by 2^256-1; MUST respect node policy rate limits, e.g., ≤10^18 units per DAA cycle). - Authorized by CA owner input (
ctx_in). - Errors:
unauthorized,exceed-max.
op=4 — burn
- Both models.
- Holder reduces their balance by
amt. - Does not reduce
max. - MUST reject if balance insufficient.
op=5 — blacklist
- Deploy-Issue only.
- Requires
caand target address. - Sub-ops:
"add"or"remove". - MUST be authorized by CA owner.
- Effect is off-consensus (policy layer): Indexers and wallets SHOULD enforce blacklists, but balances remain in consensus state. Operations are Valid consensus events updating the blacklist state table.
op=6 — chown (ownership transfer)
- Deploy-Issue only.
- Requires
caandnew_owneraddress. - Authorized by current owner (
ctx_in). - All-zero address = renounce ownership (subsequent issue/blacklist prohibited; state frozen).
- Feature
"chown-mint-only": transfer mint rights only, keeping blacklist authority. - Errors:
unauthorized.
op=7 — govern (governance proposal)
- Deploy-Issue only (features["governance"]).
- Requires
ca, proposal details as new_params map (e.g., new_max, new_dec). - Authorized by CA owner or multi-sig.
- Executes parameter updates after approval window (e.g., 7 DAA cycles) and quorum.
- Errors:
unauthorized,invalid-proposal.
6.3 Reserved Operations
30 — lock(zk/bridge, EVM-compatible).31 — release.32 — mint_dst.33 — burn_dst.34 — evm_call(EVM bridge for DeFi).35–37 — govern_*(reserved for advanced governance). Reserved for cross-chain/zk/EVM extensions. MUST NOT be redefined without governance.
7. Canonical Resolution Spec (CRS)
FUN20 events are replayed deterministically:
- Sort by DAA score.
- Then by BLAKE3(txid) (lexicographic).
- Then by input index.
- Then by payload order.
- If tied, compare byte_digests.
- P2SH Enhancement: If byte_digests are still equal, compare scriptPubKey hash (BLAKE3-160 of redeem script) as final tie-breaker to ensure deterministic ordering even for identical payloads in different P2SH contexts.
- MEV-resistant shuffle (all implementations MUST support; enabled by consensus parameter, default enabled): Use DAA-derived seed (e.g., BLAKE3(daa_score)) for fair ordering to prevent front-running. P2SH hash-based storage provides additional MEV resistance by hiding payload details until spending. Events are categorized as:
- Valid.
- Invalid (with error code).
- No-op (ignored but retained). De-duplication: All implementations MUST maintain a global applied_digest set across all tokens; if semantic_digest exists, mark as No-op/duplicate; MUST NOT apply twice. Extreme ties (identical events) are impossible post-steps 1-6; if detected, treat latter as duplicate.
8. Data Availability & zk Extensions
- Events batchable into BLAKE3 Merkle/NMT roots (window: one DAA cycle or fixed tx count; unified in SDKs).
- Batch ID + L1 root support lightweight DA proofs.
- zk systems may consume
l1_root+proof_*fields for inclusion verification. - zkEVM or external rollups may mirror FUN20 state by checking these commitments.
- EVM-compatible bridges: Reserved ops support calldata for cross-VM calls, enabling DeFi integrations.
- P2SH zk Integration: P2SH mechanism enables advanced zk-proof generation by allowing commitment to redeem script hash before revelation, supporting privacy-preserving token operations and cross-chain bridges.
- APIs:
/proof/batch/{id}→ Returnsl1_rootand batch metadata./proof/inclusion?batch=&ns=&key=→ Returns Merkle/NMT path./proof/p2sh/{txid}→ Returns complete redeem script + Merkle path for P2SH verification./script/{txid}→ Returns complete redeem script for P2SH events (indexer API).
9. Security Guardrails
- Strict schema limits (tick length, decimals, payload size, max per-tx payloads).
- Deterministic CBOR encoding with formal verification.
- Chain isolation via
chainfield. - Anti-replay:
nonce+ optionalexpiry. - Domain-separated event digests.
- Reason codes for invalid events (auditability).
- MEV resistance in CRS shuffling.
- Governance security: Proposals require multi-sig/quorum to prevent unauthorized changes.
- P2SH Security Requirements: All implementations MUST verify redeem script hash consistency (
BLAKE3-160(redeem_script_bytes) == scriptPubKey_hash) to prevent hash manipulation attacks. Formal verification MUST extend to P2SH parsing logic (TLA+/Coq) to ensure tamper-proof validation.
10. Examples (CBOR diagnostic form)
Deploy-Mint (with pre-allocation and metadata)
{2:0,3:0,4:"fun",5:8,6:h'00000186a0',7:h'0000003b9aca00',11:"mint",12:h'0000000005f5e100',13:h'ADDR',27:{'d':'Fun Token'}}
// Example byte length: ~45 bytes (fits within Deploy limit) Mint
{2:1,3:0,4:"fun",8:h'000000174876e800',13:h'ADDR'}
Transfer (Deploy-Issue, using CA)
{2:2,3:1,11:"issue",16:h'CA...',8:h'00000000017d7840',13:h'ADDR',14:0}
Issue
{2:3,3:1,16:h'CA...',8:h'000000174876e800',13:h'ADDR',14:0}
Burn
{2:4,3:1,16:h'CA...',8:h'0000000000002710',14:0}
Blacklist
{2:5,3:1,16:h'CA...',17:"remove",13:h'ADDR',14:0}
Chown (ownership transfer)
{2:6,3:1,16:h'CA...',13:h'NEW_OWNER',14:0}
Govern (parameter update)
{2:7,3:1,16:h'CA...',28:{6:h'NEW_MAX'},14:0}
(See Appendix A for array equivalents.)
11. Wallet & Indexer Expectations
Wallets
- MUST use deterministic CBOR.
- MUST validate
tick/cauniqueness and decimals. - SHOULD estimate validity locally (prevent dust or exceeding
max/lim). - SHOULD expose "reason code" feedback for rejected ops.
- Local pre-check: Validate
max/lim/dec/tick/noncebefore broadcast, returning standard error codes. - Auto-merge & no small change: Treat tiny change as fees or defer aggregation.
- Nonce strategy: Monotonic per
(addr_ns, tick|ca). Use indexer/next-noncefor convenience. - Governance support: Wallets SHOULD display proposal details and allow voting via multi-sig.
- P2SH Support: Wallets MUST support P2SH creation and spending (fork Kaspa NG), integrating with RGB/CTV protocols. When revealing redeem scripts, execute DeFi contracts automatically.
Indexers
- MUST support both CBOR map and array encodings.
- MUST implement CRS ordering with MEV shuffling.
- P2SH Processing: MUST scan scriptSig to extract complete redeem scripts, persisting them in SQLite/Redis (~10 GB/1M events) for API access.
- SHOULD expose the following APIs (all returning reason codes and event digests for reconciliation):
/tokens(list all tokens)/token/{tick|ca}(metadata, including desc/url/icon)/balance/{addr}/{tick|ca}/holders/{tick|ca}/blacklist/{ca}(current and historical)/ops(event log)/ops/{id}(event detail)/supply/{tick|ca}→max, minted, burned, circulating/owners/{ca}→ Current owner + history/policy/blacklist/{ca}→ Current blacklist + operations/next-nonce/{addr}/{tick|ca}→ For wallet queries/ops?since_daa=&limit=&filter=→ Filter by op/tick/ca/addr/govern/{ca}→ Active proposals and voting status/redeem/{txid}→ Complete redeem script for P2SH events/script/{txid}→ Redeem script metadata and validation status
12. Activation
- Implement first in Tondi Frontier.
- SDKs in Rust, Go, TypeScript with deterministic encoders and extended test vectors.
- At least one biannual cycle of Frontier testing.
- Mainnet activation requires:
- Client conformance tests (including formal verification of critical components like CBOR parsers and CRS).
- Indexer API parity.
- Independent security audit (e.g., by Certik or similar, covering all ops, zk proofs, and governance).
- Governance approval (multi-sig quorum for initial deployment).
Appendix A: Array Encoding Field Orders
- Deploy-Mint:
[0, chain, tick, dec, max, lim, pre?, to?, metadata?] - Deploy-Issue:
[0, chain, name, dec, max, pre?, to?, metadata?] - Mint:
[1, chain, tick, amt, to?, nonce?] - Issue:
[3, chain, ca, amt, to?, nonce, ctx_in] - Transfer:
[2, chain, tick|ca, amt, to?, nonce, ctx_in] - Burn:
[4, chain, tick|ca, amt, nonce, ctx_in] - Blacklist:
[5, chain, ca, subop, addr, ctx_in] - Chown:
[6, chain, ca, new_owner, ctx_in] - Govern:
[7, chain, ca, new_params, ctx_in]Note: In array encoding, Deploy-Mint vs Deploy-Issue are distinguished by the third element:tick(3-8 chars, [a-z0-9]) vsname(unrestricted tstr). The strict validation rules fortick(length 3-8, alphanumeric only) provide unambiguous parsing without requiring explicit model specification. (Optional fields omitted by truncation; test vectors in SDKs.)
Appendix B: Candidate v1.1 Extensions
- Multi-recipient transfers: Bounded outputs[] in single payload (limited to 4 outputs to prevent DoS), or batch-commit mode (total_amt + l1_root; details in /proof/inclusion).
- Tick short IDs: Off-chain SDK mapping for popular tokens (up to 12 chars for tick).
- Multi-hash proofs: Adapters for Keccak/Poseidon in bridges (BLAKE3 default).
- Governance changes: e.g., max=0 to finite.
- Global nonce mode: features["global-nonce"] per addr_ns only (simplifies multi-token tracking).
- UTXO-like balances: Explicit UTXO references for transfers to enhance efficiency and reduce indexer load.
- Advanced governance: DAO integrations for community proposals.
Appendix C: Error Codes
Consensus Invalid
exceed-maxover-limitinsufficient-balanceduplicate-deploy(Mint only)bad-noncewrong-chainbad-tickbad-decbad-encodingexpiredtoo-many-payloadsunauthorizedwrong-modelinvalid-proposalwrong-versioninvalid-p2sh-hash(P2SH hash mismatch)
No-op
duplicate-eventalready-blacklisted(Issue only)already-owner(Issue only)
Policy (Off-Consensus)
blacklist-deniedjurisdiction-deniedkyc-missing