{ "contractName": "DeliveryEscrow", "constructorInputs": [ { "name": "buyerPkh", "type": "bytes20" }, { "name": "certLock", "type": "bytes" }, { "name": "nameCommitment", "type": "bytes" }, { "name": "payloadLock", "type": "bytes" }, { "name": "beaconLock", "type": "bytes" }, { "name": "beaconDust", "type": "int" }, { "name": "refundAfter", "type": "int" } ], "abi": [ { "name": "deliver", "inputs": [] }, { "name": "refund", "inputs": [ { "name": "pk", "type": "pubkey" }, { "name": "s", "type": "sig" } ] } ], "bytecode": "OP_7 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_1 OP_NUMEQUALVERIFY OP_0 OP_OUTPOINTINDEX OP_0 OP_NUMEQUALVERIFY OP_0 OP_OUTPOINTTXHASH OP_0 OP_OUTPUTBYTECODE OP_3 OP_ROLL OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCOMMITMENT OP_ROT OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENAMOUNT OP_0 OP_NUMEQUALVERIFY OP_1 OP_OUTPUTBYTECODE OP_ROT OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_2 OP_OUTPUTBYTECODE OP_ROT OP_EQUALVERIFY OP_2 OP_OUTPUTVALUE OP_ROT OP_GREATERTHANOREQUAL OP_VERIFY OP_2 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_TXOUTPUTCOUNT OP_4 OP_LESSTHANOREQUAL OP_VERIFY OP_TXOUTPUTCOUNT OP_3 OP_GREATERTHAN OP_IF OP_3 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_3 OP_OUTPUTBYTECODE OP_1 OP_SPLIT OP_DROP 6a OP_EQUAL OP_NOT OP_VERIFY OP_ENDIF OP_2DROP OP_DROP OP_1 OP_ELSE OP_7 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_7 OP_PICK OP_HASH160 OP_EQUALVERIFY OP_7 OP_ROLL OP_7 OP_ROLL OP_CHECKSIGVERIFY OP_5 OP_PICK OP_1 000001 OP_WITHIN OP_VERIFY OP_5 OP_ROLL OP_CHECKSEQUENCEVERIFY OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ENDIF", "source": "pragma cashscript ^0.13.0;\n\n// DeliveryEscrow — payment for a name that can only be collected by the\n// transaction that registers it to the buyer.\n// (Decentralized.DNS/DESIGN-trustless-purchase.md §4.)\n//\n// STATUS: chipnet draft. NOT REVIEWED.\n//\n// A buyer who cannot build a registration themselves (pay-by-QR, any wallet)\n// pays this script instead of paying a registrar. The money then has exactly\n// two ways out:\n//\n// deliver() anyone may spend it — in a transaction that mints the agreed\n// certificate to the buyer and publishes the agreed registration.\n// \"Took the money and did not register\" is not a risk the buyer\n// has to weigh; it is a transaction that cannot be built.\n// refund() the buyer takes it back with their signature once it is\n// `refundAfter` blocks old.\n//\n// What deliver() pins down, output by output:\n// 0 the certificate — a NEW token category created by this transaction\n// (its id is input 0's outpoint), an NFT with no capability, commitment =\n// the name, no fungible amount, locked to the buyer's lock\n// 1 the registration payload, byte for byte as agreed (OP_RETURN)\n// 2 the discovery beacon, at least its dust\n// 3 optional: the deliverer's change — no token, and not a data output\n// and nothing else. Two things are being kept out:\n// · a second token of the certificate's category, which its holder could\n// later use to update the buyer's name;\n// · a second data output. A transaction is read as carrying ONE protocol\n// message; a later OP_RETURN would replace the agreed registration in\n// every resolver, and the deliverer would be paid for a certificate\n// that registers nothing.\n//\n// It cannot deliver under a TLD that requires its owner's co-signature: that\n// rule wants the TLD's certificate in an output of the registration, and no\n// extra token is allowed here. The library refuses to build an escrow for\n// such a name (escrow-covenant.js assertTldAllowsEscrow).\n//\n// The escrow must be input 1 (input 0 is the genesis outpoint). That makes it\n// one escrow per transaction: two payments for the same name could otherwise\n// both be collected against a single certificate.\n//\n// WHAT IT CANNOT PROMISE. Script sees this transaction, not the registry. If\n// the name was registered by someone else between payment and delivery, a\n// deliverer can still mint a certificate that satisfies every line here and\n// that no resolver will honour (\"first valid registration wins\" is a resolver\n// rule). The window is the buyer's to keep short: check the name is free, pay,\n// and refund if delivery does not come.\ncontract DeliveryEscrow(\n bytes20 buyerPkh,\n bytes certLock, // where the certificate must go (the buyer's token address, or their covenant)\n bytes nameCommitment, // the on-chain name, as the certificate's commitment\n bytes payloadLock, // the full OP_RETURN locking bytecode of the agreed REG payload\n bytes beaconLock,\n int beaconDust,\n int refundAfter\n) {\n function deliver() {\n require(this.activeInputIndex == 1);\n require(tx.inputs[0].outpointIndex == 0);\n bytes category = tx.inputs[0].outpointTransactionHash;\n\n require(tx.outputs[0].lockingBytecode == certLock);\n require(tx.outputs[0].tokenCategory == category);\n require(tx.outputs[0].nftCommitment == nameCommitment);\n require(tx.outputs[0].tokenAmount == 0);\n\n require(tx.outputs[1].lockingBytecode == payloadLock);\n require(tx.outputs[1].tokenCategory == 0x);\n\n require(tx.outputs[2].lockingBytecode == beaconLock);\n require(tx.outputs[2].value >= beaconDust);\n require(tx.outputs[2].tokenCategory == 0x);\n\n require(tx.outputs.length <= 4);\n if (tx.outputs.length > 3) {\n require(tx.outputs[3].tokenCategory == 0x);\n require(tx.outputs[3].lockingBytecode.split(1)[0] != 0x6a);\n }\n }\n\n function refund(pubkey pk, sig s) {\n require(hash160(pk) == buyerPkh);\n require(checkSig(s, pk));\n require(within(refundAfter, 1, 65536)); // a 16-bit block count, see subscription.cash\n require(this.age >= refundAfter);\n }\n}\n", "fingerprint": "4f43d98c69ef538b7c7588fda9b49c1f18c4f712a6ea3a5e98c7cc6d24a36b1f", "debug": { "bytecode": "5779009c63c0519d00c9009d00c800cd537a8800d18800d27b8800d3009d51cd7b8851d1008852cd7b8852cc7ba26952d10088c454a169c453a06353d1008853cd517f75016a879169686d755167577a519d5779a988577a577aad55795103000001a569557ab26d6d6d5168", "sourceMap": "59:4:81:5;;;;;60:16:60:37;:41::42;:8::44:1;61:26:61:27:0;:16::42:1;:46::47:0;:8::49:1;62:35:62:36:0;:25::61:1;64:27:64:28:0;:16::45:1;:49::57:0;;:8::59:1;65:27:65:28:0;:16::43:1;:8::57;66:27:66:28:0;:16::43:1;:47::61:0;:8::63:1;67:27:67:28:0;:16::41:1;:45::46:0;:8::48:1;69:27:69:28:0;:16::45:1;:49::60:0;:8::62:1;70:27:70:28:0;:16::43:1;:47::49:0;:8::51:1;72:27:72:28:0;:16::45:1;:49::59:0;:8::61:1;73:27:73:28:0;:16::35:1;:39::49:0;:16:::1;:8::51;74:27:74:28:0;:16::43:1;:47::49:0;:8::51:1;76:16:76:33:0;:37::38;:16:::1;:8::40;77:12:77:29:0;:32::33;:12:::1;:35:80:9:0;78:31:78:32;:20::47:1;:51::53:0;:12::55:1;79:31:79:32:0;:20::49:1;:56::57:0;:20::58:1;:::61;:65::69:0;:20:::1;;:12::71;77:35:80:9;59:23:81:5;;;:4;83::88::0;;;;84:24:84:26;;:16::27:1;:8::41;85:25:85:26:0;;:28::30;;:8::33:1;86:23:86:34:0;;:36::37;:39::44;:16::45:1;:8::47;87:28:87:39:0;;:8::41:1;83:38:88:5;;;;50:0:89:1", "logs": [], "requires": [ { "ip": 14, "line": 60 }, { "ip": 18, "line": 61 }, { "ip": 25, "line": 64 }, { "ip": 28, "line": 65 }, { "ip": 32, "line": 66 }, { "ip": 36, "line": 67 }, { "ip": 40, "line": 69 }, { "ip": 44, "line": 70 }, { "ip": 48, "line": 72 }, { "ip": 53, "line": 73 }, { "ip": 57, "line": 74 }, { "ip": 61, "line": 76 }, { "ip": 69, "line": 78 }, { "ip": 78, "line": 79 }, { "ip": 91, "line": 84 }, { "ip": 96, "line": 85 }, { "ip": 102, "line": 86 }, { "ip": 105, "line": 87 } ] }, "compiler": { "name": "cashc", "version": "0.13.3", "options": { "enforceFunctionParameterTypes": true, "enforceLocktimeGuard": true } }, "updatedAt": "2026-10-04T14:37:29.033Z" }