@totemsdk/core 1.0.0
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/dist/Streamable.d.ts +268 -0
- package/dist/Streamable.js +416 -0
- package/dist/adapters/index.d.ts +190 -0
- package/dist/adapters/index.js +107 -0
- package/dist/adapters/types.d.ts +149 -0
- package/dist/adapters/types.js +67 -0
- package/dist/binio.d.ts +9 -0
- package/dist/binio.js +30 -0
- package/dist/bip39.d.ts +91 -0
- package/dist/bip39.js +417 -0
- package/dist/derive.d.ts +2 -0
- package/dist/derive.js +9 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +53 -0
- package/dist/javaStreamables.d.ts +233 -0
- package/dist/javaStreamables.js +332 -0
- package/dist/lease/LeaseMonitor.d.ts +44 -0
- package/dist/lease/LeaseMonitor.js +107 -0
- package/dist/lease/LeaseStore.d.ts +57 -0
- package/dist/lease/LeaseStore.js +144 -0
- package/dist/lease/WatermarkStore.d.ts +61 -0
- package/dist/lease/WatermarkStore.js +202 -0
- package/dist/lease/index.d.ts +14 -0
- package/dist/lease/index.js +13 -0
- package/dist/lease-client.d.ts +31 -0
- package/dist/lease-client.js +39 -0
- package/dist/minima32.d.ts +18 -0
- package/dist/minima32.js +112 -0
- package/dist/minimaWireSerializer.d.ts +60 -0
- package/dist/minimaWireSerializer.js +71 -0
- package/dist/mmr.d.ts +156 -0
- package/dist/mmr.js +342 -0
- package/dist/mx.d.ts +1 -0
- package/dist/mx.js +1 -0
- package/dist/params.d.ts +29 -0
- package/dist/params.js +16 -0
- package/dist/script.d.ts +7 -0
- package/dist/script.js +22 -0
- package/dist/scripts/contract-helpers.d.ts +252 -0
- package/dist/scripts/contract-helpers.js +394 -0
- package/dist/scripts/dapp-types.d.ts +119 -0
- package/dist/scripts/dapp-types.js +72 -0
- package/dist/scripts/index.d.ts +4 -0
- package/dist/scripts/index.js +4 -0
- package/dist/scripts/types.d.ts +81 -0
- package/dist/scripts/types.js +145 -0
- package/dist/scripts/witness-serializer.d.ts +29 -0
- package/dist/scripts/witness-serializer.js +288 -0
- package/dist/transaction.d.ts +106 -0
- package/dist/transaction.js +284 -0
- package/dist/treekey.d.ts +383 -0
- package/dist/treekey.js +762 -0
- package/dist/tx/TransactionLifecycle.d.ts +55 -0
- package/dist/tx/TransactionLifecycle.js +189 -0
- package/dist/tx/TransactionReceiptStore.d.ts +36 -0
- package/dist/tx/TransactionReceiptStore.js +85 -0
- package/dist/tx/TransactionService.d.ts +50 -0
- package/dist/tx/TransactionService.js +178 -0
- package/dist/tx/index.d.ts +13 -0
- package/dist/tx/index.js +12 -0
- package/dist/tx/types.d.ts +114 -0
- package/dist/tx/types.js +5 -0
- package/dist/util.d.ts +1 -0
- package/dist/util.js +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +35 -0
- package/dist/verify.d.ts +18 -0
- package/dist/verify.js +162 -0
- package/dist/version.d.ts +12 -0
- package/dist/version.js +12 -0
- package/dist/wots.d.ts +232 -0
- package/dist/wots.js +538 -0
- package/package.json +65 -0
- package/src/Streamable.d.ts +265 -0
- package/src/Streamable.js +431 -0
- package/src/Streamable.ts +567 -0
- package/src/__tests__/Streamable.parity.test.ts +422 -0
- package/src/__tests__/perAddressDerivation.parity.test.ts +152 -0
- package/src/__tests__/wots-parity.test.ts +289 -0
- package/src/adapters/index.d.ts +190 -0
- package/src/adapters/index.js +117 -0
- package/src/adapters/index.ts +301 -0
- package/src/adapters/types.ts +235 -0
- package/src/addr.oracle.test.ts +25 -0
- package/src/address.test.ts +55 -0
- package/src/binio.ts +21 -0
- package/src/bip39.d.ts +91 -0
- package/src/bip39.js +427 -0
- package/src/bip39.ts +432 -0
- package/src/derive.d.ts +2 -0
- package/src/derive.js +13 -0
- package/src/derive.ts +11 -0
- package/src/index.d.ts +26 -0
- package/src/index.js +128 -0
- package/src/index.test.ts +5 -0
- package/src/index.ts +206 -0
- package/src/javaStreamables.d.ts +233 -0
- package/src/javaStreamables.js +328 -0
- package/src/javaStreamables.ts +403 -0
- package/src/lease/LeaseMonitor.d.ts +44 -0
- package/src/lease/LeaseMonitor.js +111 -0
- package/src/lease/LeaseMonitor.ts +157 -0
- package/src/lease/LeaseStore.d.ts +57 -0
- package/src/lease/LeaseStore.js +148 -0
- package/src/lease/LeaseStore.ts +203 -0
- package/src/lease/WatermarkStore.d.ts +61 -0
- package/src/lease/WatermarkStore.js +206 -0
- package/src/lease/WatermarkStore.ts +270 -0
- package/src/lease/index.d.ts +14 -0
- package/src/lease/index.js +22 -0
- package/src/lease/index.ts +41 -0
- package/src/lease-client.d.ts +31 -0
- package/src/lease-client.js +44 -0
- package/src/lease-client.ts +69 -0
- package/src/minima32.d.ts +10 -0
- package/src/minima32.js +113 -0
- package/src/minima32.test.ts +11 -0
- package/src/minima32.ts +127 -0
- package/src/minimaWireSerializer.d.ts +189 -0
- package/src/minimaWireSerializer.js +311 -0
- package/src/minimaWireSerializer.test.ts +466 -0
- package/src/minimaWireSerializer.ts +117 -0
- package/src/mmr.d.ts +153 -0
- package/src/mmr.js +351 -0
- package/src/mmr.oracle.test.ts +28 -0
- package/src/mmr.ts +429 -0
- package/src/mx.test.ts +25 -0
- package/src/mx.ts +1 -0
- package/src/params.d.ts +29 -0
- package/src/params.d.ts.map +1 -0
- package/src/params.js +20 -0
- package/src/params.js.map +1 -0
- package/src/params.ts +44 -0
- package/src/script.d.ts +7 -0
- package/src/script.js +27 -0
- package/src/script.ts +24 -0
- package/src/scripts/contract-helpers.ts +609 -0
- package/src/scripts/dapp-types.ts +240 -0
- package/src/scripts/index.ts +22 -0
- package/src/scripts/types.ts +247 -0
- package/src/scripts/witness-serializer.ts +384 -0
- package/src/transaction.ts +430 -0
- package/src/treekey.d.ts +383 -0
- package/src/treekey.js +778 -0
- package/src/treekey.parity.test.ts +459 -0
- package/src/treekey.ts +979 -0
- package/src/tx/TransactionLifecycle.d.ts +55 -0
- package/src/tx/TransactionLifecycle.js +188 -0
- package/src/tx/TransactionLifecycle.ts +280 -0
- package/src/tx/TransactionReceiptStore.d.ts +36 -0
- package/src/tx/TransactionReceiptStore.js +89 -0
- package/src/tx/TransactionReceiptStore.ts +112 -0
- package/src/tx/TransactionService.d.ts +31 -0
- package/src/tx/TransactionService.js +140 -0
- package/src/tx/TransactionService.ts +250 -0
- package/src/tx/index.d.ts +13 -0
- package/src/tx/index.js +20 -0
- package/src/tx/index.ts +44 -0
- package/src/tx/types.d.ts +80 -0
- package/src/tx/types.js +6 -0
- package/src/tx/types.ts +127 -0
- package/src/util.d.ts +1 -0
- package/src/util.js +5 -0
- package/src/util.ts +1 -0
- package/src/utils.d.ts +8 -0
- package/src/utils.js +42 -0
- package/src/utils.ts +38 -0
- package/src/verify.ts +209 -0
- package/src/version.ts +13 -0
- package/src/wots.d.ts +224 -0
- package/src/wots.d.ts.map +1 -0
- package/src/wots.js +559 -0
- package/src/wots.js.map +1 -0
- package/src/wots.ts +636 -0
- package/test-vectors.json +45 -0
|
@@ -0,0 +1,609 @@
|
|
|
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';
|
|
18
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
19
|
+
import { randomBytes } from '@noble/hashes/utils';
|
|
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
|
+
}
|