# Qlyphs native QLYP-v1: asset rules, fees and bilateral payment gate Status: current protocol rules, reviewed against the code on 2026-09-29. Protocol version byte is 1 (`QLYP-v1`). Version 0 payloads are not QLYP operations; they are ignored like foreign remarks. Development databases re-index when the rules marker changes. Mainnet configuration and its excluded activation anchor are documented in [MAINNET.md](../MAINNET.md); this spec does not attest to a deployed service. Purchases remain disabled on mainnet. ## QLYP v1: fees Qlyphs fee account, a protocol constant identical on every network: `0x2139a57532fbf4764ad95754c17545fed7af915fb008b6494b02e503084da8b3` (SS58 `qzkCoLhkccQnzEG79s61bFpf5bLKfKKnq7S5YqazgzdCwgARA`). QTC has 12 decimals. | Operation | Qlyphs fee | Required call shape | |---|---|---| | DEPLOY (create token) | 1 QTC = `1_000_000_000_000` | fee batch (below) | | MINT | 0.01 QTC = `10_000_000_000` | fee batch (below) | | INSCRIBE (create Quark) | 0.1 QTC = `100_000_000_000` | fee batch (below) | | TRANSFER (token) | none | direct signed `system.remark_with_event(payload)` | | OFFER / sale | `saleFee(price)`, paid by the buyer | direct multisig proposal; purchase batch in section 4 | Fee batch for DEPLOY, MINT and INSCRIBE, signed by the operation owner: ``` utility.batch_all([ system.remark_with_event(payload), balances.transfer_keep_alive(QLYPHS_FEE_ACCOUNT, exact_fee) ]) ``` Exactly these two calls, in this order, with byte-exact encoding. The extrinsic must succeed, and its receipt must show exactly one `remarked` by the signer, exactly one `paid` from the signer to the fee account of exactly the fee, and exactly one `batchCompleted`. Otherwise the operation is rejected (`rejected: ...` verdict) with no state effect and no sequence advance. That covers a bare `remark_with_event` (the v0 shape), a wrong amount or recipient, extra or reordered calls, and a failed payment. A v1 remark found inside any batch is checked against these rules, so a misshapen batch is rejected rather than silently ignored. A TRANSFER wrapped in a batch is rejected. `batch_all` is atomic: when the fee leg cannot be paid (for example the signer lacks the funds), the whole extrinsic fails and its remark is reverted with it. The indexer still records a signed failed extrinsic that declares a decodable DEPLOY, MINT or INSCRIBE payload as `rejected: fee batch failed` (`rejected: extrinsic failed` when the call was not a batch), with no state effect and no sequence advance; the signer still pays the network fee. Other failed extrinsics, including a failed TRANSFER, OFFER or purchase, get no QLYP verdict. Wallets check the signer's spendable balance against the Qlyphs fee, network fees and the existential deposit before signing. Sale fee, a general rule: any token-for-QTC exchange pays 1% of the QTC price to Qlyphs, rounded up: ``` saleFee(price) = ceil(price * 100 / 10000) = (price * 100 + 9999) / 10000 // integer division ``` With `price > 0`, the fee is at least 1 base unit (for example, 1 -> 1, 100 -> 1, 101 -> 2, 10000 -> 100). An OFFER must commit `fee == saleFee(price)` and `fee recipient == QLYPHS_FEE_ACCOUNT` (and the buyer cannot be the fee account). Otherwise the offer is rejected. In the purchase batch, the buyer pays the seller the full price and pays the fee to Qlyphs, so the seller's payout is unchanged. Today the only exchange is OFFER (tag 3). A future open-offer or DEX operation inherits the same 1% rule. Tags 4–8 are reserved (planned launchpad/AMM, not part of the protocol); tag 9 is INSCRIBE (`docs/native/INSCRIPTIONS.md`). Why transfers are free: a token transfer moves value between holders without creating supply or exchanging it for QTC, and a fee there would tax ordinary custody moves such as consolidation or gifts. Fees apply where Qlyphs provides the service: issuance (deploy, mint) and exchange (sale). Plain QTC sends (`sendQtc`) are native transfers, not QLYP operations, and carry no Qlyphs fee. Implementation: `packages/native/src/protocol.ts` (`QLYPHS_FEE_ACCOUNT`, `DEPLOY_FEE`, `MINT_FEE`, `INSCRIBE_FEE`, `SALE_FEE_BPS`, `saleFee`, `feeBatchCall`), mirrored by `apps/native/verifier/replay.py`. ## 1. Sources and interpretation Pinned implementation target: Quantus `v1.0.1`, runtime spec 152, transaction version 6. Read implementation bodies, not just comments; some comments describe earlier behavior. - https://github.com/Quantus-Network/chain/blob/v1.0.1/pallets/frame-system/src/lib.rs `remark_with_event` authenticates origin and emits sender/hash; bytes remain in the extrinsic. - https://github.com/Quantus-Network/chain/blob/v1.0.1/pallets/utility/src/lib.rs `batch_all` dispatches from one origin and rolls back when a child returns an error. - https://github.com/Quantus-Network/chain/blob/v1.0.1/pallets/multisig/src/lib.rs `propose` stores canonical bytes and gives proposer first approval; `approve` checks exact bytes, membership, expiry and AlreadyApproved; `cancel` removes Active or Approved proposals. `execute` requires a signer and returns outer success even on inner failure; do not use it as a payment guard. Removing a proposal does not automatically refund an escrow's principal. - https://github.com/Quantus-Network/chain/blob/v1.0.1/runtime/src/lib.rs Assets and contracts are absent from this runtime; scheduler extrinsics are disabled. - https://github.com/Quantus-Network/chain/blob/v1.0.1/runtime/src/configs/mod.rs QTC unit is 10^12; existential deposit is 10^9 base units. Confirmation depth and economic constants must be read from the actual runtime, not assumed from a UI or SDK method name. - https://docs.quantus.com/deep-dives/qpow/ PoW client finalization depth is 100 blocks. E2E waits for the node's actual finalized head; it does not replace that with the OTC's lower configurable confirmation count. - https://github.com/Quantus-Network/quantus-wasm/blob/main/README.md Official ML-DSA signing. The dependency lockfile, not a moving README, selects the binary. Existing chain code signs legacy ML-DSA-87; no switch of wallet scheme is included here. A source review, a release binary, deployed mainnet code and an executed test are distinct evidence. The integration harness records runtime code hash and refuses runtime changes while replaying. ## 2. Encoding The authoritative byte grammar is `packages/native/src/codec.ts`. No JSON is signed. Header: ASCII QLYP (4 bytes), version=1 (u8), genesis (32 bytes), sequence (u64 little-endian), operation enum tag (u8). Fixed integers follow SCALE encoding; variable byte vectors use canonical SCALE compact lengths. Full consumption and byte-for-byte re-encoding are mandatory. Operation tags/fields, in order: | Tag | Operation | Fields | |---|---|---| | 0 | DEPLOY | symbol Vec, decimals u8, cap u128, mint limit u128, policy u8 (0 open / 1 issuer) | | 1 | MINT | asset id [u8;40], amount u128 | | 2 | TRANSFER | asset id [u8;40], amount u128, recipient AccountId32 | | 3 | OFFER | asset id [u8;40], amount u128, buyer AccountId32, QTC payout AccountId32, price u128, fee u128, fee recipient AccountId32, expiry u32 | | 4–8 | RESERVED | Planned launchpad/AMM (`docs/native/LAUNCHPAD-AMM.md`, planned, not now). Rejected as `reserved operation`; never interpreted. | | 9 | INSCRIBE | content type Vec (ASCII 3–64 bytes), content Vec (≥1 byte); see `docs/native/INSCRIPTIONS.md` | | 10+ | — | Unknown operation, rejected | Payload <=1024 bytes. Positive amounts/cap/limit, cap >= limit, symbol [A-Z0-9]{1,12}, decimals 0..18. No floats or implicit partial mints. A mint may request any positive amount up to the per-mint limit and remaining cap; the limit is not a mandatory fixed mint amount. Token symbols are unique within the network: the first valid DEPLOY in block/extrinsic order claims the symbol. A losing claim does not advance the sequence, but a successful native fee payment is not refunded. Quarks do not claim symbols; see [INSCRIPTIONS.md](INSCRIPTIONS.md). An offer fee must equal `saleFee(price)` (see QLYP v1: fees). Asset ID = creator's 32-byte ID followed by the deploy sequence's 8-byte LE encoding, scoped to this genesis/version. Sequence starts at 0 for each authenticated account; only a valid token operation advances it. The maximum sequence is rejected rather than wrapping. Native extrinsic nonce is separate. ## 3. State and replay `available[asset,owner]`, `reserved[ticket]`, immutable asset definition and minted supply. Invariant: sum(available) + sum(live reservations) == minted <= cap. Transfers cannot spend reservations. Only the successful signed fee batch can deploy/mint/inscribe; only a direct successful signed `remark_with_event` can transfer (see QLYP v1: fees). An offer requires a direct successful propose of `system.remark(offer)` with exact seller/buyer 2-of-2 membership, immutable terms, matching native expiry, and sufficient available tokens AT CREATION. Invalid proposals never become valid retroactively. A ticket is `(genesis, multisig AccountId32, native proposal_id)`. Replay all transactions in block/extrinsic order; bind events to their actual phase/index. Read raw extrinsics as well as events. Unknown calls are not token operations, but all persisted multisig lifecycle events remain observable, including wrapped calls. Never infer a successful inner call from outer ExtrinsicSuccess. Failed/unexecuted raw bytes have no token effects. Block parent, height, runtime, ordering and finality are checked before committing a detached snapshot. Provisional reorg requires rollback to a known hash/height followed by replay. A rollback below finalized state is refused. Unsupported runtime stops processing rather than silently changing interpretation. The activation pin is an excluded finalized checkpoint with initially empty QLYP state. The first interpreted block is `activation.height + 1` and must have `activation.hash` as its parent; the anchor and all earlier operations are excluded. A deployment must publish the exact genesis, anchor and rules. Changing an established anchor changes protocol history. Indexer inputs are trusted-node receipts, not user-supplied proof objects. Replaying the same implementation twice is reproducibility, not an independent audit or independent second implementation. ## 4. Purchase and cancellation Seller creates a native proposal for buyer B; its valid offer locks tokens in the overlay. Buyer verifies this reservation in finalized state and signs only the canonical call: ``` utility.batch_all([ multisig.approve(M, proposal_id, exact_committed_call), balances.transfer_keep_alive(payout, price), // v1: fee = saleFee(price) >= 1, so this leg is always present balances.transfer_keep_alive(QLYPHS_FEE_ACCOUNT, fee) ]) ``` All children use buyer's origin. Fees, price, payout, buyer, asset and quantity are already committed. No purchase principal is deposited in M. Successful exact batch + matching persisted approval/payment records settles reserved tokens to B. Delivery is a deterministic protocol transition, not a server callback. Replaying the purchase with a fresh native nonce fails AlreadyApproved (or missing proposal). A native payment/fee-leg failure must roll back the approval and payment events/storage. This is the main E2E assertion. Transaction fees can still be charged. Cancel-before-buy makes approve fail, so no price is paid. Buy-before-cancel delivers tokens; later cleanup cannot release them again. First buyer approval outside the canonical successful purchase invalidates the reservation and releases it without delivery. There can be no future successful first approval on that ticket. Cancel/remove/ execute removal likewise releases only still-live reservations. Expiry is inclusive: unlock at height strictly GREATER than expiry, exactly when native approve starts refusing the ticket. This guarantee is conditional on a valid finalized ticket, a conforming buyer client, correct ingestion and the agreed overlay rules. The runtime does NOT know token balances. An arbitrary manually crafted QTC payment, an invalid offer, altered client bytes or a dishonest indexer are not protected by this specification. Do not claim consensus-native atomic token exchange. ## 5. Verification and exclusions Unit tests cover codec boundaries, malformed payloads, supply, authorization, sequence/domain replay, reservation ownership, cancellation/cleanup, expiration boundary, provisional rollback, runtime drift and atomic checkpoint errors. Real-node tests cover issuance/reservation/purchase/transfer, exact QTC balance changes, fresh-nonce duplicate purchase, payment and fee-leg rollback in native storage, wrapped cancel, standalone approval, call mismatch, oversell rejection, expiry and historical replay. The real-node suite archives public transaction receipts and replay blocks, not test seeds/private keys. Its exit status must be green on the PR's latest head; a skipped suite is not verification. CI runner uses a checksum-pinned official release and a local throwaway chain, with no production secrets. These tests do not establish mainnet witness compatibility, actual deep PoW reorg behavior, adversarial multi-node consensus/finality, high-load availability or an independent security audit. The surrounding application has durable storage, runtime pins, wallet confirmation and witness verification; their own tests and operational checks are separate evidence. Purchases remain disabled on mainnet. ## 6. Scope and release gates The current exchange is a restricted bilateral ticket for a named buyer. An anonymous-taker order book, AMM or consensus-enforced asset exchange is not implemented. No automatic bridge, asset migration, balance administrator or mutable issuance rule is part of QLYP-v1. Before enabling additional live operations, require a reviewed public spec and fixed activation, independent protocol and implementation review, durable replay/reorg/restart validation, wallet clearsigning, runtime fingerprint verification, load limits, witness divergence monitoring, measured fees and appropriate user-facing risk explanations. See [MAINNET.md](../MAINNET.md) for current deployment configuration and the remaining purchase restriction.