@fundtokens/builders 0.1.0-rc6 → 0.1.0-rc7
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/PublicFundTransactionBuilder.js +15 -6
- package/art/asset.json +1 -1
- package/art/authhead_vault.json +1 -1
- package/art/fee.json +25 -21
- package/art/fee_minter.json +1 -1
- package/art/fund.json +1 -1
- package/art/manager.json +63 -55
- package/art/mint_inflow.json +5 -5
- package/art/mint_outflow.json +8 -8
- package/art/public.json +1 -1
- package/art/public_vault.json +1 -1
- package/art/simple_minter.json +1 -1
- package/art/simple_vault.json +1 -1
- package/art/startup.json +18 -14
- package/package.json +4 -2
- package/utils.js +2 -2
|
@@ -171,6 +171,13 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
171
171
|
return this.#contracts;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
getAuthHeadOutput() {
|
|
175
|
+
const { authHeadVaultContract } = this.#contracts;
|
|
176
|
+
return withDust({
|
|
177
|
+
to: authHeadVaultContract.tokenAddress,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
174
181
|
async addBroadcast({
|
|
175
182
|
fund,
|
|
176
183
|
payBy,
|
|
@@ -190,8 +197,14 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
190
197
|
throw new Error('User genesis input is expected to be added prior to calling this function');
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
if (this.outputs.length
|
|
194
|
-
|
|
200
|
+
if (this.outputs.length === 0) { // add authhead output
|
|
201
|
+
this.addOutput(this.getAuthHeadOutput());
|
|
202
|
+
} else {
|
|
203
|
+
// verify authhead output
|
|
204
|
+
const authhead = this.outputs[0];
|
|
205
|
+
if(authhead.to != authHeadVaultContract.tokenAddress || authhead.token) {
|
|
206
|
+
throw new Error('Authhead output is incorrect, expecting to send to authhead vault with no tokens');
|
|
207
|
+
}
|
|
195
208
|
}
|
|
196
209
|
|
|
197
210
|
const genesisUtxo = this.inputs[0];
|
|
@@ -200,7 +213,6 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
200
213
|
throw new Error('First input must be a genesis input (vout is 0) with no tokens');
|
|
201
214
|
}
|
|
202
215
|
|
|
203
|
-
|
|
204
216
|
const bestFee = await getBestFee({ feeVaultContract, feeContract: createFundFeeContract, payBy, fee: this.#system.fees.create });
|
|
205
217
|
|
|
206
218
|
const startupUtxos = await startupContract.getUtxos();
|
|
@@ -245,9 +257,6 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
245
257
|
}
|
|
246
258
|
])
|
|
247
259
|
.addOutputs([
|
|
248
|
-
withDust({
|
|
249
|
-
to: authHeadVaultContract.tokenAddress,
|
|
250
|
-
}),
|
|
251
260
|
{
|
|
252
261
|
to: startupContract.tokenAddress,
|
|
253
262
|
amount: startupUtxo.satoshis,
|
package/art/asset.json
CHANGED
package/art/authhead_vault.json
CHANGED
package/art/fee.json
CHANGED
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"inputs": []
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
|
-
"bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_4 OP_AND OP_4 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_OUTPUTTOKENCATEGORY OP_5 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXOUTPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL 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_UTXOTOKENCATEGORY OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_INPUTINDEX OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_3 OP_ROLL OP_EQUAL OP_IF OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT 20 OP_SPLIT OP_DUP OP_8 OP_SPLIT OP_DUP OP_SIZE OP_NIP OP_0 OP_GREATERTHAN OP_IF OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_OVER OP_EQUALVERIFY OP_ELSE OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_6 OP_PICK OP_EQUALVERIFY OP_ENDIF OP_3 OP_PICK OP_BIN2NUM OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_1ADD OP_OUTPUTVALUE OP_2 OP_PICK OP_BIN2NUM OP_NUMEQUALVERIFY OP_ELSE OP_INPUTINDEX OP_1ADD OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_OUTPUTTOKENAMOUNT OP_2 OP_PICK OP_BIN2NUM OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_2DROP OP_ELSE OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_2 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_OUTPUTVALUE OP_3 OP_PICK OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_DROP OP_1 OP_ENDIF",
|
|
32
|
-
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FeeManager: Validates fee payments and routing\r\n * \r\n * Every fund transaction pays fees through this contract.\r\n * Validates that fees match their encoding and route to correct destination.\r\n * \r\n * Fee Token commitment format (stored in NFT):\r\n * [fee_category (32) | fee_amount (8) | fee_destination (variable)]\r\n * \r\n * Where:\r\n * - fee_category: 0x00 = Bitcoin satoshis, else = CashToken category\r\n * - fee_amount: Amount due (satoshis or token amount)\r\n * - fee_destination: Optional override destination (empty = use default)\r\n * Note: Variable-length intentionally - allows forward compatibility with future address formats\r\n * \r\n * Authorization:\r\n * - close() requires authToken to be present (protocol steward only)\r\n * - pay() validates the chosen fee is paid and routed\r\n * \r\n * Parameters:\r\n * authToken: Authorization token category (controls who can close)\r\n * destination: Default fee destination address (until override)\r\n * feeToken: Fee token category (with expected 'none' capability)\r\n * defaultValue: Default fee amount in satoshis (if no fee token encoding)\r\n */\r\ncontract FeeManager(bytes32 authToken, bytes destination, bytes32 feeToken, int defaultValue)\r\n{\r\n /**\r\n * close(): Close this fee by
|
|
31
|
+
"bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_4 OP_AND OP_4 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_OUTPUTTOKENCATEGORY OP_5 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_DUP OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUAL OP_NOT OP_VERIFY OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXOUTPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL 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_UTXOTOKENCATEGORY OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT OP_INPUTINDEX OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_3 OP_ROLL OP_EQUAL OP_IF OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT 20 OP_SPLIT OP_DUP OP_8 OP_SPLIT OP_DUP OP_SIZE OP_NIP OP_0 OP_GREATERTHAN OP_IF OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_OVER OP_EQUALVERIFY OP_ELSE OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_6 OP_PICK OP_EQUALVERIFY OP_ENDIF OP_3 OP_PICK OP_BIN2NUM OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_1ADD OP_OUTPUTVALUE OP_2 OP_PICK OP_BIN2NUM OP_NUMEQUALVERIFY OP_ELSE OP_INPUTINDEX OP_1ADD OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_OUTPUTTOKENAMOUNT OP_2 OP_PICK OP_BIN2NUM OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_2DROP OP_ELSE OP_INPUTINDEX OP_1ADD OP_OUTPUTBYTECODE OP_2 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_1ADD OP_OUTPUTVALUE OP_3 OP_PICK OP_NUMEQUALVERIFY OP_ENDIF OP_2DROP OP_DROP OP_1 OP_ENDIF",
|
|
32
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FeeManager: Validates fee payments and routing\r\n * \r\n * Every fund transaction pays fees through this contract.\r\n * Validates that fees match their encoding and route to correct destination.\r\n * \r\n * Fee Token commitment format (stored in NFT):\r\n * [fee_category (32) | fee_amount (8) | fee_destination (variable)]\r\n * \r\n * Where:\r\n * - fee_category: 0x00 = Bitcoin satoshis, else = CashToken category\r\n * - fee_amount: Amount due (satoshis or token amount)\r\n * - fee_destination: Optional override destination (empty = use default)\r\n * Note: Variable-length intentionally - allows forward compatibility with future address formats\r\n * \r\n * Authorization:\r\n * - close() requires authToken to be present (protocol steward only)\r\n * - pay() validates the chosen fee is paid and routed\r\n * \r\n * Parameters:\r\n * authToken: Authorization token category (controls who can close)\r\n * destination: Default fee destination address (until override)\r\n * feeToken: Fee token category (with expected 'none' capability)\r\n * defaultValue: Default fee amount in satoshis (if no fee token encoding)\r\n */\r\ncontract FeeManager(bytes32 authToken, bytes destination, 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 (authToken holder) to close the manager and cleanup.\r\n * Ensures no active fee tokens remain (no dangling fees).\r\n * \r\n * Validates:\r\n * - Transaction includes authToken with bit 0x04 (fee close permission)\r\n * - No FeeManager fee tokens exist in any output (burned)\r\n * \r\n * Used to close a current fee offering.\r\n */\r\n function close() {\r\n // Check for authorization token in any input\r\n // Bit 0x04 in commitment indicates fee manager closure authorization\r\n bool authorized = false;\r\n int inputIndex = 0;\r\n do {\r\n if(tx.inputs[inputIndex].tokenCategory == authToken) {\r\n if((tx.inputs[inputIndex].nftCommitment & bytes(0x04)) == 0x04) {\r\n // Found authorization token - user is authorized to close\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 fee tokens are burned (not present in any output)\r\n int outputIndex = 0;\r\n do {\r\n // Prevent any output from receiving fee tokens (forces burn)\r\n // Prevent from sending to this contract\r\n require(tx.outputs[outputIndex].tokenCategory != feeToken);\r\n require(tx.outputs[outputIndex].lockingBytecode != tx.inputs[this.activeInputIndex].lockingBytecode);\r\n outputIndex = outputIndex + 1;\r\n } while(outputIndex < tx.outputs.length);\r\n }\r\n\r\n /**\r\n * pay(): Validate fee payment according to encoding\r\n * \r\n * Routes fee to destination encoded in fee token NFT or default destination.\r\n * Supports two fee types: Bitcoin satoshis or CashTokens.\r\n * \r\n * Validates:\r\n * - This FeeManager UTXO returns to itself (no state change)\r\n * - Fee amount matches encoding (satoshis or token amount)\r\n * - Fee routes to correct destination (from encoding or default)\r\n * - Token category (if token fee) matches encoding\r\n * \r\n * Transaction Structure:\r\n * Input[activeInputIndex]: This FeeManager with fee token\r\n * Output[activeInputIndex]: This FeeManager (self-return)\r\n * Output[activeInputIndex+1]: Fee payment to destination\r\n * \r\n * Fee Encoding Logic:\r\n * - If fee_category == 0x00: Pay satoshis = fee_amount\r\n * - If fee_category != 0x00: Pay tokens = fee_amount of fee_category\r\n * - If fee_destination present: Pay to fee_destination\r\n * - Else: Pay to default destination\r\n */\r\n function pay() {\r\n // This FeeManager must return to itself (no state change)\r\n require(tx.inputs[this.activeInputIndex].lockingBytecode == tx.outputs[this.activeInputIndex].lockingBytecode);\r\n // Preserve fee token category for continuation\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == tx.outputs[this.activeInputIndex].tokenCategory);\r\n // Preserve fee token NFT commitment (maintains encoding)\r\n require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment);\r\n\r\n // Extract fee parameters from NFT commitment if this is fee token\r\n if(tx.inputs[this.activeInputIndex].tokenCategory == feeToken) {\r\n // Decode fee commitment: [category | amount | destination]\r\n bytes fee_category, bytes fee_next1 = tx.inputs[this.activeInputIndex].nftCommitment.split(32);\r\n bytes fee_amount, bytes fee_destination = fee_next1.split(8);\r\n\r\n // Determine fee destination (override or default)\r\n if(fee_destination.length > 0) {\r\n // Fee override destination specified in encoding\r\n require(tx.outputs[this.activeInputIndex + 1].lockingBytecode == fee_destination);\r\n } else {\r\n // No override - use default destination\r\n require(tx.outputs[this.activeInputIndex + 1].lockingBytecode == destination);\r\n }\r\n \r\n // Validate fee amount and type\r\n if(int(fee_category) == 0) {\r\n // Category 0 = Bitcoin satoshis (not tokens)\r\n // Output must have exact satoshi amount as encoded\r\n require(tx.outputs[this.activeInputIndex + 1].value == int(fee_amount));\r\n } else {\r\n // Non-zero category = CashToken fee type\r\n // Output must have correct token category and amount\r\n require(tx.outputs[this.activeInputIndex + 1].tokenCategory == fee_category);\r\n require(tx.outputs[this.activeInputIndex + 1].tokenAmount == int(fee_amount));\r\n }\r\n } else {\r\n // No fee token encoding - use default values\r\n // Pay default satoshi amount to default destination\r\n require(tx.outputs[this.activeInputIndex + 1].lockingBytecode == destination);\r\n require(tx.outputs[this.activeInputIndex + 1].value == defaultValue);\r\n }\r\n }\r\n}\r\n",
|
|
33
33
|
"debug": {
|
|
34
|
-
"bytecode": "
|
|
35
|
-
"sourceMap": "42:4:
|
|
34
|
+
"bytecode": "5479009c6300006576ce5379876376cf5484548763517b757c6868768b7776c39f5279919a91667c69006576d1557987916976cdc0c7879169768b7776c4a2666d6d6d755167547a519dc0c7c0cd88c0cec0d188c0cfc0d288c0ce537a8763c0cf01207f76587f76827700a063c08bcd788867c08bcd56798868537981009c63c08bcc5279819d67c08bd1547988c08bd35279819d686d6d67c08bcd527988c08bcc53799d686d755168",
|
|
35
|
+
"sourceMap": "42:4:67:5;;;;;45:26:45:31;46:25:46:26;47:8:55:62;48:25:48:35;:15::50:1;:54::63:0;;:15:::1;:65:53:13:0;49:30:49:40;:20::55:1;:64::68:0;:20::69:1;:74::78:0;:19:::1;:80:52:17:0;51:33:51:37;:20::38:1;;;49:80:52:17;48:65:53:13;54:25:54:35:0;:::39:1;:12::40;55:16:55:26:0;:29::45;:16:::1;:50::60:0;;:49:::1;:16;47:8::62;;56:16:56:26:0;:8::49:1;59:26:59:27:0;60:8:66:49;63:31:63:42;:20::57:1;:61::69:0;;:20:::1;;:12::71;64:31:64:42:0;:20::59:1;:73::94:0;:63::111:1;:20;;:12::113;65:26:65:37:0;:::41:1;:12::42;66:16:66:27:0;:30::47;60:8::49:1;;42:4:67:5;;;;;;92::132::0;;;;94:26:94:47;:16::64:1;:79::100:0;:68::117:1;:8::119;96:26:96:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;98:26:98:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;101:21:101:42:0;:11::57:1;:61::69:0;;:11:::1;:71:126:9:0;103:60:103:81;:50::96:1;:103::105:0;:50::106:1;104:54:104:63:0;:70::71;:54::72:1;107:15:107:30:0;:::37:1;;:40::41:0;:15:::1;:43:110:13:0;109:35:109:56;:::60:1;:24::77;:81::96:0;:16::98:1;110:19:113:13:0;112:35:112:56;:::60:1;:24::77;:81::92:0;;:16::94:1;110:19:113:13;116::116:31:0;;:15::32:1;:36::37:0;:15:::1;:39:120:13:0;119:35:119:56;:::60:1;:24::67;:75::85:0;;:71::86:1;:16::88;120:19:125:13:0;123:35:123:56;:::60:1;:24::75;:79::91:0;;:16::93:1;124:35:124:56:0;:::60:1;:24::73;:81::91:0;;:77::92:1;:16::94;120:19:125:13;101:71:126:9;;126:15:131::0;129:31:129:52;:::56:1;:20::73;:77::88:0;;:12::90:1;130:31:130:52:0;:::56:1;:20::63;:67::79:0;;:12::81:1;126:15:131:9;92:4:132:5;;;28:0:133:1",
|
|
36
36
|
"logs": [],
|
|
37
37
|
"requires": [
|
|
38
38
|
{
|
|
@@ -42,47 +42,51 @@
|
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"ip": 53,
|
|
45
|
-
"line":
|
|
45
|
+
"line": 63
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
"ip":
|
|
49
|
-
"line":
|
|
48
|
+
"ip": 60,
|
|
49
|
+
"line": 64
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
|
-
"ip":
|
|
52
|
+
"ip": 82,
|
|
53
53
|
"line": 94
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
|
-
"ip":
|
|
56
|
+
"ip": 87,
|
|
57
57
|
"line": 96
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
|
-
"ip":
|
|
61
|
-
"line":
|
|
60
|
+
"ip": 92,
|
|
61
|
+
"line": 98
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
64
|
"ip": 116,
|
|
65
|
-
"line":
|
|
65
|
+
"line": 109
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
|
-
"ip":
|
|
69
|
-
"line":
|
|
68
|
+
"ip": 123,
|
|
69
|
+
"line": 112
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"ip": 137,
|
|
73
|
-
"line":
|
|
73
|
+
"line": 119
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
76
|
"ip": 144,
|
|
77
|
-
"line":
|
|
77
|
+
"line": 123
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
|
-
"ip":
|
|
81
|
-
"line":
|
|
80
|
+
"ip": 151,
|
|
81
|
+
"line": 124
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
|
-
"ip":
|
|
85
|
-
"line":
|
|
84
|
+
"ip": 161,
|
|
85
|
+
"line": 129
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"ip": 167,
|
|
89
|
+
"line": 130
|
|
86
90
|
}
|
|
87
91
|
]
|
|
88
92
|
},
|
|
@@ -93,5 +97,5 @@
|
|
|
93
97
|
"enforceFunctionParameterTypes": true
|
|
94
98
|
}
|
|
95
99
|
},
|
|
96
|
-
"updatedAt": "2026-
|
|
100
|
+
"updatedAt": "2026-05-06T19:09:27.269Z"
|
|
97
101
|
}
|
package/art/fee_minter.json
CHANGED
package/art/fund.json
CHANGED
package/art/manager.json
CHANGED
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
]
|
|
51
51
|
}
|
|
52
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
|
|
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 + 2; // 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\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 while(tx.inputs[fundInputIndex].lockingBytecode == fundManager) {\r\n fundReleasedAmount = fundReleasedAmount + tx.inputs[fundInputIndex].tokenAmount;\r\n fundInputIndex = fundInputIndex + 1;\r\n }\r\n\r\n while(tx.outputs[fundOutputIndex].lockingBytecode == fundManager) {\r\n fundReleasedAmount = fundReleasedAmount - tx.outputs[fundOutputIndex].tokenAmount;\r\n fundOutputIndex = fundOutputIndex + 1;\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 + 2;\r\n require(tx.inputs[feeInputIndex].lockingBytecode == new LockingBytecodeP2SH32(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.length > fundInputIndex && 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.length > fundOutputIndex && 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 while (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 }\r\n\r\n while (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 }\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",
|
|
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
55
|
"debug": {
|
|
56
|
-
"bytecode": "
|
|
57
|
-
"sourceMap": "60:4:
|
|
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
58
|
"logs": [],
|
|
59
59
|
"requires": [
|
|
60
60
|
{
|
|
@@ -82,108 +82,116 @@
|
|
|
82
82
|
"line": 71
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
|
-
"ip":
|
|
85
|
+
"ip": 58,
|
|
86
86
|
"line": 76
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
"ip":
|
|
90
|
-
"line":
|
|
89
|
+
"ip": 63,
|
|
90
|
+
"line": 77
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
|
-
"ip":
|
|
94
|
-
"line":
|
|
93
|
+
"ip": 73,
|
|
94
|
+
"line": 83
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
|
-
"ip":
|
|
98
|
-
"line":
|
|
97
|
+
"ip": 225,
|
|
98
|
+
"line": 121
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
|
-
"ip":
|
|
102
|
-
"line":
|
|
101
|
+
"ip": 268,
|
|
102
|
+
"line": 135
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
|
-
"ip":
|
|
106
|
-
"line":
|
|
105
|
+
"ip": 275,
|
|
106
|
+
"line": 137
|
|
107
107
|
},
|
|
108
108
|
{
|
|
109
|
-
"ip":
|
|
110
|
-
"line":
|
|
109
|
+
"ip": 284,
|
|
110
|
+
"line": 141
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
|
-
"ip":
|
|
114
|
-
"line":
|
|
113
|
+
"ip": 339,
|
|
114
|
+
"line": 156
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
|
-
"ip":
|
|
118
|
-
"line":
|
|
117
|
+
"ip": 345,
|
|
118
|
+
"line": 158
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
|
-
"ip":
|
|
122
|
-
"line":
|
|
121
|
+
"ip": 352,
|
|
122
|
+
"line": 161
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
|
-
"ip":
|
|
126
|
-
"line":
|
|
125
|
+
"ip": 361,
|
|
126
|
+
"line": 165
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
|
-
"ip":
|
|
130
|
-
"line":
|
|
129
|
+
"ip": 437,
|
|
130
|
+
"line": 206
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
|
-
"ip":
|
|
134
|
-
"line":
|
|
133
|
+
"ip": 442,
|
|
134
|
+
"line": 208
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
|
-
"ip":
|
|
138
|
-
"line":
|
|
137
|
+
"ip": 447,
|
|
138
|
+
"line": 210
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
|
-
"ip":
|
|
142
|
-
"line":
|
|
141
|
+
"ip": 452,
|
|
142
|
+
"line": 212
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
|
-
"ip":
|
|
146
|
-
"line":
|
|
145
|
+
"ip": 457,
|
|
146
|
+
"line": 214
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
|
-
"ip":
|
|
150
|
-
"line":
|
|
149
|
+
"ip": 465,
|
|
150
|
+
"line": 215
|
|
151
151
|
},
|
|
152
152
|
{
|
|
153
|
-
"ip":
|
|
154
|
-
"line":
|
|
153
|
+
"ip": 478,
|
|
154
|
+
"line": 219
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
|
-
"ip":
|
|
158
|
-
"line":
|
|
157
|
+
"ip": 483,
|
|
158
|
+
"line": 220
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
|
-
"ip":
|
|
161
|
+
"ip": 537,
|
|
162
162
|
"line": 234
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
|
-
"ip":
|
|
166
|
-
"line":
|
|
165
|
+
"ip": 602,
|
|
166
|
+
"line": 241
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"ip": 629,
|
|
170
|
+
"line": 248
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"ip": 641,
|
|
174
|
+
"line": 257
|
|
167
175
|
},
|
|
168
176
|
{
|
|
169
|
-
"ip":
|
|
170
|
-
"line":
|
|
177
|
+
"ip": 652,
|
|
178
|
+
"line": 265
|
|
171
179
|
},
|
|
172
180
|
{
|
|
173
|
-
"ip":
|
|
174
|
-
"line":
|
|
181
|
+
"ip": 787,
|
|
182
|
+
"line": 284
|
|
175
183
|
},
|
|
176
184
|
{
|
|
177
|
-
"ip":
|
|
178
|
-
"line":
|
|
185
|
+
"ip": 793,
|
|
186
|
+
"line": 286
|
|
179
187
|
},
|
|
180
188
|
{
|
|
181
|
-
"ip":
|
|
182
|
-
"line":
|
|
189
|
+
"ip": 978,
|
|
190
|
+
"line": 328
|
|
183
191
|
},
|
|
184
192
|
{
|
|
185
|
-
"ip":
|
|
186
|
-
"line":
|
|
193
|
+
"ip": 985,
|
|
194
|
+
"line": 330
|
|
187
195
|
}
|
|
188
196
|
]
|
|
189
197
|
},
|
|
@@ -194,5 +202,5 @@
|
|
|
194
202
|
"enforceFunctionParameterTypes": true
|
|
195
203
|
}
|
|
196
204
|
},
|
|
197
|
-
"updatedAt": "2026-
|
|
205
|
+
"updatedAt": "2026-05-06T19:09:25.824Z"
|
|
198
206
|
}
|
package/art/mint_inflow.json
CHANGED
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
}
|
|
38
38
|
],
|
|
39
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
|
|
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
41
|
"debug": {
|
|
42
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;
|
|
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
44
|
"logs": [],
|
|
45
45
|
"requires": [
|
|
46
46
|
{
|
|
@@ -65,11 +65,11 @@
|
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"ip": 117,
|
|
68
|
-
"line":
|
|
68
|
+
"line": 80
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"ip": 123,
|
|
72
|
-
"line":
|
|
72
|
+
"line": 82
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
},
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"enforceFunctionParameterTypes": true
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
|
-
"updatedAt": "2026-
|
|
83
|
+
"updatedAt": "2026-05-06T19:09:27.520Z"
|
|
84
84
|
}
|
package/art/mint_outflow.json
CHANGED
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
}
|
|
38
38
|
],
|
|
39
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
|
|
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
41
|
"debug": {
|
|
42
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;
|
|
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
44
|
"logs": [],
|
|
45
45
|
"requires": [
|
|
46
46
|
{
|
|
@@ -53,23 +53,23 @@
|
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"ip": 30,
|
|
56
|
-
"line":
|
|
56
|
+
"line": 53
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
"ip": 35,
|
|
60
|
-
"line":
|
|
60
|
+
"line": 56
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
"ip": 40,
|
|
64
|
-
"line":
|
|
64
|
+
"line": 57
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"ip": 118,
|
|
68
|
-
"line":
|
|
68
|
+
"line": 74
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"ip": 124,
|
|
72
|
-
"line":
|
|
72
|
+
"line": 76
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
},
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"enforceFunctionParameterTypes": true
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
|
-
"updatedAt": "2026-
|
|
83
|
+
"updatedAt": "2026-05-06T19:09:27.771Z"
|
|
84
84
|
}
|
package/art/public.json
CHANGED
package/art/public_vault.json
CHANGED
package/art/simple_minter.json
CHANGED
package/art/simple_vault.json
CHANGED
package/art/startup.json
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
]
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"bytecode": "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_3 OP_PICK 20 OP_SPLIT OP_8 OP_SPLIT OP_SWAP OP_BIN2NUM OP_0 OP_GREATERTHAN OP_VERIFY OP_8 OP_SPLIT OP_SWAP OP_BIN2NUM OP_0 0040075af07507 OP_WITHIN OP_VERIFY OP_0 OP_BEGIN OP_OVER OP_SIZE OP_NIP OP_0 OP_GREATERTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER 20 OP_SPLIT OP_DUP OP_8 OP_SPLIT OP_OVER OP_BIN2NUM OP_0 OP_GREATERTHAN OP_VERIFY OP_4 OP_PICK OP_0 OP_EQUAL OP_NOTIF OP_4 OP_PICK 00 OP_CAT OP_BIN2NUM OP_4 OP_PICK 00 OP_CAT OP_BIN2NUM OP_LESSTHAN OP_VERIFY OP_ENDIF OP_DUP 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_3 OP_PICK 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_2DROP OP_2DROP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_INPUTINDEX OP_1ADD OP_UTXOTOKENCATEGORY OP_5 OP_PICK OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_2 OP_ADD OP_UTXOTOKENCATEGORY OP_6 OP_PICK OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_3 OP_ADD OP_UTXOBYTECODE aa20 OP_5 OP_ROLL OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_5 OP_ADD OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_5 OP_ADD OP_OUTPUTTOKENCOMMITMENT OP_3 OP_PICK OP_7 OP_PICK OP_HASH256 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_6 OP_ADD OP_OUTPUTTOKENCATEGORY OP_5 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_6 OP_ADD OP_OUTPUTTOKENCOMMITMENT OP_3 OP_ROLL OP_6 OP_ROLL OP_HASH256 OP_CAT OP_EQUALVERIFY OP_0 OP_BEGIN OP_DUP OP_INPUTINDEX OP_INPUTINDEX OP_7 OP_ADD OP_WITHIN OP_NOTIF OP_DUP OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOTIF OP_DUP OP_OUTPUTTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_4 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_DUP OP_OUTPUTTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_5 OP_PICK OP_EQUAL OP_NOT OP_VERIFY 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",
|
|
29
|
-
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FundStartup: Validates and initializes a new fund\r\n * \r\n * Called once during fund creation. Mints inflow/outflow tokens and validates\r\n * all fund parameters including amounts, satoshis, and asset ordering.\r\n * \r\n * Fund data format (passed as bytes):\r\n * [fund_category (32) | fund_amount (8) | satoshis (8) | \r\n * asset1_category (32) | asset1_amount (8) | ... | assetN_category (32) | assetN_amount (8)]\r\n * \r\n * Parameters:\r\n * fee: Fee contract hash (for fee input verification)\r\n * inflowToken: Inflow token category to mint (with minting capability)\r\n * outflowToken: Outflow token category to mint (with minting capability)\r\n */\r\ncontract FundStartup(bytes32 fee, bytes32 inflowToken, bytes32 outflowToken)\r\n{\r\n /**\r\n * start(bytes fund): Validates fund parameters and mints tokens\r\n * \r\n * Validates:\r\n * - Fund amount > 0\r\n * - Satoshis in valid range\r\n * - Assets are sorted by category ascending\r\n * - Each asset amount > 0\r\n * \r\n * Creates:\r\n * - Inflow token NFT with fund commitment\r\n * - Outflow token NFT with fund commitment\r\n */\r\n function start(bytes fund) {\r\n // This startup UTXO 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 require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment);\r\n\r\n // Parse fund parameters from commitment bytes\r\n bytes nft_fundCategory, bytes nft_next1 = fund.split(32);\r\n bytes nft_fundAmount, bytes nft_next2 = nft_next1.split(8);\r\n // Fund amount must be > 0 (determines token divisor)\r\n require(int(nft_fundAmount) > 0);\r\n bytes nft_satoshis, bytes nft_assets = nft_next2.split(8);\r\n // Satoshis must be within Bitcoin supply range (0 to 21 million BTC)\r\n // Note: PublicFund contract enforces stricter limits based on asset composition\r\n require(within(int(nft_satoshis), 0, 2100000000000000));\r\n\r\n // Validate asset sorting and amounts\r\n bytes lastAssetCategory = 0x;\r\n while(nft_assets.length > 0) {\r\n bytes nft_assetCategory, bytes nft_next3 = nft_assets.split(32);\r\n bytes nft_assetAmount, bytes nft_next4 = nft_next3.split(8);\r\n // Each asset amount must be > 0\r\n require(int(nft_assetAmount) > 0);\r\n\r\n // Assets must be in ascending order by category\r\n if(lastAssetCategory != 0x) {\r\n require(int(lastAssetCategory + 0x00) < int(nft_assetCategory + 0x00));\r\n }\r\n\r\n nft_assets = nft_next4;\r\n lastAssetCategory = nft_assetCategory;\r\n }\r\n\r\n // Verify inflow token input (with minting capability)\r\n require(tx.inputs[this.activeInputIndex + 1].tokenCategory == bytes(inflowToken + 0x02));\r\n // Verify outflow token input (with minting capability)\r\n require(tx.inputs[this.activeInputIndex + 2].tokenCategory == bytes(outflowToken + 0x02));\r\n\r\n // Verify fee contract input\r\n require(tx.inputs[this.activeInputIndex + 3].lockingBytecode == new LockingBytecodeP2SH32(fee));\r\n\r\n // Mint inflow token with fund commitment (fund_category | fund_hash)\r\n require(tx.outputs[this.activeInputIndex + 5].tokenCategory == inflowToken);\r\n require(tx.outputs[this.activeInputIndex + 5].nftCommitment == bytes(nft_fundCategory + hash256(fund)));\r\n \r\n // Mint outflow token with fund commitment\r\n require(tx.outputs[this.activeInputIndex + 6].tokenCategory == outflowToken);\r\n require(tx.outputs[this.activeInputIndex + 6].nftCommitment == bytes(nft_fundCategory + hash256(fund)));\r\n\r\n // Ensure no other outputs mint inflow/outflow tokens\r\n int outputIndex = 0;\r\n do {\r\n if(!within(outputIndex, this.activeInputIndex, this.activeInputIndex + 7)) {\r\n if(tx.outputs[outputIndex].tokenCategory != 0x) {\r\n // No other outputs can mint inflow or outflow tokens\r\n require(tx.outputs[outputIndex].tokenCategory.slice(0, 32) != inflowToken);\r\n require(tx.outputs[outputIndex].tokenCategory.slice(0, 32) != outflowToken);\r\n }\r\n }\r\n outputIndex = outputIndex + 1;\r\n } while(outputIndex < tx.outputs.length);\r\n }\r\n}",
|
|
28
|
+
"bytecode": "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_3 OP_PICK 20 OP_SPLIT OP_8 OP_SPLIT OP_SWAP OP_BIN2NUM OP_0 OP_GREATERTHAN OP_VERIFY OP_8 OP_SPLIT OP_SWAP OP_BIN2NUM OP_0 0040075af07507 OP_WITHIN OP_VERIFY OP_0 OP_BEGIN OP_OVER OP_SIZE OP_NIP OP_0 OP_GREATERTHAN OP_DUP OP_TOALTSTACK OP_IF OP_OVER 20 OP_SPLIT OP_DUP OP_8 OP_SPLIT OP_OVER OP_BIN2NUM OP_0 OP_GREATERTHAN OP_VERIFY OP_4 OP_PICK OP_0 OP_EQUAL OP_NOTIF OP_4 OP_PICK 00 OP_CAT OP_BIN2NUM OP_4 OP_PICK 00 OP_CAT OP_BIN2NUM OP_LESSTHAN OP_VERIFY OP_ENDIF OP_DUP 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_3 OP_PICK 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_2DROP OP_2DROP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_INPUTINDEX OP_1ADD OP_UTXOTOKENCATEGORY OP_5 OP_PICK OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_2 OP_ADD OP_UTXOTOKENCATEGORY OP_6 OP_PICK OP_2 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_3 OP_ADD OP_UTXOBYTECODE aa20 OP_5 OP_ROLL OP_CAT 87 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_3 OP_ADD OP_UTXOBYTECODE OP_INPUTINDEX OP_3 OP_ADD OP_OUTPUTBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_5 OP_ADD OP_OUTPUTTOKENCATEGORY OP_4 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_5 OP_ADD OP_OUTPUTTOKENCOMMITMENT OP_3 OP_PICK OP_7 OP_PICK OP_HASH256 OP_CAT OP_EQUALVERIFY OP_INPUTINDEX OP_6 OP_ADD OP_OUTPUTTOKENCATEGORY OP_5 OP_PICK OP_EQUALVERIFY OP_INPUTINDEX OP_6 OP_ADD OP_OUTPUTTOKENCOMMITMENT OP_3 OP_ROLL OP_6 OP_ROLL OP_HASH256 OP_CAT OP_EQUALVERIFY OP_0 OP_BEGIN OP_DUP OP_INPUTINDEX OP_INPUTINDEX OP_7 OP_ADD OP_WITHIN OP_NOTIF OP_DUP OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUAL OP_NOTIF OP_DUP OP_OUTPUTTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_4 OP_PICK OP_EQUAL OP_NOT OP_VERIFY OP_DUP OP_OUTPUTTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_5 OP_PICK OP_EQUAL OP_NOT OP_VERIFY 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",
|
|
29
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * FundStartup: Validates and initializes a new fund\r\n * \r\n * Called once during fund creation. Mints inflow/outflow tokens and validates\r\n * all fund parameters including amounts, satoshis, and asset ordering.\r\n * \r\n * Fund data format (passed as bytes):\r\n * [fund_category (32) | fund_amount (8) | satoshis (8) | \r\n * asset1_category (32) | asset1_amount (8) | ... | assetN_category (32) | assetN_amount (8)]\r\n * \r\n * Parameters:\r\n * fee: Fee contract hash (for fee input verification)\r\n * inflowToken: Inflow token category to mint (with minting capability)\r\n * outflowToken: Outflow token category to mint (with minting capability)\r\n */\r\ncontract FundStartup(bytes32 fee, bytes32 inflowToken, bytes32 outflowToken)\r\n{\r\n /**\r\n * start(bytes fund): Validates fund parameters and mints tokens\r\n * \r\n * Validates:\r\n * - Fund amount > 0\r\n * - Satoshis in valid range\r\n * - Assets are sorted by category ascending\r\n * - Each asset amount > 0\r\n * \r\n * Creates:\r\n * - Inflow token NFT with fund commitment\r\n * - Outflow token NFT with fund commitment\r\n */\r\n function start(bytes fund) {\r\n // This startup UTXO 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 require(tx.inputs[this.activeInputIndex].nftCommitment == tx.outputs[this.activeInputIndex].nftCommitment);\r\n\r\n // Parse fund parameters from commitment bytes\r\n bytes nft_fundCategory, bytes nft_next1 = fund.split(32);\r\n bytes nft_fundAmount, bytes nft_next2 = nft_next1.split(8);\r\n // Fund amount must be > 0 (determines token divisor)\r\n require(int(nft_fundAmount) > 0);\r\n bytes nft_satoshis, bytes nft_assets = nft_next2.split(8);\r\n // Satoshis must be within Bitcoin supply range (0 to 21 million BTC)\r\n // Note: PublicFund contract enforces stricter limits based on asset composition\r\n require(within(int(nft_satoshis), 0, 2100000000000000));\r\n\r\n // Validate asset sorting and amounts\r\n bytes lastAssetCategory = 0x;\r\n while(nft_assets.length > 0) {\r\n bytes nft_assetCategory, bytes nft_next3 = nft_assets.split(32);\r\n bytes nft_assetAmount, bytes nft_next4 = nft_next3.split(8);\r\n // Each asset amount must be > 0\r\n require(int(nft_assetAmount) > 0);\r\n\r\n // Assets must be in ascending order by category\r\n if(lastAssetCategory != 0x) {\r\n require(int(lastAssetCategory + 0x00) < int(nft_assetCategory + 0x00));\r\n }\r\n\r\n nft_assets = nft_next4;\r\n lastAssetCategory = nft_assetCategory;\r\n }\r\n\r\n // Verify inflow token input (with minting capability)\r\n require(tx.inputs[this.activeInputIndex + 1].tokenCategory == bytes(inflowToken + 0x02));\r\n // Verify outflow token input (with minting capability)\r\n require(tx.inputs[this.activeInputIndex + 2].tokenCategory == bytes(outflowToken + 0x02));\r\n\r\n // Verify fee contract input\r\n require(tx.inputs[this.activeInputIndex + 3].lockingBytecode == new LockingBytecodeP2SH32(fee));\r\n require(tx.inputs[this.activeInputIndex + 3].lockingBytecode == tx.outputs[this.activeInputIndex + 3].lockingBytecode); // sends back to self, it's paid\r\n\r\n // Mint inflow token with fund commitment (fund_category | fund_hash)\r\n require(tx.outputs[this.activeInputIndex + 5].tokenCategory == inflowToken);\r\n require(tx.outputs[this.activeInputIndex + 5].nftCommitment == bytes(nft_fundCategory + hash256(fund)));\r\n \r\n // Mint outflow token with fund commitment\r\n require(tx.outputs[this.activeInputIndex + 6].tokenCategory == outflowToken);\r\n require(tx.outputs[this.activeInputIndex + 6].nftCommitment == bytes(nft_fundCategory + hash256(fund)));\r\n\r\n // Ensure no other outputs mint inflow/outflow tokens\r\n int outputIndex = 0;\r\n do {\r\n if(!within(outputIndex, this.activeInputIndex, this.activeInputIndex + 7)) {\r\n if(tx.outputs[outputIndex].tokenCategory != 0x) {\r\n // No other outputs can mint inflow or outflow tokens\r\n require(tx.outputs[outputIndex].tokenCategory.slice(0, 32) != inflowToken);\r\n require(tx.outputs[outputIndex].tokenCategory.slice(0, 32) != outflowToken);\r\n }\r\n }\r\n outputIndex = outputIndex + 1;\r\n } while(outputIndex < tx.outputs.length);\r\n }\r\n}",
|
|
30
30
|
"debug": {
|
|
31
|
-
"bytecode": "
|
|
32
|
-
"sourceMap": "35:26:35:47;:16::64:1;:79::100:0;:68::117:1;:8::119;36:26:36:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;37:26:37:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;40:50:40:54:0;;:61::63;:50::64:1;41:64:41:65:0;:48::66:1;43:20:43:34:0;:16::35:1;:38::39:0;:16:::1;:8::41;44:63:44:64:0;:47::65:1;47:27:47:39:0;:23::40:1;:42::43:0;:45::61;:16::62:1;:8::64;50:34:50:36:0;51:8:64:9;:14:51:24;:::31:1;;:34::35:0;:14:::1;;;:37:64:9:0;52:55:52:65;:72::74;:55::75:1;53:53:53:62:0;:69::70;:53::71:1;55:24:55:39:0;:20::40:1;:43::44:0;:20:::1;:12::46;58:15:58:32:0;;:36::38;:15:::1;::60:13:0;59:28:59:45;;:48::52;:28:::1;:24::53;:60::77:0;;:80::84;:60:::1;:56::85;:24;:16::87;58:40:60:13;62:25:62:34:0;:12::35:1;;;;;;;;;;;;;;;;63:32:63:49:0;;:12::50:1;;;;;;;;;;;;;51:37:64:9;;;;:8;;67:26:67:47:0;:::51:1;:16::66;:76::87:0;;:90::94;:76:::1;:8::97;69:26:69:47:0;:50::51;:26:::1;:16::66;:76::88:0;;:91::95;:76:::1;:8::98;72:26:72:47:0;:50::51;:26:::1;:16::68;:72::102:0;:98::101;;:72::102:1;;;:8::104;
|
|
31
|
+
"bytecode": "c0c7c0cd88c0cec0d188c0cfc0d288537901207f587f7c8100a069587f7c8100070040075af07507a569006578827700a0766b637801207f76587f788100a0695479008764547901007e81547901007e819f696876567a757c6b7c6b7c6b7c6b7c6c6c6c6c5379557a757c6b7c6b7c6b7c6c6c6c6d6d686c9166c08bce5579527e88c05293ce5679527e88c05393c702aa20557a7e01877e88c05393c7c05393cd88c05593d1547988c05593d253795779aa7e88c05693d1557988c05693d2537a567aaa7e88006576c0c05793a56476d100876476d101207f75547987916976d101207f7555798791696868768b7776c4a2666d6d7551",
|
|
32
|
+
"sourceMap": "35:26:35:47;:16::64:1;:79::100:0;:68::117:1;:8::119;36:26:36:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;37:26:37:47:0;:16::62:1;:77::98:0;:66::113:1;:8::115;40:50:40:54:0;;:61::63;:50::64:1;41:64:41:65:0;:48::66:1;43:20:43:34:0;:16::35:1;:38::39:0;:16:::1;:8::41;44:63:44:64:0;:47::65:1;47:27:47:39:0;:23::40:1;:42::43:0;:45::61;:16::62:1;:8::64;50:34:50:36:0;51:8:64:9;:14:51:24;:::31:1;;:34::35:0;:14:::1;;;:37:64:9:0;52:55:52:65;:72::74;:55::75:1;53:53:53:62:0;:69::70;:53::71:1;55:24:55:39:0;:20::40:1;:43::44:0;:20:::1;:12::46;58:15:58:32:0;;:36::38;:15:::1;::60:13:0;59:28:59:45;;:48::52;:28:::1;:24::53;:60::77:0;;:80::84;:60:::1;:56::85;:24;:16::87;58:40:60:13;62:25:62:34:0;:12::35:1;;;;;;;;;;;;;;;;63:32:63:49:0;;:12::50:1;;;;;;;;;;;;;51:37:64:9;;;;:8;;67:26:67:47:0;:::51:1;:16::66;:76::87:0;;:90::94;:76:::1;:8::97;69:26:69:47:0;:50::51;:26:::1;:16::66;:76::88:0;;:91::95;:76:::1;:8::98;72:26:72:47:0;:50::51;:26:::1;:16::68;:72::102:0;:98::101;;:72::102:1;;;:8::104;73:26:73:47:0;:50::51;:26:::1;:16::68;:83::104:0;:107::108;:83:::1;:72::125;:8::127;76:27:76:48:0;:51::52;:27:::1;:16::67;:71::82:0;;:8::84:1;77:27:77:48:0;:51::52;:27:::1;:16::67;:77::93:0;;:104::108;;:96::109:1;:77;:8::112;80:27:80:48:0;:51::52;:27:::1;:16::67;:71::83:0;;:8::85:1;81:27:81:48:0;:51::52;:27:::1;:16::67;:77::93:0;;:104::108;;:96::109:1;:77;:8::112;84:26:84:27:0;85:8:94:49;86:23:86:34;:36::57;:59::80;:83::84;:59:::1;:16::85;:15:92:13:0;87:30:87:41;:19::56:1;:60::62:0;:19:::1;::91:17:0;89:39:89:50;:28::65:1;:75::77:0;:28::78:1;;:82::93:0;;:28:::1;;:20::95;90:39:90:50:0;:28::65:1;:75::77:0;:28::78:1;;:82::94:0;;:28:::1;;:20::96;87:64:91:17;86:87:92:13;93:26:93:37:0;:::41:1;:12::42;94:16:94:27:0;:30::47;85:8::49:1;;33:4:95:5;;;",
|
|
33
33
|
"logs": [],
|
|
34
34
|
"requires": [
|
|
35
35
|
{
|
|
@@ -73,28 +73,32 @@
|
|
|
73
73
|
"line": 72
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
|
-
"ip":
|
|
77
|
-
"line":
|
|
76
|
+
"ip": 150,
|
|
77
|
+
"line": 73
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
|
-
"ip":
|
|
80
|
+
"ip": 157,
|
|
81
81
|
"line": 76
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
|
-
"ip":
|
|
85
|
-
"line":
|
|
84
|
+
"ip": 168,
|
|
85
|
+
"line": 77
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
|
-
"ip":
|
|
88
|
+
"ip": 175,
|
|
89
89
|
"line": 80
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
|
-
"ip":
|
|
93
|
-
"line":
|
|
92
|
+
"ip": 186,
|
|
93
|
+
"line": 81
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
|
-
"ip":
|
|
96
|
+
"ip": 210,
|
|
97
97
|
"line": 89
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"ip": 220,
|
|
101
|
+
"line": 90
|
|
98
102
|
}
|
|
99
103
|
]
|
|
100
104
|
},
|
|
@@ -105,5 +109,5 @@
|
|
|
105
109
|
"enforceFunctionParameterTypes": true
|
|
106
110
|
}
|
|
107
111
|
},
|
|
108
|
-
"updatedAt": "2026-
|
|
112
|
+
"updatedAt": "2026-05-06T19:09:27.004Z"
|
|
109
113
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fundtokens/builders",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-rc7",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"dependencies": {
|
|
7
|
+
"@bitauth/libauth": "3.1.0-next.8",
|
|
7
8
|
"cashc": "0.13.0-next.6",
|
|
8
9
|
"cashscript": "0.13.0-next.6"
|
|
9
10
|
},
|
|
10
11
|
"devDependencies": {},
|
|
11
12
|
"author": "Fun(d)Tokens",
|
|
12
|
-
"description": "Transaction builders to create and interact with FundTokens."
|
|
13
|
+
"description": "Transaction builders to create and interact with FundTokens.",
|
|
14
|
+
"keywords": ["BitcoinCash", "Bitcoin Cash", "CashTokens", "DeFi", "FundTokens"]
|
|
13
15
|
}
|
package/utils.js
CHANGED
|
@@ -71,7 +71,7 @@ export function decodeFund(hex) {
|
|
|
71
71
|
hex = typeof hex === 'number' ? hex.toString(16) : hex;
|
|
72
72
|
|
|
73
73
|
const fund = {
|
|
74
|
-
category: hex.slice(0, 64),
|
|
74
|
+
category: swapEndianness(hex.slice(0, 64)),
|
|
75
75
|
amount: binToBigIntUint64LE(hexToBin(hex.slice(64, 80))),
|
|
76
76
|
satoshis: binToBigIntUint64LE(hexToBin(hex.slice(80, 96))),
|
|
77
77
|
assets: [],
|
|
@@ -81,7 +81,7 @@ export function decodeFund(hex) {
|
|
|
81
81
|
|
|
82
82
|
while(assetsHex.length > 0) {
|
|
83
83
|
fund.assets.push({
|
|
84
|
-
category: assetsHex.slice(0, 64),
|
|
84
|
+
category: swapEndianness(assetsHex.slice(0, 64)),
|
|
85
85
|
amount: binToBigIntUint64LE(hexToBin(assetsHex.slice(64, 80))),
|
|
86
86
|
});
|
|
87
87
|
assetsHex = assetsHex.slice(80);
|