@opcat-labs/scrypt-ts-opcat 2.0.2 → 2.1.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.
Files changed (105) hide show
  1. package/assets/.templates/smart-contract/builtin-libs/backtrace.scrypt.map +1 -1
  2. package/assets/.templates/smart-contract/builtin-libs/backtrace.scrypt.tpl +4 -0
  3. package/assets/.templates/smart-contract/builtin-libs/genesis.scrypt.map +1 -0
  4. package/assets/.templates/smart-contract/builtin-libs/genesis.scrypt.tpl +42 -0
  5. package/assets/.templates/smart-contract/builtin-libs/genesis.transformer.json +9 -0
  6. package/assets/.templates/smart-contract/builtin-libs/p2pk.scrypt.map +1 -1
  7. package/assets/.templates/smart-contract/builtin-libs/p2pk.scrypt.tpl +3 -0
  8. package/assets/.templates/smart-contract/builtin-libs/p2pkh.scrypt.map +1 -1
  9. package/assets/.templates/smart-contract/builtin-libs/p2pkh.scrypt.tpl +3 -0
  10. package/assets/smart-contract/builtin-libs/backtrace.scrypt +16 -12
  11. package/assets/smart-contract/builtin-libs/byteStringReader.scrypt +5 -5
  12. package/assets/smart-contract/builtin-libs/byteStringWriter.scrypt +6 -6
  13. package/assets/smart-contract/builtin-libs/contextUtils.scrypt +16 -16
  14. package/assets/smart-contract/builtin-libs/genesis.scrypt +44 -0
  15. package/assets/smart-contract/builtin-libs/p2pk.scrypt +4 -1
  16. package/assets/smart-contract/builtin-libs/p2pkh.scrypt +4 -1
  17. package/assets/smart-contract/builtin-libs/stateLib.scrypt +1 -1
  18. package/assets/smart-contract/builtin-libs/stateUtils.scrypt +1 -1
  19. package/assets/smart-contract/builtin-libs/stdUtils.scrypt +12 -12
  20. package/assets/smart-contract/builtin-libs/txHashPreimageUtils.scrypt +7 -7
  21. package/assets/smart-contract/builtin-libs/txUtils.scrypt +12 -12
  22. package/assets/smart-contract/types/structs.scrypt +8 -8
  23. package/dist/cjs/features/deployGenesis.cjs +84 -31
  24. package/dist/cjs/features/deployGenesis.js.map +1 -1
  25. package/dist/cjs/providers/dryRunProvider.cjs +57 -0
  26. package/dist/cjs/providers/dryRunProvider.js.map +1 -0
  27. package/dist/cjs/providers/dummyProvider.cjs +10 -0
  28. package/dist/cjs/providers/dummyProvider.js.map +1 -1
  29. package/dist/cjs/providers/index.cjs +4 -1
  30. package/dist/cjs/providers/index.js.map +1 -1
  31. package/dist/cjs/providers/mempoolProvider.cjs +44 -0
  32. package/dist/cjs/providers/mempoolProvider.js.map +1 -1
  33. package/dist/cjs/providers/rpcProvider.cjs +46 -3
  34. package/dist/cjs/providers/rpcProvider.js.map +1 -1
  35. package/dist/cjs/providers/utxoProvider.cjs +9 -1
  36. package/dist/cjs/providers/utxoProvider.js.map +1 -1
  37. package/dist/cjs/psbt/extPsbt.cjs +8 -1
  38. package/dist/cjs/psbt/extPsbt.js.map +1 -1
  39. package/dist/cjs/psbt/psbt.cjs +3 -1
  40. package/dist/cjs/psbt/psbt.js.map +1 -1
  41. package/dist/cjs/smart-contract/builtin-libs/backtrace.cjs +59 -2
  42. package/dist/cjs/smart-contract/builtin-libs/backtrace.js.map +1 -1
  43. package/dist/cjs/smart-contract/builtin-libs/genesis.cjs +553 -0
  44. package/dist/cjs/smart-contract/builtin-libs/genesis.js.map +1 -0
  45. package/dist/cjs/smart-contract/builtin-libs/index.cjs +5 -1
  46. package/dist/cjs/smart-contract/builtin-libs/index.js.map +1 -1
  47. package/dist/cjs/smart-contract/smartContract.cjs +22 -0
  48. package/dist/cjs/smart-contract/smartContract.js.map +1 -1
  49. package/dist/esm/features/deployGenesis.js +84 -31
  50. package/dist/esm/features/deployGenesis.js.map +1 -1
  51. package/dist/esm/providers/dryRunProvider.js +53 -0
  52. package/dist/esm/providers/dryRunProvider.js.map +1 -0
  53. package/dist/esm/providers/dummyProvider.js +10 -0
  54. package/dist/esm/providers/dummyProvider.js.map +1 -1
  55. package/dist/esm/providers/index.js +2 -1
  56. package/dist/esm/providers/index.js.map +1 -1
  57. package/dist/esm/providers/mempoolProvider.js +44 -0
  58. package/dist/esm/providers/mempoolProvider.js.map +1 -1
  59. package/dist/esm/providers/rpcProvider.js +45 -2
  60. package/dist/esm/providers/rpcProvider.js.map +1 -1
  61. package/dist/esm/providers/utxoProvider.js +7 -0
  62. package/dist/esm/providers/utxoProvider.js.map +1 -1
  63. package/dist/esm/psbt/extPsbt.js +8 -1
  64. package/dist/esm/psbt/extPsbt.js.map +1 -1
  65. package/dist/esm/psbt/psbt.js +3 -1
  66. package/dist/esm/psbt/psbt.js.map +1 -1
  67. package/dist/esm/smart-contract/builtin-libs/backtrace.js +61 -4
  68. package/dist/esm/smart-contract/builtin-libs/backtrace.js.map +1 -1
  69. package/dist/esm/smart-contract/builtin-libs/genesis.js +549 -0
  70. package/dist/esm/smart-contract/builtin-libs/genesis.js.map +1 -0
  71. package/dist/esm/smart-contract/builtin-libs/index.js +1 -0
  72. package/dist/esm/smart-contract/builtin-libs/index.js.map +1 -1
  73. package/dist/esm/smart-contract/smartContract.js +23 -1
  74. package/dist/esm/smart-contract/smartContract.js.map +1 -1
  75. package/dist/types/features/deployGenesis.d.ts +39 -12
  76. package/dist/types/features/deployGenesis.d.ts.map +1 -1
  77. package/dist/types/providers/chainProvider.d.ts +12 -0
  78. package/dist/types/providers/chainProvider.d.ts.map +1 -1
  79. package/dist/types/providers/dryRunProvider.d.ts +4 -0
  80. package/dist/types/providers/dryRunProvider.d.ts.map +1 -0
  81. package/dist/types/providers/dummyProvider.d.ts +4 -2
  82. package/dist/types/providers/dummyProvider.d.ts.map +1 -1
  83. package/dist/types/providers/index.d.ts +2 -1
  84. package/dist/types/providers/index.d.ts.map +1 -1
  85. package/dist/types/providers/mempoolProvider.d.ts +4 -2
  86. package/dist/types/providers/mempoolProvider.d.ts.map +1 -1
  87. package/dist/types/providers/rpcProvider.d.ts +4 -2
  88. package/dist/types/providers/rpcProvider.d.ts.map +1 -1
  89. package/dist/types/providers/utxoProvider.d.ts +3 -0
  90. package/dist/types/providers/utxoProvider.d.ts.map +1 -1
  91. package/dist/types/psbt/extPsbt.d.ts.map +1 -1
  92. package/dist/types/psbt/psbt.d.ts.map +1 -1
  93. package/dist/types/smart-contract/builtin-libs/backtrace.d.ts +49 -0
  94. package/dist/types/smart-contract/builtin-libs/backtrace.d.ts.map +1 -1
  95. package/dist/types/smart-contract/builtin-libs/genesis.d.ts +252 -0
  96. package/dist/types/smart-contract/builtin-libs/genesis.d.ts.map +1 -0
  97. package/dist/types/smart-contract/builtin-libs/index.d.ts +1 -0
  98. package/dist/types/smart-contract/builtin-libs/index.d.ts.map +1 -1
  99. package/dist/types/smart-contract/smartContract.d.ts +20 -0
  100. package/dist/types/smart-contract/smartContract.d.ts.map +1 -1
  101. package/package.json +5 -3
  102. package/scrypt.index.json +4 -0
  103. package/src/smart-contract/builtin-libs/backtrace.ts +62 -3
  104. package/src/smart-contract/builtin-libs/genesis.ts +569 -0
  105. package/src/smart-contract/builtin-libs/index.ts +2 -1
