@fundtokens/builders 0.1.0-rc10

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.
@@ -0,0 +1,82 @@
1
+ {
2
+ "contractName": "FeeMinter",
3
+ "constructorInputs": [
4
+ {
5
+ "name": "authorization",
6
+ "type": "bytes32"
7
+ },
8
+ {
9
+ "name": "token",
10
+ "type": "bytes32"
11
+ },
12
+ {
13
+ "name": "destination",
14
+ "type": "bytes"
15
+ }
16
+ ],
17
+ "abi": [
18
+ {
19
+ "name": "mint",
20
+ "inputs": []
21
+ }
22
+ ],
23
+ "bytecode": "OP_INPUTINDEX OP_UTXOTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_2 OP_PICK 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_EQUALVERIFY OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_16 OP_AND OP_16 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_SIZE OP_NIP 28 OP_GREATERTHANOREQUAL OP_VERIFY OP_DUP OP_OUTPUTTOKENCOMMITMENT 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_ENDIF OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXOUTPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_2DROP OP_2DROP OP_DROP OP_1",
24
+ "source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FeeMinter: Authorization-controlled fee token minting with encoded fee commitments\r\n * \r\n * Similar to SimpleMinter, but allows encoding fee details in NFT commitments.\r\n * Fee tokens carry commitment data: [category | amount | destination_override]\r\n * Used to create tokens that encode specific fee amounts and routing.\r\n * \r\n * Destination override is variable-length by design - allows forward compatibility\r\n * with future address formats or multisig patterns without redeploying contract.\r\n *\r\n * Fee minting is a tool to ensure the FundToken system has robust fee options.\r\n * Downstream \"default\" values ensure fees are capped to a Bitcoin amount:\r\n * This ensures user protection from excessive or malicious fee extraction\r\n * Users are guaranteed access to their funds\r\n * Maintainer is incentivized to provide well-formed and competitive fee options\r\n * \r\n * Parameters:\r\n * authorization: Token category that authorizes minting\r\n * token: Fee token category being minted\r\n * destination: Default destination for fee payments\r\n */\r\ncontract FeeMinter(bytes32 authorization, bytes32 token, bytes destination)\r\n{\r\n /**\r\n * mint(): Mints fee tokens with commitment encoding\r\n * \r\n * Fee commitment format: [fee_category (32) | fee_amount (8) | destination_override (0+)]\r\n * - Category: 0x00 = Bitcoin satoshis, else = token category\r\n * - Amount: int64 LE integer\r\n * - Destination: Optional locking bytecode override for fee routing\r\n */\r\n function mint() {\r\n // Verify this is a fee minting token\r\n require(tx.inputs[this.activeInputIndex].tokenCategory.slice(0, 32) == token);\r\n\r\n // Contract returns to itself (locked pattern)\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n \r\n // Keep the fee minting token\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n \r\n // Preserve NFT commitment (minting capability)\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment);\r\n\r\n // Require owner 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((tx.inputs[inputIndex].nftCommitment & bytes(0x10)) == 0x10) {\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 // Verify all outputs minting fee tokens have proper commitment\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 // Fee tokens go to the destination\r\n require(tx.outputs[outputIndex].lockingBytecode == destination);\r\n require(tx.outputs[outputIndex].tokenCategory == token);\r\n\r\n // Fee token MUST have a commitment encoding fee details\r\n require(tx.outputs[outputIndex].nftCommitment.length >= 40, \"must provide at least a category and amount\");\r\n\r\n // Parse commitment: [fee_category | fee_amount | destination_override]\r\n bytes fee_category, bytes fee_next1 = tx.outputs[outputIndex].nftCommitment.split(32);\r\n // Fee category must be specified (0x00 for Bitcoin, or token category)\r\n require(fee_category != 0x);\r\n \r\n bytes fee_amount = fee_next1.slice(0, 8);\r\n // Fee amount must be positive\r\n require(int(fee_amount) > 0);\r\n // Destination is optional (0x for default, or locking bytecode)\r\n }\r\n }\r\n outputIndex = outputIndex + 1;\r\n } while(outputIndex < tx.outputs.length);\r\n }\r\n}",
25
+ "debug": {
26
+ "bytecode": "c0ce01207f75527988c0c7c0cd88c0cec0d188c0cfc0d28800006576ce5379876376cf6084608763517b757c6868768b7776c39f5279919a91667c69006576c09e78d10087919a6376d101207f755479876376cd55798876d154798876d282770128a26976d201207f780087916976587f75768100a0696d756868768b7776c4a2666d6d7551",
27
+ "sourceMap": "36:26:36:47;:16::62:1;:72::74:0;:16::75:1;;:79::84:0;;:8::86:1;39:26:39:47:0;:16::64:1;:79::100:0;:68::117:1;:8::119;42:26:42:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;45:26:45:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;48:26:48:31:0;49:25:49:26;50:8:57:62;51:25:51:35;:15::50:1;:54::67:0;;:15:::1;:69:55:13:0;52:30:52:40;:20::55:1;:64::68:0;:20::69:1;:74::78:0;:19:::1;:80:54:17:0;53:33:53:37;:20::38:1;;;52:80:54:17;51:69:55:13;56:25:56:35:0;:::39:1;:12::40;57:16:57:26:0;:29::45;:16:::1;:50::60:0;;:49:::1;:16;50:8::62;;58:16:58:26:0;:8::49:1;61:26:61:27:0;62:8:84:49;63:15:63:26;:30::51;:15:::1;:66::77:0;:55::92:1;:96::98:0;:55:::1;;:15;:100:82:13:0;64:30:64:41;:19::56:1;:66::68:0;:19::69:1;;:73::78:0;;:19:::1;:80:81:17:0;66:39:66:50;:28::67:1;:71::82:0;;:20::84:1;67:39:67:50:0;:28::65:1;:69::74:0;;:20::76:1;70:39:70:50:0;:28::65:1;:::72;;:76::78:0;:28:::1;:20::127;73:69:73:80:0;:58::95:1;:102::104:0;:58::105:1;75:28:75:40:0;:44::46;:28:::1;;:20::48;77:39:77::0;:58::59;:39::60:1;;79:32:79:42:0;:28::43:1;:46::47:0;:28:::1;:20::49;64:80:81:17;;;63:100:82:13;83:26:83:37:0;:::41:1;:12::42;84:16:84:27:0;:30::47;62:8::49:1;;34:4:85:5;;;",
28
+ "logs": [],
29
+ "requires": [
30
+ {
31
+ "ip": 10,
32
+ "line": 36
33
+ },
34
+ {
35
+ "ip": 15,
36
+ "line": 39
37
+ },
38
+ {
39
+ "ip": 20,
40
+ "line": 42
41
+ },
42
+ {
43
+ "ip": 25,
44
+ "line": 45
45
+ },
46
+ {
47
+ "ip": 61,
48
+ "line": 58,
49
+ "message": "unauthorized user"
50
+ },
51
+ {
52
+ "ip": 87,
53
+ "line": 66
54
+ },
55
+ {
56
+ "ip": 92,
57
+ "line": 67
58
+ },
59
+ {
60
+ "ip": 99,
61
+ "line": 70,
62
+ "message": "must provide at least a category and amount"
63
+ },
64
+ {
65
+ "ip": 108,
66
+ "line": 75
67
+ },
68
+ {
69
+ "ip": 117,
70
+ "line": 79
71
+ }
72
+ ]
73
+ },
74
+ "compiler": {
75
+ "name": "cashc",
76
+ "version": "0.13.0-next.6",
77
+ "options": {
78
+ "enforceFunctionParameterTypes": true
79
+ }
80
+ },
81
+ "updatedAt": "2026-05-06T19:09:29.081Z"
82
+ }
package/art/fund.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "contractName": "FundManager",
3
+ "constructorInputs": [
4
+ {
5
+ "name": "inflowToken",
6
+ "type": "bytes32"
7
+ },
8
+ {
9
+ "name": "outflowToken",
10
+ "type": "bytes32"
11
+ },
12
+ {
13
+ "name": "fundCategory",
14
+ "type": "bytes"
15
+ },
16
+ {
17
+ "name": "fundHash",
18
+ "type": "bytes"
19
+ }
20
+ ],
21
+ "abi": [
22
+ {
23
+ "name": "mint",
24
+ "inputs": []
25
+ },
26
+ {
27
+ "name": "redeem",
28
+ "inputs": []
29
+ }
30
+ ],
31
+ "bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_2 OP_SUB OP_UTXOTOKENCATEGORY OP_EQUAL OP_INPUTINDEX OP_2 OP_SUB OP_UTXOTOKENCOMMITMENT OP_3 OP_PICK OP_5 OP_ROLL OP_CAT OP_EQUAL OP_BOOLAND OP_DUP OP_IF OP_INPUTINDEX OP_UTXOBYTECODE OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOTIF OP_INPUTINDEX OP_1ADD OP_OUTPUTTOKENCATEGORY OP_3 OP_PICK OP_EQUALVERIFY OP_ENDIF 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_UTXOTOKENCATEGORY OP_ROT OP_EQUAL OP_INPUTINDEX OP_2 OP_SUB OP_UTXOTOKENCOMMITMENT OP_3 OP_PICK OP_5 OP_ROLL OP_CAT OP_EQUAL OP_BOOLAND OP_DUP OP_IF OP_INPUTINDEX OP_UTXOBYTECODE OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_OUTPUTTOKENCATEGORY OP_3 OP_PICK OP_EQUALVERIFY OP_ENDIF OP_INPUTINDEX OP_1SUB OP_UTXOBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUAL OP_DUP OP_INPUTINDEX OP_1SUB 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",
32
+ "source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FundManager (Fund): Holds and releases fund tokens\r\n * \r\n * Core accounting contract for fund tokens. Maintains the supply of fund tokens\r\n * and enforces token conservation during inflow/outflow operations.\r\n * \r\n * During mint (inflow/deposit):\r\n * - Input has fund tokens (current supply)\r\n * - Output has fewer tokens (tokens released to user)\r\n * - Release amount = (input - output)\r\n * \r\n * During redeem (outflow/withdrawal):\r\n * - Input has fund tokens (current supply)\r\n * - Output has more tokens (tokens returned from user)\r\n * - Collection amount = (output - input)\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 * 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 bool prevInflow = tx.inputs[this.activeInputIndex - 2].tokenCategory == inflowToken && tx.inputs[this.activeInputIndex - 2].nftCommitment == bytes(fundCategory + fundHash);\r\n\r\n if(prevInflow) {\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex + 1].lockingBytecode); // at least one UTXO is returned\r\n if(tx.outputs[this.activeInputIndex + 1].tokenCategory != 0x) { // if tokens are returning, then it must be the fund's tokens\r\n require(tx.outputs[this.activeInputIndex + 1].tokenCategory == fundCategory);\r\n }\r\n }\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 * 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 bool prevOutflow = tx.inputs[this.activeInputIndex - 2].tokenCategory == outflowToken && tx.inputs[this.activeInputIndex - 2].nftCommitment == bytes(fundCategory + fundHash);\r\n\r\n if(prevOutflow) {\r\n // This contract returns to itself (fund UTXO stays alive)\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex + 1].lockingBytecode); // at least one is returned\r\n require(tx.outputs[this.activeInputIndex + 1].tokenCategory == fundCategory); // this output can only have the fund's tokens in it\r\n }\r\n bool prevFundManager = tx.inputs[this.activeInputIndex - 1].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode;\r\n if(prevFundManager && tx.inputs[this.activeInputIndex - 1].tokenCategory != 0x) {\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == fundCategory);\r\n }\r\n require(prevOutflow || prevFundManager);\r\n }\r\n}",
33
+ "debug": {
34
+ "bytecode": "5479009c63c05294ce87c05294cf5379557a7e879a7663c0c7c08bcd88c08bd1008764c08bd15379886868c08cc7c0c7879b77777767547a519dc05294ce7b87c05294cf5379557a7e879a7663c0c7c08bcd88c08bd153798868c08cc7c0c78776c08cce0087919a63c0ce547988689b777768",
35
+ "sourceMap": "35:4:46:5;;;;;36:36:36:57;:60::61;:36:::1;:26::76;:::91;:105::126:0;:129::130;:105:::1;:95::145;:155::167:0;;:170::178;;:155:::1;:95::179;:26;38:11:38:21:0;:23:43:9;39:30:39:51;:20::68:1;:83::104:0;:::108:1;:72::125;:12::127;40:26:40:47:0;:::51:1;:15::66;:70::72:0;:15:::1;::42:13:0;41:35:41:56;:::60:1;:24::75;:79::91:0;;:16::93:1;40:74:42:13;38:23:43:9;44:41:44:62:0;:::66:1;:31::83;:97::118:0;:87::135:1;:31;45:8:45:47;35:4:46:5;;;;56::69::0;;;;57:37:57:58;:61::62;:37:::1;:27::77;:81::93:0;:27:::1;:107::128:0;:131::132;:107:::1;:97::147;:157::169:0;;:172::180;;:157:::1;:97::181;:27;59:11:59:22:0;:24:63:9;61:30:61:51;:20::68:1;:83::104:0;:::108:1;:72::125;:12::127;62:31:62:52:0;:::56:1;:20::71;:75::87:0;;:12::89:1;59:24:63:9;64:41:64:62:0;:::66:1;:31::83;:97::118:0;:87::135:1;:31;65:11:65:26:0;:40::61;:::65:1;:30::80;:84::86:0;:30:::1;;:11;:88:67:9:0;66:30:66:51;:20::66:1;:70::82:0;;:12::84:1;65:88:67:9;68:8:68:48;56:4:69:5;;25:0:70:1",
36
+ "logs": [],
37
+ "requires": [
38
+ {
39
+ "ip": 32,
40
+ "line": 39
41
+ },
42
+ {
43
+ "ip": 44,
44
+ "line": 41
45
+ },
46
+ {
47
+ "ip": 54,
48
+ "line": 45
49
+ },
50
+ {
51
+ "ip": 86,
52
+ "line": 61
53
+ },
54
+ {
55
+ "ip": 92,
56
+ "line": 62
57
+ },
58
+ {
59
+ "ip": 113,
60
+ "line": 66
61
+ },
62
+ {
63
+ "ip": 116,
64
+ "line": 68
65
+ }
66
+ ]
67
+ },
68
+ "compiler": {
69
+ "name": "cashc",
70
+ "version": "0.13.0-next.6",
71
+ "options": {
72
+ "enforceFunctionParameterTypes": true
73
+ }
74
+ },
75
+ "updatedAt": "2026-05-06T19:09:26.072Z"
76
+ }
@@ -0,0 +1,206 @@
1
+ {
2
+ "contractName": "TransactionManager",
3
+ "constructorInputs": [
4
+ {
5
+ "name": "fee",
6
+ "type": "bytes32"
7
+ },
8
+ {
9
+ "name": "inflowToken",
10
+ "type": "bytes32"
11
+ },
12
+ {
13
+ "name": "outflowToken",
14
+ "type": "bytes32"
15
+ },
16
+ {
17
+ "name": "fundCategory",
18
+ "type": "bytes32"
19
+ },
20
+ {
21
+ "name": "fundHash",
22
+ "type": "bytes32"
23
+ },
24
+ {
25
+ "name": "fundContract",
26
+ "type": "bytes"
27
+ },
28
+ {
29
+ "name": "assetContract",
30
+ "type": "bytes"
31
+ }
32
+ ],
33
+ "abi": [
34
+ {
35
+ "name": "inflow",
36
+ "inputs": [
37
+ {
38
+ "name": "fund",
39
+ "type": "bytes"
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ "name": "outflow",
45
+ "inputs": [
46
+ {
47
+ "name": "fund",
48
+ "type": "bytes"
49
+ }
50
+ ]
51
+ }
52
+ ],
53
+ "bytecode": "OP_7 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_8 OP_PICK OP_HASH256 OP_5 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_2 OP_PICK 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_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_4 OP_PICK OP_6 OP_PICK OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_INPUTINDEX OP_1ADD OP_OVER OP_UTXOBYTECODE aa20 OP_4 OP_ROLL OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_OVER OP_UTXOBYTECODE OP_OVER OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_DUP OP_1ADD OP_10 OP_ROLL 20 OP_SPLIT OP_SWAP OP_7 OP_PICK OP_EQUALVERIFY OP_8 OP_SPLIT OP_SWAP OP_BIN2NUM OP_4 OP_ROLL OP_1ADD OP_3 OP_ROLL OP_1ADD aa20 20 OP_10 OP_PICK OP_CAT 20 OP_CAT OP_9 OP_ROLL OP_CAT 20 OP_CAT OP_8 OP_PICK OP_CAT 20 OP_CAT OP_7 OP_ROLL OP_CAT OP_9 OP_ROLL OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_0 OP_0 OP_BEGIN OP_TXINPUTCOUNT OP_5 OP_PICK OP_GREATERTHAN OP_OVER OP_NOT OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_4 OP_PICK OP_UTXOBYTECODE OP_3 OP_PICK OP_EQUAL OP_IF OP_OVER OP_5 OP_PICK OP_UTXOTOKENAMOUNT OP_ADD OP_ROT OP_DROP OP_SWAP OP_4 OP_PICK OP_1ADD OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ELSE OP_DROP OP_1 OP_ENDIF OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_0 OP_BEGIN OP_TXOUTPUTCOUNT OP_5 OP_PICK OP_GREATERTHAN OP_OVER OP_NOT OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_4 OP_PICK OP_OUTPUTBYTECODE OP_4 OP_PICK OP_EQUAL OP_IF OP_2 OP_PICK OP_5 OP_PICK OP_OUTPUTTOKENAMOUNT OP_SUB OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_4 OP_PICK OP_1ADD OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ELSE OP_DROP OP_1 OP_ENDIF OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_2 OP_PICK OP_7 OP_PICK OP_MOD OP_0 OP_NUMEQUALVERIFY OP_2 OP_PICK OP_7 OP_ROLL OP_DIV OP_7 OP_ROLL OP_8 OP_SPLIT OP_SWAP OP_BIN2NUM OP_7 OP_ROLL OP_OVER OP_0 OP_GREATERTHAN OP_IF 0000000000000000000000000000000000000000000000000000000000000000 OP_OVER OP_OUTPUTBYTECODE aa20 20 OP_3 OP_PICK OP_CAT 20 OP_CAT OP_15 OP_PICK OP_CAT 20 OP_CAT OP_14 OP_PICK OP_CAT OP_16 OP_PICK OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_OVER OP_OUTPUTVALUE OP_3 OP_PICK OP_MOD OP_0 OP_NUMEQUALVERIFY OP_OVER OP_OUTPUTVALUE OP_3 OP_PICK OP_DIV OP_DUP OP_6 OP_PICK OP_NUMEQUALVERIFY OP_2 OP_PICK OP_1ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_2DROP OP_ENDIF OP_ROT OP_DUP OP_SIZE OP_NIP OP_0 OP_GREATERTHAN OP_IF OP_OVER OP_BEGIN OP_OVER 20 OP_SPLIT OP_DUP OP_8 OP_SPLIT OP_OVER OP_BIN2NUM OP_5 OP_PICK OP_OUTPUTBYTECODE aa20 20 OP_7 OP_PICK OP_CAT 20 OP_CAT 14 OP_PICK OP_CAT 20 OP_CAT 13 OP_PICK OP_CAT 15 OP_PICK OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_5 OP_PICK OP_OUTPUTTOKENCATEGORY OP_5 OP_PICK OP_EQUALVERIFY OP_5 OP_PICK OP_OUTPUTTOKENAMOUNT OP_OVER OP_MOD OP_0 OP_NUMEQUALVERIFY OP_5 OP_PICK OP_OUTPUTTOKENAMOUNT OP_OVER OP_DIV OP_DUP OP_11 OP_PICK OP_NUMEQUALVERIFY OP_2 OP_PICK OP_8 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_6 OP_PICK OP_1ADD OP_7 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_2DROP OP_2DROP OP_2DROP OP_OVER OP_SIZE OP_NIP OP_0 OP_LESSTHANOREQUAL OP_UNTIL OP_DROP OP_ENDIF OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ELSE OP_7 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_7 OP_PICK OP_HASH256 OP_5 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_3 OP_PICK 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_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_4 OP_PICK OP_6 OP_PICK OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_INPUTINDEX OP_1ADD OP_OVER OP_UTXOBYTECODE aa20 OP_4 OP_ROLL OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_OVER OP_UTXOBYTECODE OP_OVER OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_DUP OP_1ADD OP_0 OP_0 OP_4 OP_ROLL OP_1ADD OP_3 OP_ROLL OP_1ADD OP_BEGIN OP_OVER OP_UTXOBYTECODE aa20 20 OP_11 OP_PICK OP_CAT 20 OP_CAT OP_10 OP_PICK OP_CAT 20 OP_CAT OP_9 OP_PICK OP_CAT 20 OP_CAT OP_8 OP_PICK OP_CAT OP_12 OP_PICK OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_EQUAL OP_DUP OP_TOALTSTACK OP_IF OP_OVER OP_UTXOTOKENCATEGORY OP_0 OP_EQUAL OP_NOTIF OP_OVER OP_UTXOTOKENCATEGORY OP_8 OP_PICK OP_EQUALVERIFY OP_3 OP_PICK OP_2 OP_PICK OP_UTXOTOKENAMOUNT OP_ADD OP_4 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF OP_OVER OP_1ADD OP_ROT OP_DROP OP_SWAP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_BEGIN OP_DUP OP_OUTPUTBYTECODE aa20 20 OP_11 OP_PICK OP_CAT 20 OP_CAT OP_10 OP_PICK OP_CAT 20 OP_CAT OP_9 OP_PICK OP_CAT 20 OP_CAT OP_8 OP_PICK OP_CAT OP_12 OP_PICK OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_EQUAL OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_OUTPUTTOKENCATEGORY OP_8 OP_PICK OP_EQUALVERIFY OP_2 OP_PICK OP_OVER OP_OUTPUTTOKENAMOUNT OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_11 OP_ROLL 20 OP_SPLIT OP_SWAP OP_9 OP_ROLL OP_EQUALVERIFY OP_8 OP_SPLIT OP_SWAP OP_BIN2NUM OP_2ROT OP_SWAP OP_SUB OP_2DUP OP_SWAP OP_MOD OP_0 OP_NUMEQUALVERIFY OP_DUP OP_ROT OP_DIV OP_ROT OP_8 OP_SPLIT OP_2ROT OP_DUP OP_0 OP_GREATERTHAN OP_VERIFY OP_3 OP_ROLL OP_BIN2NUM OP_DUP OP_0 OP_GREATERTHAN OP_IF OP_0 0000000000000000000000000000000000000000000000000000000000000000 aa20 20 OP_2 OP_PICK OP_CAT 20 OP_CAT OP_13 OP_PICK OP_CAT 20 OP_CAT OP_12 OP_PICK OP_CAT OP_15 OP_PICK OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_BEGIN OP_5 OP_PICK OP_UTXOBYTECODE OP_OVER OP_EQUAL OP_6 OP_PICK OP_UTXOTOKENCATEGORY OP_0 OP_EQUAL OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_6 OP_PICK OP_UTXOVALUE OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_5 OP_PICK OP_1ADD OP_6 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_BEGIN OP_4 OP_PICK OP_OUTPUTBYTECODE OP_OVER OP_EQUAL OP_5 OP_PICK OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_2 OP_PICK OP_5 OP_PICK OP_OUTPUTVALUE OP_SUB OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_4 OP_PICK OP_1ADD OP_5 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_2OVER OP_SWAP OP_MOD OP_0 OP_NUMEQUALVERIFY OP_2OVER OP_SWAP OP_DIV OP_8 OP_PICK OP_NUMEQUALVERIFY OP_2DROP OP_DROP OP_ENDIF OP_ROT OP_2SWAP OP_SWAP OP_BEGIN OP_DUP OP_SIZE OP_NIP OP_0 OP_GREATERTHAN OP_DUP OP_TOALTSTACK OP_IF OP_DUP 20 OP_SPLIT aa20 20 OP_3 OP_PICK OP_CAT 20 OP_CAT OP_13 OP_PICK OP_CAT 20 OP_CAT OP_12 OP_PICK OP_CAT OP_15 OP_PICK OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_0 OP_0 OP_BEGIN OP_TXINPUTCOUNT OP_8 OP_PICK OP_GREATERTHAN OP_OVER OP_NOT OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_7 OP_PICK OP_UTXOBYTECODE OP_3 OP_PICK OP_EQUAL OP_8 OP_PICK OP_UTXOTOKENCATEGORY OP_6 OP_PICK OP_EQUAL OP_BOOLAND OP_IF OP_OVER OP_8 OP_PICK OP_UTXOTOKENAMOUNT OP_ADD OP_ROT OP_DROP OP_SWAP OP_7 OP_PICK OP_1ADD OP_8 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ELSE OP_DROP OP_1 OP_ENDIF OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_DROP OP_0 OP_BEGIN OP_TXOUTPUTCOUNT OP_7 OP_PICK OP_GREATERTHAN OP_OVER OP_NOT OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_6 OP_PICK OP_OUTPUTBYTECODE OP_3 OP_PICK OP_EQUAL OP_7 OP_PICK OP_OUTPUTTOKENCATEGORY OP_6 OP_PICK OP_EQUAL OP_BOOLAND OP_IF OP_OVER OP_7 OP_PICK OP_OUTPUTTOKENAMOUNT OP_SUB OP_ROT OP_DROP OP_SWAP OP_6 OP_PICK OP_1ADD OP_7 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_ELSE OP_DROP OP_1 OP_ENDIF OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_3 OP_PICK OP_8 OP_SPLIT OP_OVER OP_BIN2NUM OP_4 OP_PICK OP_OVER OP_MOD OP_0 OP_NUMEQUALVERIFY OP_4 OP_PICK OP_OVER OP_DIV OP_13 OP_PICK OP_NUMEQUALVERIFY OP_OVER OP_9 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_2DROP OP_1 OP_ENDIF",
54
+ "source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * TransactionManager: Orchestrates inflow (deposits) and outflow (withdrawals)\r\n * \r\n * The most complex contract in FundTokens. Validates:\r\n * - Fund token accounting (release/locking amounts)\r\n * - Asset custody correctness per fund configuration\r\n * - Satoshi backing (if fund supports Bitcoin asset)\r\n * \r\n * NFT Commitment Format:\r\n * [fundCategory (32) | fundHash (32)]\r\n * \r\n * Fund Hash decodes to:\r\n * [fundCategory (32) | fundAmount (8) | satoshis (8) | \r\n * asset1Category (32) | asset1Amount (8) | ... | assetNCategory (32) | assetNAmount (8)]\r\n * \r\n * Threading Model:\r\n * - Multiple independent inflow/outflow threads possible (different asset chains)\r\n * - Each thread validates its asset amounts independently\r\n * \r\n * Parameters:\r\n * fee: Fee contract hash (validates fees paid)\r\n * inflowToken: Inflow token category (deposit signal)\r\n * outflowToken: Outflow token category (withdrawal signal)\r\n * fundCategory: Fund identifier (comes from fund creation)\r\n * fundHash: Hash of complete fund specification\r\n * fundContract: Fund manager bytecode (for hashing per-fund instance)\r\n * assetContract: Asset manager bytecode (for hashing per-fund asset instances)\r\n */\r\ncontract TransactionManager(bytes32 fee, bytes32 inflowToken, bytes32 outflowToken, bytes32 fundCategory, bytes32 fundHash, bytes fundContract, bytes assetContract)\r\n{\r\n /**\r\n * inflow(bytes fund): Release fund tokens during user deposits\r\n * \r\n * Validates fund accounting:\r\n * - Fund tokens released = (input amount - output amount) / fundAmount\r\n * - Amount is whole multiple of fundAmount (prevents fractional tokens)\r\n * - Same release amount across all assets (atomicity across asset threads)\r\n * \r\n * Transaction Structure:\r\n * Input[activeInputIndex]: Inflow token (signal: deposit occurred)\r\n * Input[activeInputIndex+1]: Fee contract (proves fees paid)\r\n * Input[activeInputIndex+2]: Fund manager (contains fund tokens)\r\n * Input[activeInputIndex+2+F]: Fund manager (contains fund tokens)\r\n * \r\n * Output[activeInputIndex]: Inflow token (returned to signal processor)\r\n * Output[activeInputIndex+1]: Fee contract (returned to fee processor)\r\n * Output[activeInputIndex+2]: Paid fee (paid to maintainer)\r\n * Output[activeInputIndex+3]: Fund manager (updated fund tokens)\r\n * Output[activeInputIndex+3+F]: Fund manager (updated fund tokens)\r\n * Output[activeInputIndex+4+F]: Asset manager (locking asset)\r\n * Output[activeInputIndex+4+F+A]: Asset manager (locking asset)\r\n * \r\n * Validates:\r\n * - Fund tokens released consistently across operation\r\n * - Satoshi backing matches if configured\r\n * - Each asset release equals fund release amount\r\n */\r\n function inflow(bytes fund) {\r\n // Fund parameter hash must match (prevents fund substitution)\r\n require(hash256(fund) == fundHash);\r\n // Verify this input carries inflow signal token\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == inflowToken);\r\n // Spend signal token back to self\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n // Signal token category preserved (maintains state)\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n // Signal token NFT commitment must match fund parameters\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment);\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == bytes(fundCategory + fundHash));\r\n\r\n int feeInputIndex = this.activeInputIndex + 1;\r\n int feeOutputIndex = this.activeInputIndex + 1; // go to the end\r\n // Fee contract input must follow fund manager (validates fees paid)\r\n require(tx.inputs[feeInputIndex].lockingBytecode == new LockingBytecodeP2SH32(fee));\r\n require(tx.inputs[feeInputIndex].lockingBytecode == tx.outputs[feeOutputIndex].lockingBytecode); // sends back to self, it's paid\r\n feeOutputIndex = feeOutputIndex + 1; // skip over paid fee\r\n\r\n // Parse fund parameters\r\n bytes nft_fundCategory, bytes nft_next1 = fund.split(32);\r\n // Fund category must match (prevents fund mixing)\r\n require(nft_fundCategory == fundCategory);\r\n\r\n // Fund amount: divisor determining whole token units\r\n bytes nft_fundAmount, bytes nft_next2 = nft_next1.split(8);\r\n int fundAmount = int(nft_fundAmount);\r\n \r\n\r\n //\r\n int fundInputIndex = feeInputIndex + 1;\r\n int fundOutputIndex = feeOutputIndex + 1;\r\n\r\n bytes fundManager = new LockingBytecodeP2SH32(hash256(0x20 + fundHash + 0x20 + fundCategory + 0x20 + outflowToken + 0x20 + inflowToken + fundContract));\r\n int fundReleasedAmount = 0;\r\n\r\n // Calculate released fund tokens\r\n // Release = (input tokens - output tokens) / fundAmount\r\n // Division ensures whole token units (no fractional tokens)\r\n bool break = false;\r\n while(tx.inputs.length > fundInputIndex && !break) {\r\n if(tx.inputs[fundInputIndex].lockingBytecode == fundManager) {\r\n fundReleasedAmount = fundReleasedAmount + tx.inputs[fundInputIndex].tokenAmount;\r\n fundInputIndex = fundInputIndex + 1;\r\n } else {\r\n break = true;\r\n }\r\n }\r\n\r\n break = false;\r\n while(tx.outputs.length > fundOutputIndex && !break) {\r\n if(tx.outputs[fundOutputIndex].lockingBytecode == fundManager) {\r\n fundReleasedAmount = fundReleasedAmount - tx.outputs[fundOutputIndex].tokenAmount;\r\n fundOutputIndex = fundOutputIndex + 1;\r\n } else {\r\n break = true;\r\n }\r\n }\r\n\r\n // Ensure release is whole multiple of fund amount\r\n require(fundReleasedAmount % fundAmount == 0);\r\n // Convert raw token change to \"whole\" fund units\r\n fundReleasedAmount = fundReleasedAmount / fundAmount;\r\n\r\n // Parse satoshi configuration\r\n bytes nft_sats, bytes nft_next3 = nft_next2.split(8);\r\n int fundSats = int(nft_sats);\r\n int satoshiOutputIndex = fundOutputIndex; // fundOutputIndex is currently the first asset in the fund\r\n \r\n // If fund supports satoshi backing\r\n if(fundSats > 0) {\r\n // Satoshi assets use special category (all zeros)\r\n bytes32 satoshiAsset = 0x0000000000000000000000000000000000000000000000000000000000000000;\r\n // Verify satoshi custody output to correct asset manager\r\n require(tx.outputs[satoshiOutputIndex].lockingBytecode == new LockingBytecodeP2SH32(hash256(0x20 + satoshiAsset + 0x20 + fundHash + 0x20 + outflowToken + assetContract)));\r\n // Satoshi value must be whole multiple of fundSats\r\n require(tx.outputs[satoshiOutputIndex].value % fundSats == 0);\r\n // Calculate collected satoshis\r\n int satoshiCollectedAmount = tx.outputs[satoshiOutputIndex].value / fundSats;\r\n // Satoshi release must equal fund token release (atomic consistency)\r\n require(satoshiCollectedAmount == fundReleasedAmount);\r\n satoshiOutputIndex = satoshiOutputIndex + 1;\r\n }\r\n\r\n // Validate assets\r\n bytes nft_assetData = nft_next3;\r\n if(nft_assetData.length > 0) {\r\n int assetOutputIndex = satoshiOutputIndex;\r\n // Iterate through each asset type in fund\r\n do {\r\n bytes nft_assetCategory, bytes nft_assetNext = nft_assetData.split(32);\r\n bytes nft_assetAmount, bytes nft_remaining = nft_assetNext.split(8);\r\n int assetAmount = int(nft_assetAmount);\r\n\r\n // Verify asset custody output to correct asset manager\r\n require(tx.outputs[assetOutputIndex].lockingBytecode == new LockingBytecodeP2SH32(hash256(0x20 + nft_assetCategory + 0x20 + fundHash + 0x20 + outflowToken + assetContract)));\r\n // Verify asset tokens are of correct category\r\n require(tx.outputs[assetOutputIndex].tokenCategory == nft_assetCategory);\r\n \r\n // Asset tokens must be whole multiple of assetAmount\r\n require(tx.outputs[assetOutputIndex].tokenAmount % assetAmount == 0);\r\n // Calculate collected asset units\r\n int assetCollectedAmount = tx.outputs[assetOutputIndex].tokenAmount / assetAmount;\r\n // Asset release must equal fund release (maintains atomicity across assets)\r\n require(assetCollectedAmount == fundReleasedAmount);\r\n \r\n nft_assetData = nft_remaining;\r\n assetOutputIndex = assetOutputIndex + 1;\r\n } while(nft_assetData.length > 0);\r\n }\r\n }\r\n\r\n /**\r\n * outflow(bytes fund): Lock fund tokens during user withdrawals\r\n * \r\n * Validates fund accounting:\r\n * - Fund tokens locked = (output amount - input amount) / fundAmount\r\n * - Amount is whole multiple of fundAmount (prevents fractional tokens)\r\n * - Same lock amount across all assets (atomicity across asset threads)\r\n * - Asset amounts released match fund lock amount\r\n * - Satoshi amounts released match fund lock amount\r\n * \r\n * Similar to inflow() but:\r\n * - Uses outflowToken instead of inflowToken\r\n * - Fund tokens INCREASE (locking) instead of DECREASE (releasing)\r\n * - Scans all inputs/outputs for asset amounts (threading tolerance)\r\n * \r\n * Transaction Structure:\r\n * Input[activeInputIndex]: Outflow token (signal: withdrawal occurred)\r\n * Input[activeInputIndex+1]: Fee contract (proves fees paid)\r\n * Input[activeInputIndex+2]: Fund manager (will receive fund tokens)\r\n * Input[activeInputIndex+2+F]: Fund manager (will receive fund tokens)\r\n * Input[activeInputIndex+3+F]: Asset inputs from all threads (sorted)\r\n * Input[activeInputIndex+3+F+A]: Asset inputs from all threads (sorted)\r\n * \r\n * Output[activeInputIndex]: Outflow token (returned to signal processor)\r\n * Output[activeInputIndex+1]: Fee contract (returned to fee processor)\r\n * Output[activeInputIndex+2]: Paid fee (paid to maintainer)\r\n * Output[activeInputIndex+3]: Fund manager (updated fund tokens)\r\n * Output[activeInputIndex+3+F]: Fund manager (updated fund tokens)\r\n * Output[activeInputIndex+4+F]: Asset custody outputs (change amounts)\r\n * Output[activeInputIndex+4+F+A]: Asset custody outputs (change amounts)\r\n */\r\n function outflow(bytes fund) {\r\n // Fund parameter hash must match (prevents fund substitution)\r\n require(hash256(fund) == fundHash);\r\n // Verify this input carries outflow signal token\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == outflowToken);\r\n // Spend signal token back to self\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n // Signal token category preserved (maintains state)\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n // Signal token NFT commitment must match fund parameters\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment);\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == bytes(fundCategory + fundHash));\r\n\r\n int feeInputIndex = this.activeInputIndex + 1;\r\n int feeOutputIndex = this.activeInputIndex + 1;\r\n require(tx.inputs[feeInputIndex].lockingBytecode == new LockingBytecodeP2SH32(fee));\r\n require(tx.inputs[feeInputIndex].lockingBytecode == tx.outputs[feeOutputIndex].lockingBytecode); // sends back to self, it's paid\r\n feeOutputIndex = feeOutputIndex + 1; // skip over paid fee\r\n\r\n\r\n int fundInputAmount = 0;\r\n int fundOutputAmount = 0;\r\n\r\n // Fund manager contract is next input\r\n int fundInputIndex = feeInputIndex + 1;\r\n int fundOutputIndex = feeOutputIndex + 1;\r\n\r\n // Verify fund manager contract address\r\n while(tx.inputs[fundInputIndex].lockingBytecode == new LockingBytecodeP2SH32(hash256(0x20 + fundHash + 0x20 + fundCategory + 0x20 + outflowToken + 0x20 + inflowToken + fundContract))) {\r\n if(tx.inputs[fundInputIndex].tokenCategory != 0x) {\r\n require(tx.inputs[fundInputIndex].tokenCategory == fundCategory);\r\n fundInputAmount = fundInputAmount + tx.inputs[fundInputIndex].tokenAmount;\r\n }\r\n fundInputIndex = fundInputIndex + 1;\r\n }\r\n\r\n while(tx.outputs[fundOutputIndex].lockingBytecode == new LockingBytecodeP2SH32(hash256(0x20 + fundHash + 0x20 + fundCategory + 0x20 + outflowToken + 0x20 + inflowToken + fundContract))) {\r\n require(tx.outputs[fundOutputIndex].tokenCategory == fundCategory);\r\n fundOutputAmount = fundOutputAmount + tx.outputs[fundOutputIndex].tokenAmount;\r\n fundOutputIndex = fundOutputIndex + 1;\r\n }\r\n\r\n // Parse fund parameters\r\n bytes nft_fundCategory, bytes nft_next1 = fund.split(32);\r\n require(nft_fundCategory == fundCategory);\r\n\r\n bytes nft_fundAmount, bytes nft_next2 = nft_next1.split(8);\r\n\r\n int fundAmount = int(nft_fundAmount);\r\n // Calculate locked (collected) fund tokens\r\n // Lock = (output tokens - input tokens) / fundAmount\r\n // Positive when tokens are collected (withdrawal creates fund share)\r\n int fundLockingAmount = fundOutputAmount - fundInputAmount;\r\n require(fundLockingAmount % fundAmount == 0);\r\n fundLockingAmount = fundLockingAmount / fundAmount;\r\n\r\n // Fee contract input must follow fund manager\r\n bytes nft_sats, bytes nft_next3 = nft_next2.split(8);\r\n\r\n int satoshiAssetInputIndex = fundInputIndex;\r\n int satoshiAssetOutputIndex = fundOutputIndex;\r\n require(satoshiAssetOutputIndex > 0);\r\n\r\n int fundSats = int(nft_sats);\r\n if(fundSats > 0) {\r\n int satoshiReleaseAmount = 0;\r\n bytes32 satoshiAsset = 0x0000000000000000000000000000000000000000000000000000000000000000;\r\n bytes satoshiLockingBytecode = new LockingBytecodeP2SH32(hash256(0x20 + satoshiAsset + 0x20 + fundHash + 0x20 + outflowToken + assetContract));\r\n\r\n while (tx.inputs[satoshiAssetInputIndex].lockingBytecode == satoshiLockingBytecode && tx.inputs[satoshiAssetInputIndex].tokenCategory == 0x) {\r\n satoshiReleaseAmount = satoshiReleaseAmount + tx.inputs[satoshiAssetInputIndex].value;\r\n satoshiAssetInputIndex = satoshiAssetInputIndex + 1;\r\n }\r\n\r\n while(tx.outputs[satoshiAssetOutputIndex].lockingBytecode == satoshiLockingBytecode && tx.outputs[satoshiAssetOutputIndex].tokenCategory == 0x) {\r\n satoshiReleaseAmount = satoshiReleaseAmount - tx.outputs[satoshiAssetOutputIndex].value;\r\n satoshiAssetOutputIndex = satoshiAssetOutputIndex + 1;\r\n }\r\n\r\n // Net satoshi release must be whole multiple of fundSats\r\n require((satoshiReleaseAmount % fundSats) == 0);\r\n // Net satoshi release must equal fund token lock (atomic consistency)\r\n require((satoshiReleaseAmount / fundSats) == fundLockingAmount);\r\n }\r\n\r\n int assetInputIndex = satoshiAssetInputIndex;\r\n int assetOutputIndex = satoshiAssetOutputIndex;\r\n\r\n // Validate all assets\r\n bytes nft_assetData = nft_next3;\r\n while(nft_assetData.length > 0) {\r\n bytes nft_assetCategory, bytes nft_assetNext = nft_assetData.split(32);\r\n // Calculate asset custody contract address\r\n bytes assetLockingBytecode = new LockingBytecodeP2SH32(hash256(0x20 + nft_assetCategory + 0x20 + fundHash + 0x20 + outflowToken + assetContract));\r\n\r\n int assetReleaseAmount = 0;\r\n\r\n bool break = false;\r\n while (tx.inputs.length > assetInputIndex && !break) {\r\n if(tx.inputs[assetInputIndex].lockingBytecode == assetLockingBytecode && tx.inputs[assetInputIndex].tokenCategory == nft_assetCategory) {\r\n // Sum all asset tokens from this asset custody\r\n assetReleaseAmount = assetReleaseAmount + tx.inputs[assetInputIndex].tokenAmount;\r\n assetInputIndex = assetInputIndex + 1;\r\n } else {\r\n break = true;\r\n }\r\n }\r\n\r\n break = false;\r\n while (tx.outputs.length > assetOutputIndex && !break) {\r\n if(tx.outputs[assetOutputIndex].lockingBytecode == assetLockingBytecode && tx.outputs[assetOutputIndex].tokenCategory == nft_assetCategory) {\r\n // Subtract asset change (net asset release)\r\n assetReleaseAmount = assetReleaseAmount - tx.outputs[assetOutputIndex].tokenAmount;\r\n assetOutputIndex = assetOutputIndex + 1;\r\n } else {\r\n break = true;\r\n }\r\n }\r\n\r\n // Parse asset amount divisor\r\n bytes nft_assetAmount, bytes nft_remaining = nft_assetNext.split(8);\r\n int assetAmount = int(nft_assetAmount);\r\n \r\n // Net asset release must be whole multiple of assetAmount\r\n require((assetReleaseAmount % assetAmount) == 0);\r\n // Net asset release must equal fund token lock (maintains atomicity)\r\n require((assetReleaseAmount / assetAmount) == fundLockingAmount);\r\n nft_assetData = nft_remaining;\r\n }\r\n }\r\n}\r\n",
55
+ "debug": {
56
+ "bytecode": "5779009c635879aa557988c0ce527988c0c7c0cd88c0cec0d188c0cfc0d288c0cf547956797e88c08bc08b78c702aa20547a7e01877e8878c778cd88768b5a7a01207f7c577988587f7c81547a8b537a8b02aa2001205a797e01207e597a7e01207e58797e01207e577a7e597a7eaa7e01877e000065c35579a078919a766b635479c753798763785579d0937b757c54798b557a757c6b7c6b7c6b7c6c6c6c67755168686c91660065c45579a078919a766b635479cd5479876352795579d394537a757c6b7c6c54798b557a757c6b7c6b7c6b7c6c6c6c67755168686c91665279577997009d5279577a96577a587f7c81577a7800a06320000000000000000000000000000000000000000000000000000000000000000078cd02aa20012053797e01207e5f797e01207e5e797e60797eaa7e01877e8878cc537997009d78cc5379967656799d52798b537a757c6b7c6c6d687b76827700a06378657801207f76587f78815579cd02aa20012057797e01207e0114797e01207e0113797e0115797eaa7e01877e885579d15579885579d37897009d5579d37896765b799d5279587a757c6b7c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6c56798b577a757c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6d6d6d78827700a16675686d6d6d6d6d6d6d5167577a519d5779aa557988c0ce537988c0c7c0cd88c0cec0d188c0cfc0d288c0cf547956797e88c08bc08b78c702aa20547a7e01877e8878c778cd88768b0000547a8b537a8b6578c702aa2001205b797e01207e5a797e01207e59797e01207e58797e5c797eaa7e01877e87766b6378ce00876478ce58798853795279d093547a757c6b7c6b7c6c6c68788b7b757c686c91666576cd02aa2001205b797e01207e5a797e01207e59797e01207e58797e5c797eaa7e01877e87766b6376d1587988527978d393537a757c6b7c6c768b77686c91665b7a01207f7c597a88587f7c81717c946e7c97009d767b967b587f717600a069537a817600a0630020000000000000000000000000000000000000000000000000000000000000000002aa20012052797e01207e5d797e01207e5c797e5f797eaa7e01877e655579c778875679ce00879a766b6352795679c693537a757c6b7c6c55798b567a757c6b7c6b7c6b7c6b7c6c6c6c6c686c9166655479cd78875579d100879a766b6352795579cc94537a757c6b7c6c54798b557a757c6b7c6b7c6b7c6c6c6c686c9166707c97009d707c9658799d6d75687b727c6576827700a0766b637601207f02aa20012053797e01207e5d797e01207e5c797e5f797eaa7e01877e000065c35879a078919a766b635779c75379875879ce5679879a63785879d0937b757c57798b587a757c6b7c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6c67755168686c9166750065c45779a078919a766b635679cd5379875779d15679879a63785779d3947b757c56798b577a757c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c67755168686c91665379587f788154797897009d547978965d799d78597a757c6b7c6b7c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6c6c6d6d6d6d686c91666d6d6d6d6d6d5168",
57
+ "sourceMap": "60:4:171:5;;;;;62:24:62:28;;:16::29:1;:33::41:0;;:8::43:1;64:26:64:47:0;:16::62:1;:66::77:0;;:8::79:1;66:26:66:47:0;:16::64:1;:79::100:0;:68::117:1;:8::119;68:26:68:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;70:26:70:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;71:26:71:47:0;:16::62:1;:72::84:0;;:87::95;;:72:::1;:8::98;73:28:73:49:0;:::53:1;74:29:74:50:0;:::54:1;76:26:76:39:0;:16::56:1;:60::90:0;:86::89;;:60::90:1;;;:8::92;77:26:77:39:0;:16::56:1;:71::85:0;:60::102:1;:8::104;78:25:78:39:0;:::43:1;81:50:81:54:0;;:61::63;:50::64:1;83:16:83:32:0;:36::48;;:8::50:1;86:64:86:65:0;:48::66:1;87:29:87:43:0;:25::44:1;91:29:91:42:0;;:::46:1;92:30:92:44:0;;:::48:1;94:28:94:159:0;:62::66;:69::77;;:62:::1;:80::84:0;:62:::1;:87::99:0;;:62:::1;:102::106:0;:62:::1;:109::121:0;;:62:::1;:124::128:0;:62:::1;:131::142:0;;:62:::1;:145::157:0;;:62:::1;:54::158;:28::159;;;95:33:95:34:0;100:21:100:26;101:8:108:9;:14:101:30;:33::47;;:14:::1;:52::57:0;:51:::1;:14;;;:59:108:9:0;102:25:102:39;;:15::56:1;:60::71:0;;:15:::1;:73:105:13:0;103:37:103:55;:68::82;;:58::95:1;:37;:16::96;;;104:33:104:47:0;;:::51:1;:16::52;;;;;;;;;;;;;105:19:107:13:0;106:16:106:29:1;;105:19:107:13;101:59:108:9;;:8;;110:16:110:21:0;111:8:118:9;:14:111:31;:34::49;;:14:::1;:54::59:0;:53:::1;:14;;;:61:118:9:0;112:26:112:41;;:15::58:1;:62::73:0;;:15:::1;:75:115:13:0;113:37:113:55;;:69::84;;:58::97:1;:37;:16::98;;;;;;;114:34:114:49:0;;:::53:1;:16::54;;;;;;;;;;;;;115:19:117:13:0;116:16:116:29:1;;115:19:117:13;111:61:118:9;;:8;;121:16:121:34:0;;:37::47;;:16:::1;:51::52:0;:8::54:1;123:29:123:47:0;;:50::60;;:29:::1;126:42:126:51:0;;:58::59;:42::60:1;127:27:127:35:0;:23::36:1;128:33:128:48:0;;131:11:131:19;:22::23;:11:::1;:25:143:9:0;133:35:133:101;135:31:135:49;:20::66:1;:70::181:0;:104::108;:111::123;;:104:::1;:126::130:0;:104:::1;:133::141:0;;:104:::1;:144::148:0;:104:::1;:151::163:0;;:104:::1;:166::179:0;;:104:::1;:96::180;:70::181;;;:12::183;137:31:137:49:0;:20::56:1;:59::67:0;;:20:::1;:71::72:0;:12::74:1;139:52:139:70:0;:41::77:1;:80::88:0;;:41:::1;141:20:141:42:0;:46::64;;:12::66:1;142:33:142:51:0;;:::55:1;:12::56;;;;;;;131:25:143:9;;146:30:146:39:0;147:11:147:24;:::31:1;;:34::35:0;:11:::1;:37:170:9:0;148:35:148:53;150:12:169:46;151:63:151:76;:83::85;:63::86:1;152:61:152:74:0;:81::82;:61::83:1;153:38:153:53:0;:34::54:1;156:35:156:51:0;;:24::68:1;:72::188:0;:106::110;:113::130;;:106:::1;:133::137:0;:106:::1;:140::148:0;;:106:::1;:151::155:0;:106:::1;:158::170:0;;:106:::1;:173::186:0;;:106:::1;:98::187;:72::188;;;:16::190;158:35:158:51:0;;:24::66:1;:70::87:0;;:16::89:1;161:35:161:51:0;;:24::64:1;:67::78:0;:24:::1;:82::83:0;:16::85:1;163:54:163:70:0;;:43::83:1;:86::97:0;:43:::1;165:24:165:44:0;:48::66;;:16::68:1;167:32:167:45:0;;:16::46:1;;;;;;;;;;;;;;;;;;;;;;168:35:168:51:0;;:::55:1;:16::56;;;;;;;;;;;;;;;;;;;150:15:169:13;;;169:20::33:0;:::40:1;;:43::44:0;150:12::46:1;;147:37:170:9;;60:4:171:5;;;;;;;;;204::333::0;;;;206:24:206:28;;:16::29:1;:33::41:0;;:8::43:1;208:26:208:47:0;:16::62:1;:66::78:0;;:8::80:1;210:26:210:47:0;:16::64:1;:79::100:0;:68::117:1;:8::119;212:26:212:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;214:26:214:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;215:26:215:47:0;:16::62:1;:72::84:0;;:87::95;;:72:::1;:8::98;217:28:217:49:0;:::53:1;218:29:218:50:0;:::54:1;219:26:219:39:0;:16::56:1;:60::90:0;:86::89;;:60::90:1;;;:8::92;220:26:220:39:0;:16::56:1;:71::85:0;:60::102:1;:8::104;221:25:221:39:0;:::43:1;224:30:224:31:0;225:31:225:32;228:29:228:42;;:::46:1;229:30:229:44:0;;:::48:1;232:8:238:9:0;:24:232:38;:14::55:1;:59::190:0;:93::97;:100::108;;:93:::1;:111::115:0;:93:::1;:118::130:0;;:93:::1;:133::137:0;:93:::1;:140::152:0;;:93:::1;:155::159:0;:93:::1;:162::173:0;;:93:::1;:176::188:0;;:93:::1;:85::189;:59::190;;;:14;;;:192:238:9:0;233:25:233:39;:15::54:1;:58::60:0;:15:::1;::236:13:0;234:34:234:48;:24::63:1;:67::79:0;;:16::81:1;235:34:235:49:0;;:62::76;;:52::89:1;:34;:16::90;;;;;;;;;;233:62:236:13;237:29:237:43:0;:::47:1;:12::48;;;232:192:238:9;;:8;;240::244::0;:25:240:40;:14::57:1;:61::192:0;:95::99;:102::110;;:95:::1;:113::117:0;:95:::1;:120::132:0;;:95:::1;:135::139:0;:95:::1;:142::154:0;;:95:::1;:157::161:0;:95:::1;:164::175:0;;:95:::1;:178::190:0;;:95:::1;:87::191;:61::192;;;:14;;;:194:244:9:0;241:31:241:46;:20::61:1;:65::77:0;;:12::79:1;242:31:242:47:0;;:61::76;:50::89:1;:31;:12::90;;;;;;;243:30:243:45:0;:::49:1;:12::50;240:194:244:9;;:8;;247:50:247:54:0;;:61::63;:50::64:1;248:16:248:32:0;:36::48;;:8::50:1;250:64:250:65:0;:48::66:1;252:29:252:43:0;:25::44:1;256:32:256:66:0;;::::1;257:16:257:46:0;;::::1;:50::51:0;:8::53:1;258:28:258:45:0;:48::58;:28:::1;261:42:261:51:0;:58::59;:42::60:1;263:37:264:53:0;265:16:265:39;:42::43;:16:::1;:8::45;267:27:267:35:0;;:23::36:1;268:11:268:19:0;:22::23;:11:::1;:25:287:9:0;269:39:269:40;270:35:270:101;271:43:271:154;:77::81;:84::96;;:77:::1;:99::103:0;:77:::1;:106::114:0;;:77:::1;:117::121:0;:77:::1;:124::136:0;;:77:::1;:139::152:0;;:77:::1;:69::153;:43::154;;;273:12:276:13:0;:29:273:51;;:19::68:1;:72::94:0;:19:::1;:108::130:0;;:98::145:1;:149::151:0;:98:::1;:19;;;:153:276:13:0;274:39:274:59;;:72::94;;:62::101:1;:39;:16::102;;;;;;;275:41:275:63:0;;:::67:1;:16::68;;;;;;;;;;;;;;;;273:153:276:13;;:12;;278::281::0;:29:278:52;;:18::69:1;:73::95:0;:18:::1;:110::133:0;;:99::148:1;:152::154:0;:99:::1;:18;;;:156:281:13:0;279:39:279:59;;:73::96;;:62::103:1;:39;:16::104;;;;;;;280:42:280:65:0;;:::69:1;:16::70;;;;;;;;;;;;;278:156:281:13;;:12;;284:21:284:52:0;;::::1;:57::58:0;:12::60:1;286:21:286:52:0;;::::1;:57::74:0;;:12::76:1;268:25:287:9;;;289:30:289:52:0;290:31:293:39;;294:8:332:9;:14:294:27;:::34:1;;:37::38:0;:14:::1;;;:40:332:9:0;295:59:295:72;:79::81;:59::82:1;297:41:297:157:0;:75::79;:82::99;;:75:::1;:102::106:0;:75:::1;:109::117:0;;:75:::1;:120::124:0;:75:::1;:127::139:0;;:75:::1;:142::155:0;;:75:::1;:67::156;:41::157;;;299:37:299:38:0;301:25:301:30;302:12:310:13;:19:302:35;:38::53;;:19:::1;:58::63:0;:57:::1;:19;;;:65:310:13:0;303:29:303:44;;:19::61:1;:65::85:0;;:19:::1;:99::114:0;;:89::129:1;:133::150:0;;:89:::1;:19;:152:307:17:0;305:41:305:59;:72::87;;:62::100:1;:41;:20::101;;;306:38:306:53:0;;:::57:1;:20::58;;;;;;;;;;;;;;;;;;;;;;307:23:309:17:0;308:20:308:33:1;;307:23:309:17;302:65:310:13;;:12;;312::312:26;;313::321:13:0;:19:313:36;:39::55;;:19:::1;:60::65:0;:59:::1;:19;;;:67:321:13:0;314:30:314:46;;:19::63:1;:67::87:0;;:19:::1;:102::118:0;;:91::133:1;:137::154:0;;:91:::1;:19;:156:318:17:0;316:41:316:59;:73::89;;:62::102:1;:41;:20::103;;;317:39:317:55:0;;:::59:1;:20::60;;;;;;;;;;;;;;;;;;;318:23:320:17:0;319:20:319:33:1;;318:23:320:17;313:67:321:13;;:12;;324:57:324:70:0;;:77::78;:57::79:1;325:34:325:49:0;:30::50:1;328:21:328:39:0;;:42::53;:21:::1;:58::59:0;:12::61:1;330:21:330:39:0;;:42::53;:21:::1;:58::75:0;;:12::77:1;331:28:331:41:0;:12::42:1;;;;;;;;;;;;;;;;;;;;;;;;;294:40:332:9;;;;;;:8;;204:4:333:5;;;;;;;31:0:334:1",
58
+ "logs": [],
59
+ "requires": [
60
+ {
61
+ "ip": 17,
62
+ "line": 62
63
+ },
64
+ {
65
+ "ip": 22,
66
+ "line": 64
67
+ },
68
+ {
69
+ "ip": 27,
70
+ "line": 66
71
+ },
72
+ {
73
+ "ip": 32,
74
+ "line": 68
75
+ },
76
+ {
77
+ "ip": 37,
78
+ "line": 70
79
+ },
80
+ {
81
+ "ip": 45,
82
+ "line": 71
83
+ },
84
+ {
85
+ "ip": 58,
86
+ "line": 76
87
+ },
88
+ {
89
+ "ip": 63,
90
+ "line": 77
91
+ },
92
+ {
93
+ "ip": 73,
94
+ "line": 83
95
+ },
96
+ {
97
+ "ip": 225,
98
+ "line": 121
99
+ },
100
+ {
101
+ "ip": 268,
102
+ "line": 135
103
+ },
104
+ {
105
+ "ip": 275,
106
+ "line": 137
107
+ },
108
+ {
109
+ "ip": 284,
110
+ "line": 141
111
+ },
112
+ {
113
+ "ip": 339,
114
+ "line": 156
115
+ },
116
+ {
117
+ "ip": 345,
118
+ "line": 158
119
+ },
120
+ {
121
+ "ip": 352,
122
+ "line": 161
123
+ },
124
+ {
125
+ "ip": 361,
126
+ "line": 165
127
+ },
128
+ {
129
+ "ip": 437,
130
+ "line": 206
131
+ },
132
+ {
133
+ "ip": 442,
134
+ "line": 208
135
+ },
136
+ {
137
+ "ip": 447,
138
+ "line": 210
139
+ },
140
+ {
141
+ "ip": 452,
142
+ "line": 212
143
+ },
144
+ {
145
+ "ip": 457,
146
+ "line": 214
147
+ },
148
+ {
149
+ "ip": 465,
150
+ "line": 215
151
+ },
152
+ {
153
+ "ip": 478,
154
+ "line": 219
155
+ },
156
+ {
157
+ "ip": 483,
158
+ "line": 220
159
+ },
160
+ {
161
+ "ip": 537,
162
+ "line": 234
163
+ },
164
+ {
165
+ "ip": 602,
166
+ "line": 241
167
+ },
168
+ {
169
+ "ip": 629,
170
+ "line": 248
171
+ },
172
+ {
173
+ "ip": 641,
174
+ "line": 257
175
+ },
176
+ {
177
+ "ip": 652,
178
+ "line": 265
179
+ },
180
+ {
181
+ "ip": 787,
182
+ "line": 284
183
+ },
184
+ {
185
+ "ip": 793,
186
+ "line": 286
187
+ },
188
+ {
189
+ "ip": 978,
190
+ "line": 328
191
+ },
192
+ {
193
+ "ip": 985,
194
+ "line": 330
195
+ }
196
+ ]
197
+ },
198
+ "compiler": {
199
+ "name": "cashc",
200
+ "version": "0.13.0-next.6",
201
+ "options": {
202
+ "enforceFunctionParameterTypes": true
203
+ }
204
+ },
205
+ "updatedAt": "2026-05-06T19:09:25.824Z"
206
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "contractName": "FundInflowMint",
3
+ "constructorInputs": [
4
+ {
5
+ "name": "validator",
6
+ "type": "bytes32"
7
+ },
8
+ {
9
+ "name": "inflowToken",
10
+ "type": "bytes32"
11
+ },
12
+ {
13
+ "name": "outflowToken",
14
+ "type": "bytes32"
15
+ },
16
+ {
17
+ "name": "fee",
18
+ "type": "bytes32"
19
+ },
20
+ {
21
+ "name": "managerContract",
22
+ "type": "bytes"
23
+ },
24
+ {
25
+ "name": "fundContract",
26
+ "type": "bytes"
27
+ },
28
+ {
29
+ "name": "assetContract",
30
+ "type": "bytes"
31
+ }
32
+ ],
33
+ "abi": [
34
+ {
35
+ "name": "mint",
36
+ "inputs": []
37
+ }
38
+ ],
39
+ "bytecode": "OP_INPUTINDEX OP_1SUB OP_UTXOBYTECODE aa20 OP_ROT OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOBYTECODE OP_INPUTINDEX OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_OVER OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_UTXOTOKENCATEGORY OP_2 OP_PICK OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_4 OP_ADD OP_OUTPUTTOKENCOMMITMENT 20 OP_SPLIT OP_0 OP_DUP OP_9 OP_ROLL OP_SIZE OP_NIP OP_ADD OP_2 OP_ADD OP_ACTIVEBYTECODE OP_OVER OP_SPLIT OP_DROP OP_ROT OP_SPLIT OP_NIP OP_SWAP OP_DUP OP_9 OP_ROLL OP_SIZE OP_NIP OP_ADD OP_2 OP_ADD OP_ACTIVEBYTECODE OP_SWAP OP_SPLIT OP_DROP OP_SWAP OP_SPLIT OP_NIP OP_INPUTINDEX OP_4 OP_ADD OP_OUTPUTBYTECODE aa20 OP_2SWAP OP_CAT 20 OP_CAT OP_3 OP_ROLL OP_CAT 20 OP_CAT OP_3 OP_ROLL OP_CAT 20 OP_CAT OP_4 OP_ROLL OP_CAT 20 OP_CAT OP_3 OP_PICK OP_CAT 20 OP_CAT OP_4 OP_ROLL OP_CAT OP_4 OP_ROLL OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_4 OP_ADD OP_OUTPUTTOKENCATEGORY OP_EQUAL",
40
+ "source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FundMint: Creates per-fund contract instances via bytecode extraction and hashing\r\n * \r\n * Mints both inflow and outflow tokens with carefully calculated contract addresses.\r\n * Each fund gets unique Asset and Fund manager contract instances derived from:\r\n * - Fund category and hash (fund identity)\r\n * - Inflow/outflow token categories\r\n * - Fee contract hash\r\n * - Manager contract bytecode\r\n * - Asset and Fund contract templates (extracted from activation bytecode)\r\n * \r\n * Contract structure in activation bytecode:\r\n * [assetContract bytes (push bytecode) | fundContract bytes (push bytecode)]\r\n * Push bytecodes include the CashScript push opcode (overhead ~2 bytes for >75 bytes)\r\n * \r\n * Parameters (all provided to FundMint at creation):\r\n * validator: Validator contract hash (initial authorizer)\r\n * inflowToken: Inflow token category (minting capability)\r\n * outflowToken: Outflow token category (minting capability)\r\n * fee: Fee contract hash\r\n * managerContract: TransactionManager bytecode template\r\n * fundContract: Fund contract bytecode template\r\n * assetContract: Asset contract bytecode template\r\n */\r\ncontract FundInflowMint(bytes32 validator, bytes32 inflowToken, bytes32 outflowToken, bytes32 fee, bytes managerContract, bytes fundContract, bytes assetContract)\r\n{\r\n /**\r\n * mintInflow(): Create Asset contract instance for inflow operations\r\n * \r\n * Mints inflow token to newly calculated Asset contract address.\r\n * This asset manager controls fund deposits and token releases.\r\n * \r\n * Validates:\r\n * - Validator input precedes this mint\r\n * - This FundMint UTXO returns to itself with minting token\r\n * - Outflow token input present with minting capability\r\n * - Both inputs from same contract (prevents cross-fund confusion)\r\n * - Output receives correct Asset contract address and inflow token\r\n * \r\n * Contract calculation by hashing:\r\n * hash256(assetContractParam + fundContractParam + fundHash + fundCategory + \r\n * outflowToken + inflowToken + fee + managerContract)\r\n * \r\n * This ensures Asset contract is unique per fund and cannot be reused.\r\n */\r\n function mint() {\r\n // Validator must immediately precede this mint (sequence control)\r\n require(tx.inputs[this.activeInputIndex - 1].lockingBytecode == new LockingBytecodeP2SH32(validator));\r\n\r\n // This FundMint UTXO returns to itself (self-contained minting)\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n // Must have inflow token with minting capability (0x02 flag)\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == (inflowToken + 0x02));\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n\r\n // Outflow token input must be present with minting capability\r\n require(tx.inputs[this.activeInputIndex + 1].tokenCategory == (outflowToken + 0x02));\r\n\r\n // Extract fund parameters from output NFT commitment\r\n bytes fundCategory, bytes fundHash = tx.outputs[this.activeInputIndex + 4].nftCommitment.split(32);\r\n\r\n // Extract asset contract bytecode from this contract's activation bytecode\r\n // Push opcodes add ~2 bytes of overhead for >75 byte push\r\n // Push opcodes add 1 byte of overhead for <75 byte push\r\n int assetContractStart = 0;\r\n int assetContractStop = assetContractStart + assetContract.length + 2;\r\n bytes assetContractParam = this.activeBytecode.slice(assetContractStart, assetContractStop);\r\n\r\n // Extract fund contract bytecode from this contract's activation bytecode\r\n // Positioned immediately after asset contract\r\n int fundContractStart = assetContractStop;\r\n int fundContractStop = fundContractStart + fundContract.length + 2;\r\n bytes fundContractParam = this.activeBytecode.slice(fundContractStart, fundContractStop);\r\n\r\n // Compute Asset contract address by hashing:\r\n // [asset_bytecode | fund_bytecode | fundHash | fundCategory | outflows | inflows | fees | manager_bytecode]\r\n // This calculation must match the per-fund Asset instance created elsewhere\r\n require(tx.outputs[this.activeInputIndex + 4].lockingBytecode == new LockingBytecodeP2SH32(hash256(assetContractParam + fundContractParam + 0x20 + fundHash + 0x20 + fundCategory + 0x20 + outflowToken + 0x20 + inflowToken + 0x20 + fee + managerContract)));\r\n // Asset receives inflow token to control inflow operations\r\n require(tx.outputs[this.activeInputIndex + 4].tokenCategory == inflowToken);\r\n }\r\n}",
41
+ "debug": {
42
+ "bytecode": "c08cc702aa207b7e01877e88c0c7c0cd88c0ce78527e88c0cec0d188c08bce5279527e88c05493d201207f0076597a8277935293c1787f757b7f777c76597a8277935293c17c7f757c7f77c05493cd02aa20727e01207e537a7e01207e537a7e01207e547a7e01207e53797e01207e547a7e547a7eaa7e01877e88c05493d187",
43
+ "sourceMap": "50:26:50:47;:::51:1;:16::68;:72::108:0;:98::107;:72::108:1;;;:8::110;53:26:53:47:0;:16::64:1;:79::100:0;:68::117:1;:8::119;55:26:55:47:0;:16::62:1;:67::78:0;:81::85;:67:::1;:8::88;56:26:56:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;59:26:59:47:0;:::51:1;:16::66;:71::83:0;;:86::90;:71:::1;:8::93;62:56:62:77:0;:80::81;:56:::1;:45::96;:103::105:0;:45::106:1;67:33:67:34:0;68:32:68:50;:53::66;;:::73:1;;:32;:76::77:0;:32:::1;69:35:69:54:0;:81::98;:35::99:1;;:61::79:0;:35::99:1;;73:32:73:49:0;74:31:74:48;:51::63;;:::70:1;;:31;:73::74:0;:31:::1;75:34:75:53:0;:79::95;:34::96:1;;:60::77:0;:34::96:1;;80:27:80:48:0;:51::52;:27:::1;:16::69;:73::261:0;:107::145;::::1;:148::152:0;:107:::1;:155::163:0;;:107:::1;:166::170:0;:107:::1;:173::185:0;;:107:::1;:188::192:0;:107:::1;:195::207:0;;:107:::1;:210::214:0;:107:::1;:217::228:0;;:107:::1;:231::235:0;:107:::1;:238::241:0;;:107:::1;:244::259:0;;:107:::1;:99::260;:73::261;;;:8::263;82:27:82:48:0;:51::52;:27:::1;:16::67;:8::84",
44
+ "logs": [],
45
+ "requires": [
46
+ {
47
+ "ip": 15,
48
+ "line": 50
49
+ },
50
+ {
51
+ "ip": 20,
52
+ "line": 53
53
+ },
54
+ {
55
+ "ip": 26,
56
+ "line": 55
57
+ },
58
+ {
59
+ "ip": 31,
60
+ "line": 56
61
+ },
62
+ {
63
+ "ip": 39,
64
+ "line": 59
65
+ },
66
+ {
67
+ "ip": 117,
68
+ "line": 80
69
+ },
70
+ {
71
+ "ip": 123,
72
+ "line": 82
73
+ }
74
+ ]
75
+ },
76
+ "compiler": {
77
+ "name": "cashc",
78
+ "version": "0.13.0-next.6",
79
+ "options": {
80
+ "enforceFunctionParameterTypes": true
81
+ }
82
+ },
83
+ "updatedAt": "2026-05-06T19:09:27.520Z"
84
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "contractName": "FundOutflowMint",
3
+ "constructorInputs": [
4
+ {
5
+ "name": "validator",
6
+ "type": "bytes32"
7
+ },
8
+ {
9
+ "name": "inflowToken",
10
+ "type": "bytes32"
11
+ },
12
+ {
13
+ "name": "outflowToken",
14
+ "type": "bytes32"
15
+ },
16
+ {
17
+ "name": "fee",
18
+ "type": "bytes32"
19
+ },
20
+ {
21
+ "name": "managerContract",
22
+ "type": "bytes"
23
+ },
24
+ {
25
+ "name": "fundContract",
26
+ "type": "bytes"
27
+ },
28
+ {
29
+ "name": "assetContract",
30
+ "type": "bytes"
31
+ }
32
+ ],
33
+ "abi": [
34
+ {
35
+ "name": "mint",
36
+ "inputs": []
37
+ }
38
+ ],
39
+ "bytecode": "OP_INPUTINDEX OP_2 OP_SUB OP_UTXOBYTECODE aa20 OP_ROT OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_1SUB OP_UTXOTOKENCATEGORY OP_OVER OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_2 OP_PICK OP_2 OP_CAT 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_4 OP_ADD OP_OUTPUTTOKENCOMMITMENT 20 OP_SPLIT OP_0 OP_DUP OP_9 OP_ROLL OP_SIZE OP_NIP OP_ADD OP_2 OP_ADD OP_ACTIVEBYTECODE OP_OVER OP_SPLIT OP_DROP OP_ROT OP_SPLIT OP_NIP OP_SWAP OP_DUP OP_9 OP_ROLL OP_SIZE OP_NIP OP_ADD OP_2 OP_ADD OP_ACTIVEBYTECODE OP_SWAP OP_SPLIT OP_DROP OP_SWAP OP_SPLIT OP_NIP OP_INPUTINDEX OP_4 OP_ADD OP_OUTPUTBYTECODE aa20 OP_2SWAP OP_CAT 20 OP_CAT OP_3 OP_ROLL OP_CAT 20 OP_CAT OP_3 OP_ROLL OP_CAT 20 OP_CAT OP_4 OP_PICK OP_CAT 20 OP_CAT OP_3 OP_ROLL OP_CAT 20 OP_CAT OP_4 OP_ROLL OP_CAT OP_4 OP_ROLL OP_CAT OP_HASH256 OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_4 OP_ADD OP_OUTPUTTOKENCATEGORY OP_EQUAL",
40
+ "source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FundMint: Creates per-fund contract instances via bytecode extraction and hashing\r\n * \r\n * Mints both inflow and outflow tokens with carefully calculated contract addresses.\r\n * Each fund gets unique Asset and Fund manager contract instances derived from:\r\n * - Fund category and hash (fund identity)\r\n * - Inflow/outflow token categories\r\n * - Fee contract hash\r\n * - Manager contract bytecode\r\n * - Asset and Fund contract templates (extracted from activation bytecode)\r\n * \r\n * Contract structure in activation bytecode:\r\n * [assetContract bytes (push bytecode) | fundContract bytes (push bytecode)]\r\n * Push bytecodes include the CashScript push opcode (overhead ~2 bytes for >75 bytes)\r\n * \r\n * Parameters (all provided to FundMint at creation):\r\n * validator: Validator contract hash (initial authorizer)\r\n * inflowToken: Inflow token category (minting capability)\r\n * outflowToken: Outflow token category (minting capability)\r\n * fee: Fee contract hash\r\n * managerContract: TransactionManager bytecode template\r\n * fundContract: Fund contract bytecode template\r\n * assetContract: Asset contract bytecode template\r\n */\r\ncontract FundOutflowMint(bytes32 validator, bytes32 inflowToken, bytes32 outflowToken, bytes32 fee, bytes managerContract, bytes fundContract, bytes assetContract)\r\n{\r\n /**\r\n * mintOutflow(): Create Fund contract instance for fund account management\r\n * \r\n * Mints outflow token to newly calculated Fund contract address.\r\n * This fund manager controls token accounting and redemption.\r\n * \r\n * Validates:\r\n * - Validator input 2 positions before this mint (sequence control)\r\n * - Inflow token input immediately before this mint with minting capability\r\n * - This FundMint UTXO holds outflow token with minting capability\r\n * - This UTXO returns to itself\r\n * - Output receives correct Fund contract address and outflow token\r\n * \r\n * Contract calculation uses same formula as mintInflow but different output token.\r\n * Fund contract is unique per fund and handles token accounting.\r\n */\r\n function mint() {\r\n // Validator must be 2 positions before this mint (part of sequence)\r\n require(tx.inputs[this.activeInputIndex - 2].lockingBytecode == new LockingBytecodeP2SH32(validator));\r\n\r\n // Inflow token input must be immediately before (handles sequencing)\r\n require(tx.inputs[this.activeInputIndex - 1].tokenCategory == (inflowToken + 0x02));\r\n\r\n // This FundMint holds outflow token with minting capability\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == outflowToken + 0x02);\r\n\r\n // This FundMint returns to itself\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n\r\n // Extract fund parameters from output NFT commitment\r\n bytes fundCategory, bytes fundHash = tx.outputs[this.activeInputIndex + 4].nftCommitment.split(32);\r\n \r\n // Extract asset contract bytecode from activation bytecode\r\n int assetContractStart = 0;\r\n int assetContractStop = assetContractStart + assetContract.length + 2;\r\n bytes assetContractParam = this.activeBytecode.slice(assetContractStart, assetContractStop);\r\n\r\n // Extract fund contract bytecode from activation bytecode\r\n int fundContractStart = assetContractStop;\r\n int fundContractStop = fundContractStart + fundContract.length + 2;\r\n bytes fundContractParam = this.activeBytecode.slice(fundContractStart, fundContractStop);\r\n\r\n // Compute Fund contract address using same hash as Asset contract\r\n // This will differ only in the output token category\r\n require(tx.outputs[this.activeInputIndex + 4].lockingBytecode == new LockingBytecodeP2SH32(hash256(assetContractParam + fundContractParam + 0x20 + fundHash + 0x20 + fundCategory + 0x20 + outflowToken + 0x20 + inflowToken + 0x20 + fee + managerContract)));\r\n // Fund receives outflow token to control outflow (redemption) operations\r\n require(tx.outputs[this.activeInputIndex + 4].tokenCategory == outflowToken);\r\n }\r\n}",
41
+ "debug": {
42
+ "bytecode": "c05294c702aa207b7e01877e88c08cce78527e88c0ce5279527e88c0c7c0cd88c0cec0d188c05493d201207f0076597a8277935293c1787f757b7f777c76597a8277935293c17c7f757c7f77c05493cd02aa20727e01207e537a7e01207e537a7e01207e54797e01207e537a7e01207e547a7e547a7eaa7e01877e88c05493d187",
43
+ "sourceMap": "47:26:47:47;:50::51;:26:::1;:16::68;:72::108:0;:98::107;:72::108:1;;;:8::110;50:26:50:47:0;:::51:1;:16::66;:71::82:0;:85::89;:71:::1;:8::92;53:26:53:47:0;:16::62:1;:66::78:0;;:81::85;:66:::1;:8::87;56:26:56:47:0;:16::64:1;:79::100:0;:68::117:1;:8::119;57:26:57:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;60:56:60:77:0;:80::81;:56:::1;:45::96;:103::105:0;:45::106:1;63:33:63:34:0;64:32:64:50;:53::66;;:::73:1;;:32;:76::77:0;:32:::1;65:35:65:54:0;:81::98;:35::99:1;;:61::79:0;:35::99:1;;68:32:68:49:0;69:31:69:48;:51::63;;:::70:1;;:31;:73::74:0;:31:::1;70:34:70:53:0;:79::95;:34::96:1;;:60::77:0;:34::96:1;;74:27:74:48:0;:51::52;:27:::1;:16::69;:73::261:0;:107::145;::::1;:148::152:0;:107:::1;:155::163:0;;:107:::1;:166::170:0;:107:::1;:173::185:0;;:107:::1;:188::192:0;:107:::1;:195::207:0;;:107:::1;:210::214:0;:107:::1;:217::228:0;;:107:::1;:231::235:0;:107:::1;:238::241:0;;:107:::1;:244::259:0;;:107:::1;:99::260;:73::261;;;:8::263;76:27:76:48:0;:51::52;:27:::1;:16::67;:8::85",
44
+ "logs": [],
45
+ "requires": [
46
+ {
47
+ "ip": 16,
48
+ "line": 47
49
+ },
50
+ {
51
+ "ip": 23,
52
+ "line": 50
53
+ },
54
+ {
55
+ "ip": 30,
56
+ "line": 53
57
+ },
58
+ {
59
+ "ip": 35,
60
+ "line": 56
61
+ },
62
+ {
63
+ "ip": 40,
64
+ "line": 57
65
+ },
66
+ {
67
+ "ip": 118,
68
+ "line": 74
69
+ },
70
+ {
71
+ "ip": 124,
72
+ "line": 76
73
+ }
74
+ ]
75
+ },
76
+ "compiler": {
77
+ "name": "cashc",
78
+ "version": "0.13.0-next.6",
79
+ "options": {
80
+ "enforceFunctionParameterTypes": true
81
+ }
82
+ },
83
+ "updatedAt": "2026-05-06T19:09:27.771Z"
84
+ }