@fundtokens/builders 0.1.0-rc13 → 0.1.0-rc14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FundTokenTransactionBuilder.js +3 -3
- package/FundTokensRegistry.js +82 -0
- package/PublicFundTransactionBuilder.js +19 -29
- package/README.md +16 -9
- package/art/asset.json +34 -34
- package/art/authhead_vault.json +35 -36
- package/art/fee.json +43 -44
- package/art/fee_minter.json +40 -40
- package/art/fund.json +38 -38
- package/art/instance_vault.json +63 -0
- package/art/manager.json +75 -76
- package/art/mint_inflow.json +42 -41
- package/art/mint_outflow.json +42 -41
- package/art/public.json +55 -60
- package/art/public_vault.json +48 -57
- package/art/simple_minter.json +40 -40
- package/art/simple_vault.json +30 -30
- package/art/startup.json +50 -49
- package/constants.js +9 -1
- package/index.js +16 -1
- package/package.json +1 -1
- package/utils.js +53 -26
package/art/fee.json
CHANGED
|
@@ -1,45 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
"contractName": "FeeManager",
|
|
3
|
-
"constructorInputs": [
|
|
4
|
-
{ "name": "authToken", "type": "bytes32" },
|
|
5
|
-
{ "name": "
|
|
6
|
-
{ "name": "feeToken", "type": "bytes32" },
|
|
7
|
-
{ "name": "defaultValue", "type": "int" }
|
|
8
|
-
],
|
|
9
|
-
"abi": [
|
|
10
|
-
{ "name": "close", "inputs": [] },
|
|
11
|
-
{ "name": "pay", "inputs": [] }
|
|
12
|
-
],
|
|
13
|
-
"bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_0 OP_BEGIN OP_DUP OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_DUP
|
|
14
|
-
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FeeManager: Validates fee payments and
|
|
15
|
-
"fingerprint": "
|
|
16
|
-
"debug": {
|
|
17
|
-
"bytecode": "
|
|
18
|
-
"sourceMap": "
|
|
19
|
-
"logs": [],
|
|
20
|
-
"requires": [
|
|
21
|
-
{ "ip": 17, "line":
|
|
22
|
-
{ "ip":
|
|
23
|
-
{ "ip":
|
|
24
|
-
{ "ip":
|
|
25
|
-
{ "ip":
|
|
26
|
-
{ "ip":
|
|
27
|
-
{ "ip":
|
|
28
|
-
{ "ip":
|
|
29
|
-
{ "ip":
|
|
30
|
-
{ "ip":
|
|
31
|
-
{ "ip":
|
|
32
|
-
{ "ip":
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"updatedAt": "2026-07-29T19:00:43.169Z"
|
|
1
|
+
{
|
|
2
|
+
"contractName": "FeeManager",
|
|
3
|
+
"constructorInputs": [
|
|
4
|
+
{ "name": "authToken", "type": "bytes32" },
|
|
5
|
+
{ "name": "defaultDestination", "type": "bytes" },
|
|
6
|
+
{ "name": "feeToken", "type": "bytes32" },
|
|
7
|
+
{ "name": "defaultValue", "type": "int" }
|
|
8
|
+
],
|
|
9
|
+
"abi": [
|
|
10
|
+
{ "name": "close", "inputs": [] },
|
|
11
|
+
{ "name": "pay", "inputs": [] }
|
|
12
|
+
],
|
|
13
|
+
"bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_0 OP_BEGIN OP_DUP OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXOUTPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_4 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_3 OP_SPLIT OP_DROP OP_1 OP_SPLIT OP_NIP 0004 OP_AND 0004 OP_EQUAL OP_IF OP_1 OP_ROT OP_DROP OP_SWAP OP_ENDIF OP_ENDIF OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXINPUTCOUNT OP_LESSTHAN OP_2 OP_PICK OP_NOT OP_BOOLAND OP_NOT OP_UNTIL OP_SWAP OP_VERIFY OP_2DROP OP_2DROP OP_2DROP OP_DROP OP_1 OP_ELSE OP_4 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_INPUTINDEX OP_UTXOBYTECODE OP_INPUTINDEX OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_DUP OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_4 OP_ROLL OP_EQUAL OP_IF OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_1 OP_SPLIT OP_OVER OP_1 OP_EQUAL OP_2 OP_PICK OP_2 OP_EQUAL OP_BOOLOR OP_VERIFY OP_OVER OP_1 OP_EQUAL OP_IF OP_DUP 20 OP_SPLIT OP_DUP OP_8 OP_SPLIT OP_DUP OP_SIZE OP_NIP OP_0 OP_GREATERTHAN OP_IF OP_6 OP_PICK OP_OUTPUTBYTECODE OP_OVER OP_EQUALVERIFY OP_ELSE OP_6 OP_PICK OP_OUTPUTBYTECODE OP_9 OP_PICK OP_EQUALVERIFY OP_ENDIF OP_OVER OP_BIN2NUM OP_4 OP_PICK OP_BIN2NUM OP_0 OP_NUMEQUAL OP_IF OP_7 OP_PICK OP_OUTPUTVALUE OP_OVER OP_NUMEQUALVERIFY OP_ELSE OP_7 OP_PICK OP_OUTPUTTOKENCATEGORY OP_5 OP_PICK OP_EQUALVERIFY OP_7 OP_PICK OP_OUTPUTTOKENAMOUNT OP_OVER OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_2DROP OP_DROP OP_ENDIF OP_2DROP OP_ELSE OP_DUP OP_OUTPUTBYTECODE OP_3 OP_PICK OP_EQUALVERIFY OP_DUP OP_OUTPUTVALUE OP_4 OP_PICK OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_2DROP OP_1 OP_ENDIF",
|
|
14
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FeeManager: Validates fee payments are paid and routed using the default payment or by using the fee NFT\r\n * \r\n * Every FundToken operation pays fees through this contract.\r\n * Validates that fees match their encoding and route to correct destination.\r\n * \r\n * Parameters:\r\n * authToken: Authorization token (controls who can close)\r\n * defaultDestination: Default destination locking bytecode\r\n * feeToken: Fee token category (with expected 'none' capability)\r\n * defaultValue: Default fee amount in satoshis\r\n */\r\ncontract FeeManager(bytes32 authToken, bytes defaultDestination, bytes32 feeToken, int defaultValue)\r\n{\r\n /**\r\n * close(): Close this fee by ensuring all fee tokens are burned\r\n * \r\n * Allows protocol steward to close the fees and cleanup stale UTXOs.\r\n * \r\n * Ensures:\r\n * - Transaction includes authToken with bit 0x0004 (fee close permission)\r\n * - All fee NFT tokens must be implicitly burned\r\n */\r\n function close() {\r\n //\r\n // Verify all fee tokens are implicitly burned\r\n int outputIndex = 0;\r\n do {\r\n require(tx.outputs[outputIndex].tokenCategory != feeToken);\r\n outputIndex = outputIndex + 1;\r\n } while(outputIndex < tx.outputs.length);\r\n\r\n //\r\n // Check for authorization token in any input\r\n // Bit 0x0004 in commitment indicates fee manager closure permission\r\n bool authorized = false;\r\n int inputIndex = 0;\r\n do {\r\n if(tx.inputs[inputIndex].tokenCategory == authToken) {\r\n if((bytes(tx.inputs[inputIndex].nftCommitment.slice(1, 3)) & bytes(0x0004)) == 0x0004) {\r\n authorized = true;\r\n }\r\n }\r\n inputIndex = inputIndex + 1;\r\n } while(inputIndex < tx.inputs.length && !authorized);\r\n require(authorized, \"unauthorized user\");\r\n }\r\n\r\n /**\r\n * pay(): Validate fee payment according to provided UTXO, default or use NFT commitment encoding\r\n * \r\n * Routes fee to destination encoded in fee token NFT or use default destination.\r\n * Supports two fee types: Bitcoin and CashTokens.\r\n * \r\n * Ensures:\r\n * - This UTXO returns to itself (no state change)\r\n * - Fee amount is at least the encoded amount (satoshis or token)\r\n * - Fee routes to correct destination (from encoding or default)\r\n * - Token category (if token fee) matches encoding\r\n */\r\n function pay() {\r\n //\r\n // Verify this input and UTXO is preserved\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n int feeOutput = this.activeInputIndex + 1;\r\n bytes thisInputCategory = tx.inputs[this.activeInputIndex].tokenCategory;\r\n require(thisInputCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n\r\n //\r\n // Check for encoded fee or default\r\n if(thisInputCategory == feeToken) {\r\n //\r\n // Check fee type to enforce or voluntary\r\n bytes fee_type, bytes fee_next0 = tx.inputs[this.activeInputIndex].nftCommitment.split(1);\r\n require(fee_type == 0x01 || fee_type == 0x02);\r\n\r\n if(fee_type == 0x01) {\r\n //\r\n // Continue reading from the NFT to enforce the encoded fee\r\n bytes fee_category, bytes fee_next1 = fee_next0.split(32);\r\n bytes fee_amount_bytes, bytes fee_destination = fee_next1.split(8);\r\n\r\n //\r\n // Verify fee routed to correct destination\r\n if(fee_destination.length > 0) {\r\n require(tx.outputs[feeOutput].lockingBytecode == fee_destination);\r\n } else {\r\n require(tx.outputs[feeOutput].lockingBytecode == defaultDestination);\r\n }\r\n\r\n //\r\n // Verify the fee is paid\r\n int fee_amount = int(fee_amount_bytes);\r\n if(int(fee_category) == 0) {\r\n require(tx.outputs[feeOutput].value == fee_amount);\r\n } else {\r\n require(tx.outputs[feeOutput].tokenCategory == fee_category);\r\n require(tx.outputs[feeOutput].tokenAmount == fee_amount);\r\n }\r\n }\r\n // fee_type == 0x02 has nothing to validate\r\n } else {\r\n //\r\n // Verify default route and at least default sats are paid\r\n require(tx.outputs[feeOutput].lockingBytecode == defaultDestination);\r\n require(tx.outputs[feeOutput].value == defaultValue);\r\n }\r\n }\r\n}\r\n",
|
|
15
|
+
"fingerprint": "b6c6b3601cb2ec247863e85f950bd87bb139a8db5b69cb0da161ed58fed4d905",
|
|
16
|
+
"debug": {
|
|
17
|
+
"bytecode": "5479009c63006576d15479879169768b7776c4a26600006576ce5479876376cf537f75517f77020004840200048763517b757c6868768b7776c39f5279919a91667c696d6d6d755167547a519dc0c7c0cd88c08bc0ce76c0d188547a8763c0cf517f785187527952879b69785187637601207f76587f76827700a0635679cd7888675679cd597988687881547981009c635779cc789d675779d15579885779d3789d686d6d75686d6776cd53798876cc54799d686d6d5168",
|
|
18
|
+
"sourceMap": "26:4:49:5;;;;;29:26:29:27;30:8:33:49;31:31:31:42;:20::57:1;:61::69:0;;:20:::1;;:12::71;32:26:32:37:0;:::41:1;:12::42;33:16:33:27:0;:30::47;30:8::49:1;;38:26:38:31:0;39:25:39:26;40:8:47:62;41:25:41:35;:15::50:1;:54::63:0;;:15:::1;:65:45:13:0;42:36:42:46;:26::61:1;:71::72:0;:26::73:1;;:68::69:0;:26::73:1;;:83::89:0;:20::90:1;:95::101:0;:19:::1;:103:44:17:0;43:33:43:37;:20::38:1;;;42:103:44:17;41:65:45:13;46:25:46:35:0;:::39:1;:12::40;47:16:47:26:0;:29::45;:16:::1;:50::60:0;;:49:::1;:16;40:8::62;;48:16:48:26:0;:8::49:1;26:21:49:5;;;;;:4;63::110::0;;;;66:26:66:47;:16::64:1;:79::100:0;:68::117:1;:8::119;67:24:67:45:0;:::49:1;68:44:68:65:0;:34::80:1;69:16:69:33:0;:48::69;:37::84:1;:8::86;73:32:73:40:0;;:11:::1;:42:104:9:0;76:56:76:77;:46::92:1;:99::100:0;:46::101:1;77:20:77:28:0;:32::36;:20:::1;:40::48:0;;:52::56;:40:::1;:20;:12::58;79:15:79:23:0;:27::31;:15:::1;:33:102:13:0;82:54:82:63;:70::72;:54::73:1;83:64:83::0;:80::81;:64::82:1;87:19:87:34:0;:::41:1;;:44::45:0;:19:::1;:47:89:17:0;88:39:88:48;;:28::65:1;:69::84:0;:20::86:1;89:23:91:17:0;90:39:90:48;;:28::65:1;:69::87:0;;:20::89:1;89:23:91:17;95:37:95:53:0;:33::54:1;96:23:96:35:0;;:19::36:1;:40::41:0;:19:::1;:43:98:17:0;97:39:97:48;;:28::55:1;:59::69:0;:20::71:1;98:23:101:17:0;99:39:99:48;;:28::63:1;:67::79:0;;:20::81:1;100:39:100:48:0;;:28::61:1;:65::75:0;:20::77:1;98:23:101:17;79:33:102:13;;;;73:42:104:9;104:15:109::0;107:31:107:40;:20::57:1;:61::79:0;;:12::81:1;108:31:108:40:0;:20::47:1;:51::63:0;;:12::65:1;104:15:109:9;63:19:110:5;;;15:0:111:1",
|
|
19
|
+
"logs": [],
|
|
20
|
+
"requires": [
|
|
21
|
+
{ "ip": 17, "line": 31 },
|
|
22
|
+
{ "ip": 66, "line": 48, "message": "unauthorized user" },
|
|
23
|
+
{ "ip": 81, "line": 66 },
|
|
24
|
+
{ "ip": 89, "line": 69 },
|
|
25
|
+
{ "ip": 106, "line": 77 },
|
|
26
|
+
{ "ip": 127, "line": 88 },
|
|
27
|
+
{ "ip": 134, "line": 90 },
|
|
28
|
+
{ "ip": 148, "line": 97 },
|
|
29
|
+
{ "ip": 155, "line": 99 },
|
|
30
|
+
{ "ip": 160, "line": 100 },
|
|
31
|
+
{ "ip": 172, "line": 107 },
|
|
32
|
+
{ "ip": 177, "line": 108 }
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"compiler": {
|
|
36
|
+
"name": "cashc",
|
|
37
|
+
"version": "0.13.0",
|
|
38
|
+
"options": {
|
|
39
|
+
"enforceFunctionParameterTypes": true,
|
|
40
|
+
"enforceLocktimeGuard": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"updatedAt": "2026-09-04T16:02:31.304Z"
|
|
45
44
|
}
|
package/art/fee_minter.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
"contractName": "FeeMinter",
|
|
3
|
-
"constructorInputs": [
|
|
4
|
-
{ "name": "authorization", "type": "bytes32" },
|
|
5
|
-
{ "name": "token", "type": "bytes32" },
|
|
6
|
-
{ "name": "
|
|
7
|
-
],
|
|
8
|
-
"abi": [
|
|
9
|
-
{ "name": "mint", "inputs": [] }
|
|
10
|
-
],
|
|
11
|
-
"bytecode": "OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_3 OP_SPLIT OP_DROP OP_1 OP_SPLIT OP_NIP 0010 OP_AND 0010 OP_EQUAL OP_IF OP_1 OP_ROT OP_DROP OP_SWAP OP_ENDIF OP_ENDIF OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXINPUTCOUNT OP_LESSTHAN OP_2 OP_PICK OP_NOT OP_BOOLAND OP_NOT OP_UNTIL OP_SWAP OP_VERIFY OP_0 OP_BEGIN OP_DUP OP_INPUTINDEX OP_NUMNOTEQUAL OP_OVER OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOT OP_BOOLAND OP_IF OP_DUP OP_OUTPUTTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_4 OP_PICK OP_EQUAL OP_IF OP_DUP OP_OUTPUTBYTECODE OP_5 OP_PICK OP_EQUALVERIFY OP_DUP OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_DUP OP_OUTPUTTOKENCOMMITMENT
|
|
12
|
-
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FeeMinter:
|
|
13
|
-
"fingerprint": "
|
|
14
|
-
"debug": {
|
|
15
|
-
"bytecode": "
|
|
16
|
-
"sourceMap": "
|
|
17
|
-
"logs": [],
|
|
18
|
-
"requires": [
|
|
19
|
-
{ "ip": 44, "line":
|
|
20
|
-
{ "ip": 70, "line":
|
|
21
|
-
{ "ip": 75, "line":
|
|
22
|
-
{ "ip":
|
|
23
|
-
{ "ip":
|
|
24
|
-
{ "ip":
|
|
25
|
-
{ "ip":
|
|
26
|
-
{ "ip":
|
|
27
|
-
{ "ip":
|
|
28
|
-
{ "ip":
|
|
29
|
-
{ "ip":
|
|
30
|
-
]
|
|
31
|
-
},
|
|
32
|
-
"compiler": {
|
|
33
|
-
"name": "cashc",
|
|
34
|
-
"version": "0.13.0",
|
|
35
|
-
"options": {
|
|
36
|
-
"enforceFunctionParameterTypes": true,
|
|
37
|
-
"enforceLocktimeGuard": true
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
"updatedAt": "2026-
|
|
1
|
+
{
|
|
2
|
+
"contractName": "FeeMinter",
|
|
3
|
+
"constructorInputs": [
|
|
4
|
+
{ "name": "authorization", "type": "bytes32" },
|
|
5
|
+
{ "name": "token", "type": "bytes32" },
|
|
6
|
+
{ "name": "next", "type": "bytes" }
|
|
7
|
+
],
|
|
8
|
+
"abi": [
|
|
9
|
+
{ "name": "mint", "inputs": [] }
|
|
10
|
+
],
|
|
11
|
+
"bytecode": "OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_3 OP_SPLIT OP_DROP OP_1 OP_SPLIT OP_NIP 0010 OP_AND 0010 OP_EQUAL OP_IF OP_1 OP_ROT OP_DROP OP_SWAP OP_ENDIF OP_ENDIF OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXINPUTCOUNT OP_LESSTHAN OP_2 OP_PICK OP_NOT OP_BOOLAND OP_NOT OP_UNTIL OP_SWAP OP_VERIFY OP_0 OP_BEGIN OP_DUP OP_INPUTINDEX OP_NUMNOTEQUAL OP_OVER OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOT OP_BOOLAND OP_IF OP_DUP OP_OUTPUTTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_4 OP_PICK OP_EQUAL OP_IF OP_DUP OP_OUTPUTBYTECODE OP_5 OP_PICK OP_EQUALVERIFY OP_DUP OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_DUP OP_OUTPUTTOKENCOMMITMENT OP_1 OP_SPLIT OP_OVER OP_1 OP_EQUAL OP_2 OP_PICK OP_2 OP_EQUAL OP_BOOLOR OP_VERIFY OP_OVER OP_1 OP_EQUAL OP_IF OP_DUP 20 OP_SPLIT OP_OVER OP_0 OP_EQUAL OP_NOT OP_VERIFY OP_DUP OP_8 OP_SPLIT OP_DROP OP_DUP OP_BIN2NUM OP_0 OP_GREATERTHAN OP_VERIFY OP_2DROP OP_DROP OP_ENDIF OP_OVER OP_2 OP_EQUAL OP_IF OP_DUP OP_0 OP_EQUALVERIFY OP_ENDIF OP_2DROP OP_ENDIF OP_ENDIF OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXOUTPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_DUP OP_5 OP_ROLL OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOBYTECODE OP_INPUTINDEX OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_INPUTINDEX OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY OP_2DROP OP_2DROP OP_1",
|
|
12
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FeeMinter: Vault to holding fee minting token with strict minting enforcement and authorization requirements\r\n * \r\n * Minting fee tokens provides a way to compose a flexible fee structure into the protocol.\r\n * Operations are authorized via token using commitment as permission bits.\r\n * \r\n * Parameters:\r\n * authorization: Token category that authorizes operations\r\n * token: Fee token category being minted\r\n * next: Destination for newly minted fee tokens (fee enforcement contract)\r\n */\r\ncontract FeeMinter(bytes32 authorization, bytes32 token, bytes next)\r\n{\r\n /**\r\n * mint(): Mints fee tokens with commitment encoding\r\n * \r\n * Acts as a vault for long term fee token holding.\r\n * Allows authorized fee token minting to FeeManager with NFT commitment structure validation.\r\n * Ensures this minting token UTXO is preserved\r\n *\r\n * Ensures:\r\n * - Action is authorized with permission bit 0x0010\r\n * - Enforces minted tokens destination to FeeManager\r\n * - Ensures proper NFT commitment structure (type and additional data as needed)\r\n */\r\n function mint() {\r\n //\r\n // Check for authorization token in ANY input\r\n // Bit 0x0010 in commitment indicates fee minting permission\r\n bool authorized = false;\r\n int inputIndex = 0;\r\n do {\r\n if(tx.inputs[inputIndex].tokenCategory == authorization) {\r\n if((bytes(tx.inputs[inputIndex].nftCommitment.slice(1, 3)) & bytes(0x0010)) == 0x0010) {\r\n authorized = true;\r\n }\r\n }\r\n inputIndex = inputIndex + 1;\r\n } while(inputIndex < tx.inputs.length && !authorized);\r\n require(authorized, \"unauthorized user\");\r\n\r\n //\r\n // Verify ALL outputs with locking bytecode enforcement\r\n int outputIndex = 0;\r\n do {\r\n if(outputIndex != this.activeInputIndex && tx.outputs[outputIndex].tokenCategory != 0x) {\r\n if(tx.outputs[outputIndex].tokenCategory.slice(0, 32) == token) {\r\n //\r\n // Validate minted token output including NFT commitment\r\n require(tx.outputs[outputIndex].lockingBytecode == next);\r\n require(tx.outputs[outputIndex].tokenCategory == token);\r\n\r\n bytes fee_type, bytes fee_next0 = tx.outputs[outputIndex].nftCommitment.split(1);\r\n require(fee_type == 0x01 || fee_type == 0x02); // minting type not allowed\r\n\r\n if(fee_type == 0x01) {\r\n bytes fee_category, bytes fee_next1 = fee_next0.split(32);\r\n require(fee_category != 0x);\r\n \r\n bytes fee_amount = fee_next1.slice(0, 8);\r\n require(int(fee_amount) > 0); // treat as int\r\n \r\n // Destination is optional (0x for default, or locking bytecode)\r\n }\r\n\r\n if(fee_type == 0x02) {\r\n require(fee_next0 == 0x); // No additional commitment needed\r\n }\r\n }\r\n }\r\n outputIndex = outputIndex + 1;\r\n } while(outputIndex < tx.outputs.length);\r\n\r\n\r\n //\r\n // Verify this is a fee minting token and the UTXO is preserved\r\n bytes thisInputCategory = tx.inputs[this.activeInputIndex].tokenCategory;\r\n require(thisInputCategory == (token + 0x02));\r\n require(thisInputCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment);\r\n }\r\n}",
|
|
13
|
+
"fingerprint": "ae77e726fa1107b1c446e9fa9c9591502b51c8260bfb77cb1ef9193d6e871ee6",
|
|
14
|
+
"debug": {
|
|
15
|
+
"bytecode": "00006576ce5379876376cf537f75517f77020010840200108763517b757c6868768b7776c39f5279919a91667c69006576c09e78d10087919a6376d101207f755479876376cd55798876d154798876d2517f785187527952879b69785187637601207f780087916976587f75768100a0696d756878528763760088686d6868768b7776c4a266c0ce76557a527e88c0d188c0c7c0cd88c0cfc0d2886d6d51",
|
|
16
|
+
"sourceMap": "32:26:32:31;33:25:33:26;34:8:41:62;35:25:35:35;:15::50:1;:54::67:0;;:15:::1;:69:39:13:0;36:36:36:46;:26::61:1;:71::72:0;:26::73:1;;:68::69:0;:26::73:1;;:83::89:0;:20::90:1;:95::101:0;:19:::1;:103:38:17:0;37:33:37:37;:20::38:1;;;36:103:38:17;35:69:39:13;40:25:40:35:0;:::39:1;:12::40;41:16:41:26:0;:29::45;:16:::1;:50::60:0;;:49:::1;:16;34:8::62;;42:16:42:26:0;:8::49:1;46:26:46:27:0;47:8:74:49;48:15:48:26;:30::51;:15:::1;:66::77:0;:55::92:1;:96::98:0;:55:::1;;:15;:100:72:13:0;49:30:49:41;:19::56:1;:66::68:0;:19::69:1;;:73::78:0;;:19:::1;:80:71:17:0;52:39:52:50;:28::67:1;:71::75:0;;:20::77:1;53:39:53:50:0;:28::65:1;:69::74:0;;:20::76:1;55:65:55::0;:54::91:1;:98::99:0;:54::100:1;56:28:56:36:0;:40::44;:28:::1;:48::56:0;;:60::64;:48:::1;:28;:20::66;58:23:58:31:0;:35::39;:23:::1;:41:66:21:0;59:62:59:71;:78::80;:62::81:1;60:32:60:44:0;:48::50;:32:::1;;:24::52;62:43:62::0;:62::63;:43::64:1;;63:36:63:46:0;:32::47:1;:50::51:0;:32:::1;:24::53;58:41:66:21;;;68:23:68:31:0;:35::39;:23:::1;:41:70:21:0;69:32:69:41;:45::47;:24::49:1;68:41:70:21;49:80:71:17;;48:100:72:13;73:26:73:37:0;:::41:1;:12::42;74:16:74:27:0;:30::47;47:8::49:1;;79:44:79:65:0;:34::80:1;80:16:80:33:0;:38::43;;:46::50;:38:::1;:8::53;81:48:81:69:0;:37::84:1;:8::86;82:26:82:47:0;:16::64:1;:79::100:0;:68::117:1;:8::119;83:26:83:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;28:20:84:5;;",
|
|
17
|
+
"logs": [],
|
|
18
|
+
"requires": [
|
|
19
|
+
{ "ip": 44, "line": 42, "message": "unauthorized user" },
|
|
20
|
+
{ "ip": 70, "line": 52 },
|
|
21
|
+
{ "ip": 75, "line": 53 },
|
|
22
|
+
{ "ip": 88, "line": 56 },
|
|
23
|
+
{ "ip": 100, "line": 60 },
|
|
24
|
+
{ "ip": 109, "line": 63 },
|
|
25
|
+
{ "ip": 119, "line": 69 },
|
|
26
|
+
{ "ip": 138, "line": 80 },
|
|
27
|
+
{ "ip": 141, "line": 81 },
|
|
28
|
+
{ "ip": 146, "line": 82 },
|
|
29
|
+
{ "ip": 151, "line": 83 }
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"compiler": {
|
|
33
|
+
"name": "cashc",
|
|
34
|
+
"version": "0.13.0",
|
|
35
|
+
"options": {
|
|
36
|
+
"enforceFunctionParameterTypes": true,
|
|
37
|
+
"enforceLocktimeGuard": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"updatedAt": "2026-09-04T16:02:32.964Z"
|
|
41
41
|
}
|
package/art/fund.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"contractName": "FundManager",
|
|
3
|
-
"constructorInputs": [
|
|
4
|
-
{ "name": "inflowToken", "type": "bytes32" },
|
|
5
|
-
{ "name": "outflowToken", "type": "bytes32" },
|
|
6
|
-
{ "name": "fundCategory", "type": "bytes" },
|
|
7
|
-
{ "name": "fundHash", "type": "bytes" }
|
|
8
|
-
],
|
|
9
|
-
"abi": [
|
|
10
|
-
{ "name": "mint", "inputs": [] },
|
|
11
|
-
{ "name": "redeem", "inputs": [] }
|
|
12
|
-
],
|
|
13
|
-
"bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_2 OP_SUB OP_DUP OP_UTXOTOKENCATEGORY OP_ROT OP_EQUAL OP_SWAP OP_UTXOTOKENCOMMITMENT
|
|
14
|
-
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FundManager (Fund):
|
|
15
|
-
"fingerprint": "
|
|
16
|
-
"debug": {
|
|
17
|
-
"bytecode": "
|
|
18
|
-
"sourceMap": "
|
|
19
|
-
"logs": [],
|
|
20
|
-
"requires": [
|
|
21
|
-
{ "ip":
|
|
22
|
-
{ "ip":
|
|
23
|
-
{ "ip":
|
|
24
|
-
{ "ip":
|
|
25
|
-
{ "ip":
|
|
26
|
-
{ "ip":
|
|
27
|
-
{ "ip":
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
"compiler": {
|
|
31
|
-
"name": "cashc",
|
|
32
|
-
"version": "0.13.0",
|
|
33
|
-
"options": {
|
|
34
|
-
"enforceFunctionParameterTypes": true,
|
|
35
|
-
"enforceLocktimeGuard": true
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
"updatedAt": "2026-
|
|
1
|
+
{
|
|
2
|
+
"contractName": "FundManager",
|
|
3
|
+
"constructorInputs": [
|
|
4
|
+
{ "name": "inflowToken", "type": "bytes32" },
|
|
5
|
+
{ "name": "outflowToken", "type": "bytes32" },
|
|
6
|
+
{ "name": "fundCategory", "type": "bytes" },
|
|
7
|
+
{ "name": "fundHash", "type": "bytes" }
|
|
8
|
+
],
|
|
9
|
+
"abi": [
|
|
10
|
+
{ "name": "mint", "inputs": [] },
|
|
11
|
+
{ "name": "redeem", "inputs": [] }
|
|
12
|
+
],
|
|
13
|
+
"bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_2 OP_SUB OP_DUP OP_UTXOTOKENCATEGORY OP_ROT OP_EQUAL OP_SWAP OP_UTXOTOKENCOMMITMENT OP_2 OP_4 OP_PICK OP_CAT OP_5 OP_ROLL OP_CAT OP_EQUAL OP_BOOLAND OP_DUP OP_IF OP_INPUTINDEX OP_1ADD OP_INPUTINDEX OP_UTXOBYTECODE OP_OVER OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_DUP OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUAL OP_OVER OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_BOOLOR OP_VERIFY OP_DROP OP_ENDIF OP_INPUTINDEX OP_1SUB OP_UTXOBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUAL OP_BOOLOR OP_NIP OP_NIP OP_NIP OP_ELSE OP_4 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_INPUTINDEX OP_2 OP_SUB OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_ROLL OP_EQUAL OP_SWAP OP_UTXOTOKENCOMMITMENT OP_2 OP_4 OP_PICK OP_CAT OP_5 OP_ROLL OP_CAT OP_EQUAL OP_BOOLAND OP_DUP OP_IF OP_INPUTINDEX OP_1ADD OP_INPUTINDEX OP_UTXOBYTECODE OP_OVER OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_DUP OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_DROP OP_ENDIF OP_INPUTINDEX OP_1SUB OP_DUP OP_UTXOBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUAL OP_DUP OP_ROT OP_UTXOTOKENCATEGORY OP_0 OP_EQUAL OP_NOT OP_BOOLAND OP_IF OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_ENDIF OP_BOOLOR OP_NIP OP_NIP OP_ENDIF",
|
|
14
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FundManager (Fund): Maintains a fund's tokens for minting and redemptions\r\n * \r\n * This contract maintains the supply of this fund's tokens.\r\n * Minting and redemption check for inflow/outflow token signals expecting a strict tx shape.\r\n * Efficient contract linking is used for scaling token inputs.\r\n * \r\n * Parameters:\r\n * inflowToken: Inflow token category (signals minting operation)\r\n * outflowToken: Outflow token category (signals redemption operation)\r\n * fundCategory: Fund token category held in this contract\r\n * fundHash: Hash of fund parameters (ensures correct fund)\r\n */\r\ncontract FundManager(bytes32 inflowToken, bytes32 outflowToken, bytes fundCategory, bytes fundHash)\r\n{ \r\n /**\r\n * mint(): Release fund tokens during inflow (user deposit)\r\n *\r\n * The heavy calculation work is deferred to the fund's TransactionManager (holder of the inflow/outflow tokens).\r\n * Look for the inflow token or another instance of this contract.\r\n * Ensure at least the leading contract is returned (i.e. at least one UTXO is returned for future redemptions).\r\n * \r\n * Ensures:\r\n * - Linked to a FundManager or inflow token at expected input\r\n * - At least one output is returned (ready for redeem)\r\n * - If tokens are returned, then the token is the fund's\r\n */\r\n function mint() {\r\n //\r\n // Check for inflow and verify it's for this fund\r\n int prevInflowInput = this.activeInputIndex - 2;\r\n bool prevInflow = tx.inputs[prevInflowInput].tokenCategory == inflowToken && tx.inputs[prevInflowInput].nftCommitment == bytes(0x02 + fundCategory + fundHash);\r\n if(prevInflow) {\r\n int nextOutput = this.activeInputIndex + 1;\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[nextOutput].lockingBytecode); // at least one UTXO is returned\r\n require(tx.outputs[nextOutput].tokenCategory == fundCategory || tx.outputs[nextOutput].tokenCategory == 0x);\r\n }\r\n\r\n //\r\n // Previous input must be the inflow token or another FundManager for this fund\r\n bool prevFundManager = tx.inputs[this.activeInputIndex - 1].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode;\r\n require(prevInflow || prevFundManager);\r\n }\r\n\r\n /**\r\n * redeem(): Collect fund tokens during outflow (user withdrawal)\r\n * \r\n * The heavy calculation work is deferred tot he fund's TransactionManager.\r\n * Look for the outflow token or another instance of this contract.\r\n *\r\n * Ensures:\r\n * - Link to a FundManager or outflow token at expected input\r\n * - At least one output is returned\r\n * - Tokens returned are the fund's tokens\r\n */\r\n function redeem() {\r\n //\r\n // Check for outflow and verify it's for this fund\r\n int prevInflowInput = this.activeInputIndex - 2;\r\n bool prevOutflow = tx.inputs[prevInflowInput].tokenCategory == outflowToken && tx.inputs[prevInflowInput].nftCommitment == bytes(0x02 + fundCategory + fundHash);\r\n if(prevOutflow) {\r\n int nextOutput = this.activeInputIndex + 1;\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[nextOutput].lockingBytecode);\r\n require(tx.outputs[nextOutput].tokenCategory == fundCategory);\r\n }\r\n\r\n //\r\n // Previous input must be outflow token or another FundManager for this fund\r\n int prevInput = this.activeInputIndex - 1;\r\n bool prevFundManager = tx.inputs[prevInput].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode;\r\n if(prevFundManager && tx.inputs[prevInput].tokenCategory != 0x) {\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == fundCategory);\r\n }\r\n require(prevOutflow || prevFundManager);\r\n }\r\n}",
|
|
15
|
+
"fingerprint": "0a5c0b42e80684b239f2b6aee964e6ac5646d94d684fedc9b1276632797d5d9d",
|
|
16
|
+
"debug": {
|
|
17
|
+
"bytecode": "5479009c63c0529476ce7b877ccf5254797e557a7e879a7663c08bc0c778cd8876d154798778d100879b697568c08cc7c0c7879b77777767547a519dc0529476ce537a877ccf5254797e557a7e879a7663c08bc0c778cd8876d15479887568c08c76c7c0c787767bce0087919a63c0ce547988689b777768",
|
|
18
|
+
"sourceMap": "30:4:45:5;;;;;33:30:33:51;:54::55;:30:::1;34:36:34:51:0;:26::66:1;:70::81:0;:26:::1;:95::110:0;:85::125:1;:135::139:0;:142::154;;:135:::1;:157::165:0;;:135:::1;:85::166;:26;35:11:35:21:0;:23:39:9;36:29:36:50;:::54:1;37:30:37:51:0;:20::68:1;:83::93:0;:72::110:1;:12::112;38:31:38:41:0;:20::56:1;:60::72:0;;:20:::1;:87::97:0;:76::112:1;:116::118:0;:76:::1;:20;:12::120;35:23:39:9;;43:41:43:62:0;:::66:1;:31::83;:97::118:0;:87::135:1;:31;44:8:44:47;30:20:45:5;;;:4;58::77::0;;;;61:30:61:51;:54::55;:30:::1;62:37:62:52:0;:27::67:1;:71::83:0;;:27:::1;:97::112:0;:87::127:1;:137::141:0;:144::156;;:137:::1;:159::167:0;;:137:::1;:87::168;:27;63:11:63:22:0;:24:67:9;64:29:64:50;:::54:1;65:30:65:51:0;:20::68:1;:83::93:0;:72::110:1;:12::112;66:31:66:41:0;:20::56:1;:60::72:0;;:12::74:1;63:24:67:9;;71::71:45:0;:::49:1;72:41:72:50:0;:31::67:1;:81::102:0;:71::119:1;:31;73:11:73:26:0;:40::49;:30::64:1;:68::70:0;:30:::1;;:11;:72:75:9:0;74:30:74:51;:20::66:1;:70::82:0;;:12::84:1;73:72:75:9;76:8:76:48;58:22:77:5;;16:0:78:1",
|
|
19
|
+
"logs": [],
|
|
20
|
+
"requires": [
|
|
21
|
+
{ "ip": 35, "line": 37 },
|
|
22
|
+
{ "ip": 46, "line": 38 },
|
|
23
|
+
{ "ip": 56, "line": 44 },
|
|
24
|
+
{ "ip": 91, "line": 65 },
|
|
25
|
+
{ "ip": 96, "line": 66 },
|
|
26
|
+
{ "ip": 118, "line": 74 },
|
|
27
|
+
{ "ip": 121, "line": 76 }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"compiler": {
|
|
31
|
+
"name": "cashc",
|
|
32
|
+
"version": "0.13.0",
|
|
33
|
+
"options": {
|
|
34
|
+
"enforceFunctionParameterTypes": true,
|
|
35
|
+
"enforceLocktimeGuard": true
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"updatedAt": "2026-09-04T16:02:30.208Z"
|
|
39
39
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contractName": "InstanceVault",
|
|
3
|
+
"constructorInputs": [
|
|
4
|
+
{ "name": "instance", "type": "bytes32" },
|
|
5
|
+
{ "name": "authorization", "type": "bytes32" }
|
|
6
|
+
],
|
|
7
|
+
"abi": [
|
|
8
|
+
{ "name": "burn", "inputs": [] },
|
|
9
|
+
{ "name": "update", "inputs": [] },
|
|
10
|
+
{ "name": "proof", "inputs": [] },
|
|
11
|
+
{ "name": "data", "inputs": [] }
|
|
12
|
+
],
|
|
13
|
+
"bytecode": "OP_2 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_OVER OP_1 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_4 OP_SPLIT OP_NIP 20 OP_SPLIT OP_TXINPUTCOUNT OP_INPUTINDEX OP_2 OP_ADD OP_GREATERTHANOREQUAL OP_VERIFY OP_INPUTINDEX OP_1ADD OP_DUP OP_UTXOBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_OVER OP_SWAP OP_UTXOTOKENCOMMITMENT OP_CAT OP_HASH256 OP_ROT OP_EQUALVERIFY OP_0 OP_BEGIN OP_DUP OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOTIF OP_DUP OP_OUTPUTTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_3 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_ENDIF OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXOUTPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_6 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_3 OP_SPLIT OP_DROP OP_1 OP_SPLIT OP_NIP 0040 OP_AND 0040 OP_EQUAL OP_IF OP_1 OP_ROT OP_DROP OP_SWAP OP_ENDIF OP_ENDIF OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXINPUTCOUNT OP_LESSTHAN OP_2 OP_PICK OP_NOT OP_BOOLAND OP_NOT OP_UNTIL OP_SWAP OP_VERIFY OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ELSE OP_2 OP_PICK OP_1 OP_NUMEQUAL OP_IF OP_TXINPUTCOUNT OP_INPUTINDEX OP_2 OP_ADD OP_GREATERTHANOREQUAL OP_VERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_SWAP OP_1 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_1 OP_SPLIT OP_1 OP_SPLIT OP_NIP OP_INPUTINDEX OP_OUTPUTTOKENCOMMITMENT OP_1 OP_SPLIT OP_SWAP OP_3 OP_ROLL OP_EQUALVERIFY OP_1 OP_SPLIT OP_SWAP 00 OP_EQUAL OP_NOT OP_VERIFY OP_OVER OP_EQUALVERIFY OP_2 OP_SPLIT OP_NIP 20 OP_SPLIT OP_INPUTINDEX OP_1ADD OP_DUP OP_UTXOBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_DUP OP_UTXOBYTECODE OP_OVER OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_DUP OP_UTXOTOKENCATEGORY OP_OVER OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_DUP OP_UTXOTOKENCOMMITMENT OP_OVER OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY OP_OVER OP_SWAP OP_UTXOTOKENCOMMITMENT OP_CAT OP_HASH256 OP_ROT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOBYTECODE OP_INPUTINDEX OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUAL OP_NIP OP_NIP OP_NIP OP_ELSE OP_2 OP_PICK OP_2 OP_NUMEQUAL OP_IF OP_TXINPUTCOUNT OP_INPUTINDEX OP_2 OP_ADD OP_GREATERTHANOREQUAL OP_VERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_SWAP OP_1 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_4 OP_SPLIT OP_NIP 20 OP_SPLIT OP_INPUTINDEX OP_1ADD OP_DUP OP_UTXOBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_DUP OP_UTXOBYTECODE OP_OVER OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_DUP OP_UTXOTOKENCATEGORY OP_OVER OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_DUP OP_UTXOTOKENCOMMITMENT OP_OVER OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY OP_OVER OP_SWAP OP_UTXOTOKENCOMMITMENT OP_CAT OP_HASH256 OP_ROT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOBYTECODE OP_INPUTINDEX OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_INPUTINDEX OP_OUTPUTTOKENCOMMITMENT OP_EQUAL OP_NIP OP_NIP OP_NIP OP_ELSE OP_ROT OP_3 OP_NUMEQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_INPUTINDEX OP_1SUB OP_INPUTINDEX OP_OUTPOINTTXHASH OP_OVER OP_OUTPOINTTXHASH OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOBYTECODE OP_SWAP OP_UTXOBYTECODE OP_EQUAL OP_NIP OP_ENDIF OP_ENDIF OP_ENDIF",
|
|
14
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * InstanceVault: Holding vault for encoded FundToken contract parameters and on-chain proofs\r\n * \r\n * Vault to store on-chain the instance parameters into a NFT token commitment.\r\n * Enables on-chain parameter validation and ensures contract parameters are always recoverable.\r\n * \r\n * Parameters:\r\n * instance: Token category of the holding NFT\r\n * authorization: Token category that authorizes sensitive operations\r\n */\r\ncontract InstanceVault(bytes32 instance, bytes32 authorization)\r\n{\r\n /**\r\n * burn(): Burn the tokens to signal this instance is no longer being used or maintained\r\n * \r\n * Validates and aggregates all instance NFT commitment data, verifies the hash,\r\n * confirms authorization (bit 0x0040), and burns all instance tokens from outputs.\r\n * \r\n * Notes:\r\n * - Instance parameters data can be recovered from transaction history even after burning\r\n * - Intended to signal for rare closure event only\r\n * - Requires authorization token with bit 0x0040 (instance deprecation permission)\r\n * \r\n * Ensures:\r\n * - Both inputs must be processed sequentially\r\n * - Authorization token (bit 0x0040) must be present somewhere in the transaction\r\n * - No instance tokens allowed in any output (complete token burn)\r\n */\r\n function burn() {\r\n // ensure only spends for expected token\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == (instance + 0x01));\r\n // build data\r\n bytes hashAndData = tx.inputs[this.activeInputIndex].nftCommitment.split(4)[1]; // skip validating meta\r\n bytes hash, bytes data = hashAndData.split(32);\r\n require(tx.inputs.length >= this.activeInputIndex + 2);\r\n int dataIndex = this.activeInputIndex + 1;\r\n require(tx.inputs[dataIndex].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode, 'Data input must spend from this lockingBytecode');\r\n data = data + tx.inputs[dataIndex].nftCommitment;\r\n\r\n // hash and verify\r\n require(hash256(data) == hash, 'Encoded hash and calculated hash must match'); \r\n\r\n // burn the tokens\r\n int outputIndex = 0;\r\n do {\r\n if(tx.outputs[outputIndex].tokenCategory != 0x) {\r\n require(tx.outputs[outputIndex].tokenCategory.split(32)[0] != instance);\r\n }\r\n outputIndex = outputIndex + 1;\r\n } while(outputIndex < tx.outputs.length);\r\n\r\n\r\n // check for authorization\r\n bool authorized = false;\r\n int inputIndex = 0;\r\n do {\r\n if(tx.inputs[inputIndex].tokenCategory == authorization) {\r\n if((bytes(tx.inputs[inputIndex].nftCommitment.slice(1, 3)) & bytes(0x0040)) == 0x0040) {\r\n authorized = true;\r\n }\r\n }\r\n inputIndex = inputIndex + 1;\r\n } while(inputIndex < tx.inputs.length && !authorized);\r\n require(authorized);\r\n }\r\n \r\n /**\r\n * update(): Updates state of instance composition on-chain\r\n * \r\n * Validates that both instance inputs are properly preserved (allowing limited changes).\r\n * Aggregate NFT commitment data and verify the hash matches expected state.\r\n * \r\n * Ensures:\r\n * - Active input (this UTXO) must be preserved allowing for a state change\r\n * - The next input must be in matching criteria\r\n * - Commitment data has mutable limitations\r\n */\r\n function update() {\r\n //\r\n // Check the starting input (and shortcut input validation because we know the length)\r\n require(tx.inputs.length >= this.activeInputIndex + 2);\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == (instance + 0x01));\r\n\r\n //\r\n // Start splitting the input token and validate against the mutable output\r\n bytes type, bytes stateAndRest = tx.inputs[this.activeInputIndex].nftCommitment.split(1);\r\n bytes versionAndRest = stateAndRest.split(1)[1]; // No need to validate the input's type or state\r\n bytes o_type, bytes o_stateAndRest = tx.outputs[this.activeInputIndex].nftCommitment.split(1);\r\n require(o_type == type); // type cannot change\r\n bytes o_state, bytes o_versionAndRest = o_stateAndRest.split(1);\r\n require(o_state != 0x00); // state can change\r\n require(o_versionAndRest == versionAndRest); // library version, hash, and data cannot change\r\n\r\n //\r\n // Build data and verify against the hash\r\n // Only two tokens expected, verify the next token is a data token w/ token preservation\r\n bytes hash, bytes data = versionAndRest.split(2)[1].split(32);\r\n\r\n int dataIndex = this.activeInputIndex + 1;\r\n require(tx.inputs[dataIndex].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode, 'Data input must spend from this lockingBytecode');\r\n require(tx.inputs[dataIndex].lockingBytecode == tx.outputs[dataIndex].lockingBytecode, 'Data input must be preserved');\r\n require(tx.inputs[dataIndex].tokenCategory == tx.outputs[dataIndex].tokenCategory, 'Data input category must be preserved');\r\n require(tx.inputs[dataIndex].nftCommitment == tx.outputs[dataIndex].nftCommitment, 'Data input commitment must be preserved');\r\n\r\n //\r\n // Concate the data and verify the hash\r\n data = data + tx.inputs[dataIndex].nftCommitment;\r\n require(hash256(data) == hash, 'Encoded hash and calculated hash must match');\r\n\r\n //\r\n // return input to contract\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode, 'This input must be preserved');\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory, 'This input category must be preserved');\r\n }\r\n\r\n /**\r\n * proof(): Proves instance composition on-chain\r\n * \r\n * Validates that both instance inputs are properly forwarded to matching outputs.\r\n * Aggregate NFT commitment data and verify the hash matches expected state.\r\n * \r\n * Ensures:\r\n * - Active input (this UTXO) must forward without change\r\n * - The next input must be in matching criteria\r\n * - Commitment data is immutable (same in/out)\r\n */\r\n function proof() {\r\n //\r\n // Check the starting input (and shortcut input validation because we know the length)\r\n require(tx.inputs.length >= this.activeInputIndex + 2);\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == (instance + 0x01));\r\n\r\n //\r\n // Start splitting this token and validate data as we go\r\n bytes hashAndData = tx.inputs[this.activeInputIndex].nftCommitment.split(4)[1]; // no need to validate meta here, it's for external tooling signaling\r\n bytes hash, bytes data = hashAndData.split(32);\r\n\r\n //\r\n // Only two tokens expected, verify the next token is a data token w/ token preservation\r\n int dataIndex = this.activeInputIndex + 1;\r\n require(tx.inputs[dataIndex].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode, 'Data input must spend from this lockingBytecode');\r\n require(tx.inputs[dataIndex].lockingBytecode == tx.outputs[dataIndex].lockingBytecode, 'Data input must be preserved');\r\n require(tx.inputs[dataIndex].tokenCategory == tx.outputs[dataIndex].tokenCategory, 'Data input category must be preserved');\r\n require(tx.inputs[dataIndex].nftCommitment == tx.outputs[dataIndex].nftCommitment, 'Data input commitment must be preserved');\r\n\r\n //\r\n // Concate the data and verify the hash\r\n data = data + tx.inputs[dataIndex].nftCommitment;\r\n require(hash256(data) == hash, 'Encoded hash and calculated hash must match');\r\n\r\n //\r\n // return input to contract\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode, 'This input must be preserved');\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory, 'This input category must be preserved');\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment, 'This input commitment must be preserved');\r\n }\r\n\r\n /**\r\n * data(): Validates commitment data continuity in the proof chain\r\n * \r\n * Ensures this UTXO is part of a continuous chain of instance proofs.\r\n * \r\n * Ensures\r\n * - This input must contain instance token\r\n * - Previous input must have identical locking bytecode\r\n * - Previous input must have identical token category\r\n * - Previous input must be from the same transaction (outpointTransactionHash)\r\n */\r\n function data() {\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == instance);\r\n int prevInput = this.activeInputIndex - 1;\r\n require(tx.inputs[this.activeInputIndex].outpointTransactionHash == tx.inputs[prevInput].outpointTransactionHash);\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.inputs[prevInput].lockingBytecode);\r\n }\r\n}",
|
|
15
|
+
"fingerprint": "cf8186ec90c2f25f859ddd395038436e46b75109ea567d7fd07d1ede1ef88a54",
|
|
16
|
+
"debug": {
|
|
17
|
+
"bytecode": "5279009c63c0ce78517e88c0cf547f7701207fc3c05293a269c08b76c7c0c788787ccf7eaa7b88006576d100876476d101207f75537987916968768b7776c4a26600006576ce5679876376cf537f75517f77020040840200408763517b757c6868768b7776c39f5279919a91667c696d6d6d51675279519c63c3c05293a269c0ce7c517e88c0cf517f517f77c0d2517f7c537a88517f7c01008791697888527f7701207fc08b76c7c0c78876c778cd8876ce78d18876cf78d288787ccf7eaa7b88c0c7c0cd88c0cec0d187777777675279529c63c3c05293a269c0ce7c517e88c0cf547f7701207fc08b76c7c0c78876c778cd8876ce78d18876cf78d288787ccf7eaa7b88c0c7c0cd88c0cec0d188c0cfc0d287777777677b539dc0ce88c08cc0c878c888c0c77cc78777686868",
|
|
18
|
+
"sourceMap": "31:4:67:5;;;;;33:26:33:47;:16::62:1;:67::75:0;:78::82;:67:::1;:8::85;35:38:35:59:0;:28::74:1;:81::82:0;:28::83:1;:::86;36:51:36:53:0;:33::54:1;37:16:37:32:0;:36::57;:60::61;:36:::1;:16;:8::63;38:24:38:45:0;:::49:1;39:26:39:35:0;:16::52:1;:66::87:0;:56::104:1;:8::157;40:15:40:19:0;:32::41;:22::56:1;:15;43:16:43:29;:33::37:0;:8::86:1;46:26:46:27:0;47:8:52:49;48:26:48:37;:15::52:1;:56::58:0;:15:::1;::50:13:0;49:35:49:46;:24::61:1;:68::70:0;:24::71:1;:::74;:78::86:0;;:24:::1;;:16::88;48:60:50:13;51:26:51:37:0;:::41:1;:12::42;52:16:52:27:0;:30::47;47:8::49:1;;56:26:56:31:0;57:25:57:26;58:8:65:62;59:25:59:35;:15::50:1;:54::67:0;;:15:::1;:69:63:13:0;60:36:60:46;:26::61:1;:71::72:0;:26::73:1;;:68::69:0;:26::73:1;;:83::89:0;:20::90:1;:95::101:0;:19:::1;:103:62:17:0;61:33:61:37;:20::38:1;;;60:103:62:17;59:69:63:13;64:25:64:35:0;:::39:1;:12::40;65:16:65:26:0;:29::45;:16:::1;:50::60:0;;:49:::1;:16;58:8::62;;66:16:66:26:0;:8::28:1;31:20:67:5;;;;:4;80::116::0;;;;;83:16:83:32;:36::57;:60::61;:36:::1;:16;:8::63;84:26:84:47:0;:16::62:1;:67::75:0;:78::82;:67:::1;:8::85;88:51:88:72:0;:41::87:1;:94::95:0;:41::96:1;89:50:89:51:0;:31::52:1;:::55;90:56:90:77:0;:45::92:1;:99::100:0;:45::101:1;91:16:91:22:0;:26::30;;:8::32:1;92:69:92:70:0;:48::71:1;93:16:93:23:0;:27::31;:16:::1;;:8::33;94:36:94:50:0;:8::52:1;99:54:99:55:0;:33::56:1;:::59;:66::68:0;:33::69:1;101:24:101:45:0;:::49:1;102:26:102:35:0;:16::52:1;:66::87:0;:56::104:1;:8::157;103:26:103:35:0;:16::52:1;:67::76:0;:56::93:1;:8::127;104:26:104:35:0;:16::50:1;:65::74:0;:54::89:1;:8::132;105:26:105:35:0;:16::50:1;:65::74:0;:54::89:1;:8::134;109:15:109:19:0;:32::41;:22::56:1;:15;110:16:110:29;:33::37:0;:8::86:1;114:26:114:47:0;:16::64:1;:79::100:0;:68::117:1;:8::151;115:26:115:47:0;:16::62:1;:77::98:0;:66::113:1;:8::156;80:22:116:5;;;:4;129::158::0;;;;;132:16:132:32;:36::57;:60::61;:36:::1;:16;:8::63;133:26:133:47:0;:16::62:1;:67::75:0;:78::82;:67:::1;:8::85;137:38:137:59:0;:28::74:1;:81::82:0;:28::83:1;:::86;138:51:138:53:0;:33::54:1;142:24:142:45:0;:::49:1;143:26:143:35:0;:16::52:1;:66::87:0;:56::104:1;:8::157;144:26:144:35:0;:16::52:1;:67::76:0;:56::93:1;:8::127;145:26:145:35:0;:16::50:1;:65::74:0;:54::89:1;:8::132;146:26:146:35:0;:16::50:1;:65::74:0;:54::89:1;:8::134;150:15:150:19:0;:32::41;:22::56:1;:15;151:16:151:29;:33::37:0;:8::86:1;155:26:155:47:0;:16::64:1;:79::100:0;:68::117:1;:8::151;156:26:156:47:0;:16::62:1;:77::98:0;:66::113:1;:8::156;157:26:157:47:0;:16::62:1;:77::98:0;:66::113:1;:8::158;129:21:158:5;;;:4;171::176::0;;;172:26:172:47;:16::62:1;:8::76;173:24:173:45:0;:::49:1;174:26:174:47:0;:16::72:1;:86::95:0;:76::120:1;:8::122;175:26:175:47:0;:16::64:1;:78::87:0;:68::104:1;:8::106;171:20:176:5;13:0:177:1;;",
|
|
19
|
+
"logs": [],
|
|
20
|
+
"requires": [
|
|
21
|
+
{ "ip": 12, "line": 33 },
|
|
22
|
+
{ "ip": 25, "line": 37 },
|
|
23
|
+
{ "ip": 32, "line": 39, "message": "Data input must spend from this lockingBytecode" },
|
|
24
|
+
{ "ip": 39, "line": 43, "message": "Encoded hash and calculated hash must match" },
|
|
25
|
+
{ "ip": 56, "line": 49 },
|
|
26
|
+
{ "ip": 106, "line": 66 },
|
|
27
|
+
{ "ip": 122, "line": 83 },
|
|
28
|
+
{ "ip": 128, "line": 84 },
|
|
29
|
+
{ "ip": 143, "line": 91 },
|
|
30
|
+
{ "ip": 150, "line": 93 },
|
|
31
|
+
{ "ip": 152, "line": 94 },
|
|
32
|
+
{ "ip": 164, "line": 102, "message": "Data input must spend from this lockingBytecode" },
|
|
33
|
+
{ "ip": 169, "line": 103, "message": "Data input must be preserved" },
|
|
34
|
+
{ "ip": 174, "line": 104, "message": "Data input category must be preserved" },
|
|
35
|
+
{ "ip": 179, "line": 105, "message": "Data input commitment must be preserved" },
|
|
36
|
+
{ "ip": 186, "line": 110, "message": "Encoded hash and calculated hash must match" },
|
|
37
|
+
{ "ip": 191, "line": 114, "message": "This input must be preserved" },
|
|
38
|
+
{ "ip": 197, "line": 115, "message": "This input category must be preserved" },
|
|
39
|
+
{ "ip": 211, "line": 132 },
|
|
40
|
+
{ "ip": 217, "line": 133 },
|
|
41
|
+
{ "ip": 231, "line": 143, "message": "Data input must spend from this lockingBytecode" },
|
|
42
|
+
{ "ip": 236, "line": 144, "message": "Data input must be preserved" },
|
|
43
|
+
{ "ip": 241, "line": 145, "message": "Data input category must be preserved" },
|
|
44
|
+
{ "ip": 246, "line": 146, "message": "Data input commitment must be preserved" },
|
|
45
|
+
{ "ip": 253, "line": 151, "message": "Encoded hash and calculated hash must match" },
|
|
46
|
+
{ "ip": 258, "line": 155, "message": "This input must be preserved" },
|
|
47
|
+
{ "ip": 263, "line": 156, "message": "This input category must be preserved" },
|
|
48
|
+
{ "ip": 269, "line": 157, "message": "This input commitment must be preserved" },
|
|
49
|
+
{ "ip": 278, "line": 172 },
|
|
50
|
+
{ "ip": 285, "line": 174 },
|
|
51
|
+
{ "ip": 291, "line": 175 }
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"compiler": {
|
|
55
|
+
"name": "cashc",
|
|
56
|
+
"version": "0.13.0",
|
|
57
|
+
"options": {
|
|
58
|
+
"enforceFunctionParameterTypes": true,
|
|
59
|
+
"enforceLocktimeGuard": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"updatedAt": "2026-09-04T16:02:33.438Z"
|
|
63
|
+
}
|