@@ -0,0 +1,569 @@
1
+ import { SmartContract } from '../smartContract.js';
2
+ import { method, tags } from '../decorators.js';
3
+ import { assert, toByteString, len, sha256, fill } from '../fns/index.js';
4
+ import { FixedArray, TxOut } from '../types/index.js';
5
+ import { TxUtils } from './txUtils.js';
6
+ import { ContextUtils } from './contextUtils.js';
7
+ import { ContractCall } from '../../psbt/types.js';
8
+ import { uint8ArrayToHex } from '../../utils/common.js';
9
+
10
+ /**
11
+ * Maximum number of inputs allowed during genesis deployment.
12
+ *
13
+ * ## Purpose
14
+ * Limits the number of inputs validated to prevent script size bloat while
15
+ * maintaining sufficient security coverage.
16
+ *
17
+ * ## Why 6?
18
+ * - **Typical deployments**: 1-3 inputs (Genesis UTXO + fee UTXOs)
19
+ * - **Edge case coverage**: Handles multi-input deployment scenarios
20
+ * - **Script size**: Each input check adds ~50 bytes; 6 is optimal balance
21
+ * - **Security**: Validates all inputs in normal cases
22
+ * - **sCrypt constraint**: Bitcoin Script loops must be unrolled at compile time
23
+ *
24
+ * ## Security Implications
25
+ * Transactions with more than 6 inputs will be **rejected by the contract**.
26
+ * This prevents attackers from hiding duplicate scriptHashes in unchecked input
27
+ * indices beyond the validation limit.
28
+ *
29
+ * @constant
30
+ * @see {@link Genesis.checkDeploy} - Uses this constant for input validation
31
+ * @category Genesis
32
+ */
33
+ export const MAX_GENESIS_CHECK_INPUT = 6;
34
+
35
+ /**
36
+ * Maximum number of outputs to check during genesis deployment.
37
+ *
38
+ * ## Purpose
39
+ * Limits the number of outputs validated during deployment to match input
40
+ * validation coverage and support typical deployment patterns.
41
+ *
42
+ * ## Usage Scenarios
43
+ * - **1 output**: Deploy single contract
44
+ * - **2-3 outputs**: Deploy contract + change outputs
45
+ * - **4-6 outputs**: Multi-contract deployment or complex output structures
46
+ *
47
+ * ## Validation Scope
48
+ * Only `output[0]` must be unique. Other outputs (1-5) can have duplicate
49
+ * scriptHashes as long as they differ from `output[0]`.
50
+ *
51
+ * @constant
52
+ * @see {@link Genesis.checkDeploy} - Uses this constant for output validation
53
+ * @see {@link MAX_GENESIS_CHECK_INPUT} - Corresponding input limit
54
+ * @category Genesis
55
+ */
56
+ export const MAX_GENESIS_CHECK_OUTPUT = 6;
57
+
58
+ /**
59
+ * Genesis contract for validating initial deployment outputs.
60
+ *
61
+ * ## Purpose
62
+ * The Genesis contract ensures that deployed contracts have unique scriptHashes,
63
+ * preventing duplicate deployments and establishing a verifiable deployment origin.
64
+ * It acts as a "genesis UTXO" that validates the first deployment of a contract.
65
+ *
66
+ * ## Deployment Validation Rules
67
+ * 1. **Genesis position**: Genesis must be unlocked at input index 0
68
+ * 2. **Output uniqueness**: Contract at `output[0]` must have unique scriptHash among all outputs
69
+ * 3. **Input differentiation**: Contract at `output[0]` must differ from all input scriptHashes
70
+ * 4. **Input limit**: Transaction must have ≤ {@link MAX_GENESIS_CHECK_INPUT} inputs
71
+ * 5. **Output limit**: Transaction must specify ≤ {@link MAX_GENESIS_CHECK_OUTPUT} outputs
72
+ *
73
+ * ## Why output[0]?
74
+ * Only `output[0]` requires uniqueness validation. This design allows:
75
+ * - Deploying the **primary contract** at output[0] with guaranteed uniqueness
76
+ * - Including **auxiliary contracts** or **change outputs** at indices 1-5
77
+ * - **Multi-output deployments** where only the main contract needs uniqueness
78
+ *
79
+ * ## Empty Placeholders
80
+ * When fewer than {@link MAX_GENESIS_CHECK_OUTPUT} outputs exist, use empty
81
+ * placeholders (scriptHash = empty ByteString) for unused slots. These are
82
+ * ignored during validation.
83
+ *
84
+ * @example
85
+ * **Basic single contract deployment:**
86
+ * ```typescript
87
+ * import { Genesis, genesisCheckDeploy } from '@opcat-labs/scrypt-ts-opcat';
88
+ * import { ExtPsbt } from '@opcat-labs/scrypt-ts-opcat';
89
+ *
90
+ * // 1. Create and bind Genesis contract
91
+ * const genesis = new Genesis();
92
+ * genesis.bindToUtxo(genesisUtxo);
93
+ *
94
+ * // 2. Create the contract to deploy
95
+ * const minter = new CAT20Minter(...);
96
+ *
97
+ * // 3. Build deployment transaction
98
+ * const psbt = new ExtPsbt({ network })
99
+ * .addContractInput(genesis, genesisCheckDeploy()) // Genesis validates deployment
100
+ * .addContractOutput(minter, 1000n) // Deploy at output[0]
101
+ * .change(changeAddress, feeRate)
102
+ * .seal();
103
+ *
104
+ * // 4. Finalize and broadcast
105
+ * await psbt.finalizeAllInputs();
106
+ * const txid = await psbt.broadcast();
107
+ * console.log(`Contract deployed at ${txid}:0`);
108
+ * ```
109
+ *
110
+ * @category Contract
111
+ * @category Genesis
112
+ * @see {@link genesisCheckDeploy} - Helper function for easier deployment
113
+ * @see {@link MAX_GENESIS_CHECK_INPUT} - Maximum inputs validated
114
+ * @see {@link MAX_GENESIS_CHECK_OUTPUT} - Maximum outputs validated
115
+ * @onchain
116
+ */
117
+ @tags(['GENESIS'])
118
+ export class Genesis extends SmartContract {
119
+ constructor() {
120
+ // eslint-disable-next-line prefer-rest-params
121
+ super(...arguments);
122
+ }
123
+
124
+ /**
125
+ * Validates the deployment transaction outputs.
126
+ *
127
+ * ## Validation Process
128
+ * This method performs a **two-phase validation**:
129
+ *
130
+ * **Phase 1: Output Serialization & Uniqueness** (lines 168-183)
131
+ * - Iterates through all outputs up to MAX_GENESIS_CHECK_OUTPUT
132
+ * - Serializes valid outputs (index < outputCount) for context matching
133
+ * - Validates `output[0]` scriptHash is unique among all outputs
134
+ * - Ensures no duplicate contracts are deployed in a single transaction
135
+ *
136
+ * **Phase 2: Input Differentiation** (lines 185-195)
137
+ * - Checks `output[0]` differs from all input scriptHashes
138
+ * - Prevents redeployment attacks using existing contract UTXOs
139
+ * - Validates only inputs within MAX_GENESIS_CHECK_INPUT limit
140
+ *
141
+ * ## Why Two Separate Loops?
142
+ * 1. **Output loop**: Dual-purpose (serialize + validate uniqueness)
143
+ * 2. **Input loop**: Independent validation after outputs are processed
144
+ * 3. **sCrypt constraint**: Loops must be unrolled at compile time
145
+ *
146
+ * @param outputs - Fixed array of 6 outputs; fill unused slots with empty placeholders
147
+ * @param outputCount - Number of actual outputs (1-6); outputs beyond this are ignored
148
+ *
149
+ * @throws {Error} 'Genesis must be unlocked at input index 0' - Genesis not at input 0
150
+ * @throws {Error} 'Too many inputs to validate' - More than MAX_GENESIS_CHECK_INPUT inputs
151
+ * @throws {Error} 'Invalid outputCount' - outputCount out of range [1, 6]
152
+ * @throws {Error} 'Output scriptHash must be non-empty' - Empty scriptHash in valid output
153
+ * @throws {Error} 'output[0] must be unique among all outputs' - Duplicate scriptHash in outputs
154
+ * @throws {Error} 'output[0] must differ from all input scriptHashes' - Matches an input
155
+ * @throws {Error} 'Outputs mismatch with the transaction context' - Serialization mismatch
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * // Advanced: Direct method call (most users should use genesisCheckDeploy helper)
160
+ * const genesis = new Genesis();
161
+ * genesis.bindToUtxo(genesisUtxo);
162
+ *
163
+ * // Create output array with empty placeholders
164
+ * const outputs: TxOut[] = [
165
+ * { scriptHash: sha256(contractScript), satoshis: 1000n, dataHash: sha256('') },
166
+ * ...fill({ scriptHash: toByteString(''), satoshis: 0n, dataHash: sha256('') }, 5)
167
+ * ];
168
+ *
169
+ * await genesis.methods.checkDeploy(
170
+ * outputs as FixedArray<TxOut, 6>,
171
+ * 1n // Only 1 real output, rest are placeholders
172
+ * );
173
+ * ```
174
+ *
175
+ * @see {@link genesisCheckDeploy} - Helper function for easier usage
176
+ * @see {@link MAX_GENESIS_CHECK_INPUT} - Maximum inputs validated
177
+ * @see {@link MAX_GENESIS_CHECK_OUTPUT} - Maximum outputs validated
178
+ * @onchain
179
+ */
180
+ @method()
181
+ public checkDeploy(outputs: FixedArray<TxOut, typeof MAX_GENESIS_CHECK_OUTPUT>, outputCount: bigint) {
182
+ // === PRELIMINARY CHECKS ===
183
+
184
+ // Ensure Genesis is unlocked at input index 0
185
+ // This guarantees Genesis validates the deployment and prevents position-based attacks
186
+ assert(this.ctx.inputIndex == 0n, 'Genesis must be unlocked at input index 0');
187
+
188
+ // Ensure input count does not exceed the maximum we can check
189
+ // SECURITY: Prevents attackers from hiding duplicate scriptHashes in unchecked input indices
190
+ assert(this.ctx.inputCount <= BigInt(MAX_GENESIS_CHECK_INPUT), 'Too many inputs to validate');
191
+
192
+ // Validate outputCount is within valid range [1, MAX_GENESIS_CHECK_OUTPUT]
193
+ assert(
194
+ outputCount > 0n && outputCount <= BigInt(MAX_GENESIS_CHECK_OUTPUT),
195
+ 'Invalid outputCount: must be between 1 and MAX_GENESIS_CHECK_OUTPUT'
196
+ );
197
+
198
+ // Cache output[0] scriptHash for comparison in validation loops
199
+ // This is more efficient than repeatedly accessing outputs[0].scriptHash
200
+ const output0ScriptHash = outputs[0].scriptHash;
201
+
202
+ // === PHASE 1: SERIALIZE OUTPUTS & VALIDATE UNIQUENESS ===
203
+
204
+ // Loop through all output slots to:
205
+ // 1. Serialize valid outputs for context matching
206
+ // 2. Validate output[0] uniqueness among all outputs
207
+ // Note: Loop is unrolled at compile time (sCrypt constraint)
208
+ let outputBytes = toByteString('');
209
+ for (let index = 0; index < MAX_GENESIS_CHECK_OUTPUT; index++) {
210
+ const _output = outputs[index];
211
+ if (index < outputCount) {
212
+ // Validate: All valid outputs must have non-empty scriptHash
213
+ assert(len(_output.scriptHash) > 0n, 'Output scriptHash must be non-empty for valid outputs');
214
+
215
+ // Serialize this output for later context verification
216
+ outputBytes += TxUtils.buildDataOutput(
217
+ _output.scriptHash,
218
+ _output.satoshis,
219
+ _output.dataHash,
220
+ );
221
+
222
+ // Validate: output[0] must be unique among all outputs
223
+ // Skip index 0 (can't compare output[0] with itself)
224
+ if (index > 0) {
225
+ assert(output0ScriptHash != _output.scriptHash, 'output[0] must be unique among all outputs');
226
+ }
227
+ }
228
+ // Else: index >= outputCount, this is an empty placeholder, skip it
229
+ }
230
+
231
+ // === PHASE 2: VALIDATE INPUT DIFFERENTIATION ===
232
+
233
+ // Ensure output[0] scriptHash differs from all input scriptHashes
234
+ // SECURITY: Prevents redeployment using existing contract UTXOs as inputs
235
+ for (let index = 0; index < MAX_GENESIS_CHECK_INPUT; index++) {
236
+ if (index < this.ctx.inputCount) {
237
+ // Extract scriptHash of input at this index
238
+ const inputScriptHash = ContextUtils.getSpentScriptHash(
239
+ this.ctx.spentScriptHashes,
240
+ BigInt(index)
241
+ );
242
+ // Validate: output[0] must differ from this input
243
+ assert(output0ScriptHash != inputScriptHash, 'output[0] must differ from all input scriptHashes');
244
+ }
245
+ // Else: index >= inputCount, no input at this index, skip it
246
+ }
247
+
248
+ // === FINAL CHECK: CONTEXT VERIFICATION ===
249
+
250
+ // Verify that our serialized outputs match the transaction's actual outputs
251
+ // This ensures the outputs array passed to this method is truthful
252
+ assert(this.checkOutputs(outputBytes), 'Outputs mismatch with the transaction context');
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Creates a contract call function for Genesis.checkDeploy that automatically
258
+ * builds the TxOut array from transaction outputs.
259
+ *
260
+ * ## What it does
261
+ * This helper function simplifies Genesis deployment by:
262
+ * 1. **Extracting outputs** from the PSBT transaction
263
+ * 2. **Computing hashes** (scriptHash and dataHash) for each output
264
+ * 3. **Creating placeholders** for unused output slots (up to 6 total)
265
+ * 4. **Invoking checkDeploy** with properly formatted parameters
266
+ *
267
+ * ## When to use
268
+ * - **Recommended**: Use this helper when building deployment transactions with ExtPsbt
269
+ * - **Advanced**: Call {@link Genesis.checkDeploy} directly for manual control
270
+ *
271
+ * ## Output Handling
272
+ * - Processes up to {@link MAX_GENESIS_CHECK_OUTPUT} (6) outputs
273
+ * - Automatically limits `outputCount` via `Math.min(txOutputs.length, 6)`
274
+ * - Fills unused slots with empty placeholders (scriptHash = empty ByteString)
275
+ *
276
+ * @returns A ContractCall function compatible with ExtPsbt.addContractInput
277
+ *
278
+ * @example
279
+ * **Basic single contract deployment:**
280
+ * ```typescript
281
+ * import { Genesis, genesisCheckDeploy } from '@opcat-labs/scrypt-ts-opcat';
282
+ * import { ExtPsbt } from '@opcat-labs/scrypt-ts-opcat';
283
+ *
284
+ * // 1. Setup Genesis contract
285
+ * const genesis = new Genesis();
286
+ * genesis.bindToUtxo(genesisUtxo);
287
+ *
288
+ * // 2. Create contract to deploy
289
+ * const minter = new CAT20Minter(...);
290
+ *
291
+ * // 3. Build deployment transaction
292
+ * const psbt = new ExtPsbt({ network })
293
+ * .addContractInput(genesis, genesisCheckDeploy()) // Genesis validates
294
+ * .addContractOutput(minter, 1000n) // Deploy at output[0]
295
+ * .change(changeAddress, feeRate) // Change output
296
+ * .seal();
297
+ *
298
+ * // 4. Finalize and broadcast
299
+ * await psbt.finalizeAllInputs();
300
+ * const txid = await psbt.broadcast();
301
+ * console.log(`Contract deployed at ${txid}:0`);
302
+ * ```
303
+ *
304
+ * @example
305
+ * **Multi-output deployment:**
306
+ * ```typescript
307
+ * // Deploy primary contract + auxiliary contracts
308
+ * const psbt = new ExtPsbt({ network })
309
+ * .addContractInput(genesis, genesisCheckDeploy())
310
+ * .addContractOutput(primaryContract, 2000n) // output[0] - must be unique
311
+ * .addContractOutput(helperContract, 1000n) // output[1] - can match output[2+]
312
+ * .addContractOutput(anotherHelper, 1000n) // output[2] - can match output[1]
313
+ * .change(changeAddress, feeRate)
314
+ * .seal();
315
+ * ```
316
+ *
317
+ * @example
318
+ * **Error handling:**
319
+ * ```typescript
320
+ * try {
321
+ * const psbt = new ExtPsbt({ network })
322
+ * .addContractInput(genesis, genesisCheckDeploy())
323
+ * .addContractOutput(contract1, 1000n) // output[0]
324
+ * .addContractOutput(contract1, 1000n) // ❌ Same as output[0] - will fail!
325
+ * .seal();
326
+ * await psbt.finalizeAllInputs();
327
+ * await psbt.broadcast();
328
+ * } catch (error) {
329
+ * // Error: output[0] must be unique among all outputs
330
+ * console.error('Deployment failed:', error.message);
331
+ * }
332
+ * ```
333
+ *
334
+ * @category Genesis
335
+ * @see {@link Genesis.checkDeploy} - The underlying contract method
336
+ * @see {@link MAX_GENESIS_CHECK_OUTPUT} - Maximum outputs validated
337
+ */
338
+ export function genesisCheckDeploy(): ContractCall<Genesis> {
339
+ return (contract, psbt) => {
340
+ // Create empty placeholder for unused output slots
341
+ // Empty scriptHash signals "no output" to the Genesis contract
342
+ const emptyOutput: TxOut = {
343
+ scriptHash: toByteString(''), // Empty = placeholder
344
+ satoshis: 0n, // Placeholder value
345
+ dataHash: sha256(toByteString('')), // Hash of empty data
346
+ };
347
+
348
+ // Initialize array with 6 empty placeholders
349
+ // Genesis contract requires exactly 6 output slots (sCrypt FixedArray constraint)
350
+ const outputs: TxOut[] = fill(emptyOutput, MAX_GENESIS_CHECK_OUTPUT);
351
+
352
+ // Extract actual outputs from the transaction being built
353
+ const txOutputs = psbt.txOutputs;
354
+
355
+ // Limit to MAX_GENESIS_CHECK_OUTPUT to prevent validation errors
356
+ // If tx has >6 outputs, only first 6 are validated (rare edge case)
357
+ const outputCount = Math.min(txOutputs.length, MAX_GENESIS_CHECK_OUTPUT);
358
+
359
+ // Fill the output array with actual transaction outputs
360
+ for (let i = 0; i < outputCount; i++) {
361
+ const output = txOutputs[i];
362
+ outputs[i] = {
363
+ // Compute scriptHash: SHA256 of the output's locking script
364
+ scriptHash: sha256(toByteString(uint8ArrayToHex(output.script))),
365
+ // Output amount in satoshis
366
+ satoshis: BigInt(output.value),
367
+ // Compute dataHash: SHA256 of the output's data field (if any)
368
+ dataHash: sha256(toByteString(uint8ArrayToHex(output.data))),
369
+ };
370
+ }
371
+ // Remaining slots (i >= outputCount) remain as empty placeholders
372
+
373
+ contract.checkDeploy(
374
+ outputs as FixedArray<TxOut, typeof MAX_GENESIS_CHECK_OUTPUT>,
375
+ BigInt(outputCount)
376
+ );
377
+ };
378
+ }
379
+
380
+ /**
381
+ * Embedded contract artifact for the Genesis contract.
382
+ *
383
+ * ## What is this?
384
+ * This object contains the compiled sCrypt bytecode and metadata for the Genesis contract.
385
+ * It includes:
386
+ * - **Compiled bytecode** (hex, asm fields) - Executable Bitcoin Script
387
+ * - **Contract ABI** - Method signatures and parameter types
388
+ * - **Struct definitions** (TxOut, SHPreimage) - Type information for complex data structures
389
+ * - **Compiler metadata** - Version, MD5 hash for artifact validation
390
+ *
391
+ * ## Auto-generation
392
+ * This artifact is **automatically generated and updated** by the build script:
393
+ * ```bash
394
+ * npm run gen:contract Genesis
395
+ * ```
396
+ *
397
+ * This command:
398
+ * 1. Compiles the Genesis contract using `npx tspc`
399
+ * 2. Generates artifact JSON in `test/fixtures/genesis.json`
400
+ * 3. Embeds the artifact into this source file (via `updateContractDesc.ts`)
401
+ * 4. Cleans up intermediate compilation files
402
+ *
403
+ * ## Why embedded in source?
404
+ * Embedding the artifact directly in the source file provides:
405
+ * - **Zero-config usage** - No need to load external JSON files
406
+ * - **Type safety** - TypeScript can validate the artifact structure
407
+ * - **Bundler compatibility** - Works seamlessly with webpack, rollup, etc.
408
+ * - **Single-file distribution** - Easier to import and use
409
+ *
410
+ * ## When to update
411
+ * The artifact must be regenerated whenever:
412
+ * - The Genesis contract logic changes
413
+ * - Struct definitions (TxOut, SHPreimage) are modified
414
+ * - The sCrypt compiler version is updated
415
+ *
416
+ * ⚠️ **DO NOT manually edit this object** - Changes will be overwritten on next compilation.
417
+ * To update: modify the contract code above, then run `npm run gen:contract Genesis`.
418
+ *
419
+ * @see {@link updateContractDesc} - Script that embeds this artifact (scripts/updateContractDesc.ts)
420
+ */
421
+ const desc = {
422
+ version: 10,
423
+ compilerVersion: "1.21.0+commit.2ada378",
424
+ contract: "Genesis",
425
+ md5: "0b1e184a0aecd042ae661eb8d44a0483",
426
+ structs: [
427
+ {
428
+ name: "TxOut",
429
+ params: [
430
+ {
431
+ name: "scriptHash",
432
+ type: "bytes"
433
+ },
434
+ {
435
+ name: "dataHash",
436
+ type: "bytes"
437
+ },
438
+ {
439
+ name: "satoshis",
440
+ type: "int"
441
+ }
442
+ ],
443
+ genericTypes: []
444
+ },
445
+ {
446
+ name: "SHPreimage",
447
+ params: [
448
+ {
449
+ name: "nVersion",
450
+ type: "bytes"
451
+ },
452
+ {
453
+ name: "hashPrevouts",
454
+ type: "bytes"
455
+ },
456
+ {
457
+ name: "spentScriptHash",
458
+ type: "bytes"
459
+ },
460
+ {
461
+ name: "spentDataHash",
462
+ type: "bytes"
463
+ },
464
+ {
465
+ name: "value",
466
+ type: "int"
467
+ },
468
+ {
469
+ name: "nSequence",
470
+ type: "bytes"
471
+ },
472
+ {
473
+ name: "hashSpentAmounts",
474
+ type: "bytes"
475
+ },
476
+ {
477
+ name: "hashSpentScriptHashes",
478
+ type: "bytes"
479
+ },
480
+ {
481
+ name: "hashSpentDataHashes",
482
+ type: "bytes"
483
+ },
484
+ {
485
+ name: "hashSequences",
486
+ type: "bytes"
487
+ },
488
+ {
489
+ name: "hashOutputs",
490
+ type: "bytes"
491
+ },
492
+ {
493
+ name: "inputIndex",
494
+ type: "int"
495
+ },
496
+ {
497
+ name: "nLockTime",
498
+ type: "int"
499
+ },
500
+ {
501
+ name: "sigHashType",
502
+ type: "int"
503
+ }
504
+ ],
505
+ genericTypes: []
506
+ }
507
+ ],
508
+ library: [
509
+ {
510
+ name: "TxUtils",
511
+ params: [],
512
+ properties: [],
513
+ genericTypes: []
514
+ },
515
+ {
516
+ name: "ContextUtils",
517
+ params: [],
518
+ properties: [],
519
+ genericTypes: []
520
+ },
521
+ {
522
+ name: "StdUtils",
523
+ params: [],
524
+ properties: [],
525
+ genericTypes: []
526
+ }
527
+ ],
528
+ alias: [],
529
+ abi: [
530
+ {
531
+ type: "function",
532
+ name: "checkDeploy",
533
+ index: 0,
534
+ params: [
535
+ {
536
+ name: "outputs",
537
+ type: "TxOut[6]"
538
+ },
539
+ {
540
+ name: "outputCount",
541
+ type: "int"
542
+ },
543
+ {
544
+ name: "__scrypt_ts_shPreimage",
545
+ type: "SHPreimage"
546
+ },
547
+ {
548
+ name: "__scrypt_ts_spentAmounts",
549
+ type: "bytes"
550
+ },
551
+ {
552
+ name: "__scrypt_ts_spentScriptHashes",
553
+ type: "bytes"
554
+ }
555
+ ]
556
+ },
557
+ {
558
+ type: "constructor",
559
+ params: []
560
+ }
561
+ ],
562
+ stateProps: [],
563
+ buildType: "release",
564
+ file: "",
565
+ hex: "512097dfd76851bf465e8f715593b217714858bbe9570ff3bd5e33840a34e20ff0262102ba79df5f8ae7604a9830f03c7933028186aede0675a16f025dc4f8be8eec0382201008ce7480da41702918d1ec8e6849ba32b4d65b1e40dc669c31a1e6306b266c6161011379011379011379011379011379011379011379011379011379011379011379011379011379011379615d798277549c695c79827701209c695b79827701209c695a79827701209c69597900a26958798277549c695779827701209c695679827701209c695579827701209c695479827701209c695379827701209c69527900a269517900a2690079519c640079529c675168640079539c6751686400790281009c6751686400790282009c6751686400790283009c675168695d795d797e5c797e5b797e5a7961007961007900a263007909ffffffffffffffff00a1670068690079586151795179519380007952797f75007f77517a75517a75517a7561517a7561517a75617e59797e58797e57797e56797e55797e54797e5379546151795179519380007952797f75007f77517a75517a75517a75617e5279546151795179519380007952797f75007f77517a75517a75517a75617e517954807e0079517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a7561547961517955795579210ac407f0e4bd44bfc207355a778b046225a7068fc59ee7eda43ad905aadbffc800206c266b30e6a1319c66dc401e5bd6b432ba49688eecd118297041da8074ce081057795679615679aa0079610079517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e01007e81517a75615779567956795679567961537956795479577995939521414136d08c5ed2bf3ba048afe6dcaebafeffffffffffffffffffffffffffffff00517951796151795179970079009f63007952799367007968517a75517a75517a7561527a75517a517951795296a0630079527994527a75517a6853798277527982775379012080517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e01205279947f7754537993527993013051797e527e54797e58797e527e53797e52797e57797e0079517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a756100795779ac517a75517a75517a75517a75517a75517a75517a75517a75517a7561517a75517a75616955795e79615179aa5179876951795861517982770079527997009c690079527996517a75517a75517a7561517a75517a756155795e795279615279aa5279876900795379012061517982770079527997009c690079527996517a75517a75517a75619c6951517a75517a75517a7561755979009c69007956a16901157900a06301157956a167006869006101286b6c766b796c766b796c766b796c7500011a799f635279827700a0695379537952795479615279827751798277517901209c69007901209c69537900a269537961007961007900a263007909ffffffffffffffff00a1670068690079586151795179519380007952797f75007f77517a75517a75517a7561517a7561517a756155797e53797e517a75517a75517a75517a75517a75617e547a75537a537a537a537975687575756101256b6c766b796c766b796c766b796c7551011a799f635279827700a0695379537952795479615279827751798277517901209c69007901209c69537900a269537961007961007900a263007909ffffffffffffffff00a1670068690079586151795179519380007952797f75007f77517a75517a75517a7561517a7561517a756155797e53797e517a75517a75517a75517a75517a75617e547a75537a537a537a537975687575756101226b6c766b796c766b796c766b796c7552011a799f635279827700a0695379537952795479615279827751798277517901209c69007901209c69537900a269537961007961007900a263007909ffffffffffffffff00a1670068690079586151795179519380007952797f75007f77517a75517a75517a7561517a7561517a756155797e53797e517a75517a75517a75517a75517a75617e547a75537a537a537a5379756875757561011f6b6c766b796c766b796c766b796c7553011a799f635279827700a0695379537952795479615279827751798277517901209c69007901209c69537900a269537961007961007900a263007909ffffffffffffffff00a1670068690079586151795179519380007952797f75007f77517a75517a75517a7561517a7561517a756155797e53797e517a75517a75517a75517a75517a75617e547a75537a537a537a5379756875757561011c6b6c766b796c766b796c766b796c7554011a799f635279827700a0695379537952795479615279827751798277517901209c69007901209c69537900a269537961007961007900a263007909ffffffffffffffff00a1670068690079586151795179519380007952797f75007f77517a75517a75517a7561517a7561517a756155797e53797e517a75517a75517a75517a75517a75617e547a75537a537a537a537975687575756101196b6c766b796c766b796c766b796c7555011a799f635279827700a0695379537952795479615279827751798277517901209c69007901209c69537900a269537961007961007900a263007909ffffffffffffffff00a1670068690079586151795179519380007952797f75007f77517a75517a75517a7561517a7561517a756155797e53797e517a75517a75517a75517a75517a75617e547a75537a537a537a5379756875757501286b6c766b796c766b796c766b796c75527a527a527a7575610053799f63577900615179517951930120957f7551790120957f77517a75517a7561517951798791697568615153799f63577951615179517951930120957f7551790120957f77517a75517a7561517951798791697568615253799f63577952615179517951930120957f7551790120957f77517a75517a7561517951798791697568615353799f63577953615179517951930120957f7551790120957f77517a75517a7561517951798791697568615453799f63577954615179517951930120957f7551790120957f77517a75517a7561517951798791697568615553799f63577955615179517951930120957f7551790120957f77517a75517a756151795179879169756801177951a063007901276b6c766b796c766b796c766b796c75527a527a527a75758791696801177952a063007901246b6c766b796c766b796c766b796c75527a527a527a75758791696801177953a063007901216b6c766b796c766b796c766b796c75527a527a527a75758791696801177954a0630079011e6b6c766b796c766b796c766b796c75527a527a527a75758791696801177955a0630079011b6b6c766b796c766b796c766b796c75527a527a527a7575879169685179aa5d7987777777777777777777777777777777777777777777777777777777777777777777777777777777777777",
566
+ sourceMapFile: ""
567
+ };
568
+
569
+ Genesis.loadArtifact(desc);
@@ -4,4 +4,5 @@ export { StateUtils } from './stateUtils.js';
4
4
  export { StdUtils } from './stdUtils.js';
5
5
  export { Backtrace, type ChainTxVerifyResponse } from './backtrace.js';
6
6
  export { StateLib } from './stateLib.js';
7
- export { TxHashPreimageUtils } from './txHashPreimageUtils.js';
7
+ export { TxHashPreimageUtils } from './txHashPreimageUtils.js';
8
+ export { Genesis, MAX_GENESIS_CHECK_OUTPUT, genesisCheckDeploy } from './genesis.js';