@totemsdk/core 1.0.9 → 1.0.10

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.
Files changed (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -5
  3. package/dist/Streamable.js +54 -31
  4. package/dist/adapters/index.js +18 -8
  5. package/dist/adapters/types.js +19 -8
  6. package/dist/binio.js +5 -1
  7. package/dist/bip39.js +24 -14
  8. package/dist/derive.js +11 -7
  9. package/dist/index.js +130 -22
  10. package/dist/javaStreamables.js +49 -30
  11. package/dist/lease/LeaseMonitor.js +7 -3
  12. package/dist/lease/LeaseStore.js +7 -3
  13. package/dist/lease/WatermarkStore.js +7 -3
  14. package/dist/lease/index.js +13 -4
  15. package/dist/lease-client.js +8 -3
  16. package/dist/minima32.js +22 -13
  17. package/dist/minimaWireSerializer.js +20 -14
  18. package/dist/mmr.js +35 -23
  19. package/dist/mx.js +10 -1
  20. package/dist/params.js +9 -5
  21. package/dist/script.js +12 -7
  22. package/dist/scripts/contract-helpers.js +37 -26
  23. package/dist/scripts/dapp-types.js +11 -6
  24. package/dist/scripts/index.js +38 -4
  25. package/dist/scripts/types.js +26 -12
  26. package/dist/scripts/witness-serializer.js +48 -30
  27. package/dist/transaction.js +55 -46
  28. package/dist/treekey.js +74 -58
  29. package/dist/tx/TransactionLifecycle.js +11 -5
  30. package/dist/tx/TransactionReceiptStore.js +7 -3
  31. package/dist/tx/TransactionService.js +14 -10
  32. package/dist/tx/index.js +11 -3
  33. package/dist/tx/types.js +2 -1
  34. package/dist/util.js +5 -1
  35. package/dist/utils.js +12 -5
  36. package/dist/verify.js +36 -25
  37. package/dist/version.js +5 -2
  38. package/dist/wots.js +94 -64
  39. package/package.json +31 -6
  40. package/src/Streamable.d.ts +0 -268
  41. package/src/Streamable.js +0 -416
  42. package/src/Streamable.ts +0 -567
  43. package/src/__tests__/Streamable.parity.test.ts +0 -422
  44. package/src/__tests__/perAddressDerivation.parity.test.ts +0 -152
  45. package/src/__tests__/roottree-prepare.test.ts +0 -101
  46. package/src/__tests__/wots-parity.test.ts +0 -289
  47. package/src/adapters/index.d.ts +0 -190
  48. package/src/adapters/index.js +0 -117
  49. package/src/adapters/index.ts +0 -301
  50. package/src/adapters/types.ts +0 -235
  51. package/src/addr.oracle.test.ts +0 -25
  52. package/src/address.test.ts +0 -55
  53. package/src/binio.ts +0 -21
  54. package/src/bip39.d.ts +0 -91
  55. package/src/bip39.js +0 -427
  56. package/src/bip39.ts +0 -432
  57. package/src/derive.d.ts +0 -2
  58. package/src/derive.js +0 -13
  59. package/src/derive.ts +0 -11
  60. package/src/index.d.ts +0 -26
  61. package/src/index.js +0 -128
  62. package/src/index.test.ts +0 -5
  63. package/src/index.ts +0 -221
  64. package/src/javaStreamables.d.ts +0 -233
  65. package/src/javaStreamables.js +0 -332
  66. package/src/javaStreamables.ts +0 -403
  67. package/src/lease/LeaseMonitor.d.ts +0 -44
  68. package/src/lease/LeaseMonitor.js +0 -111
  69. package/src/lease/LeaseMonitor.ts +0 -157
  70. package/src/lease/LeaseStore.d.ts +0 -57
  71. package/src/lease/LeaseStore.js +0 -148
  72. package/src/lease/LeaseStore.ts +0 -203
  73. package/src/lease/WatermarkStore.d.ts +0 -61
  74. package/src/lease/WatermarkStore.js +0 -206
  75. package/src/lease/WatermarkStore.ts +0 -270
  76. package/src/lease/index.d.ts +0 -14
  77. package/src/lease/index.js +0 -22
  78. package/src/lease/index.ts +0 -41
  79. package/src/lease-client.d.ts +0 -31
  80. package/src/lease-client.js +0 -44
  81. package/src/lease-client.ts +0 -69
  82. package/src/minima32.d.ts +0 -10
  83. package/src/minima32.js +0 -113
  84. package/src/minima32.test.ts +0 -11
  85. package/src/minima32.ts +0 -127
  86. package/src/minimaWireSerializer.d.ts +0 -189
  87. package/src/minimaWireSerializer.js +0 -311
  88. package/src/minimaWireSerializer.test.ts +0 -466
  89. package/src/minimaWireSerializer.ts +0 -117
  90. package/src/mmr.d.ts +0 -157
  91. package/src/mmr.js +0 -340
  92. package/src/mmr.oracle.test.ts +0 -28
  93. package/src/mmr.ts +0 -429
  94. package/src/mx.test.ts +0 -25
  95. package/src/mx.ts +0 -1
  96. package/src/params.d.ts +0 -29
  97. package/src/params.d.ts.map +0 -1
  98. package/src/params.js +0 -20
  99. package/src/params.js.map +0 -1
  100. package/src/params.ts +0 -44
  101. package/src/script.d.ts +0 -7
  102. package/src/script.js +0 -27
  103. package/src/script.ts +0 -24
  104. package/src/scripts/contract-helpers.ts +0 -609
  105. package/src/scripts/dapp-types.ts +0 -240
  106. package/src/scripts/index.ts +0 -22
  107. package/src/scripts/types.d.ts +0 -81
  108. package/src/scripts/types.js +0 -145
  109. package/src/scripts/types.ts +0 -247
  110. package/src/scripts/witness-serializer.ts +0 -384
  111. package/src/transaction.ts +0 -430
  112. package/src/treekey.d.ts +0 -383
  113. package/src/treekey.js +0 -774
  114. package/src/treekey.parity.test.ts +0 -459
  115. package/src/treekey.ts +0 -975
  116. package/src/tx/TransactionLifecycle.d.ts +0 -55
  117. package/src/tx/TransactionLifecycle.js +0 -188
  118. package/src/tx/TransactionLifecycle.ts +0 -280
  119. package/src/tx/TransactionReceiptStore.d.ts +0 -36
  120. package/src/tx/TransactionReceiptStore.js +0 -89
  121. package/src/tx/TransactionReceiptStore.ts +0 -112
  122. package/src/tx/TransactionService.d.ts +0 -31
  123. package/src/tx/TransactionService.js +0 -140
  124. package/src/tx/TransactionService.ts +0 -252
  125. package/src/tx/index.d.ts +0 -13
  126. package/src/tx/index.js +0 -20
  127. package/src/tx/index.ts +0 -44
  128. package/src/tx/types.d.ts +0 -80
  129. package/src/tx/types.js +0 -6
  130. package/src/tx/types.ts +0 -130
  131. package/src/util.d.ts +0 -1
  132. package/src/util.js +0 -5
  133. package/src/util.ts +0 -1
  134. package/src/utils.d.ts +0 -8
  135. package/src/utils.js +0 -42
  136. package/src/utils.ts +0 -38
  137. package/src/verify.ts +0 -210
  138. package/src/version.ts +0 -13
  139. package/src/wots.d.ts +0 -224
  140. package/src/wots.d.ts.map +0 -1
  141. package/src/wots.js +0 -565
  142. package/src/wots.js.map +0 -1
  143. package/src/wots.ts +0 -651
  144. package/test-vectors.json +0 -45
@@ -1,609 +0,0 @@
1
- /**
2
- * Contract Helpers
3
- *
4
- * Provides high-level helpers for building specific contract types:
5
- * - Timelock contracts
6
- * - HTLC (Hashed Timelock Contracts)
7
- * - MAST (Merkelized Abstract Syntax Trees)
8
- * - Exchange contracts
9
- * - Vault/covenant contracts
10
- * - Flash/slow cash
11
- * - Stateful game contracts
12
- *
13
- * Each helper validates inputs and builds the appropriate ScriptDescriptor
14
- * for use with the EnhancedTransactionBuilder.
15
- */
16
-
17
- import { sha3_256 } from '@noble/hashes/sha3.js';
18
- import { sha256 } from '@noble/hashes/sha2.js';
19
- import { randomBytes } from '@noble/hashes/utils.js';
20
- import type {
21
- ScriptDescriptor,
22
- StateValue,
23
- MMRProof,
24
- VerifyOutExpectation
25
- } from './types.js';
26
- import {
27
- createTimelockDescriptor,
28
- createHTLCDescriptor,
29
- createMASTDescriptor,
30
- createExchangeDescriptor,
31
- createFlashCashDescriptor,
32
- createSlowCashDescriptor,
33
- createEmptyMMRProof
34
- } from './types.js';
35
-
36
- function kissHex(hex: string): string {
37
- const raw = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;
38
- return '0x' + raw.toUpperCase();
39
- }
40
-
41
- function hexToBytes(hex: string): Uint8Array {
42
- const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
43
- const bytes = new Uint8Array(cleanHex.length / 2);
44
- for (let i = 0; i < cleanHex.length; i += 2) {
45
- bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
46
- }
47
- return bytes;
48
- }
49
-
50
- function bytesToHex(bytes: Uint8Array): string {
51
- return '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
52
- }
53
-
54
- /**
55
- * Timelock Helper
56
- *
57
- * Creates timelocked scripts that can only be spent after a certain block.
58
- */
59
- export class TimelockHelper {
60
- /**
61
- * Create a timelock script that unlocks at a specific block.
62
- */
63
- static createBlockTimelock(
64
- publicKey: string,
65
- unlockBlock: bigint
66
- ): { script: string; address: string } {
67
- const script = `RETURN SIGNEDBY(${kissHex(publicKey)}) AND @BLOCK GT ${unlockBlock}`;
68
- const address = computeScriptAddress(script);
69
-
70
- return { script, address };
71
- }
72
-
73
- /**
74
- * Create a timelock script based on coin age.
75
- */
76
- static createCoinageTimelock(
77
- publicKey: string,
78
- minCoinAge: bigint
79
- ): { script: string; address: string } {
80
- const script = `RETURN SIGNEDBY(${kissHex(publicKey)}) AND @COINAGE GT ${minCoinAge}`;
81
- const address = computeScriptAddress(script);
82
-
83
- return { script, address };
84
- }
85
-
86
- /**
87
- * Check if a timelock is satisfied given current block.
88
- */
89
- static isUnlocked(unlockBlock: bigint, currentBlock: bigint): boolean {
90
- return currentBlock > unlockBlock;
91
- }
92
-
93
- /**
94
- * Build ScriptDescriptor for a timelock spend.
95
- */
96
- static buildDescriptor(
97
- address: string,
98
- publicKey: string,
99
- unlockBlock: bigint
100
- ): ScriptDescriptor {
101
- return createTimelockDescriptor(address, publicKey, unlockBlock);
102
- }
103
- }
104
-
105
- /**
106
- * HTLC Helper
107
- *
108
- * Creates Hashed Timelock Contracts for atomic swaps and lightning-style payments.
109
- */
110
- export class HTLCHelper {
111
- /**
112
- * Generate a random preimage and its hash.
113
- */
114
- static generateSecret(): { preimage: string; hash: string } {
115
- const preimageBytes = randomBytes(32);
116
- const preimage = bytesToHex(preimageBytes);
117
-
118
- const hashBytes = sha3_256(preimageBytes);
119
- const hash = bytesToHex(hashBytes);
120
-
121
- return { preimage, hash };
122
- }
123
-
124
- /**
125
- * Hash a preimage using SHA3 (default) or SHA2.
126
- */
127
- static hashPreimage(preimage: string, algorithm: 'sha3' | 'sha2' = 'sha3'): string {
128
- const preimageBytes = hexToBytes(preimage);
129
-
130
- if (algorithm === 'sha2') {
131
- const hashBytes = sha256(preimageBytes);
132
- return bytesToHex(hashBytes);
133
- } else {
134
- const hashBytes = sha3_256(preimageBytes);
135
- return bytesToHex(hashBytes);
136
- }
137
- }
138
-
139
- /**
140
- * Verify a preimage matches a hash.
141
- */
142
- static verifyPreimage(
143
- preimage: string,
144
- expectedHash: string,
145
- algorithm: 'sha3' | 'sha2' = 'sha3'
146
- ): boolean {
147
- const computedHash = this.hashPreimage(preimage, algorithm);
148
- return computedHash.toLowerCase() === expectedHash.toLowerCase();
149
- }
150
-
151
- /**
152
- * Create an HTLC script.
153
- *
154
- * The script allows:
155
- * - Recipient to claim with preimage before timeout
156
- * - Sender to refund after timeout
157
- */
158
- static createHTLC(
159
- senderPublicKey: string,
160
- recipientPublicKey: string,
161
- hashLock: string,
162
- timeoutBlock: bigint,
163
- algorithm: 'sha3' | 'sha2' = 'sha3'
164
- ): { script: string; address: string } {
165
- const hashFunc = algorithm === 'sha2' ? 'SHA2' : 'SHA3';
166
-
167
- const script = `IF @BLOCK GT ${timeoutBlock} AND SIGNEDBY(${kissHex(senderPublicKey)}) THEN RETURN TRUE ENDIF RETURN (SIGNEDBY(${kissHex(recipientPublicKey)}) AND ${hashFunc}(STATE(1)) EQ ${kissHex(hashLock)})`;
168
-
169
- const address = computeScriptAddress(script);
170
-
171
- return { script, address };
172
- }
173
-
174
- /**
175
- * Build ScriptDescriptor to claim HTLC with preimage.
176
- */
177
- static buildClaimDescriptor(
178
- address: string,
179
- senderPublicKey: string,
180
- recipientPublicKey: string,
181
- hashLock: string,
182
- timeoutBlock: bigint,
183
- preimage: string
184
- ): ScriptDescriptor {
185
- return createHTLCDescriptor(
186
- address,
187
- senderPublicKey,
188
- recipientPublicKey,
189
- hashLock,
190
- timeoutBlock,
191
- false,
192
- preimage
193
- );
194
- }
195
-
196
- /**
197
- * Build ScriptDescriptor to refund HTLC after timeout.
198
- */
199
- static buildRefundDescriptor(
200
- address: string,
201
- senderPublicKey: string,
202
- recipientPublicKey: string,
203
- hashLock: string,
204
- timeoutBlock: bigint
205
- ): ScriptDescriptor {
206
- return createHTLCDescriptor(
207
- address,
208
- senderPublicKey,
209
- recipientPublicKey,
210
- hashLock,
211
- timeoutBlock,
212
- true
213
- );
214
- }
215
- }
216
-
217
- /**
218
- * MAST Helper
219
- *
220
- * Creates Merkelized Abstract Syntax Tree contracts for privacy and scalability.
221
- */
222
- export class MASTHelper {
223
- /**
224
- * Compute hash of a script for MAST leaf.
225
- */
226
- static hashScript(script: string): string {
227
- const scriptBytes = new TextEncoder().encode(script.trim().toUpperCase());
228
- const hashBytes = sha3_256(scriptBytes);
229
- return bytesToHex(hashBytes);
230
- }
231
-
232
- /**
233
- * Build a simple MAST tree from multiple scripts.
234
- * Returns the root hash and proofs for each script.
235
- *
236
- * For a proper implementation, this should call the mmrcreate RPC.
237
- * This is a simplified local version for 2 scripts.
238
- */
239
- static buildSimpleTree(scripts: string[]): {
240
- root: string;
241
- proofs: Map<string, { proof: string; index: number }>;
242
- } {
243
- if (scripts.length === 0) {
244
- throw new Error('MAST requires at least one script');
245
- }
246
-
247
- if (scripts.length === 1) {
248
- const hash = this.hashScript(scripts[0]);
249
- return {
250
- root: hash,
251
- proofs: new Map([[scripts[0], { proof: '', index: 0 }]])
252
- };
253
- }
254
-
255
- const hashes = scripts.map(s => this.hashScript(s));
256
- const proofs = new Map<string, { proof: string; index: number }>();
257
-
258
- for (let i = 0; i < scripts.length; i++) {
259
- const siblingIndex = i % 2 === 0 ? i + 1 : i - 1;
260
- const siblingHash = hashes[siblingIndex] || hashes[i];
261
- const isLeft = i % 2 === 1;
262
-
263
- const proofHex = encodeSimpleProof(siblingHash, isLeft);
264
- proofs.set(scripts[i], { proof: proofHex, index: i });
265
- }
266
-
267
- const combinedBytes = hexToBytes(hashes[0].slice(2) + hashes[1].slice(2));
268
- const rootBytes = sha3_256(combinedBytes);
269
- const root = bytesToHex(rootBytes);
270
-
271
- return { root, proofs };
272
- }
273
-
274
- /**
275
- * Create a MAST script with the given root hash.
276
- */
277
- static createMASTScript(rootHash: string): { script: string; address: string } {
278
- const script = `MAST ${kissHex(rootHash)}`;
279
- const address = computeScriptAddress(script);
280
-
281
- return { script, address };
282
- }
283
-
284
- /**
285
- * Build ScriptDescriptor for spending a MAST branch.
286
- */
287
- static buildDescriptor(
288
- address: string,
289
- rootHash: string,
290
- branchScript: string,
291
- branchProof: string,
292
- wotsPublicKey?: string
293
- ): ScriptDescriptor {
294
- return createMASTDescriptor(
295
- address,
296
- rootHash,
297
- branchScript,
298
- branchProof,
299
- wotsPublicKey
300
- );
301
- }
302
- }
303
-
304
- /**
305
- * Exchange Contract Helper
306
- *
307
- * Creates DEX-style exchange contracts using VERIFYOUT.
308
- */
309
- export class ExchangeHelper {
310
- /**
311
- * Create an exchange offer script.
312
- *
313
- * Owner can cancel, or anyone can take the offer by providing
314
- * the specified output.
315
- */
316
- static createOffer(
317
- ownerPublicKey: string,
318
- desiredAddress: string,
319
- desiredAmount: string,
320
- desiredTokenId: string
321
- ): { script: string; address: string } {
322
- const script = `IF SIGNEDBY(PREVSTATE(0)) THEN RETURN TRUE ENDIF ASSERT VERIFYOUT(@INPUT PREVSTATE(1) PREVSTATE(2) PREVSTATE(3) TRUE) RETURN TRUE`;
323
- const address = computeScriptAddress(script);
324
-
325
- return { script, address };
326
- }
327
-
328
- /**
329
- * Build state variables for an exchange offer.
330
- */
331
- static buildOfferState(
332
- ownerPublicKey: string,
333
- desiredAddress: string,
334
- desiredAmount: string,
335
- desiredTokenId: string
336
- ): StateValue[] {
337
- return [
338
- { port: 0, value: ownerPublicKey, type: 'hex' },
339
- { port: 1, value: desiredAddress, type: 'hex' },
340
- { port: 2, value: desiredAmount, type: 'number' },
341
- { port: 3, value: desiredTokenId, type: 'hex' }
342
- ];
343
- }
344
-
345
- /**
346
- * Build ScriptDescriptor for taking an exchange offer.
347
- */
348
- static buildTakeOfferDescriptor(
349
- address: string,
350
- ownerPublicKey: string,
351
- desiredAddress: string,
352
- desiredAmount: string,
353
- desiredTokenId: string
354
- ): ScriptDescriptor {
355
- return createExchangeDescriptor(
356
- address,
357
- ownerPublicKey,
358
- desiredAddress,
359
- desiredAmount,
360
- desiredTokenId
361
- );
362
- }
363
-
364
- /**
365
- * Validate VERIFYOUT for an exchange transaction.
366
- */
367
- static validateExchange(
368
- outputs: Array<{ address: string; amount: string; tokenId: string }>,
369
- expectedAddress: string,
370
- expectedAmount: string,
371
- expectedTokenId: string,
372
- inputIndex: number
373
- ): { valid: boolean; error?: string } {
374
- if (inputIndex >= outputs.length) {
375
- return { valid: false, error: `Output at index ${inputIndex} does not exist` };
376
- }
377
-
378
- const output = outputs[inputIndex];
379
-
380
- if (output.address.toLowerCase() !== expectedAddress.toLowerCase()) {
381
- return { valid: false, error: 'Address mismatch' };
382
- }
383
-
384
- if (output.amount !== expectedAmount) {
385
- return { valid: false, error: 'Amount mismatch' };
386
- }
387
-
388
- if (output.tokenId.toLowerCase() !== expectedTokenId.toLowerCase()) {
389
- return { valid: false, error: 'TokenId mismatch' };
390
- }
391
-
392
- return { valid: true };
393
- }
394
- }
395
-
396
- /**
397
- * Vault Helper
398
- *
399
- * Creates vault/covenant contracts with safe house enforcement.
400
- */
401
- export class VaultHelper {
402
- /**
403
- * Generate safe house script from vault parameters.
404
- */
405
- static generateSafeHouseScript(
406
- coldKey: string,
407
- hotKey: string,
408
- cooldownBlocks: bigint = 20n
409
- ): string {
410
- return `LET pkcold = ${kissHex(coldKey)} LET pkhot = ${kissHex(hotKey)} IF SIGNEDBY(pkcold) THEN RETURN TRUE ENDIF IF SIGNEDBY(pkhot) THEN IF @COINAGE GT ${cooldownBlocks} THEN RETURN VERIFYOUT(@INPUT PREVSTATE(21) @AMOUNT @TOKENID TRUE) ENDIF ENDIF`;
411
- }
412
-
413
- /**
414
- * Create a vault script.
415
- */
416
- static createVault(
417
- coldKey: string,
418
- hotKey: string,
419
- cooldownBlocks: bigint = 20n
420
- ): { vaultScript: string; vaultAddress: string; safeHouseScript: string; safeHouseAddress: string } {
421
- const safeHouseScript = this.generateSafeHouseScript(coldKey, hotKey, cooldownBlocks);
422
- const safeHouseAddress = computeScriptAddress(safeHouseScript);
423
-
424
- const vaultScript = `LET pkcold = ${kissHex(coldKey)} LET pkhot = ${kissHex(hotKey)} IF SIGNEDBY(pkcold) THEN RETURN TRUE ENDIF LET amt = STATE(20) LET recip = STATE(21) LET safehouse = [${safeHouseScript}] ASSERT VERIFYOUT(@INPUT ADDRESS(safehouse) amt @TOKENID TRUE) LET chg = @AMOUNT - amt IF chg GT 0 THEN ASSERT VERIFYOUT(INC(@INPUT) @ADDRESS (@AMOUNT - amt) @TOKENID TRUE) ENDIF RETURN SIGNEDBY(pkhot)`;
425
-
426
- const vaultAddress = computeScriptAddress(vaultScript);
427
-
428
- return {
429
- vaultScript,
430
- vaultAddress,
431
- safeHouseScript,
432
- safeHouseAddress
433
- };
434
- }
435
-
436
- /**
437
- * Build state for vault withdrawal.
438
- */
439
- static buildWithdrawalState(
440
- amount: string,
441
- recipientAddress: string
442
- ): StateValue[] {
443
- return [
444
- { port: 20, value: amount, type: 'number' },
445
- { port: 21, value: recipientAddress, type: 'hex' }
446
- ];
447
- }
448
- }
449
-
450
- /**
451
- * Flash Cash Helper
452
- *
453
- * Creates flash loan contracts for single-transaction borrowing.
454
- */
455
- export class FlashCashHelper {
456
- /**
457
- * Create a flash cash contract.
458
- */
459
- static createFlashCash(
460
- ownerPublicKey: string,
461
- interestMultiplier: string = '1.01'
462
- ): { script: string; address: string } {
463
- const script = `IF SIGNEDBY(PREVSTATE(1)) THEN RETURN TRUE ENDIF ASSERT SAMESTATE(1 1) RETURN VERIFYOUT(@INPUT @ADDRESS @AMOUNT*${interestMultiplier} @TOKENID TRUE)`;
464
- const address = computeScriptAddress(script);
465
-
466
- return { script, address };
467
- }
468
-
469
- /**
470
- * Calculate return amount with interest.
471
- */
472
- static calculateReturn(
473
- borrowAmount: bigint,
474
- interestMultiplier: number
475
- ): bigint {
476
- const multiplied = Number(borrowAmount) * interestMultiplier;
477
- return BigInt(Math.ceil(multiplied));
478
- }
479
-
480
- /**
481
- * Build ScriptDescriptor for borrowing flash cash.
482
- */
483
- static buildBorrowDescriptor(
484
- address: string,
485
- ownerPublicKey: string,
486
- interestMultiplier: string = '1.01'
487
- ): ScriptDescriptor {
488
- return createFlashCashDescriptor(address, ownerPublicKey, interestMultiplier);
489
- }
490
- }
491
-
492
- /**
493
- * Slow Cash Helper
494
- *
495
- * Creates rate-limited withdrawal contracts.
496
- */
497
- export class SlowCashHelper {
498
- /**
499
- * Create a slow cash contract.
500
- */
501
- static createSlowCash(
502
- ownerPublicKey: string,
503
- withdrawalPercent: string = '0.9',
504
- cooldownBlocks: bigint = 10000n
505
- ): { script: string; address: string } {
506
- const script = `IF @COINAGE LT ${cooldownBlocks} THEN RETURN FALSE ENDIF ASSERT SIGNEDBY(${kissHex(ownerPublicKey)}) AND VERIFYOUT(@INPUT @ADDRESS @AMOUNT*${withdrawalPercent} @TOKENID TRUE)`;
507
- const address = computeScriptAddress(script);
508
-
509
- return { script, address };
510
- }
511
-
512
- /**
513
- * Calculate withdrawal amount.
514
- */
515
- static calculateWithdrawal(
516
- currentAmount: bigint,
517
- withdrawalPercent: number
518
- ): { withdrawal: bigint; remaining: bigint } {
519
- const remaining = BigInt(Math.floor(Number(currentAmount) * withdrawalPercent));
520
- const withdrawal = currentAmount - remaining;
521
-
522
- return { withdrawal, remaining };
523
- }
524
-
525
- /**
526
- * Check if withdrawal is allowed based on coin age.
527
- */
528
- static canWithdraw(coinAge: bigint, cooldownBlocks: bigint): boolean {
529
- return coinAge >= cooldownBlocks;
530
- }
531
-
532
- /**
533
- * Build ScriptDescriptor for slow cash withdrawal.
534
- */
535
- static buildWithdrawalDescriptor(
536
- address: string,
537
- ownerPublicKey: string,
538
- withdrawalPercent: string = '0.9',
539
- cooldownBlocks: bigint = 10000n
540
- ): ScriptDescriptor {
541
- return createSlowCashDescriptor(address, ownerPublicKey, withdrawalPercent, cooldownBlocks);
542
- }
543
- }
544
-
545
- /**
546
- * Stateful Game Helper
547
- *
548
- * Creates multi-round stateful contracts (like coin flip).
549
- */
550
- export class StatefulGameHelper {
551
- /**
552
- * Create a round increment assertion.
553
- */
554
- static createRoundCheck(): string {
555
- return `LET round = STATE(0) LET prevround = PREVSTATE(0) ASSERT round EQ INC(prevround)`;
556
- }
557
-
558
- /**
559
- * Build state for next round.
560
- */
561
- static buildNextRoundState(
562
- currentRound: number,
563
- preservedPorts: number[],
564
- newStates: StateValue[]
565
- ): StateValue[] {
566
- const states: StateValue[] = [
567
- { port: 0, value: BigInt(currentRound + 1), type: 'number' }
568
- ];
569
-
570
- for (const sv of newStates) {
571
- states.push(sv);
572
- }
573
-
574
- return states;
575
- }
576
-
577
- /**
578
- * Validate round progression.
579
- */
580
- static validateRound(
581
- previousRound: number,
582
- currentRound: number
583
- ): boolean {
584
- return currentRound === previousRound + 1;
585
- }
586
- }
587
-
588
- function computeScriptAddress(script: string): string {
589
- const cleanScript = script.trim().toUpperCase();
590
- const scriptBytes = new TextEncoder().encode(cleanScript);
591
- const hashBytes = sha3_256(scriptBytes);
592
- return bytesToHex(hashBytes);
593
- }
594
-
595
- function encodeSimpleProof(siblingHash: string, isLeft: boolean): string {
596
- const parts: number[] = [];
597
-
598
- parts.push(0, 0, 0, 1, 0, 1);
599
-
600
- parts.push(isLeft ? 1 : 0);
601
-
602
- const hashBytes = hexToBytes(siblingHash);
603
- parts.push(0, 0, 0, hashBytes.length);
604
- parts.push(...Array.from(hashBytes));
605
-
606
- parts.push(0, 0, 0, 0, 1, 0);
607
-
608
- return bytesToHex(new Uint8Array(parts));
609
- }