@fundtokens/builders 0.1.0-rc13 → 0.1.0-rc14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FundTokenTransactionBuilder.js +3 -3
- package/FundTokensRegistry.js +82 -0
- package/PublicFundTransactionBuilder.js +19 -29
- package/README.md +16 -9
- package/art/asset.json +34 -34
- package/art/authhead_vault.json +35 -36
- package/art/fee.json +43 -44
- package/art/fee_minter.json +40 -40
- package/art/fund.json +38 -38
- package/art/instance_vault.json +63 -0
- package/art/manager.json +75 -76
- package/art/mint_inflow.json +42 -41
- package/art/mint_outflow.json +42 -41
- package/art/public.json +55 -60
- package/art/public_vault.json +48 -57
- package/art/simple_minter.json +40 -40
- package/art/simple_vault.json +30 -30
- package/art/startup.json +50 -49
- package/constants.js +9 -1
- package/index.js +16 -1
- package/package.json +1 -1
- package/utils.js +53 -26
|
@@ -29,11 +29,11 @@ const sortDecreasingTokenAmount = (a, b) => {
|
|
|
29
29
|
const aAmount = a.token?.amount ?? 0n;
|
|
30
30
|
const bAmount = b.token?.amount ?? 0n;
|
|
31
31
|
|
|
32
|
-
if(aAmount === bAmount) {
|
|
32
|
+
if (aAmount === bAmount) {
|
|
33
33
|
return 0;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
if(aAmount > bAmount) {
|
|
36
|
+
if (aAmount > bAmount) {
|
|
37
37
|
return -1;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -94,7 +94,7 @@ export default class FundTokenTransactionBuilder extends TransactionBuilder {
|
|
|
94
94
|
outflow: swapEndianness(system.outflow),
|
|
95
95
|
authorization: swapEndianness(system.authorization),
|
|
96
96
|
fee: {
|
|
97
|
-
nft: swapEndianness(system.fee.nft),
|
|
97
|
+
nft: swapEndianness(system.fee.nft), // TODO: enable passing global setting structure for easy usage
|
|
98
98
|
},
|
|
99
99
|
};
|
|
100
100
|
this.#fund = {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { FundTypes } from "./constants";
|
|
2
|
+
|
|
3
|
+
function clone(obj) {
|
|
4
|
+
return JSON.parse(JSON.stringify(obj));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
async function fetchJson(url) {
|
|
8
|
+
const httpResponse = await fetch(url);
|
|
9
|
+
if(httpResponse.status !== 200) {
|
|
10
|
+
throw new Error('Server returned unsuccessful status');
|
|
11
|
+
}
|
|
12
|
+
const response = await httpResponse.json();
|
|
13
|
+
return response;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default class FundTokensRegistry {
|
|
17
|
+
#network;
|
|
18
|
+
#url;
|
|
19
|
+
|
|
20
|
+
#current = {};
|
|
21
|
+
#instances = [];
|
|
22
|
+
|
|
23
|
+
constructor ({ network, url }) {
|
|
24
|
+
this.#network = network || 'chipnet';
|
|
25
|
+
this.#url = url || `https://${this.#network}-registry.fundtokens.cash/`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
clear() {
|
|
29
|
+
this.#current = {};
|
|
30
|
+
this.#instances = [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async getHealth() {
|
|
34
|
+
const httpResponse = await fetch(this.#url + 'api/health');
|
|
35
|
+
const status = httpResponse.status;
|
|
36
|
+
const response = await httpResponse.text();
|
|
37
|
+
const healthy = status === 200 && response === 'OK';
|
|
38
|
+
return {
|
|
39
|
+
status: httpResponse.status,
|
|
40
|
+
text: status === 200 ? status : 'Unhealthy',
|
|
41
|
+
ready: healthy,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// return the registry data
|
|
46
|
+
async getCurrent(type = FundTypes.FixedBasket.code) { // fixed-basket, mean-reversion, etc
|
|
47
|
+
if(this.#current[type]) {
|
|
48
|
+
return clone(this.#current[type].parameters);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const current = await fetchJson(this.#url + 'api/current');
|
|
52
|
+
const instance = await fetchJson(this.#url + 'api/instances/' + current[type]);
|
|
53
|
+
|
|
54
|
+
this.#current[type] = instance;
|
|
55
|
+
return clone(instance.parameters);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async getInstance({ id, hash }) {
|
|
59
|
+
if(this.#instances.some(i => i.id === id || i.hash === hash)) {
|
|
60
|
+
return clone(this.#instances.find(i => i.id === id || i.hash === hash).parameters);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if(id) {
|
|
64
|
+
const instance = await fetchJson(this.#url + 'api/instances/' + id);
|
|
65
|
+
this.#instances.push(instance);
|
|
66
|
+
return clone(instance.parameters);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if(hash) {
|
|
70
|
+
await this.fetchInstances();
|
|
71
|
+
return clone(this.#instances.find(i => i.hash === hash)?.parameters);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw new Error('id or hash must be provided');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async fetchInstances() {
|
|
78
|
+
const instances = await fetchJson(this.#url + 'api/instances');
|
|
79
|
+
this.#instances = instances;
|
|
80
|
+
return clone(this.#instances);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
} from '@bitauth/libauth';
|
|
12
12
|
import {
|
|
13
13
|
getBestFee,
|
|
14
|
-
|
|
14
|
+
getFundCommitment,
|
|
15
15
|
getFundHex,
|
|
16
16
|
getRandomInt,
|
|
17
|
+
hashFund,
|
|
17
18
|
withDust,
|
|
18
19
|
} from './utils.js';
|
|
19
20
|
import FundTokenTransactionBuilder from './FundTokenTransactionBuilder.js';
|
|
@@ -202,7 +203,7 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
202
203
|
} else {
|
|
203
204
|
// verify authhead output
|
|
204
205
|
const authhead = this.outputs[0];
|
|
205
|
-
if(authhead.to != authHeadVaultContract.tokenAddress || authhead.token) {
|
|
206
|
+
if (authhead.to != authHeadVaultContract.tokenAddress || authhead.token) {
|
|
206
207
|
throw new Error('Authhead output is incorrect, expecting to send to authhead vault with no tokens');
|
|
207
208
|
}
|
|
208
209
|
}
|
|
@@ -234,10 +235,13 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
234
235
|
|
|
235
236
|
const fundTokenAmount = 9223372036854775807n;
|
|
236
237
|
|
|
238
|
+
const fundHex = getFundHex(fund);
|
|
239
|
+
const fundHash = hashFund(fund);
|
|
240
|
+
|
|
237
241
|
this.addInputs([
|
|
238
242
|
{
|
|
239
243
|
...startupUtxo,
|
|
240
|
-
unlocker: startupContract.unlock.start(
|
|
244
|
+
unlocker: startupContract.unlock.start(fundHex),
|
|
241
245
|
},
|
|
242
246
|
{
|
|
243
247
|
...inflowUtxo,
|
|
@@ -253,7 +257,7 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
253
257
|
},
|
|
254
258
|
{
|
|
255
259
|
...publicFundUtxo,
|
|
256
|
-
unlocker: publicFundContract.unlock.broadcast(
|
|
260
|
+
unlocker: publicFundContract.unlock.broadcast()
|
|
257
261
|
}
|
|
258
262
|
])
|
|
259
263
|
.addOutputs([
|
|
@@ -279,7 +283,7 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
279
283
|
...inflowUtxo.token,
|
|
280
284
|
nft: {
|
|
281
285
|
capability: 'none',
|
|
282
|
-
commitment: swapEndianness(genesisUtxo.txid) +
|
|
286
|
+
commitment: '02' + swapEndianness(genesisUtxo.txid) + fundHash,
|
|
283
287
|
}
|
|
284
288
|
}
|
|
285
289
|
}),
|
|
@@ -289,7 +293,7 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
289
293
|
...outflowUtxo.token,
|
|
290
294
|
nft: {
|
|
291
295
|
capability: 'none',
|
|
292
|
-
commitment: swapEndianness(genesisUtxo.txid) +
|
|
296
|
+
commitment: '02' + swapEndianness(genesisUtxo.txid) + fundHash,
|
|
293
297
|
}
|
|
294
298
|
}
|
|
295
299
|
}),
|
|
@@ -302,34 +306,18 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
302
306
|
}),
|
|
303
307
|
withDust({
|
|
304
308
|
to: publicFundContract.tokenAddress,
|
|
305
|
-
token:
|
|
306
|
-
category: this.#system.publicFund,
|
|
307
|
-
amount: 0n,
|
|
308
|
-
nft: {
|
|
309
|
-
capability: 'minting',
|
|
310
|
-
commitment: '',
|
|
311
|
-
}
|
|
312
|
-
}
|
|
309
|
+
token: publicFundUtxo.token,
|
|
313
310
|
}),
|
|
314
311
|
]);
|
|
315
312
|
|
|
316
313
|
|
|
317
|
-
const maxSize = 128 * 2;
|
|
318
|
-
|
|
319
|
-
const fundHex = getFundHex(fund);
|
|
320
|
-
const fundHexParts = [];
|
|
321
314
|
|
|
315
|
+
const maxSize = 128 * 2; // NFT commitment max size 128 bytes Layla - May 2026
|
|
322
316
|
let curr = 0;
|
|
323
317
|
let next = maxSize;
|
|
324
318
|
|
|
325
|
-
|
|
326
|
-
while (curr <
|
|
327
|
-
fundHexParts.push(fundHex.slice(curr, next));
|
|
328
|
-
curr = next;
|
|
329
|
-
next += maxSize
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
fundHexParts.forEach(part => {
|
|
319
|
+
const fundCommitment = getFundCommitment(fund);
|
|
320
|
+
while (curr < fundCommitment.length) {
|
|
333
321
|
this.addOutput(withDust({
|
|
334
322
|
to: publicFundVaultContract.tokenAddress,
|
|
335
323
|
token: {
|
|
@@ -337,10 +325,12 @@ export default class PublicFundTransactionBuilder extends TransactionBuilder {
|
|
|
337
325
|
amount: 0n,
|
|
338
326
|
nft: {
|
|
339
327
|
capability: 'none',
|
|
340
|
-
commitment:
|
|
328
|
+
commitment: fundCommitment.slice(curr, next)
|
|
341
329
|
}
|
|
342
330
|
}
|
|
343
|
-
}))
|
|
344
|
-
|
|
331
|
+
}));
|
|
332
|
+
curr = next;
|
|
333
|
+
next += maxSize
|
|
334
|
+
}
|
|
345
335
|
}
|
|
346
336
|
}
|
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
# FundTokens
|
|
1
|
+
# FundTokens Builders
|
|
2
2
|
|
|
3
|
-
A JavaScript library for interacting with FundTokens smart contracts on the Bitcoin Cash network. This library provides tools for creating, minting, and redeeming fund tokens while handling the complex multi-contract operations required by the FundTokens protocol.
|
|
3
|
+
A JavaScript library for interacting with FundTokens smart contracts on the Bitcoin Cash network. This library provides tools for discovering, creating, minting, and redeeming fund tokens while handling the complex multi-contract operations required by the FundTokens protocol.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
- **Public Fund Discovery**: Find trustlessly created funds
|
|
7
8
|
- **Fund Creation**: Create new public funds with custom asset compositions
|
|
8
9
|
- **Token Minting**: Deposit assets to mint fund tokens
|
|
9
10
|
- **Token Redemption**: Withdraw assets by redeeming fund tokens
|
|
@@ -30,9 +31,11 @@ const fund = {
|
|
|
30
31
|
assets: [{ category: 'asset_token_id', amount: 2n }]
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
//
|
|
34
|
+
// Add user's genesis UTXO and additional inputs
|
|
35
|
+
// If adding outputs, ensure to add our identity output as the first output
|
|
34
36
|
await publicBuilder.addBroadcast({ fund });
|
|
35
|
-
//
|
|
37
|
+
// Add additional IO
|
|
38
|
+
// Add Bitcoin change
|
|
36
39
|
await publicBuilder.send();
|
|
37
40
|
```
|
|
38
41
|
|
|
@@ -45,8 +48,10 @@ const fundBuilder = new FundTokenTransactionBuilder({
|
|
|
45
48
|
provider, system, fund
|
|
46
49
|
});
|
|
47
50
|
|
|
51
|
+
// Add inputs/outputs but inputs.length must equal outputs.length before continuing
|
|
48
52
|
await fundBuilder.addInflow({ amount: 1n });
|
|
49
53
|
// Add user asset inputs and fund token outputs
|
|
54
|
+
// Add Bitcoin change output
|
|
50
55
|
await fundBuilder.send();
|
|
51
56
|
```
|
|
52
57
|
|
|
@@ -57,8 +62,10 @@ const fundBuilder = new FundTokenTransactionBuilder({
|
|
|
57
62
|
provider, system, fund
|
|
58
63
|
});
|
|
59
64
|
|
|
65
|
+
// Add inputs/outputs but inputs.length must equal outputs.length before continuing
|
|
60
66
|
await fundBuilder.addOutflow({ amount: 1n });
|
|
61
|
-
// Add user inputs
|
|
67
|
+
// Add user fund token inputs and asset outputs
|
|
68
|
+
// Add Bitcoin change output
|
|
62
69
|
await fundBuilder.send();
|
|
63
70
|
```
|
|
64
71
|
|
|
@@ -67,18 +74,18 @@ await fundBuilder.send();
|
|
|
67
74
|
### Fund Lifecycle
|
|
68
75
|
|
|
69
76
|
* Fund Creation - Broadcast fund parameters
|
|
70
|
-
* Fund Operations -
|
|
77
|
+
* Fund Operations - Minting (inflow) and redeeming (outflow) fund tokens
|
|
71
78
|
|
|
72
79
|
## Security Model
|
|
73
80
|
|
|
74
81
|
* Non-Custodial: Funds held in contract UTXOs controlled by code
|
|
75
|
-
* Parameter Immutability:
|
|
82
|
+
* Parameter Immutability: Public fund details are hashed and committed to tokens
|
|
76
83
|
* Contract Isolation: Each contract has single, verified responsibility
|
|
77
|
-
*
|
|
78
|
-
* Atomic Validation: Multi-contract validation ensures consistency
|
|
84
|
+
* Atomic Validation: Orchestrated multi-contract validation ensures consistency
|
|
79
85
|
|
|
80
86
|
## Requirements
|
|
81
87
|
* Bitcoin Cash network access
|
|
88
|
+
* Node
|
|
82
89
|
|
|
83
90
|
## License
|
|
84
91
|
|
package/art/asset.json
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
"contractName": "AssetManager",
|
|
3
|
-
"constructorInputs": [
|
|
4
|
-
{ "name": "outflowToken", "type": "bytes32" },
|
|
5
|
-
{ "name": "fundHash", "type": "bytes32" },
|
|
6
|
-
{ "name": "assetCategory", "type": "bytes32" }
|
|
7
|
-
],
|
|
8
|
-
"abi": [
|
|
9
|
-
{ "name": "release", "inputs": [] }
|
|
10
|
-
],
|
|
11
|
-
"bytecode": "OP_0 OP_INPUTINDEX OP_3 OP_SUB OP_BEGIN OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_2 OP_PICK OP_NOT OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT
|
|
12
|
-
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * AssetManager: Holds individual
|
|
13
|
-
"fingerprint": "
|
|
14
|
-
"debug": {
|
|
15
|
-
"bytecode": "
|
|
16
|
-
"sourceMap": "34:
|
|
17
|
-
"logs": [],
|
|
18
|
-
"requires": [
|
|
19
|
-
{ "ip":
|
|
20
|
-
{ "ip":
|
|
21
|
-
{ "ip":
|
|
22
|
-
{ "ip":
|
|
23
|
-
{ "ip":
|
|
24
|
-
]
|
|
25
|
-
},
|
|
26
|
-
"compiler": {
|
|
27
|
-
"name": "cashc",
|
|
28
|
-
"version": "0.13.0",
|
|
29
|
-
"options": {
|
|
30
|
-
"enforceFunctionParameterTypes": true,
|
|
31
|
-
"enforceLocktimeGuard": true
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
"updatedAt": "2026-
|
|
1
|
+
{
|
|
2
|
+
"contractName": "AssetManager",
|
|
3
|
+
"constructorInputs": [
|
|
4
|
+
{ "name": "outflowToken", "type": "bytes32" },
|
|
5
|
+
{ "name": "fundHash", "type": "bytes32" },
|
|
6
|
+
{ "name": "assetCategory", "type": "bytes32" }
|
|
7
|
+
],
|
|
8
|
+
"abi": [
|
|
9
|
+
{ "name": "release", "inputs": [] }
|
|
10
|
+
],
|
|
11
|
+
"bytecode": "OP_2 OP_PICK 0000000000000000000000000000000000000000000000000000000000000000 OP_EQUAL OP_IF OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ELSE OP_INPUTINDEX OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUALVERIFY OP_ENDIF OP_0 OP_INPUTINDEX OP_3 OP_SUB OP_BEGIN OP_DUP OP_0 OP_GREATERTHANOREQUAL OP_2 OP_PICK OP_NOT OP_BOOLAND OP_DUP OP_TOALTSTACK OP_IF OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT 41 OP_SPLIT OP_DROP 21 OP_SPLIT OP_NIP OP_4 OP_PICK OP_EQUALVERIFY OP_1 OP_ROT OP_DROP OP_SWAP OP_ENDIF OP_DUP OP_1SUB OP_NIP OP_ENDIF OP_FROMALTSTACK OP_NOT OP_UNTIL OP_INPUTINDEX OP_1SUB OP_INPUTBYTECODE OP_4 OP_SPLIT OP_NIP 20 OP_SPLIT OP_INPUTINDEX OP_INPUTBYTECODE OP_DUP OP_SIZE OP_NIP OP_1SUB OP_SPLIT OP_DROP 24 OP_SPLIT OP_NIP OP_EQUAL OP_DUP OP_IF OP_OVER 00 OP_CAT OP_BIN2NUM OP_7 OP_PICK 00 OP_CAT OP_BIN2NUM OP_2DUP OP_LESSTHAN OP_2 OP_PICK OP_2 OP_PICK OP_NUMEQUAL OP_BOOLOR OP_VERIFY OP_2DROP OP_ENDIF OP_3 OP_ROLL OP_SWAP OP_BOOLOR OP_VERIFY OP_2DROP OP_2DROP OP_DROP OP_1",
|
|
12
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * AssetManager: Holds individual assets for funds\r\n * \r\n * This contract gates asset access during redemption (outflow) transactions.\r\n * Amount verification is performed by the TransactionManager contract.\r\n * Each asset in a fund has a dedicated AssetManager contract instance.\r\n * \r\n * Parameters:\r\n * outflowToken: Token category that authorizes redemption\r\n * fundHash: Hash of fund parameters (prevents spending wrong fund's assets)\r\n * assetCategory: The specific asset category this contract holds\r\n * (0x00...00 = Bitcoin, else = token category)\r\n */\r\ncontract AssetManager(bytes32 outflowToken, bytes32 fundHash, bytes32 assetCategory) \r\n{\r\n /**\r\n * release(): Releases held assets when outflow is authorized\r\n *\r\n * AssetManager relies on TransactionManager for redeeming asset amount verification.\r\n * To release, checking for outflow token or linking w/ this contract.\r\n * \r\n * Ensures:\r\n * - Outflow token with matching fund hash is present (correct redemption)\r\n * - Asset type is verified (Bitcoin or tokens)\r\n * - Outflow token at \"expected\" input or uses efficient linking to a previous AssetManager\r\n */\r\n function release() {\r\n //\r\n // Verify this spends the correct asset\r\n // Bitcoin for 0x00... otherwise verify token category\r\n if(assetCategory == 0x0000000000000000000000000000000000000000000000000000000000000000) {\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == 0x);\r\n } else {\r\n require(tx.inputs[this.activeInputIndex].tokenCategory == assetCategory);\r\n }\r\n\r\n //\r\n // Find the outflow, searching to the start\r\n bool prevOutflow = false;\r\n int inputIndex = this.activeInputIndex - 3; // at least this far but could be further\r\n while(inputIndex >= 0 && !prevOutflow) {\r\n if(tx.inputs[inputIndex].tokenCategory == outflowToken) {\r\n require(tx.inputs[inputIndex].nftCommitment.slice(33, 65) == fundHash); // type 0x02 + fund category 32 bytes + fund hash 32 bytes\r\n prevOutflow = true;\r\n }\r\n inputIndex = inputIndex - 1;\r\n }\r\n \r\n //\r\n // Check if the previous input is an AssetManager contract for this fund\r\n int prevInput = this.activeInputIndex - 1;\r\n bytes prevParamsAndBytecode = tx.inputs[prevInput].unlockingBytecode.split(4)[1];\r\n bytes prevCategory, bytes prevUnlockingBytecode = prevParamsAndBytecode.split(32);\r\n bytes thisUnlockingBytecode = tx.inputs[this.activeInputIndex].unlockingBytecode;\r\n bool additionalAssetManager = thisUnlockingBytecode.slice(36, thisUnlockingBytecode.length - 1) == prevUnlockingBytecode; // slice out the asset category\r\n if(additionalAssetManager) {\r\n int iPrevious = int(prevCategory + 0x00);\r\n int iCurrent = int(assetCategory + 0x00);\r\n\r\n require(iPrevious < iCurrent || iPrevious == iCurrent);\r\n }\r\n\r\n //\r\n // Must have redemption signal or previous AssetManager contract\r\n require(prevOutflow || additionalAssetManager);\r\n }\r\n}",
|
|
13
|
+
"fingerprint": "41f470287ebc55c0d733d7bb0e3c497457df2972daa8bc71c62cbdcd48724087",
|
|
14
|
+
"debug": {
|
|
15
|
+
"bytecode": "52792000000000000000000000000000000000000000000000000000000000000000008763c0ce008867c0ce5379886800c05394657600a25279919a766b6376ce5379876376cf01417f7501217f77547988517b757c68768c77686c9166c08cca547f7701207fc0ca7682778c7f7501247f778776637801007e81577901007e816e9f527952799c9b696d68537a7c9b696d6d7551",
|
|
16
|
+
"sourceMap": "33:11:33:24;;:28::94;:11:::1;:96:35:9:0;34:30:34:51;:20::66:1;:70::72:0;:12::74:1;35:15:37:9:0;36:30:36:51;:20::66:1;:70::83:0;;:12::85:1;35:15:37:9;41:27:41:32:0;42:25:42:46;:49::50;:25:::1;43:8:49:9:0;:14:43:24;:28::29;:14:::1;:34::45:0;;:33:::1;:14;;;:47:49:9:0;44:25:44:35;:15::50:1;:54::66:0;;:15:::1;:68:47:13:0;45:34:45:44;:24::59:1;:70::72:0;:24::73:1;;:66::68:0;:24::73:1;;:77::85:0;;:16::87:1;46:30:46:34:0;:16::35:1;;;44:68:47:13;48:25:48:35:0;:::39:1;:12::40;43:47:49:9;;:8;;53:24:53:45:0;:::49:1;54:38:54:76;:83::84:0;:38::85:1;:::88;55:86:55::0;:58::89:1;56:48:56:69:0;:38::88:1;57::57:91:0;:70::98:1;;:::102;:38::103;;:66::68:0;:38::103:1;;:::128;58:11:58:33:0;:35:63:9;59:32:59:44;:47::51;:32:::1;:28::52;60:31:60:44:0;;:47::51;:31:::1;:27::52;62:20:62:40:0;::::1;:44::53:0;;:57::65;;:44:::1;:20;:12::67;58:35:63:9;;67:16:67:27:0;;:31::53;:16:::1;:8::55;29:23:68:5;;;",
|
|
17
|
+
"logs": [],
|
|
18
|
+
"requires": [
|
|
19
|
+
{ "ip": 11, "line": 34 },
|
|
20
|
+
{ "ip": 17, "line": 36 },
|
|
21
|
+
{ "ip": 50, "line": 45 },
|
|
22
|
+
{ "ip": 102, "line": 62 },
|
|
23
|
+
{ "ip": 109, "line": 67 }
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"compiler": {
|
|
27
|
+
"name": "cashc",
|
|
28
|
+
"version": "0.13.0",
|
|
29
|
+
"options": {
|
|
30
|
+
"enforceFunctionParameterTypes": true,
|
|
31
|
+
"enforceLocktimeGuard": true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"updatedAt": "2026-09-04T16:02:30.436Z"
|
|
35
35
|
}
|
package/art/authhead_vault.json
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"contractName": "AuthHeadVault",
|
|
3
|
-
"constructorInputs": [
|
|
4
|
-
{ "name": "authToken", "type": "bytes32" }
|
|
5
|
-
],
|
|
6
|
-
"abi": [
|
|
7
|
-
{ "name": "update", "inputs": [] },
|
|
8
|
-
{ "name": "burn", "inputs": [] }
|
|
9
|
-
],
|
|
10
|
-
"bytecode": "OP_OVER OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_0 OP_NUMEQUALVERIFY OP_INPUTINDEX
|
|
11
|
-
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * AuthHeadVault:
|
|
12
|
-
"fingerprint": "
|
|
13
|
-
"debug": {
|
|
14
|
-
"bytecode": "
|
|
15
|
-
"sourceMap": "
|
|
16
|
-
"logs": [],
|
|
17
|
-
"requires": [
|
|
18
|
-
{ "ip": 7, "line":
|
|
19
|
-
{ "ip": 12, "line":
|
|
20
|
-
{ "ip": 16, "line":
|
|
21
|
-
{ "ip": 57, "line":
|
|
22
|
-
{ "ip": 70, "line":
|
|
23
|
-
{ "ip": 74, "line":
|
|
24
|
-
{ "ip":
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"updatedAt": "2026-07-29T19:00:44.322Z"
|
|
1
|
+
{
|
|
2
|
+
"contractName": "AuthHeadVault",
|
|
3
|
+
"constructorInputs": [
|
|
4
|
+
{ "name": "authToken", "type": "bytes32" }
|
|
5
|
+
],
|
|
6
|
+
"abi": [
|
|
7
|
+
{ "name": "update", "inputs": [] },
|
|
8
|
+
{ "name": "burn", "inputs": [] }
|
|
9
|
+
],
|
|
10
|
+
"bytecode": "OP_OVER OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_0 OP_NUMEQUALVERIFY OP_INPUTINDEX OP_OUTPUTBYTECODE OP_INPUTINDEX OP_UTXOBYTECODE OP_EQUALVERIFY OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_0 OP_1 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_3 OP_SPLIT OP_DROP OP_1 OP_SPLIT OP_NIP 0002 OP_AND 0002 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_DROP OP_NIP OP_NIP OP_ELSE OP_SWAP OP_1 OP_NUMEQUALVERIFY OP_0 OP_OUTPUTBYTECODE OP_1 OP_SPLIT OP_DROP 6a OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_0 OP_0 OP_BEGIN OP_DUP OP_UTXOTOKENCATEGORY OP_3 OP_PICK OP_EQUAL OP_IF OP_DUP OP_UTXOTOKENCOMMITMENT OP_3 OP_SPLIT OP_DROP OP_1 OP_SPLIT OP_NIP 0020 OP_AND 0020 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_DROP OP_NIP OP_ENDIF",
|
|
11
|
+
"source": "pragma cashscript ~0.13.0;\r\n\r\n/**\r\n * AuthHeadVault: Token identity vault with authorization requirements\r\n * \r\n * Used specifically for authorizing fund's BCMR update operations.\r\n * Operations are authorized via token using commitment as permission bits.\r\n * \r\n * Parameters:\r\n * authToken: Token category that authorizes contract operations\r\n */\r\ncontract AuthHeadVault(bytes32 authToken)\r\n{\r\n /**\r\n * update(): Authorizes authhead update with strict transaction structure\r\n * \r\n * There are no restrictions on updating BCMR or identity.\r\n * Preserves the identity UTXO without combining identites.\r\n * Only one identity can be updated in a tx.\r\n *\r\n * Ensures:\r\n * - This vault is the first input (positioning control)\r\n * - First output has no tokens (clean state to maintain authhead integrity)\r\n * - Authorization token is present with bit 0x0002 (authhead update permission)\r\n */\r\n function update() {\r\n //\r\n // Verify this input\r\n require(this.activeInputIndex == 0, \"expected to be the first input\");\r\n\r\n //\r\n // Verify the identity output\r\n require(tx.outputs[this.activeInputIndex].lockingBytecode == tx.inputs[this.activeInputIndex].lockingBytecode);\r\n require(tx.outputs[this.activeInputIndex].tokenCategory == 0x, \"no token allowed on authhead\");\r\n\r\n //\r\n // Check for authorization token in ANY input but can skip the identity\r\n // Bit 0x0002 in commitment indicates authhead (BCMR maintenance) authorization\r\n bool authorized = false;\r\n int inputIndex = 1;\r\n do {\r\n if(tx.inputs[inputIndex].tokenCategory == authToken) {\r\n if((bytes(tx.inputs[inputIndex].nftCommitment.slice(1, 3)) & bytes(0x0002)) == 0x0002) {\r\n authorized = true;\r\n }\r\n }\r\n inputIndex = inputIndex + 1;\r\n } while(inputIndex < tx.inputs.length && !authorized);\r\n require(authorized, \"unauthorized user\");\r\n }\r\n\r\n /**\r\n * burn(): Authorizes authhead identity burning with strict transaction structure\r\n *\r\n * Able to burn multiple identities in a single tx.\r\n * Used to signal this identity is no longer maintained and for UTXO cleanup.\r\n *\r\n * Ensures:\r\n * - The outputs' authhead is an OP_RETURN\r\n * - The outputs' authhead has no value or tokens\r\n * - Authorization token is present with bit 0x0020 (authhead identity burning permission)\r\n */\r\n function burn() {\r\n //\r\n // Verify the burned identity\r\n require(tx.outputs[0].lockingBytecode.slice(0, 1) == 0x6a, \"first output must be an OP_RETURN\");\r\n require(tx.outputs[0].tokenCategory == 0x, \"no token allowed on authhead\");\r\n\r\n //\r\n // Check for authorization token in ANY input\r\n // Bit 0x0020 in commitment indicates authhead (BCMR maintenance) identity burning\r\n bool authorized = false;\r\n int inputIndex = 0;\r\n do {\r\n if(tx.inputs[inputIndex].tokenCategory == authToken) {\r\n if((bytes(tx.inputs[inputIndex].nftCommitment.slice(1, 3)) & bytes(0x0020)) == 0x0020) {\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}",
|
|
12
|
+
"fingerprint": "36471eb6fd442dd97f66c4ad592ff4b1ac1fd687fe4a83249db5b9632c71de11",
|
|
13
|
+
"debug": {
|
|
14
|
+
"bytecode": "78009c63c0009dc0cdc0c788c0d1008800516576ce5379876376cf537f75517f77020002840200028763517b757c6868768b7776c39f5279919a9166757777677c519d00cd517f75016a8800d1008800006576ce5379876376cf537f75517f77020020840200208763517b757c6868768b7776c39f5279919a9166757768",
|
|
15
|
+
"sourceMap": "26:4:50:5;;;;29:16:29:37;:41::42;:8::78:1;33:27:33:48:0;:16::65:1;:79::100:0;:69::117:1;:8::119;34:27:34:48:0;:16::63:1;:67::69:0;:8::103:1;39:26:39:31:0;40:25:40:26;41:8:48:62;42:25:42:35;:15::50:1;:54::63:0;;:15:::1;:65:46:13:0;43:36:43:46;:26::61:1;:71::72:0;:26::73:1;;:68::69:0;:26::73:1;;:83::89:0;:20::90:1;:95::101:0;:19:::1;:103:45:17:0;44:33:44:37;:20::38:1;;;43:103:45:17;42:65:46:13;47:25:47:35:0;:::39:1;:12::40;48:16:48:26:0;:29::45;:16:::1;:50::60:0;;:49:::1;:16;41:8::62;;26:22:50:5;;;:4;63::83::0;;;66:27:66:28;:16::45:1;:55::56:0;:16::57:1;;:61::65:0;:8::104:1;67:27:67:28:0;:16::43:1;:47::49:0;:8::83:1;72:26:72:31:0;73:25:73:26;74:8:81:62;75:25:75:35;:15::50:1;:54::63:0;;:15:::1;:65:79:13:0;76:36:76:46;:26::61:1;:71::72:0;:26::73:1;;:68::69:0;:26::73:1;;:83::89:0;:20::90:1;:95::101:0;:19:::1;:103:78:17:0;77:33:77:37;:20::38:1;;;76:103:78:17;75:65:79:13;80:25:80:35:0;:::39:1;:12::40;81:16:81:26:0;:29::45;:16:::1;:50::60:0;;:49:::1;:16;74:8::62;;63:20:83:5;;12:0:84:1",
|
|
16
|
+
"logs": [],
|
|
17
|
+
"requires": [
|
|
18
|
+
{ "ip": 7, "line": 29, "message": "expected to be the first input" },
|
|
19
|
+
{ "ip": 12, "line": 33 },
|
|
20
|
+
{ "ip": 16, "line": 34, "message": "no token allowed on authhead" },
|
|
21
|
+
{ "ip": 57, "line": 49, "message": "unauthorized user" },
|
|
22
|
+
{ "ip": 70, "line": 66, "message": "first output must be an OP_RETURN" },
|
|
23
|
+
{ "ip": 74, "line": 67, "message": "no token allowed on authhead" },
|
|
24
|
+
{ "ip": 115, "line": 82, "message": "unauthorized user" }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"compiler": {
|
|
28
|
+
"name": "cashc",
|
|
29
|
+
"version": "0.13.0",
|
|
30
|
+
"options": {
|
|
31
|
+
"enforceFunctionParameterTypes": true,
|
|
32
|
+
"enforceLocktimeGuard": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"updatedAt": "2026-09-04T16:02:32.347Z"
|
|
37
36
|
}
|