{ "contractName": "Subscription", "constructorInputs": [ { "name": "buyerPkh", "type": "bytes20" }, { "name": "hostLock", "type": "bytes" }, { "name": "priceSats", "type": "int" }, { "name": "periodBlocks", "type": "int" }, { "name": "maxFeeSats", "type": "int" } ], "abi": [ { "name": "pull", "inputs": [] }, { "name": "cancel", "inputs": [ { "name": "pk", "type": "pubkey" }, { "name": "s", "type": "sig" } ] } ], "bytecode": "OP_5 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_3 OP_PICK OP_1 000001 OP_WITHIN OP_VERIFY OP_3 OP_ROLL OP_CHECKSEQUENCEVERIFY OP_DROP OP_TXINPUTCOUNT OP_1 OP_NUMEQUALVERIFY OP_TXOUTPUTCOUNT OP_2 OP_LESSTHANOREQUAL OP_VERIFY OP_0 OP_UTXOVALUE OP_0 OP_OUTPUTBYTECODE OP_3 OP_ROLL OP_EQUALVERIFY OP_0 OP_OUTPUTVALUE OP_3 OP_PICK OP_NUMEQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ROT OP_SUB OP_ROT OP_SUB OP_DUP 2202 OP_GREATERTHANOREQUAL OP_IF OP_1 OP_OUTPUTBYTECODE OP_0 OP_UTXOBYTECODE OP_EQUALVERIFY OP_1 OP_OUTPUTVALUE OP_OVER OP_GREATERTHANOREQUAL OP_VERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ELSE OP_TXOUTPUTCOUNT OP_1 OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_DROP OP_1 OP_ELSE OP_5 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_5 OP_PICK OP_HASH160 OP_EQUALVERIFY OP_2ROT OP_CHECKSIGVERIFY OP_2DROP OP_2DROP OP_1 OP_ENDIF", "source": "pragma cashscript ^0.13.0;\n\n// Subscription — a prepaid balance the host can draw from once per period,\n// and the buyer can take back at any time.\n// (Argus/src/lib/plans.js SUBSCRIPTION_ROADMAP; ROADMAP-MAINNET.md §9 step 4.)\n//\n// STATUS: chipnet draft. NOT REVIEWED.\n//\n// The buyer funds this script. Three things then hold, and the buyer can check\n// each from the script alone:\n//\n// · the balance stays theirs — cancel() needs only their signature, no\n// notice, no cooperation, and returns whatever is left;\n// · the host cannot take more than one period's price per period — pull()\n// pays exactly the host's lock at least `priceSats`, sends the rest back to\n// this same script, and can only run on an output that is `periodBlocks`\n// old, which the re-created output is not;\n// · nobody else gains anything — pull() needs no signature, so the host does\n// not have to be online with a key, but it has at most two outputs: the\n// price to the host, exactly, and the rest back to this script. Whatever\n// the fee allowance does not use goes to the miner, not to whoever built\n// the transaction.\n//\n// ONE BALANCE, ONE OUTPUT. The once-per-period limit is a property of an\n// output, not of an address. A buyer who pays the same address twice has two\n// balances, and each can be drawn every period. To add funds: cancel and fund\n// again, in one transaction if you like.\n//\n// A pull spends this input ALONE. With two inputs, one \"price to the host\"\n// output and one \"rest back to the script\" output could satisfy both, and the\n// host would collect two balances for one payment. One input means the fee\n// comes out of the balance too, bounded by `maxFeeSats`.\n//\n// The first period is not special-cased: the balance is drawn one period\n// after it is funded. A host that wants payment up front takes the first\n// period in the funding transaction itself.\ncontract Subscription(bytes20 buyerPkh, bytes hostLock, int priceSats, int periodBlocks, int maxFeeSats) {\n // One period's payment to the host.\n function pull() {\n // A relative lock is a 16-bit block count. Anything outside that range\n // would be read as something else entirely: only its low bits, a\n // number of seconds, or no lock at all.\n require(within(periodBlocks, 1, 65536));\n require(this.age >= periodBlocks);\n require(tx.inputs.length == 1);\n require(tx.outputs.length <= 2);\n\n int balance = tx.inputs[0].value;\n require(tx.outputs[0].lockingBytecode == hostLock);\n require(tx.outputs[0].value == priceSats);\n require(tx.outputs[0].tokenCategory == 0x);\n\n // Everything else stays in the subscription, less a bounded fee. When\n // what would remain is too small to be an output, it is the fee.\n int rest = balance - priceSats - maxFeeSats;\n if (rest >= 546) {\n require(tx.outputs[1].lockingBytecode == tx.inputs[0].lockingBytecode);\n require(tx.outputs[1].value >= rest);\n require(tx.outputs[1].tokenCategory == 0x);\n } else {\n // Nothing is returned, so there must be nothing else: the leftover\n // is the fee, not a second output for whoever built the draw.\n require(tx.outputs.length == 1);\n }\n }\n\n // The buyer ends it and takes the balance. No conditions on where it goes.\n function cancel(pubkey pk, sig s) {\n require(hash160(pk) == buyerPkh);\n require(checkSig(s, pk));\n }\n}\n", "fingerprint": "d5796aeddba44e7674c0caad36eb5c7ab658a0a4fe9c7ebf8701acb5cc8ba302", "debug": { "bytecode": "5579009c6353795103000001a569537ab275c3519dc452a16900c600cd537a8800cc53799d00d100887b947b9476022202a26351cd00c78851cc78a26951d1008867c4519d686d755167557a519d5579a98871ad6d6d5168", "sourceMap": "39:4:65:5;;;;;43:23:43:35;;:37::38;:40::45;:16::46:1;:8::48;44:28:44:40:0;;:8::42:1;;45:16:45:32:0;:36::37;:8::39:1;46:16:46:33:0;:37::38;:16:::1;:8::40;48:32:48:33:0;:22::40:1;49:27:49:28:0;:16::45:1;:49::57:0;;:8::59:1;50:27:50:28:0;:16::35:1;:39::48:0;;:8::50:1;51:27:51:28:0;:16::43:1;:47::49:0;:8::51:1;55:29:55:38:0;:19:::1;:41::51:0;:19:::1;56:12:56:16:0;:20::23;:12:::1;:25:60:9:0;57:31:57:32;:20::49:1;:63::64:0;:53::81:1;:12::83;58:31:58:32:0;:20::39:1;:43::47:0;:20:::1;:12::49;59:31:59:32:0;:20::47:1;:51::53:0;:12::55:1;60:15:64:9:0;63:20:63:37;:41::42;:12::44:1;60:15:64:9;39:20:65:5;;;:4;68::71::0;;;;69:24:69:26;;:16::27:1;:8::41;70:25:70:30:0;:8::33:1;68:38:71:5;;;37:0:72:1", "logs": [], "requires": [ { "ip": 15, "line": 43 }, { "ip": 18, "line": 44 }, { "ip": 22, "line": 45 }, { "ip": 26, "line": 46 }, { "ip": 33, "line": 49 }, { "ip": 38, "line": 50 }, { "ip": 42, "line": 51 }, { "ip": 55, "line": 57 }, { "ip": 60, "line": 58 }, { "ip": 64, "line": 59 }, { "ip": 68, "line": 63 }, { "ip": 81, "line": 69 }, { "ip": 83, "line": 70 } ] }, "compiler": { "name": "cashc", "version": "0.13.3", "options": { "enforceFunctionParameterTypes": true, "enforceLocktimeGuard": true } }, "updatedAt": "2026-10-04T14:39:23.518Z" }