feat(mba): rename covenants to CoinMBA / ShareMBA / OracleMBA + reserve DynamicMBA
Clean names that match the Mathematically Backed Asset framing (which came
first; the covenant source files were a later exploratory implementation
whose historical names outlived their usefulness). Rename is bytecode-
preserving — the chipnet-validated 52,998-swap proof carries over unchanged
because cashscript's `contract Name { }` is a source-level identifier that
doesn't enter the compiled script.
Source renames (git mv):
contracts/DeviantOpenMintFT.cash → contracts/CoinMBA.cash (templateId 0x00)
contracts/DeviantShareMBA.cash → contracts/ShareMBA.cash (templateId 0x01)
contracts/DeviantOracleMBA.cash → contracts/OracleMBA.cash (templateId 0x02)
Deleted:
contracts/ArtemisionMintFT.cash (byte-identical duplicate of DeviantOpenMintFT;
"Artemision" lives on only as the wizard UI
concept, not a distinct template)
New:
contracts/DynamicMBA.cash (templateId 0x03 — reserved for the Dynamic
Rate Asset covenant; scaffold only, rejects
every call. DRA is distinct from FIATμ:
FIATμ = dynamic-rate + oracle, DRA = pure
state-driven, no oracle, reflective
against a shared pool)
Compiled artifacts rebuilt via `cashc` and shipped to:
- design/potidaea/src/artifacts/ (live client consumption)
- site/deviant/docs/mba/artifacts/ (public download URLs for third-party
wallet integration; also deployed to
silentmode primary + Sia fallback)
Bytecode verification (sha256-of-bytecode):
CoinMBA == DeviantOpenMintFT ✓ (6a9ae89b...)
ShareMBA == DeviantShareMBA ✓
OracleMBA == DeviantOracleMBA ✓
Client code updates (live surface only — historical scripts, snapshots,
knowledgebase etc. keep their original names since they reference specific
chipnet tx hashes that happened under those names):
- design/potidaea/src/orbital.ts — artifact imports
- design/potidaea/src/genesis.ts — artifact imports + Artemision deprecation note
- design/potidaea/src/registry.ts — artifact imports + dispatch doc + 0x03 note
- design/potidaea/src/publish.ts — TEMPLATE_* constants renamed + 0x03 added
- design/potidaea/src/main.ts — TEMPLATE_* usages renamed
Docs updates:
- site/deviant/docs/mba/index.html — templates table expanded to 4 rows,
DRA vs FIATμ note added, dev-section
code-reference lines updated
- site/deviant/docs/mba/artifacts/index.html — one card per new artifact, DRA
scaffold flagged "not deployable"
Follow-up (not in this commit): the chipnet reseed. Current ASM/ASM2
instances were minted under the DeviantOpenMintFT covenant address and
remain redeemable (bytecode is identical, addresses derive to the same
value). A fresh-start deploy that mints new CoinMBA instances under the
new identity is a separate step requiring real chipnet BCH funding +
Katalogos publish. ASM/ASM2 seed entries stay in orbital.ts SEED_MBAS
until that follow-up ships.
Also deferred: the ~15 experimental MBA-adjacent covenants
(DeviantOpenMintFT_Backdoor, _Escalating, _NoPeg, _Whitelist,
DeviantOracleMBAFloor, _DraFee, DeviantManagedMintFT,
DeviantTieredMintOracleFT, OraclePricedVault, ExpiringMBA,
DeviantPlatformMBA, etc.) stay in contracts/ at their current names —
none are user-facing templates, and their fate (archive, promote,
retire) deserves its own taxonomy session.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
6a58143966
commit
9a24a19c94
22 changed files with 203 additions and 400 deletions
|
|
@ -1,117 +0,0 @@
|
|||
pragma cashscript ^0.12.0;
|
||||
|
||||
// ArtemisionMintFT — the first covenant in the Artemision template family.
|
||||
//
|
||||
// Semantically IDENTICAL to DeviantOpenMintFT.cash at genesis: same
|
||||
// reservoir model, same claim() invariants, same self-backed FT outputs,
|
||||
// same 0..500 bps founder fee. Deployed as a SIBLING (not a replacement)
|
||||
// so:
|
||||
//
|
||||
// • Every asset already deployed on chipnet using DeviantOpenMintFT.cash
|
||||
// (ASM, ASM2, CAP, …) keeps working forever — assets are immutable at
|
||||
// genesis (ADR-0054); the covenant that mints them cannot be swapped
|
||||
// out without redeploying the asset itself (which would be a different
|
||||
// category, i.e. a different asset).
|
||||
// • The Artemision wizard uses THIS file for every new asset from now on,
|
||||
// making the branding chain honest: "Artemision creates
|
||||
// ArtemisionMintFT covenants which get listed in Katalogos."
|
||||
// • This sibling is FREE TO DIVERGE. Version 1 mirrors DeviantOpenMintFT
|
||||
// one-to-one; a v2 sibling can add a richer metadata slot or an
|
||||
// alternative fee curve without disturbing anything already deployed.
|
||||
//
|
||||
// FAMILY (in this repo — see Parameters/Artemision/):
|
||||
//
|
||||
// ArtemisionMintFT.cash — this file. Static rate, open mint,
|
||||
// fixed founder fee. Baseline for
|
||||
// vanilla MBAs (ASM-shape).
|
||||
// DeviantOracleMBA.cash — oracle-priced backing. Used by the
|
||||
// BCHm family (USDm/EURm/…). Chipnet-
|
||||
// validated in Parameters/ASM_Native/FIATm.
|
||||
// DeviantShareMBA.cash — fee-split-at-consensus fork: each
|
||||
// claim's fee splits into a company
|
||||
// stream (companyLockingBytecode) +
|
||||
// a shareholder stream
|
||||
// (distributionLockingBytecode).
|
||||
// Chipnet-proven (Parameters/ASM_Native/
|
||||
// MBA_Shares/SESSION-RECORD_shares-
|
||||
// products-chipnet.md).
|
||||
// DistributionVault.cash — companion payout side for
|
||||
// DeviantShareMBA. Oracle-attested
|
||||
// (recipient+amount), replay-bound to
|
||||
// the vault's own outpoint. Chipnet-
|
||||
// proven.
|
||||
// TermCouponVault.cash — DistributionVault + CLTV maturity
|
||||
// sweep: t-bond variant. Chipnet-
|
||||
// proven.
|
||||
// DART / DEA / Elevation — dynamic-rate variants (designed,
|
||||
// not deployed).
|
||||
//
|
||||
// Artemision presets that compose these:
|
||||
// ① Rewards/loyalty coin — DeviantShareMBA · companySplitBps=10000
|
||||
// (all fee to company; no shareholder
|
||||
// stream; DistributionVault unused).
|
||||
// ② Shares + dividends — DeviantShareMBA + DistributionVault.
|
||||
// ③ T-bond — DeviantShareMBA + TermCouponVault.
|
||||
//
|
||||
// Artemision the WIZARD chooses among these templates based on the asset
|
||||
// class the user picks; this file is what "static MBA" resolves to.
|
||||
//
|
||||
// Everything below the constructor is line-for-line the same as
|
||||
// DeviantOpenMintFT.cash so a chipnet reader that already understands
|
||||
// one can read the other. If a future v2 wants to diverge, it does so
|
||||
// in a NEW file (ArtemisionMintFT_V2.cash) rather than editing this one —
|
||||
// assets minted by this covenant address depend on this bytecode.
|
||||
|
||||
contract ArtemisionMintFT(
|
||||
bytes tokenCategory, // bare 32-byte category id == genesis TXID
|
||||
int backingRateSats, // sats per FT unit, >= 1000 per spec §1.3
|
||||
int founderFeeBps, // 0..500 (0% .. 5%)
|
||||
bytes founderLockingBytecode // 25-byte P2PKH bytecode of fee recipient
|
||||
) {
|
||||
// claim() — permissionless transfer-from-reservoir.
|
||||
//
|
||||
// Despite the historical "mint" naming in the CashTokens / spec
|
||||
// literature, this function does NOT create new FT supply (that
|
||||
// happened once at genesis and is consensus-fixed thereafter).
|
||||
// It transfers `tokenAmount` FT units from the reservoir UTXO at
|
||||
// this contract to `recipientLockingBytecode`, in exchange for
|
||||
// `tokenAmount * backingRateSats` of BCH that the caller provides.
|
||||
// Anyone holding BCH can call it.
|
||||
function claim(int tokenAmount, bytes recipientLockingBytecode) {
|
||||
require(tokenAmount > 0);
|
||||
|
||||
int grossSats = tokenAmount * backingRateSats;
|
||||
int feeSats = grossSats * founderFeeBps / 10000;
|
||||
|
||||
// [0] reservoir back to this contract. Three invariants:
|
||||
// - same lockingBytecode → stays at this contract address
|
||||
// - same tokenCategory → preserves NFT category+capability
|
||||
// - FT amount reduced by exactly tokenAmount (transferred to [1])
|
||||
require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);
|
||||
require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);
|
||||
require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);
|
||||
require(tx.outputs[0].value >= 546);
|
||||
|
||||
// [1] FT to recipient, self-backed.
|
||||
// tokenCategory on FT outputs has no capability suffix — it is
|
||||
// exactly the 32-byte category id we hold as the constructor
|
||||
// parameter.
|
||||
require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);
|
||||
require(tx.outputs[1].tokenCategory == tokenCategory);
|
||||
require(tx.outputs[1].tokenAmount == tokenAmount);
|
||||
require(tx.outputs[1].value == grossSats);
|
||||
|
||||
// [2] Founder fee — only constrained when non-zero. If zero, the
|
||||
// tx may have additional outputs (change, etc.) starting at
|
||||
// index 2 with no contract restriction.
|
||||
if (feeSats > 0) {
|
||||
// Anti-dust: sub-dust fees are forbidden. Rounding noise must
|
||||
// not produce non-standard outputs. Off-chain math must mirror.
|
||||
require(feeSats >= 546);
|
||||
require(tx.outputs[2].lockingBytecode == founderLockingBytecode);
|
||||
require(tx.outputs[2].value == feeSats);
|
||||
// Fee output is pure BCH — no token category.
|
||||
require(tx.outputs[2].tokenCategory == 0x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ pragma cashscript ^0.12.0;
|
|||
// forbids). This contract is the consensus-correct realisation. See
|
||||
// snapshot 0008.
|
||||
|
||||
contract DeviantOpenMintFT(
|
||||
contract CoinMBA(
|
||||
bytes tokenCategory, // bare 32-byte category id == genesis TXID
|
||||
int backingRateSats, // sats per FT unit, >= 1000 per spec §1.3
|
||||
int founderFeeBps, // 0..500 (0% .. 5%)
|
||||
34
contracts/DynamicMBA.cash
Normal file
34
contracts/DynamicMBA.cash
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
pragma cashscript ^0.12.0;
|
||||
|
||||
// DynamicMBA — scaffold reserved for Dynamic Rate Asset (DRA) covenant.
|
||||
//
|
||||
// DRA is a Mathematically Backed Asset whose per-unit backing rate is
|
||||
// computed from on-chain state (reservoir depth, time, mint-count catalysts)
|
||||
// rather than:
|
||||
// - a constant (that's CoinMBA, templateId 0x00)
|
||||
// - a signed feed (that's OracleMBA, templateId 0x02)
|
||||
//
|
||||
// Explicitly NOT FIATμ. FIATμ combines a dynamic-rate layer with an oracle
|
||||
// signal to track a fiat purchasing-power target; DRA is the pure
|
||||
// state-driven variant and is reflective against a shared pool rather than
|
||||
// tracking an external price.
|
||||
//
|
||||
// Full design is intentionally deferred — the shared-pool reflective
|
||||
// mechanics (see memory [[dra-reflective-fiatm-flagship]]) and the Odyssean
|
||||
// maturity curve need a dedicated specification pass. This file reserves
|
||||
// templateId 0x03 in the DVMB dispatch table and the artifact-name slot so
|
||||
// nothing else lands under the DynamicMBA name by accident. It compiles,
|
||||
// but it is not meant to be deployed.
|
||||
|
||||
// Minimum legal cashscript covenant — one trivial branch. Will be replaced
|
||||
// in full when the DRA covenant lands.
|
||||
contract DynamicMBA(
|
||||
bytes32 tokenCategory,
|
||||
) {
|
||||
function placeholder() {
|
||||
// Not deployable — rejects every call.
|
||||
// Replace this body with the DRA state-machine when it lands.
|
||||
require(1 == 2, 'DynamicMBA scaffold — covenant not yet implemented');
|
||||
require(tokenCategory.length == 32);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ pragma cashscript ^0.12.0;
|
|||
// [2] founder fee (only if >= dust; sub-dust fees are WAIVED, not rejected)
|
||||
// [3+] BCH change (unconstrained)
|
||||
|
||||
contract DeviantOracleMBA(
|
||||
contract OracleMBA(
|
||||
bytes tokenCategory, // bare 32-byte category id (consensus LE order)
|
||||
pubkey oraclePk, // founder price-oracle public key
|
||||
int founderFeeBps, // e.g. 5 = 0.05%, 500 = 5%
|
||||
|
|
@ -24,7 +24,7 @@ pragma cashscript ^0.12.0;
|
|||
// cannot take or redirect it. (Payout from that covenant — oracle-attributed,
|
||||
// time-weighted — is the next ADR-0042 step; this proves the COLLECTION split.)
|
||||
|
||||
contract DeviantShareMBA(
|
||||
contract ShareMBA(
|
||||
bytes tokenCategory, // bare 32-byte category id == genesis TXID
|
||||
int backingRateSats, // sats per FT unit, >= 1000
|
||||
int feeBps, // total fee, 0..500 (0% .. 5%)
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
38
design/potidaea/src/artifacts/DynamicMBA.json
Normal file
38
design/potidaea/src/artifacts/DynamicMBA.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"contractName": "DynamicMBA",
|
||||
"constructorInputs": [
|
||||
{
|
||||
"name": "tokenCategory",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"abi": [
|
||||
{
|
||||
"name": "placeholder",
|
||||
"inputs": []
|
||||
}
|
||||
],
|
||||
"bytecode": "OP_1 OP_2 OP_NUMEQUALVERIFY OP_SIZE OP_NIP 20 OP_NUMEQUAL",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DynamicMBA — scaffold reserved for Dynamic Rate Asset (DRA) covenant.\n//\n// DRA is a Mathematically Backed Asset whose per-unit backing rate is\n// computed from on-chain state (reservoir depth, time, mint-count catalysts)\n// rather than:\n// - a constant (that's CoinMBA, templateId 0x00)\n// - a signed feed (that's OracleMBA, templateId 0x02)\n//\n// Explicitly NOT FIATμ. FIATμ combines a dynamic-rate layer with an oracle\n// signal to track a fiat purchasing-power target; DRA is the pure\n// state-driven variant and is reflective against a shared pool rather than\n// tracking an external price.\n//\n// Full design is intentionally deferred — the shared-pool reflective\n// mechanics (see memory [[dra-reflective-fiatm-flagship]]) and the Odyssean\n// maturity curve need a dedicated specification pass. This file reserves\n// templateId 0x03 in the DVMB dispatch table and the artifact-name slot so\n// nothing else lands under the DynamicMBA name by accident. It compiles,\n// but it is not meant to be deployed.\n\n// Minimum legal cashscript covenant — one trivial branch. Will be replaced\n// in full when the DRA covenant lands.\ncontract DynamicMBA(\n bytes32 tokenCategory,\n) {\n function placeholder() {\n // Not deployable — rejects every call.\n // Replace this body with the DRA state-machine when it lands.\n require(1 == 2, 'DynamicMBA scaffold — covenant not yet implemented');\n require(tokenCategory.length == 32);\n }\n}\n",
|
||||
"debug": {
|
||||
"bytecode": "51529d827701209c",
|
||||
"sourceMap": "31:16:31:17;:21::22;:8::78:1;32:16:32:36;;:40::42:0;:8::44:1",
|
||||
"logs": [],
|
||||
"requires": [
|
||||
{
|
||||
"ip": 3,
|
||||
"line": 31,
|
||||
"message": "DynamicMBA scaffold — covenant not yet implemented"
|
||||
},
|
||||
{
|
||||
"ip": 8,
|
||||
"line": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"compiler": {
|
||||
"name": "cashc",
|
||||
"version": "0.12.1"
|
||||
},
|
||||
"updatedAt": "2026-10-08T21:54:02.107Z"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"contractName": "DeviantOracleMBA",
|
||||
"contractName": "OracleMBA",
|
||||
"constructorInputs": [
|
||||
{
|
||||
"name": "tokenCategory",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
}
|
||||
],
|
||||
"bytecode": "OP_4 OP_PICK OP_0 OP_GREATERTHAN OP_VERIFY OP_6 OP_ROLL OP_7 OP_PICK OP_3 OP_ROLL OP_CHECKDATASIGVERIFY OP_5 OP_PICK OP_SIZE OP_NIP OP_16 OP_NUMEQUALVERIFY OP_5 OP_ROLL OP_12 OP_SPLIT OP_SWAP OP_SIZE OP_NIP OP_12 OP_NUMEQUALVERIFY OP_BIN2NUM OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY OP_4 OP_PICK OP_SWAP OP_MUL OP_DUP OP_3 OP_ROLL OP_MUL 1027 OP_DIV OP_0 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENAMOUNT OP_INPUTINDEX OP_UTXOTOKENAMOUNT OP_6 OP_PICK OP_SUB OP_NUMEQUALVERIFY OP_0 OP_OUTPUTVALUE 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_1 OP_OUTPUTBYTECODE OP_6 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_3 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENAMOUNT OP_4 OP_ROLL OP_NUMEQUALVERIFY OP_1 OP_OUTPUTVALUE OP_ROT OP_NUMEQUALVERIFY OP_DUP 2202 OP_GREATERTHANOREQUAL OP_IF OP_2 OP_OUTPUTBYTECODE OP_2 OP_PICK OP_EQUALVERIFY OP_2 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_2 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ENDIF OP_2DROP OP_1",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantOracleMBA — oracle-priced self-backed MBA (USD/EUR stablecoin shape).\n//\n// Sibling of DeviantOpenMintFT (fixed rate). Here the per-unit backing rate is\n// NOT a constructor constant — it is supplied by a founder-run price oracle at\n// claim time (checkDataSig). The FORWARD swap (BCH -> stablecoin) is oracle-\n// priced; the BACKWARD swap (stablecoin -> BCH) needs NO oracle and NO contract\n// path: each minted FT UTXO carries its mint-time BCH inside it, so redemption\n// is a plain P2PKH spend that releases exactly the locked BCH (self-backed).\n//\n// HONEST SEMANTICS: each unit is self-backed at its MINT-TIME price. A unit\n// minted at 1 USD = 10000 sats locks 10000; one minted at 12000 locks 12000.\n// So this is a \"dollar-worth-of-BCH-at-mint voucher, always redeemable for that\n// BCH\" — NOT a coin that keeps tracking USD after mint. That is the only\n// unilaterally-redeemable (MBA-shaped) stablecoin; a live USD tracker needs a\n// CDP/rebalancer and loses unilateral redemption.\n//\n// Oracle message (16 bytes, AnyHedge/Paryon format, all uint32 LE):\n// timestamp(4) || sequence(4) || contentSequence(4) || price(4)\n// price = sats per ATOMIC token unit (off-chain divides satsPerUSD by 10^dec).\n// Freshness (timestamp binding) is NOT enforced here — the known open limitation\n// (covenant verifies THAT the oracle signed, not WHEN). For production add a\n// CLTV/age bound on the timestamp field.\n//\n// Output layout (claim):\n// [0] reservoir back to contract: FT reduced, category/NFT preserved\n// [1] FT to recipient, self-backed at tokenAmount * price sats\n// [2] founder fee (only if >= dust; sub-dust fees are WAIVED, not rejected)\n// [3+] BCH change (unconstrained)\n\ncontract DeviantOracleMBA(\n bytes tokenCategory, // bare 32-byte category id (consensus LE order)\n pubkey oraclePk, // founder price-oracle public key\n int founderFeeBps, // e.g. 5 = 0.05%, 500 = 5%\n bytes founderLockingBytecode // 25-byte P2PKH of fee recipient\n) {\n function claim(\n int tokenAmount,\n bytes recipientLockingBytecode,\n datasig oracleSig,\n bytes oracleMessage\n ) {\n require(tokenAmount > 0);\n\n // --- oracle price attestation (forward swap only) ---\n require(checkDataSig(oracleSig, oracleMessage, oraclePk));\n require(oracleMessage.length == 16);\n bytes head, bytes priceBytes = oracleMessage.split(12); // last 4 bytes = price\n require(head.length == 12); // (also consumes `head`)\n int price = int(priceBytes);\n require(price > 0);\n\n int grossSats = tokenAmount * price;\n int feeSats = grossSats * founderFeeBps / 10000;\n\n // [0] reservoir back to this contract, FT reduced by tokenAmount.\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed at the oracle price (redeemable, no oracle).\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2] founder fee — enforced only when it clears dust; sub-dust waived.\n if (feeSats >= 546) {\n require(tx.outputs[2].lockingBytecode == founderLockingBytecode);\n require(tx.outputs[2].value == feeSats);\n require(tx.outputs[2].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantOracleMBA — oracle-priced self-backed MBA (USD/EUR stablecoin shape).\n//\n// Sibling of DeviantOpenMintFT (fixed rate). Here the per-unit backing rate is\n// NOT a constructor constant — it is supplied by a founder-run price oracle at\n// claim time (checkDataSig). The FORWARD swap (BCH -> stablecoin) is oracle-\n// priced; the BACKWARD swap (stablecoin -> BCH) needs NO oracle and NO contract\n// path: each minted FT UTXO carries its mint-time BCH inside it, so redemption\n// is a plain P2PKH spend that releases exactly the locked BCH (self-backed).\n//\n// HONEST SEMANTICS: each unit is self-backed at its MINT-TIME price. A unit\n// minted at 1 USD = 10000 sats locks 10000; one minted at 12000 locks 12000.\n// So this is a \"dollar-worth-of-BCH-at-mint voucher, always redeemable for that\n// BCH\" — NOT a coin that keeps tracking USD after mint. That is the only\n// unilaterally-redeemable (MBA-shaped) stablecoin; a live USD tracker needs a\n// CDP/rebalancer and loses unilateral redemption.\n//\n// Oracle message (16 bytes, AnyHedge/Paryon format, all uint32 LE):\n// timestamp(4) || sequence(4) || contentSequence(4) || price(4)\n// price = sats per ATOMIC token unit (off-chain divides satsPerUSD by 10^dec).\n// Freshness (timestamp binding) is NOT enforced here — the known open limitation\n// (covenant verifies THAT the oracle signed, not WHEN). For production add a\n// CLTV/age bound on the timestamp field.\n//\n// Output layout (claim):\n// [0] reservoir back to contract: FT reduced, category/NFT preserved\n// [1] FT to recipient, self-backed at tokenAmount * price sats\n// [2] founder fee (only if >= dust; sub-dust fees are WAIVED, not rejected)\n// [3+] BCH change (unconstrained)\n\ncontract OracleMBA(\n bytes tokenCategory, // bare 32-byte category id (consensus LE order)\n pubkey oraclePk, // founder price-oracle public key\n int founderFeeBps, // e.g. 5 = 0.05%, 500 = 5%\n bytes founderLockingBytecode // 25-byte P2PKH of fee recipient\n) {\n function claim(\n int tokenAmount,\n bytes recipientLockingBytecode,\n datasig oracleSig,\n bytes oracleMessage\n ) {\n require(tokenAmount > 0);\n\n // --- oracle price attestation (forward swap only) ---\n require(checkDataSig(oracleSig, oracleMessage, oraclePk));\n require(oracleMessage.length == 16);\n bytes head, bytes priceBytes = oracleMessage.split(12); // last 4 bytes = price\n require(head.length == 12); // (also consumes `head`)\n int price = int(priceBytes);\n require(price > 0);\n\n int grossSats = tokenAmount * price;\n int feeSats = grossSats * founderFeeBps / 10000;\n\n // [0] reservoir back to this contract, FT reduced by tokenAmount.\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed at the oracle price (redeemable, no oracle).\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2] founder fee — enforced only when it clears dust; sub-dust waived.\n if (feeSats >= 546) {\n require(tx.outputs[2].lockingBytecode == founderLockingBytecode);\n require(tx.outputs[2].value == feeSats);\n require(tx.outputs[2].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"debug": {
|
||||
"bytecode": "547900a069567a5779537abb55798277609d557a5c7f7c82775c9d817600a06954797c9576537a950210279600cdc0c78800d1c0ce8800d3c0d05679949d00cc022202a26951cd567a8851d1537a8851d3547a9d51cc7b9d76022202a26352cd52798852cc789d52d10088686d51",
|
||||
"sourceMap": "44:16:44:27;;:30::31;:16:::1;:8::33;47:29:47:38:0;;:40::53;;:55::63;;:8::66:1;48:16:48:29:0;;:::36:1;;:40::42:0;:8::44:1;49:39:49:52:0;;:59::61;:39::62:1;50:16:50:20:0;:::27:1;;:31::33:0;:8::35:1;51:20:51;52:16:52:21:0;:24::25;:16:::1;:8::27;54:24:54:35:0;;:38::43;:24:::1;55:22:55:31:0;:34::47;;:22:::1;:50::55:0;:22:::1;58:27:58:28:0;:16::45:1;:59::80:0;:49::97:1;:8::99;59:27:59:28:0;:16::43:1;:57::78:0;:47::93:1;:8::95;60:27:60:28:0;:16::41:1;:55::76:0;:45::89:1;:92::103:0;;:45:::1;:8::105;61:27:61:28:0;:16::35:1;:39::42:0;:16:::1;:8::44;64:27:64:28:0;:16::45:1;:49::73:0;;:8::75:1;65:27:65:28:0;:16::43:1;:47::60:0;;:8::62:1;66:27:66:28:0;:16::41:1;:45::56:0;;:8::58:1;67:27:67:28:0;:16::35:1;:39::48:0;:8::50:1;70:12:70:19:0;:23::26;:12:::1;:28:74:9:0;71:31:71:32;:20::49:1;:53::75:0;;:12::77:1;72:31:72:32:0;:20::39:1;:43::50:0;:12::52:1;73:31:73:32:0;:20::47:1;:51::53:0;:12::55:1;70:28:74:9;38:4:75:5;",
|
||||
|
|
@ -118,5 +118,5 @@
|
|||
"name": "cashc",
|
||||
"version": "0.12.1"
|
||||
},
|
||||
"updatedAt": "2026-09-06T16:25:52.645Z"
|
||||
"updatedAt": "2026-10-08T21:54:01.371Z"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"contractName": "DeviantShareMBA",
|
||||
"contractName": "ShareMBA",
|
||||
"constructorInputs": [
|
||||
{
|
||||
"name": "tokenCategory",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
}
|
||||
],
|
||||
"bytecode": "OP_6 OP_PICK OP_0 OP_GREATERTHAN OP_VERIFY OP_6 OP_PICK OP_ROT OP_MUL OP_DUP OP_3 OP_ROLL OP_MUL 1027 OP_DIV OP_DUP OP_4 OP_ROLL OP_MUL 1027 OP_DIV OP_2DUP OP_SUB OP_0 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENAMOUNT OP_INPUTINDEX OP_UTXOTOKENAMOUNT OP_9 OP_PICK OP_SUB OP_NUMEQUALVERIFY OP_0 OP_OUTPUTVALUE 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_1 OP_OUTPUTBYTECODE OP_9 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_5 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENAMOUNT OP_7 OP_ROLL OP_NUMEQUALVERIFY OP_1 OP_OUTPUTVALUE OP_4 OP_ROLL OP_NUMEQUALVERIFY OP_ROT OP_0 OP_GREATERTHAN OP_IF OP_OVER 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_DUP 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_2 OP_OUTPUTBYTECODE OP_3 OP_PICK OP_EQUALVERIFY OP_2 OP_OUTPUTVALUE OP_2 OP_PICK OP_NUMEQUALVERIFY OP_2 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_3 OP_OUTPUTBYTECODE OP_4 OP_PICK OP_EQUALVERIFY OP_3 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_3 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ENDIF OP_2DROP OP_2DROP OP_1",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantShareMBA — fork of DeviantOpenMintFT (ADR-0042, collection side).\n//\n// Splits the per-claim fee into TWO consensus-enforced streams instead of\n// one founder output:\n// - companySats -> companyLockingBytecode (the company treasury, paid directly)\n// - shareholderSats -> distributionLockingBytecode (the consensus-locked distribution\n// covenant — the holders' profit-share)\n//\n// fee = gross * feeBps / 10000\n// companySats = fee * companySplitBps / 10000 (company's share OF THE FEE)\n// shareholderSats = fee - companySats (routed to the distribution covenant)\n//\n// Output layout (claim):\n// [0] reservoir UTXO back to contract (minting NFT + remaining FT)\n// [1] FT to recipient, self-backed at tokenAmount * rate\n// [2] company fee -> companyLockingBytecode (companySats)\n// [3] shareholder share -> distributionLockingBytecode (shareholderSats)\n// [4+] BCH change (unconstrained)\n//\n// The shareholder stream never touches a company key: consensus routes it\n// straight to the distribution covenant's locking bytecode. The company\n// cannot take or redirect it. (Payout from that covenant — oracle-attributed,\n// time-weighted — is the next ADR-0042 step; this proves the COLLECTION split.)\n\ncontract DeviantShareMBA(\n bytes tokenCategory, // bare 32-byte category id == genesis TXID\n int backingRateSats, // sats per FT unit, >= 1000\n int feeBps, // total fee, 0..500 (0% .. 5%)\n int companySplitBps, // company's share of the fee, 0..10000\n bytes companyLockingBytecode, // company treasury (direct)\n bytes distributionLockingBytecode // consensus-locked distribution covenant\n) {\n function claim(int tokenAmount, bytes recipientLockingBytecode) {\n require(tokenAmount > 0);\n\n int grossSats = tokenAmount * backingRateSats;\n int feeSats = grossSats * feeBps / 10000;\n int companySats = feeSats * companySplitBps / 10000;\n int shareholderSats = feeSats - companySats;\n\n // [0] reservoir back to this contract (lockingBytecode + category + FT reduced).\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed.\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2]+[3] split fee. Both portions must clear dust when a fee is charged.\n // (Sub-dust waiver for tiny claims is a later refinement; the prototype\n // sizes claims so both sides clear 546.)\n if (feeSats > 0) {\n require(companySats >= 546);\n require(shareholderSats >= 546);\n\n // [2] company treasury — paid directly.\n require(tx.outputs[2].lockingBytecode == companyLockingBytecode);\n require(tx.outputs[2].value == companySats);\n require(tx.outputs[2].tokenCategory == 0x);\n\n // [3] shareholder profit-share — routed to the distribution covenant.\n require(tx.outputs[3].lockingBytecode == distributionLockingBytecode);\n require(tx.outputs[3].value == shareholderSats);\n require(tx.outputs[3].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantShareMBA — fork of DeviantOpenMintFT (ADR-0042, collection side).\n//\n// Splits the per-claim fee into TWO consensus-enforced streams instead of\n// one founder output:\n// - companySats -> companyLockingBytecode (the company treasury, paid directly)\n// - shareholderSats -> distributionLockingBytecode (the consensus-locked distribution\n// covenant — the holders' profit-share)\n//\n// fee = gross * feeBps / 10000\n// companySats = fee * companySplitBps / 10000 (company's share OF THE FEE)\n// shareholderSats = fee - companySats (routed to the distribution covenant)\n//\n// Output layout (claim):\n// [0] reservoir UTXO back to contract (minting NFT + remaining FT)\n// [1] FT to recipient, self-backed at tokenAmount * rate\n// [2] company fee -> companyLockingBytecode (companySats)\n// [3] shareholder share -> distributionLockingBytecode (shareholderSats)\n// [4+] BCH change (unconstrained)\n//\n// The shareholder stream never touches a company key: consensus routes it\n// straight to the distribution covenant's locking bytecode. The company\n// cannot take or redirect it. (Payout from that covenant — oracle-attributed,\n// time-weighted — is the next ADR-0042 step; this proves the COLLECTION split.)\n\ncontract ShareMBA(\n bytes tokenCategory, // bare 32-byte category id == genesis TXID\n int backingRateSats, // sats per FT unit, >= 1000\n int feeBps, // total fee, 0..500 (0% .. 5%)\n int companySplitBps, // company's share of the fee, 0..10000\n bytes companyLockingBytecode, // company treasury (direct)\n bytes distributionLockingBytecode // consensus-locked distribution covenant\n) {\n function claim(int tokenAmount, bytes recipientLockingBytecode) {\n require(tokenAmount > 0);\n\n int grossSats = tokenAmount * backingRateSats;\n int feeSats = grossSats * feeBps / 10000;\n int companySats = feeSats * companySplitBps / 10000;\n int shareholderSats = feeSats - companySats;\n\n // [0] reservoir back to this contract (lockingBytecode + category + FT reduced).\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed.\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2]+[3] split fee. Both portions must clear dust when a fee is charged.\n // (Sub-dust waiver for tiny claims is a later refinement; the prototype\n // sizes claims so both sides clear 546.)\n if (feeSats > 0) {\n require(companySats >= 546);\n require(shareholderSats >= 546);\n\n // [2] company treasury — paid directly.\n require(tx.outputs[2].lockingBytecode == companyLockingBytecode);\n require(tx.outputs[2].value == companySats);\n require(tx.outputs[2].tokenCategory == 0x);\n\n // [3] shareholder profit-share — routed to the distribution covenant.\n require(tx.outputs[3].lockingBytecode == distributionLockingBytecode);\n require(tx.outputs[3].value == shareholderSats);\n require(tx.outputs[3].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"debug": {
|
||||
"bytecode": "567900a06956797b9576537a950210279676547a95021027966e9400cdc0c78800d1c0ce8800d3c0d05979949d00cc022202a26951cd597a8851d1557a8851d3577a9d51cc547a9d7b00a06378022202a26976022202a26952cd53798852cc52799d52d1008853cd54798853cc789d53d10088686d6d51",
|
||||
"sourceMap": "36:16:36:27;;:30::31;:16:::1;:8::33;38:24:38:35:0;;:38::53;:24:::1;39:22:39:31:0;:34::40;;:22:::1;:43::48:0;:22:::1;40:26:40:33:0;:36::51;;:26:::1;:54::59:0;:26:::1;41:30:41:51:0;::::1;44:27:44:28:0;:16::45:1;:59::80:0;:49::97:1;:8::99;45:27:45:28:0;:16::43:1;:57::78:0;:47::93:1;:8::95;46:27:46:28:0;:16::41:1;:55::76:0;:45::89:1;:92::103:0;;:45:::1;:8::105;47:27:47:28:0;:16::35:1;:39::42:0;:16:::1;:8::44;50:27:50:28:0;:16::45:1;:49::73:0;;:8::75:1;51:27:51:28:0;:16::43:1;:47::60:0;;:8::62:1;52:27:52:28:0;:16::41:1;:45::56:0;;:8::58:1;53:27:53:28:0;:16::35:1;:39::48:0;;:8::50:1;58:12:58:19:0;:22::23;:12:::1;:25:71:9:0;59:20:59:31;:35::38;:20:::1;:12::40;60:20:60:35:0;:39::42;:20:::1;:12::44;63:31:63:32:0;:20::49:1;:53::75:0;;:12::77:1;64:31:64:32:0;:20::39:1;:43::54:0;;:12::56:1;65:31:65:32:0;:20::47:1;:51::53:0;:12::55:1;68:31:68:32:0;:20::49:1;:53::80:0;;:12::82:1;69:31:69:32:0;:20::39:1;:43::58:0;:12::60:1;70:31:70:32:0;:20::47:1;:51::53:0;:12::55:1;58:25:71:9;35:4:72:5;;",
|
||||
|
|
@ -122,5 +122,5 @@
|
|||
"name": "cashc",
|
||||
"version": "0.12.1"
|
||||
},
|
||||
"updatedAt": "2026-09-06T16:25:52.850Z"
|
||||
"updatedAt": "2026-10-08T21:54:00.544Z"
|
||||
}
|
||||
|
|
@ -16,8 +16,11 @@
|
|||
// wizard's "Broadcast" button. Users pay only the miner fee (no founder
|
||||
// fee on genesis; the founder fee applies to claim() outputs later).
|
||||
|
||||
import artifact from './artifacts/ArtemisionMintFT.json';
|
||||
import shareArtifact from './artifacts/DeviantShareMBA.json';
|
||||
// ArtemisionMintFT was a duplicate of DeviantOpenMintFT (same bytecode); the
|
||||
// "Artemision" name lives on only as the wizard UI concept. Both genesis
|
||||
// paths now use the clean CoinMBA + ShareMBA covenants directly.
|
||||
import artifact from './artifacts/CoinMBA.json';
|
||||
import shareArtifact from './artifacts/ShareMBA.json';
|
||||
import type { Artifact } from 'cashscript';
|
||||
import { Contract, ElectrumNetworkProvider, TransactionBuilder, SignatureTemplate } from 'cashscript';
|
||||
import type { Signer } from './signer-store';
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import {
|
|||
claimViaWcWithRetry, claimViaLocalSignerWithRetry, claimViaLocalSignerShareWithRetry,
|
||||
} from './retry';
|
||||
import { genesisArtemisionMBA, genesisDeviantShareMBA, loadLocalKnownMbas, clearAllLocalAssets, type GenesisResult, type ShareGenesisResult, type ShareGenesisParams } from './genesis';
|
||||
import { publishViaLocalSigner, PublishError, TEMPLATE_ARTEMISION_MINT_FT, TEMPLATE_DEVIANT_SHARE_MBA, type PublishMetadata } from './publish';
|
||||
import { publishViaLocalSigner, PublishError, TEMPLATE_COIN_MBA, TEMPLATE_SHARE_MBA, type PublishMetadata } from './publish';
|
||||
import { uploadIcon, IconUploadError } from './icon-upload';
|
||||
import { bchUsd, refreshBchUsd } from './pricing';
|
||||
import { BCH_ICON_DATA_URI } from './icons';
|
||||
|
|
@ -1759,7 +1759,7 @@ async function runRealPublish(sym: string): Promise<void> {
|
|||
// distribution + split; Artemision (0x00 / undefined) publishes the
|
||||
// founder LB. Common fields (ticker/name/icon) ride on top per DVMB.
|
||||
const meta: PublishMetadata = mba.templateId === 0x01 ? {
|
||||
templateId: TEMPLATE_DEVIANT_SHARE_MBA,
|
||||
templateId: TEMPLATE_SHARE_MBA,
|
||||
rateSats: mba.rateSats,
|
||||
feeBps: mba.founderFeeBps, // slot reuse from Share's genesis stashing
|
||||
companySplitBps: mba.companySplitBps,
|
||||
|
|
@ -1770,7 +1770,7 @@ async function runRealPublish(sym: string): Promise<void> {
|
|||
iconSha256Hex: iconOk ? assetRow?.iconSha256 : undefined,
|
||||
iconPath: iconOk ? iconPath : undefined,
|
||||
} : {
|
||||
templateId: TEMPLATE_ARTEMISION_MINT_FT,
|
||||
templateId: TEMPLATE_COIN_MBA,
|
||||
rateSats: mba.rateSats,
|
||||
founderFeeBps: mba.founderFeeBps,
|
||||
founderLockingBytecodeHex: mba.founderLockingBytecodeHex,
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
// param order. Only difference is signer: script uses a WIF loaded from disk;
|
||||
// mockup uses WC round-trip.
|
||||
|
||||
import artifact from './artifacts/DeviantOpenMintFT.json';
|
||||
import shareArtifact from './artifacts/DeviantShareMBA.json';
|
||||
import artifact from './artifacts/CoinMBA.json';
|
||||
import shareArtifact from './artifacts/ShareMBA.json';
|
||||
import type { Artifact } from 'cashscript';
|
||||
import { Contract, ElectrumNetworkProvider, TransactionBuilder, SignatureTemplate } from 'cashscript';
|
||||
import { secp256k1, decodeCashAddress } from '@bitauth/libauth';
|
||||
|
|
|
|||
|
|
@ -143,12 +143,13 @@ export class PublishError extends Error {
|
|||
// 0x01 = DeviantShareMBA (later — needs additional fields)
|
||||
// 0x02 = DeviantOracleMBA (later)
|
||||
export const OP_RETURN_MAGIC = 'DVMB'; // 4-byte ASCII
|
||||
export const TEMPLATE_ARTEMISION_MINT_FT = 0x00;
|
||||
export const TEMPLATE_DEVIANT_SHARE_MBA = 0x01;
|
||||
export const TEMPLATE_DEVIANT_ORACLE_MBA = 0x02;
|
||||
export const TEMPLATE_COIN_MBA = 0x00;
|
||||
export const TEMPLATE_SHARE_MBA = 0x01;
|
||||
export const TEMPLATE_ORACLE_MBA = 0x02;
|
||||
export const TEMPLATE_DYNAMIC_MBA = 0x03; // DRA scaffold — covenant not yet implemented
|
||||
|
||||
export interface PublishMetadata {
|
||||
templateId: number; // TEMPLATE_ARTEMISION_MINT_FT | _SHARE_MBA | _ORACLE_MBA
|
||||
templateId: number; // TEMPLATE_COIN_MBA | _SHARE_MBA | _ORACLE_MBA
|
||||
// Common when template is ArtemisionMintFT / OpenMintFT
|
||||
rateSats?: bigint; // sats per FT unit — Artemision + Share
|
||||
founderFeeBps?: bigint; // 0..500 — Artemision + Oracle (Share uses feeBps)
|
||||
|
|
@ -209,26 +210,26 @@ function encodeMetadataBlob(m: PublishMetadata): string {
|
|||
// if a publisher hits the limit on mainnet, they drop the icon (still fine)
|
||||
// or the name chunk. All chunks past chunk 1 are optional.
|
||||
switch (m.templateId) {
|
||||
case TEMPLATE_ARTEMISION_MINT_FT: {
|
||||
case TEMPLATE_COIN_MBA: {
|
||||
if (m.rateSats == null || m.founderFeeBps == null || !m.founderLockingBytecodeHex) {
|
||||
throw new PublishError('ArtemisionMintFT DVMB needs rateSats + founderFeeBps + founderLockingBytecodeHex', 'other');
|
||||
}
|
||||
const buf = new Uint8Array(1 + 8 + 2 + 20);
|
||||
const dv = new DataView(buf.buffer);
|
||||
buf[0] = TEMPLATE_ARTEMISION_MINT_FT;
|
||||
buf[0] = TEMPLATE_COIN_MBA;
|
||||
writeU64LE(dv, 1, m.rateSats);
|
||||
dv.setUint16(9, Number(m.founderFeeBps & 0xffffn), true);
|
||||
buf.set(pkhFromLB(m.founderLockingBytecodeHex, 'founder'), 11);
|
||||
return '0x' + Array.from(buf, (b) => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
case TEMPLATE_DEVIANT_SHARE_MBA: {
|
||||
case TEMPLATE_SHARE_MBA: {
|
||||
if (m.rateSats == null || m.feeBps == null || m.companySplitBps == null ||
|
||||
!m.companyLockingBytecodeHex || !m.distributionLockingBytecodeHex) {
|
||||
throw new PublishError('DeviantShareMBA DVMB needs rate + fee + split + company/distribution LB', 'other');
|
||||
}
|
||||
const buf = new Uint8Array(1 + 8 + 2 + 2 + 20 + 20);
|
||||
const dv = new DataView(buf.buffer);
|
||||
buf[0] = TEMPLATE_DEVIANT_SHARE_MBA;
|
||||
buf[0] = TEMPLATE_SHARE_MBA;
|
||||
writeU64LE(dv, 1, m.rateSats);
|
||||
dv.setUint16(9, Number(m.feeBps & 0xffffn), true);
|
||||
dv.setUint16(11, Number(m.companySplitBps & 0xffffn), true);
|
||||
|
|
@ -236,14 +237,14 @@ function encodeMetadataBlob(m: PublishMetadata): string {
|
|||
buf.set(pkhFromLB(m.distributionLockingBytecodeHex, 'distribution'), 33);
|
||||
return '0x' + Array.from(buf, (b) => b.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
case TEMPLATE_DEVIANT_ORACLE_MBA: {
|
||||
case TEMPLATE_ORACLE_MBA: {
|
||||
if (m.founderFeeBps == null || !m.founderLockingBytecodeHex || !m.oraclePkHex) {
|
||||
throw new PublishError('DeviantOracleMBA DVMB needs founderFeeBps + founderLockingBytecodeHex + oraclePkHex', 'other');
|
||||
}
|
||||
if (!/^[0-9a-f]{66}$/i.test(m.oraclePkHex)) throw new PublishError('oraclePkHex must be 66 hex chars (33-byte compressed)', 'other');
|
||||
const buf = new Uint8Array(1 + 2 + 20 + 33);
|
||||
const dv = new DataView(buf.buffer);
|
||||
buf[0] = TEMPLATE_DEVIANT_ORACLE_MBA;
|
||||
buf[0] = TEMPLATE_ORACLE_MBA;
|
||||
dv.setUint16(1, Number(m.founderFeeBps & 0xffffn), true);
|
||||
buf.set(pkhFromLB(m.founderLockingBytecodeHex, 'founder'), 3);
|
||||
buf.set(hexToBin(m.oraclePkHex), 23);
|
||||
|
|
|
|||
|
|
@ -47,13 +47,13 @@ export interface RegisteredEntry {
|
|||
/** DVMB (Deviant Mint Bounds) — the OP_RETURN convention publish.ts writes
|
||||
* alongside each register(). See publish.ts encodeMetadataBlob. */
|
||||
const DVMB_MAGIC = new Uint8Array([0x44, 0x56, 0x4d, 0x42]); // "DVMB"
|
||||
const TEMPLATE_ARTEMISION_MINT_FT = 0x00;
|
||||
const TEMPLATE_DEVIANT_SHARE_MBA = 0x01;
|
||||
const TEMPLATE_DEVIANT_ORACLE_MBA = 0x02;
|
||||
const TEMPLATE_COIN_MBA = 0x00;
|
||||
const TEMPLATE_SHARE_MBA = 0x01;
|
||||
const TEMPLATE_ORACLE_MBA = 0x02;
|
||||
|
||||
export interface DiscoveredParams {
|
||||
templateId: number;
|
||||
// Common (ArtemisionMintFT + Share)
|
||||
// Common (CoinMBA + Share)
|
||||
rateSats?: bigint;
|
||||
// Fee — meaning depends on template (Artemision + Oracle: founder fee;
|
||||
// Share: total fee before split)
|
||||
|
|
@ -110,19 +110,19 @@ export async function fetchEntryParams(
|
|||
let companyPkh: Uint8Array | undefined;
|
||||
let distributionPkh: Uint8Array | undefined;
|
||||
let oraclePkHex: string | undefined;
|
||||
if (templateId === TEMPLATE_ARTEMISION_MINT_FT) {
|
||||
if (templateId === TEMPLATE_COIN_MBA) {
|
||||
if (blob.length !== 1 + 8 + 2 + 20) { console.warn('[registry] Artemision DVMB size', blob.length); continue; }
|
||||
rateSats = (BigInt(dv.getUint32(5, true)) << 32n) | BigInt(dv.getUint32(1, true));
|
||||
feeBps = BigInt(dv.getUint16(9, true));
|
||||
founderPkh = blob.slice(11, 31);
|
||||
} else if (templateId === TEMPLATE_DEVIANT_SHARE_MBA) {
|
||||
} else if (templateId === TEMPLATE_SHARE_MBA) {
|
||||
if (blob.length !== 1 + 8 + 2 + 2 + 20 + 20) { console.warn('[registry] Share DVMB size', blob.length); continue; }
|
||||
rateSats = (BigInt(dv.getUint32(5, true)) << 32n) | BigInt(dv.getUint32(1, true));
|
||||
feeBps = BigInt(dv.getUint16(9, true));
|
||||
companySplitBps = BigInt(dv.getUint16(11, true));
|
||||
companyPkh = blob.slice(13, 33);
|
||||
distributionPkh = blob.slice(33, 53);
|
||||
} else if (templateId === TEMPLATE_DEVIANT_ORACLE_MBA) {
|
||||
} else if (templateId === TEMPLATE_ORACLE_MBA) {
|
||||
if (blob.length !== 1 + 2 + 20 + 33) { console.warn('[registry] Oracle DVMB size', blob.length); continue; }
|
||||
feeBps = BigInt(dv.getUint16(1, true));
|
||||
founderPkh = blob.slice(3, 23);
|
||||
|
|
@ -276,13 +276,14 @@ function categoryToConsensusLE(hex: string): Uint8Array {
|
|||
/** For an entry whose DVMB params were decoded, derive a KnownMba record
|
||||
* that orbital.ts can trade against. Dispatches by templateId:
|
||||
*
|
||||
* 0x00 Artemision → ArtemisionMintFT contract address
|
||||
* 0x01 DeviantShareMBA → DeviantShareMBA contract address (SWAP not yet
|
||||
* wired in orbital.ts — the entry surfaces in the
|
||||
* picker as view-only until claimShareMBA lands)
|
||||
* 0x02 DeviantOracleMBA → DeviantOracleMBA contract address (same
|
||||
* view-only caveat — oracle-priced claim path
|
||||
* follows in a next pass)
|
||||
* 0x00 CoinMBA → CoinMBA contract address (Static family)
|
||||
* 0x01 ShareMBA → ShareMBA contract address (SWAP not yet wired in
|
||||
* orbital.ts — the entry surfaces in the picker as
|
||||
* view-only until claimShareMBA lands)
|
||||
* 0x02 OracleMBA → OracleMBA contract address (same view-only caveat
|
||||
* — oracle-priced claim path follows in a next pass)
|
||||
* 0x03 DynamicMBA → DRA / dynamic-rate covenant (scaffold-only — not
|
||||
* yet implemented; no claim path)
|
||||
*
|
||||
* The address derivation always runs so the resulting KnownMba can be
|
||||
* used later without a re-derive; a Katalogos row that CAN'T claim yet
|
||||
|
|
@ -296,10 +297,10 @@ export async function discoveredParamsToKnownMba(
|
|||
const catLE = categoryToConsensusLE(entry.categoryId);
|
||||
const displayTicker = params.ticker ?? ('K:' + entry.categoryId.slice(0, 6));
|
||||
|
||||
if (params.templateId === TEMPLATE_ARTEMISION_MINT_FT) {
|
||||
if (params.templateId === TEMPLATE_COIN_MBA) {
|
||||
if (!params.founderPkh || params.rateSats == null || params.feeBps == null) return null;
|
||||
const founderLB = pkhToLB(params.founderPkh);
|
||||
const artifact = (await import('./artifacts/ArtemisionMintFT.json')).default;
|
||||
const artifact = (await import('./artifacts/CoinMBA.json')).default;
|
||||
const contract = new Contract(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
artifact as any,
|
||||
|
|
@ -318,12 +319,12 @@ export async function discoveredParamsToKnownMba(
|
|||
};
|
||||
}
|
||||
|
||||
if (params.templateId === TEMPLATE_DEVIANT_SHARE_MBA) {
|
||||
if (params.templateId === TEMPLATE_SHARE_MBA) {
|
||||
if (!params.companyPkh || !params.distributionPkh ||
|
||||
params.rateSats == null || params.feeBps == null || params.companySplitBps == null) return null;
|
||||
const companyLB = pkhToLB(params.companyPkh);
|
||||
const distributionLB = pkhToLB(params.distributionPkh);
|
||||
const artifact = (await import('./artifacts/DeviantShareMBA.json')).default;
|
||||
const artifact = (await import('./artifacts/ShareMBA.json')).default;
|
||||
const contract = new Contract(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
artifact as any,
|
||||
|
|
@ -344,10 +345,10 @@ export async function discoveredParamsToKnownMba(
|
|||
};
|
||||
}
|
||||
|
||||
if (params.templateId === TEMPLATE_DEVIANT_ORACLE_MBA) {
|
||||
if (params.templateId === TEMPLATE_ORACLE_MBA) {
|
||||
if (!params.founderPkh || !params.oraclePkHex || params.feeBps == null) return null;
|
||||
const founderLB = pkhToLB(params.founderPkh);
|
||||
const artifact = (await import('./artifacts/DeviantOracleMBA.json')).default;
|
||||
const artifact = (await import('./artifacts/OracleMBA.json')).default;
|
||||
const contract = new Contract(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
artifact as any,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
38
site/deviant/docs/mba/artifacts/DynamicMBA.json
Normal file
38
site/deviant/docs/mba/artifacts/DynamicMBA.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"contractName": "DynamicMBA",
|
||||
"constructorInputs": [
|
||||
{
|
||||
"name": "tokenCategory",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"abi": [
|
||||
{
|
||||
"name": "placeholder",
|
||||
"inputs": []
|
||||
}
|
||||
],
|
||||
"bytecode": "OP_1 OP_2 OP_NUMEQUALVERIFY OP_SIZE OP_NIP 20 OP_NUMEQUAL",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DynamicMBA — scaffold reserved for Dynamic Rate Asset (DRA) covenant.\n//\n// DRA is a Mathematically Backed Asset whose per-unit backing rate is\n// computed from on-chain state (reservoir depth, time, mint-count catalysts)\n// rather than:\n// - a constant (that's CoinMBA, templateId 0x00)\n// - a signed feed (that's OracleMBA, templateId 0x02)\n//\n// Explicitly NOT FIATμ. FIATμ combines a dynamic-rate layer with an oracle\n// signal to track a fiat purchasing-power target; DRA is the pure\n// state-driven variant and is reflective against a shared pool rather than\n// tracking an external price.\n//\n// Full design is intentionally deferred — the shared-pool reflective\n// mechanics (see memory [[dra-reflective-fiatm-flagship]]) and the Odyssean\n// maturity curve need a dedicated specification pass. This file reserves\n// templateId 0x03 in the DVMB dispatch table and the artifact-name slot so\n// nothing else lands under the DynamicMBA name by accident. It compiles,\n// but it is not meant to be deployed.\n\n// Minimum legal cashscript covenant — one trivial branch. Will be replaced\n// in full when the DRA covenant lands.\ncontract DynamicMBA(\n bytes32 tokenCategory,\n) {\n function placeholder() {\n // Not deployable — rejects every call.\n // Replace this body with the DRA state-machine when it lands.\n require(1 == 2, 'DynamicMBA scaffold — covenant not yet implemented');\n require(tokenCategory.length == 32);\n }\n}\n",
|
||||
"debug": {
|
||||
"bytecode": "51529d827701209c",
|
||||
"sourceMap": "31:16:31:17;:21::22;:8::78:1;32:16:32:36;;:40::42:0;:8::44:1",
|
||||
"logs": [],
|
||||
"requires": [
|
||||
{
|
||||
"ip": 3,
|
||||
"line": 31,
|
||||
"message": "DynamicMBA scaffold — covenant not yet implemented"
|
||||
},
|
||||
{
|
||||
"ip": 8,
|
||||
"line": 32
|
||||
}
|
||||
]
|
||||
},
|
||||
"compiler": {
|
||||
"name": "cashc",
|
||||
"version": "0.12.1"
|
||||
},
|
||||
"updatedAt": "2026-10-08T21:54:02.107Z"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"contractName": "DeviantOracleMBA",
|
||||
"contractName": "OracleMBA",
|
||||
"constructorInputs": [
|
||||
{
|
||||
"name": "tokenCategory",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
}
|
||||
],
|
||||
"bytecode": "OP_4 OP_PICK OP_0 OP_GREATERTHAN OP_VERIFY OP_6 OP_ROLL OP_7 OP_PICK OP_3 OP_ROLL OP_CHECKDATASIGVERIFY OP_5 OP_PICK OP_SIZE OP_NIP OP_16 OP_NUMEQUALVERIFY OP_5 OP_ROLL OP_12 OP_SPLIT OP_SWAP OP_SIZE OP_NIP OP_12 OP_NUMEQUALVERIFY OP_BIN2NUM OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY OP_4 OP_PICK OP_SWAP OP_MUL OP_DUP OP_3 OP_ROLL OP_MUL 1027 OP_DIV OP_0 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENAMOUNT OP_INPUTINDEX OP_UTXOTOKENAMOUNT OP_6 OP_PICK OP_SUB OP_NUMEQUALVERIFY OP_0 OP_OUTPUTVALUE 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_1 OP_OUTPUTBYTECODE OP_6 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_3 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENAMOUNT OP_4 OP_ROLL OP_NUMEQUALVERIFY OP_1 OP_OUTPUTVALUE OP_ROT OP_NUMEQUALVERIFY OP_DUP 2202 OP_GREATERTHANOREQUAL OP_IF OP_2 OP_OUTPUTBYTECODE OP_2 OP_PICK OP_EQUALVERIFY OP_2 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_2 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ENDIF OP_2DROP OP_1",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantOracleMBA — oracle-priced self-backed MBA (USD/EUR stablecoin shape).\n//\n// Sibling of DeviantOpenMintFT (fixed rate). Here the per-unit backing rate is\n// NOT a constructor constant — it is supplied by a founder-run price oracle at\n// claim time (checkDataSig). The FORWARD swap (BCH -> stablecoin) is oracle-\n// priced; the BACKWARD swap (stablecoin -> BCH) needs NO oracle and NO contract\n// path: each minted FT UTXO carries its mint-time BCH inside it, so redemption\n// is a plain P2PKH spend that releases exactly the locked BCH (self-backed).\n//\n// HONEST SEMANTICS: each unit is self-backed at its MINT-TIME price. A unit\n// minted at 1 USD = 10000 sats locks 10000; one minted at 12000 locks 12000.\n// So this is a \"dollar-worth-of-BCH-at-mint voucher, always redeemable for that\n// BCH\" — NOT a coin that keeps tracking USD after mint. That is the only\n// unilaterally-redeemable (MBA-shaped) stablecoin; a live USD tracker needs a\n// CDP/rebalancer and loses unilateral redemption.\n//\n// Oracle message (16 bytes, AnyHedge/Paryon format, all uint32 LE):\n// timestamp(4) || sequence(4) || contentSequence(4) || price(4)\n// price = sats per ATOMIC token unit (off-chain divides satsPerUSD by 10^dec).\n// Freshness (timestamp binding) is NOT enforced here — the known open limitation\n// (covenant verifies THAT the oracle signed, not WHEN). For production add a\n// CLTV/age bound on the timestamp field.\n//\n// Output layout (claim):\n// [0] reservoir back to contract: FT reduced, category/NFT preserved\n// [1] FT to recipient, self-backed at tokenAmount * price sats\n// [2] founder fee (only if >= dust; sub-dust fees are WAIVED, not rejected)\n// [3+] BCH change (unconstrained)\n\ncontract DeviantOracleMBA(\n bytes tokenCategory, // bare 32-byte category id (consensus LE order)\n pubkey oraclePk, // founder price-oracle public key\n int founderFeeBps, // e.g. 5 = 0.05%, 500 = 5%\n bytes founderLockingBytecode // 25-byte P2PKH of fee recipient\n) {\n function claim(\n int tokenAmount,\n bytes recipientLockingBytecode,\n datasig oracleSig,\n bytes oracleMessage\n ) {\n require(tokenAmount > 0);\n\n // --- oracle price attestation (forward swap only) ---\n require(checkDataSig(oracleSig, oracleMessage, oraclePk));\n require(oracleMessage.length == 16);\n bytes head, bytes priceBytes = oracleMessage.split(12); // last 4 bytes = price\n require(head.length == 12); // (also consumes `head`)\n int price = int(priceBytes);\n require(price > 0);\n\n int grossSats = tokenAmount * price;\n int feeSats = grossSats * founderFeeBps / 10000;\n\n // [0] reservoir back to this contract, FT reduced by tokenAmount.\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed at the oracle price (redeemable, no oracle).\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2] founder fee — enforced only when it clears dust; sub-dust waived.\n if (feeSats >= 546) {\n require(tx.outputs[2].lockingBytecode == founderLockingBytecode);\n require(tx.outputs[2].value == feeSats);\n require(tx.outputs[2].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantOracleMBA — oracle-priced self-backed MBA (USD/EUR stablecoin shape).\n//\n// Sibling of DeviantOpenMintFT (fixed rate). Here the per-unit backing rate is\n// NOT a constructor constant — it is supplied by a founder-run price oracle at\n// claim time (checkDataSig). The FORWARD swap (BCH -> stablecoin) is oracle-\n// priced; the BACKWARD swap (stablecoin -> BCH) needs NO oracle and NO contract\n// path: each minted FT UTXO carries its mint-time BCH inside it, so redemption\n// is a plain P2PKH spend that releases exactly the locked BCH (self-backed).\n//\n// HONEST SEMANTICS: each unit is self-backed at its MINT-TIME price. A unit\n// minted at 1 USD = 10000 sats locks 10000; one minted at 12000 locks 12000.\n// So this is a \"dollar-worth-of-BCH-at-mint voucher, always redeemable for that\n// BCH\" — NOT a coin that keeps tracking USD after mint. That is the only\n// unilaterally-redeemable (MBA-shaped) stablecoin; a live USD tracker needs a\n// CDP/rebalancer and loses unilateral redemption.\n//\n// Oracle message (16 bytes, AnyHedge/Paryon format, all uint32 LE):\n// timestamp(4) || sequence(4) || contentSequence(4) || price(4)\n// price = sats per ATOMIC token unit (off-chain divides satsPerUSD by 10^dec).\n// Freshness (timestamp binding) is NOT enforced here — the known open limitation\n// (covenant verifies THAT the oracle signed, not WHEN). For production add a\n// CLTV/age bound on the timestamp field.\n//\n// Output layout (claim):\n// [0] reservoir back to contract: FT reduced, category/NFT preserved\n// [1] FT to recipient, self-backed at tokenAmount * price sats\n// [2] founder fee (only if >= dust; sub-dust fees are WAIVED, not rejected)\n// [3+] BCH change (unconstrained)\n\ncontract OracleMBA(\n bytes tokenCategory, // bare 32-byte category id (consensus LE order)\n pubkey oraclePk, // founder price-oracle public key\n int founderFeeBps, // e.g. 5 = 0.05%, 500 = 5%\n bytes founderLockingBytecode // 25-byte P2PKH of fee recipient\n) {\n function claim(\n int tokenAmount,\n bytes recipientLockingBytecode,\n datasig oracleSig,\n bytes oracleMessage\n ) {\n require(tokenAmount > 0);\n\n // --- oracle price attestation (forward swap only) ---\n require(checkDataSig(oracleSig, oracleMessage, oraclePk));\n require(oracleMessage.length == 16);\n bytes head, bytes priceBytes = oracleMessage.split(12); // last 4 bytes = price\n require(head.length == 12); // (also consumes `head`)\n int price = int(priceBytes);\n require(price > 0);\n\n int grossSats = tokenAmount * price;\n int feeSats = grossSats * founderFeeBps / 10000;\n\n // [0] reservoir back to this contract, FT reduced by tokenAmount.\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed at the oracle price (redeemable, no oracle).\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2] founder fee — enforced only when it clears dust; sub-dust waived.\n if (feeSats >= 546) {\n require(tx.outputs[2].lockingBytecode == founderLockingBytecode);\n require(tx.outputs[2].value == feeSats);\n require(tx.outputs[2].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"debug": {
|
||||
"bytecode": "547900a069567a5779537abb55798277609d557a5c7f7c82775c9d817600a06954797c9576537a950210279600cdc0c78800d1c0ce8800d3c0d05679949d00cc022202a26951cd567a8851d1537a8851d3547a9d51cc7b9d76022202a26352cd52798852cc789d52d10088686d51",
|
||||
"sourceMap": "44:16:44:27;;:30::31;:16:::1;:8::33;47:29:47:38:0;;:40::53;;:55::63;;:8::66:1;48:16:48:29:0;;:::36:1;;:40::42:0;:8::44:1;49:39:49:52:0;;:59::61;:39::62:1;50:16:50:20:0;:::27:1;;:31::33:0;:8::35:1;51:20:51;52:16:52:21:0;:24::25;:16:::1;:8::27;54:24:54:35:0;;:38::43;:24:::1;55:22:55:31:0;:34::47;;:22:::1;:50::55:0;:22:::1;58:27:58:28:0;:16::45:1;:59::80:0;:49::97:1;:8::99;59:27:59:28:0;:16::43:1;:57::78:0;:47::93:1;:8::95;60:27:60:28:0;:16::41:1;:55::76:0;:45::89:1;:92::103:0;;:45:::1;:8::105;61:27:61:28:0;:16::35:1;:39::42:0;:16:::1;:8::44;64:27:64:28:0;:16::45:1;:49::73:0;;:8::75:1;65:27:65:28:0;:16::43:1;:47::60:0;;:8::62:1;66:27:66:28:0;:16::41:1;:45::56:0;;:8::58:1;67:27:67:28:0;:16::35:1;:39::48:0;:8::50:1;70:12:70:19:0;:23::26;:12:::1;:28:74:9:0;71:31:71:32;:20::49:1;:53::75:0;;:12::77:1;72:31:72:32:0;:20::39:1;:43::50:0;:12::52:1;73:31:73:32:0;:20::47:1;:51::53:0;:12::55:1;70:28:74:9;38:4:75:5;",
|
||||
|
|
@ -118,5 +118,5 @@
|
|||
"name": "cashc",
|
||||
"version": "0.12.1"
|
||||
},
|
||||
"updatedAt": "2026-09-06T16:25:52.645Z"
|
||||
"updatedAt": "2026-10-08T21:54:01.371Z"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"contractName": "DeviantShareMBA",
|
||||
"contractName": "ShareMBA",
|
||||
"constructorInputs": [
|
||||
{
|
||||
"name": "tokenCategory",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
}
|
||||
],
|
||||
"bytecode": "OP_6 OP_PICK OP_0 OP_GREATERTHAN OP_VERIFY OP_6 OP_PICK OP_ROT OP_MUL OP_DUP OP_3 OP_ROLL OP_MUL 1027 OP_DIV OP_DUP OP_4 OP_ROLL OP_MUL 1027 OP_DIV OP_2DUP OP_SUB OP_0 OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENAMOUNT OP_INPUTINDEX OP_UTXOTOKENAMOUNT OP_9 OP_PICK OP_SUB OP_NUMEQUALVERIFY OP_0 OP_OUTPUTVALUE 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_1 OP_OUTPUTBYTECODE OP_9 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_5 OP_ROLL OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENAMOUNT OP_7 OP_ROLL OP_NUMEQUALVERIFY OP_1 OP_OUTPUTVALUE OP_4 OP_ROLL OP_NUMEQUALVERIFY OP_ROT OP_0 OP_GREATERTHAN OP_IF OP_OVER 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_DUP 2202 OP_GREATERTHANOREQUAL OP_VERIFY OP_2 OP_OUTPUTBYTECODE OP_3 OP_PICK OP_EQUALVERIFY OP_2 OP_OUTPUTVALUE OP_2 OP_PICK OP_NUMEQUALVERIFY OP_2 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_3 OP_OUTPUTBYTECODE OP_4 OP_PICK OP_EQUALVERIFY OP_3 OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_3 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ENDIF OP_2DROP OP_2DROP OP_1",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantShareMBA — fork of DeviantOpenMintFT (ADR-0042, collection side).\n//\n// Splits the per-claim fee into TWO consensus-enforced streams instead of\n// one founder output:\n// - companySats -> companyLockingBytecode (the company treasury, paid directly)\n// - shareholderSats -> distributionLockingBytecode (the consensus-locked distribution\n// covenant — the holders' profit-share)\n//\n// fee = gross * feeBps / 10000\n// companySats = fee * companySplitBps / 10000 (company's share OF THE FEE)\n// shareholderSats = fee - companySats (routed to the distribution covenant)\n//\n// Output layout (claim):\n// [0] reservoir UTXO back to contract (minting NFT + remaining FT)\n// [1] FT to recipient, self-backed at tokenAmount * rate\n// [2] company fee -> companyLockingBytecode (companySats)\n// [3] shareholder share -> distributionLockingBytecode (shareholderSats)\n// [4+] BCH change (unconstrained)\n//\n// The shareholder stream never touches a company key: consensus routes it\n// straight to the distribution covenant's locking bytecode. The company\n// cannot take or redirect it. (Payout from that covenant — oracle-attributed,\n// time-weighted — is the next ADR-0042 step; this proves the COLLECTION split.)\n\ncontract DeviantShareMBA(\n bytes tokenCategory, // bare 32-byte category id == genesis TXID\n int backingRateSats, // sats per FT unit, >= 1000\n int feeBps, // total fee, 0..500 (0% .. 5%)\n int companySplitBps, // company's share of the fee, 0..10000\n bytes companyLockingBytecode, // company treasury (direct)\n bytes distributionLockingBytecode // consensus-locked distribution covenant\n) {\n function claim(int tokenAmount, bytes recipientLockingBytecode) {\n require(tokenAmount > 0);\n\n int grossSats = tokenAmount * backingRateSats;\n int feeSats = grossSats * feeBps / 10000;\n int companySats = feeSats * companySplitBps / 10000;\n int shareholderSats = feeSats - companySats;\n\n // [0] reservoir back to this contract (lockingBytecode + category + FT reduced).\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed.\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2]+[3] split fee. Both portions must clear dust when a fee is charged.\n // (Sub-dust waiver for tiny claims is a later refinement; the prototype\n // sizes claims so both sides clear 546.)\n if (feeSats > 0) {\n require(companySats >= 546);\n require(shareholderSats >= 546);\n\n // [2] company treasury — paid directly.\n require(tx.outputs[2].lockingBytecode == companyLockingBytecode);\n require(tx.outputs[2].value == companySats);\n require(tx.outputs[2].tokenCategory == 0x);\n\n // [3] shareholder profit-share — routed to the distribution covenant.\n require(tx.outputs[3].lockingBytecode == distributionLockingBytecode);\n require(tx.outputs[3].value == shareholderSats);\n require(tx.outputs[3].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"source": "pragma cashscript ^0.12.0;\n\n// DeviantShareMBA — fork of DeviantOpenMintFT (ADR-0042, collection side).\n//\n// Splits the per-claim fee into TWO consensus-enforced streams instead of\n// one founder output:\n// - companySats -> companyLockingBytecode (the company treasury, paid directly)\n// - shareholderSats -> distributionLockingBytecode (the consensus-locked distribution\n// covenant — the holders' profit-share)\n//\n// fee = gross * feeBps / 10000\n// companySats = fee * companySplitBps / 10000 (company's share OF THE FEE)\n// shareholderSats = fee - companySats (routed to the distribution covenant)\n//\n// Output layout (claim):\n// [0] reservoir UTXO back to contract (minting NFT + remaining FT)\n// [1] FT to recipient, self-backed at tokenAmount * rate\n// [2] company fee -> companyLockingBytecode (companySats)\n// [3] shareholder share -> distributionLockingBytecode (shareholderSats)\n// [4+] BCH change (unconstrained)\n//\n// The shareholder stream never touches a company key: consensus routes it\n// straight to the distribution covenant's locking bytecode. The company\n// cannot take or redirect it. (Payout from that covenant — oracle-attributed,\n// time-weighted — is the next ADR-0042 step; this proves the COLLECTION split.)\n\ncontract ShareMBA(\n bytes tokenCategory, // bare 32-byte category id == genesis TXID\n int backingRateSats, // sats per FT unit, >= 1000\n int feeBps, // total fee, 0..500 (0% .. 5%)\n int companySplitBps, // company's share of the fee, 0..10000\n bytes companyLockingBytecode, // company treasury (direct)\n bytes distributionLockingBytecode // consensus-locked distribution covenant\n) {\n function claim(int tokenAmount, bytes recipientLockingBytecode) {\n require(tokenAmount > 0);\n\n int grossSats = tokenAmount * backingRateSats;\n int feeSats = grossSats * feeBps / 10000;\n int companySats = feeSats * companySplitBps / 10000;\n int shareholderSats = feeSats - companySats;\n\n // [0] reservoir back to this contract (lockingBytecode + category + FT reduced).\n require(tx.outputs[0].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\n require(tx.outputs[0].tokenCategory == tx.inputs[this.activeInputIndex].tokenCategory);\n require(tx.outputs[0].tokenAmount == tx.inputs[this.activeInputIndex].tokenAmount - tokenAmount);\n require(tx.outputs[0].value >= 546);\n\n // [1] FT to recipient, self-backed.\n require(tx.outputs[1].lockingBytecode == recipientLockingBytecode);\n require(tx.outputs[1].tokenCategory == tokenCategory);\n require(tx.outputs[1].tokenAmount == tokenAmount);\n require(tx.outputs[1].value == grossSats);\n\n // [2]+[3] split fee. Both portions must clear dust when a fee is charged.\n // (Sub-dust waiver for tiny claims is a later refinement; the prototype\n // sizes claims so both sides clear 546.)\n if (feeSats > 0) {\n require(companySats >= 546);\n require(shareholderSats >= 546);\n\n // [2] company treasury — paid directly.\n require(tx.outputs[2].lockingBytecode == companyLockingBytecode);\n require(tx.outputs[2].value == companySats);\n require(tx.outputs[2].tokenCategory == 0x);\n\n // [3] shareholder profit-share — routed to the distribution covenant.\n require(tx.outputs[3].lockingBytecode == distributionLockingBytecode);\n require(tx.outputs[3].value == shareholderSats);\n require(tx.outputs[3].tokenCategory == 0x);\n }\n }\n}\n",
|
||||
"debug": {
|
||||
"bytecode": "567900a06956797b9576537a950210279676547a95021027966e9400cdc0c78800d1c0ce8800d3c0d05979949d00cc022202a26951cd597a8851d1557a8851d3577a9d51cc547a9d7b00a06378022202a26976022202a26952cd53798852cc52799d52d1008853cd54798853cc789d53d10088686d6d51",
|
||||
"sourceMap": "36:16:36:27;;:30::31;:16:::1;:8::33;38:24:38:35:0;;:38::53;:24:::1;39:22:39:31:0;:34::40;;:22:::1;:43::48:0;:22:::1;40:26:40:33:0;:36::51;;:26:::1;:54::59:0;:26:::1;41:30:41:51:0;::::1;44:27:44:28:0;:16::45:1;:59::80:0;:49::97:1;:8::99;45:27:45:28:0;:16::43:1;:57::78:0;:47::93:1;:8::95;46:27:46:28:0;:16::41:1;:55::76:0;:45::89:1;:92::103:0;;:45:::1;:8::105;47:27:47:28:0;:16::35:1;:39::42:0;:16:::1;:8::44;50:27:50:28:0;:16::45:1;:49::73:0;;:8::75:1;51:27:51:28:0;:16::43:1;:47::60:0;;:8::62:1;52:27:52:28:0;:16::41:1;:45::56:0;;:8::58:1;53:27:53:28:0;:16::35:1;:39::48:0;;:8::50:1;58:12:58:19:0;:22::23;:12:::1;:25:71:9:0;59:20:59:31;:35::38;:20:::1;:12::40;60:20:60:35:0;:39::42;:20:::1;:12::44;63:31:63:32:0;:20::49:1;:53::75:0;;:12::77:1;64:31:64:32:0;:20::39:1;:43::54:0;;:12::56:1;65:31:65:32:0;:20::47:1;:51::53:0;:12::55:1;68:31:68:32:0;:20::49:1;:53::80:0;;:12::82:1;69:31:69:32:0;:20::39:1;:43::58:0;:12::60:1;70:31:70:32:0;:20::47:1;:51::53:0;:12::55:1;58:25:71:9;35:4:72:5;;",
|
||||
|
|
@ -122,5 +122,5 @@
|
|||
"name": "cashc",
|
||||
"version": "0.12.1"
|
||||
},
|
||||
"updatedAt": "2026-09-06T16:25:52.850Z"
|
||||
"updatedAt": "2026-10-08T21:54:00.544Z"
|
||||
}
|
||||
|
|
@ -43,37 +43,38 @@
|
|||
|
||||
<ul class="artifact-list">
|
||||
<li>
|
||||
<h3>DeviantOpenMintFT.json</h3>
|
||||
<h3>CoinMBA.json</h3>
|
||||
<p class="template-id">templateId <code>0x00</code> · Static family</p>
|
||||
<p>The default MBA covenant. Fixed sats/unit rate baked at genesis, flat <code>founderFeeBps</code> (0..500). Reservoir carries the minting NFT and the circulating supply; every claim consumes the current reservoir and emits a fresh one with updated supply.</p>
|
||||
<p>The default MBA covenant. Fixed sats/unit rate baked at genesis, flat <code>founderFeeBps</code> (0..500). Reservoir carries the minting NFT and the circulating supply; every claim consumes the current reservoir and emits a fresh one with updated supply. The sound-money archetype of the family — "coin" is literal: a measured amount of BCH wrapped in a covenant stamp.</p>
|
||||
<p><strong>Constructor args</strong>: <code>[tokenCategory (bytes32), rateSats (int), feeBps (int), founderLockingBytecode (bytes25)]</code></p>
|
||||
<a class="download" href="./DeviantOpenMintFT.json" download>Download JSON</a>
|
||||
<a class="download" href="./CoinMBA.json" download>Download JSON</a>
|
||||
</li>
|
||||
<li>
|
||||
<h3>ArtemisionMintFT.json</h3>
|
||||
<p class="template-id">templateId <code>0x00</code> · Static family (genesis template)</p>
|
||||
<p>Equivalent shape to DeviantOpenMintFT — used by the Artemision wizard to deploy fresh MBAs. The two artifacts have the same bytecode in practice; the separate file is kept for provenance and compiler-version pinning.</p>
|
||||
<a class="download" href="./ArtemisionMintFT.json" download>Download JSON</a>
|
||||
</li>
|
||||
<li>
|
||||
<h3>DeviantShareMBA.json</h3>
|
||||
<h3>ShareMBA.json</h3>
|
||||
<p class="template-id">templateId <code>0x01</code> · Share family</p>
|
||||
<p>Flat <code>feeBps</code>, split by <code>companySplitBps</code> between a company treasury output and a shareholder distribution vault output. Each claim produces exactly four non-change outputs: new reservoir, user's MBA, company share, distribution share.</p>
|
||||
<p><strong>Constructor args</strong>: <code>[tokenCategory, rateSats, feeBps, companySplitBps, companyLockingBytecode (bytes25), distributionLockingBytecode (bytes25)]</code></p>
|
||||
<a class="download" href="./DeviantShareMBA.json" download>Download JSON</a>
|
||||
<a class="download" href="./ShareMBA.json" download>Download JSON</a>
|
||||
</li>
|
||||
<li>
|
||||
<h3>DeviantOracleMBA.json</h3>
|
||||
<h3>OracleMBA.json</h3>
|
||||
<p class="template-id">templateId <code>0x02</code> · Oracle family</p>
|
||||
<p>Rate set by a signed AnyHedge-format price feed at claim time (<code>OP_CHECKDATASIG</code>). Flat <code>feeBps</code> on top. Oracle pubkey is baked at genesis; changing it requires a new covenant deployment.</p>
|
||||
<p><strong>Constructor args</strong>: <code>[tokenCategory, feeBps, founderLockingBytecode, oraclePubKey (bytes33)]</code></p>
|
||||
<a class="download" href="./DeviantOracleMBA.json" download>Download JSON</a>
|
||||
<a class="download" href="./OracleMBA.json" download>Download JSON</a>
|
||||
</li>
|
||||
<li>
|
||||
<h3>DynamicMBA.json <span class="template-id" style="display:inline;color:var(--muted)">(scaffold)</span></h3>
|
||||
<p class="template-id">templateId <code>0x03</code> · Dynamic Rate family · <em>not yet deployable</em></p>
|
||||
<p>Reserved for the Dynamic Rate Asset (DRA) covenant — a pure state-driven rate (reservoir depth, time, mint-count catalysts), no oracle. Distinct from FIATμ, which combines a dynamic-rate layer with an oracle signal to track a fiat purchasing-power target; DRA is the pure state-driven variant and is reflective against a shared pool. The current artifact is a scaffold stub (rejects every call); it reserves the templateId slot and the artifact filename so nothing else lands under this name by accident.</p>
|
||||
<p><strong>Constructor args</strong>: <code>[tokenCategory (bytes32)]</code> <em>(placeholder — real signature arrives with the DRA spec)</em></p>
|
||||
<a class="download" href="./DynamicMBA.json" download>Download JSON (scaffold)</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Example — construct and verify</h2>
|
||||
<pre>import { Contract, ElectrumNetworkProvider } from 'cashscript';
|
||||
import artifact from './DeviantOpenMintFT.json';
|
||||
import artifact from './CoinMBA.json';
|
||||
|
||||
// Params read from the DVMB OP_RETURN of the asset's genesis (or Katalogos entry).
|
||||
const categoryId = '09af8a5b2fc2c9a2449045a8d5a6aa60db493989682eca257a57fa8d75d67231';
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@
|
|||
We do not call a backed asset a "token" — "token" in this project means an unbacked CashToken (ASM, GMX, etc. that stand on their own economics). The on-chain backing is the deliberate differentiator; "MBA" is the only name we use for a backed one. The raw chain primitive is "CashToken FT" but the product is a backed asset.
|
||||
</aside>
|
||||
|
||||
<h2 id="families">Three template families</h2>
|
||||
<p>Every MBA you encounter uses one of three cashscript covenants. The one you're reading is identifiable from the <code>templateId</code> byte in the asset's DVMB OP_RETURN sidecar, or from the covenant bytecode itself.</p>
|
||||
<h2 id="families">Template families</h2>
|
||||
<p>Every MBA uses one of four cashscript covenants. The one you're reading is identifiable from the <code>templateId</code> byte in the asset's DVMB OP_RETURN sidecar, or from the covenant bytecode itself. Three families are live (CoinMBA, ShareMBA, OracleMBA); the fourth (DynamicMBA) is a reserved slot for the Dynamic Rate Asset covenant.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>templateId</th><th>Family</th><th>Rate</th><th>Fee behavior</th><th>Who gets paid</th></tr>
|
||||
|
|
@ -94,27 +94,35 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td class="mono">0x00</td>
|
||||
<td>Static · <code>DeviantOpenMintFT</code> / <code>ArtemisionMintFT</code></td>
|
||||
<td>Static · <code>CoinMBA</code></td>
|
||||
<td>Fixed sats/unit, baked at genesis</td>
|
||||
<td>Flat <code>founderFeeBps</code> (0..500)</td>
|
||||
<td>Founder of the asset</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">0x01</td>
|
||||
<td>Share · <code>DeviantShareMBA</code></td>
|
||||
<td>Share · <code>ShareMBA</code></td>
|
||||
<td>Fixed sats/unit</td>
|
||||
<td>Flat <code>feeBps</code>, split <code>companySplitBps</code>:(10000-split) between company and shareholder vault</td>
|
||||
<td>Company treasury + distribution vault</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">0x02</td>
|
||||
<td>Oracle · <code>DeviantOracleMBA</code></td>
|
||||
<td>Oracle · <code>OracleMBA</code></td>
|
||||
<td>Set by a signed price feed at claim time</td>
|
||||
<td>Flat <code>feeBps</code> + oracle-sized overhead</td>
|
||||
<td>Founder; oracle receives no fee from this flow</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">0x03</td>
|
||||
<td>Dynamic · <code>DynamicMBA</code> <em>(scaffold)</em></td>
|
||||
<td>Computed from on-chain state (reservoir depth, time, mint-count catalysts) — no oracle</td>
|
||||
<td>TBD with the DRA spec</td>
|
||||
<td>TBD</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><em>DynamicMBA is distinct from FIATμ: FIATμ combines a dynamic-rate layer with an oracle signal to track a fiat purchasing-power target, while pure DRA (what DynamicMBA covers) works without any oracle, reflective against a shared pool.</em></p>
|
||||
<p>The protocol-level fee behavior is the same across all three: on top of the covenant's own fee, a <strong>platform fee of 0.5%</strong> currently applies (40% to the Potidaea platform, 60% to the DEA Yield vault), configured in the client at swap-build time. The platform fee is a client enforcement, not a covenant rule — a wallet that strips it still produces a tx the covenant accepts, so trust in that fee rests on the client, not the chain.</p>
|
||||
|
||||
<h2 id="interacting">How to interact</h2>
|
||||
|
|
@ -163,7 +171,7 @@
|
|||
<p>Needed if the wallet wants to let the user mint or redeem an MBA without routing them to the Potidaea dApp. Requires the covenant artifact plus the exact transaction shape.</p>
|
||||
<p><strong>What you need:</strong></p>
|
||||
<ul>
|
||||
<li><strong>The covenant artifact JSON</strong> — all four templates published at <a href="./artifacts/">/docs/mba/artifacts/</a> (Static, Share, Oracle, plus the Artemision genesis variant). Pass the right one to <code>new Contract(artifact, [...args])</code>. The artifacts page also carries the constructor argument order and a verification example you can copy.</li>
|
||||
<li><strong>The covenant artifact JSON</strong> — the three live templates (CoinMBA, ShareMBA, OracleMBA) and the DynamicMBA scaffold are published at <a href="./artifacts/">/docs/mba/artifacts/</a>. Pass the right one to <code>new Contract(artifact, [...args])</code>. The artifacts page carries the constructor argument order and a verification example you can copy.</li>
|
||||
<li><strong>Constructor args</strong>, in order: <code>[tokenCategory: bytes32, backingRateSats: int, founderFeeBps: int, founderLockingBytecodeHex: bytes25]</code>. <code>tokenCategory</code> is the categoryId in VM byte order (reversed from the display hex — cashscript's <code>token.category</code> is byte-reversed vs <code>OP_*TOKENCATEGORY</code>). Fetch these from the DVMB OP_RETURN sidecar on the genesis tx, or from our Katalogos entry's parameters chunk.</li>
|
||||
<li><strong>Address derivation:</strong> rebuild <code>new Contract(artifact, [cat, rate, feeBps, founderLB])</code> locally and verify <code>contract.tokenAddress</code> equals the on-chain reservoir address before trusting any sidecar params.</li>
|
||||
</ul>
|
||||
|
|
@ -220,7 +228,7 @@ output [1] dust padding at payer [only if leftoverBacking < 1000 sa
|
|||
<h2 id="devs">For developers</h2>
|
||||
<p>Everything a dApp needs to interact with an MBA sits in two files:</p>
|
||||
<ul>
|
||||
<li><code>contracts/DeviantOpenMintFT.cash</code> — the Static MBA covenant. Share and Oracle variants live beside it.</li>
|
||||
<li><code>contracts/CoinMBA.cash</code> — the Static MBA covenant. <code>ShareMBA.cash</code> and <code>OracleMBA.cash</code> live beside it; <code>DynamicMBA.cash</code> is the reserved slot for the DRA covenant.</li>
|
||||
<li><code>design/potidaea/src/orbital.ts</code> — the reference client: builds claim and redeem transactions with cashscript, broadcasts via a multi-source fan-out (own-node BCHN + ephemeral public Fulcrums).</li>
|
||||
</ul>
|
||||
<p>A claim transaction's shape is deterministic:</p>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue