ox 0.14.17 → 0.14.19

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 (56) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
  3. package/_cjs/tempo/VirtualAddress.js +70 -0
  4. package/_cjs/tempo/VirtualAddress.js.map +1 -0
  5. package/_cjs/tempo/VirtualMaster.js +278 -0
  6. package/_cjs/tempo/VirtualMaster.js.map +1 -0
  7. package/_cjs/tempo/index.js +3 -1
  8. package/_cjs/tempo/index.js.map +1 -1
  9. package/_cjs/tempo/internal/mine.wasm.js +6 -0
  10. package/_cjs/tempo/internal/mine.wasm.js.map +1 -0
  11. package/_cjs/tempo/internal/virtualMasterPool.js +186 -0
  12. package/_cjs/tempo/internal/virtualMasterPool.js.map +1 -0
  13. package/_cjs/version.js +1 -1
  14. package/_esm/tempo/TxEnvelopeTempo.js +6 -3
  15. package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
  16. package/_esm/tempo/VirtualAddress.js +154 -0
  17. package/_esm/tempo/VirtualAddress.js.map +1 -0
  18. package/_esm/tempo/VirtualMaster.js +483 -0
  19. package/_esm/tempo/VirtualMaster.js.map +1 -0
  20. package/_esm/tempo/index.js +54 -0
  21. package/_esm/tempo/index.js.map +1 -1
  22. package/_esm/tempo/internal/mine.wasm.js +15 -0
  23. package/_esm/tempo/internal/mine.wasm.js.map +1 -0
  24. package/_esm/tempo/internal/virtualMasterPool.js +216 -0
  25. package/_esm/tempo/internal/virtualMasterPool.js.map +1 -0
  26. package/_esm/version.js +1 -1
  27. package/_types/core/RpcSchema.d.ts +0 -2
  28. package/_types/core/RpcSchema.d.ts.map +1 -1
  29. package/_types/tempo/TxEnvelopeTempo.d.ts +6 -3
  30. package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  31. package/_types/tempo/VirtualAddress.d.ts +129 -0
  32. package/_types/tempo/VirtualAddress.d.ts.map +1 -0
  33. package/_types/tempo/VirtualMaster.d.ts +237 -0
  34. package/_types/tempo/VirtualMaster.d.ts.map +1 -0
  35. package/_types/tempo/index.d.ts +54 -0
  36. package/_types/tempo/index.d.ts.map +1 -1
  37. package/_types/tempo/internal/mine.wasm.d.ts +5 -0
  38. package/_types/tempo/internal/mine.wasm.d.ts.map +1 -0
  39. package/_types/tempo/internal/virtualMasterPool.d.ts +46 -0
  40. package/_types/tempo/internal/virtualMasterPool.d.ts.map +1 -0
  41. package/_types/version.d.ts +1 -1
  42. package/core/RpcSchema.ts +0 -2
  43. package/package.json +11 -1
  44. package/tempo/TxEnvelopeTempo.ts +6 -3
  45. package/tempo/VirtualAddress/package.json +6 -0
  46. package/tempo/VirtualAddress.test.ts +88 -0
  47. package/tempo/VirtualAddress.ts +201 -0
  48. package/tempo/VirtualMaster/package.json +6 -0
  49. package/tempo/VirtualMaster.test.ts +204 -0
  50. package/tempo/VirtualMaster.ts +711 -0
  51. package/tempo/e2e.test.ts +0 -2
  52. package/tempo/index.ts +55 -0
  53. package/tempo/internal/mine.c +182 -0
  54. package/tempo/internal/mine.wasm.ts +17 -0
  55. package/tempo/internal/virtualMasterPool.ts +254 -0
  56. package/version.ts +1 -1
@@ -0,0 +1,711 @@
1
+ import { keccak_256 } from '@noble/hashes/sha3'
2
+ import * as Address from '../core/Address.js'
3
+ import * as Bytes from '../core/Bytes.js'
4
+ import * as Errors from '../core/Errors.js'
5
+ import * as Hash from '../core/Hash.js'
6
+ import * as Hex from '../core/Hex.js'
7
+ import * as VirtualMasterPool from './internal/virtualMasterPool.js'
8
+ import * as TempoAddress from './TempoAddress.js'
9
+ import * as VirtualAddress from './VirtualAddress.js'
10
+
11
+ const tip20Prefix = '0x20c000000000000000000000'
12
+ const zeroAddress = '0x0000000000000000000000000000000000000000'
13
+
14
+ /** A valid salt input for TIP-1022 master registration. */
15
+ export type Salt = Hex.Hex | Bytes.Bytes | number | bigint
16
+
17
+ /**
18
+ * Computes the TIP-1022 registration hash for a master address and salt.
19
+ *
20
+ * [TIP-1022](https://docs.tempo.xyz/protocol/tips/tip-1022)
21
+ *
22
+ * The registration hash is `keccak256(masterAddress || salt)` where `salt`
23
+ * is encoded as a 32-byte value.
24
+ *
25
+ * Master addresses must satisfy TIP-1022 registration constraints: they cannot
26
+ * be the zero address, another virtual address, or a TIP-20 token address.
27
+ *
28
+ * @example
29
+ * ```ts twoslash
30
+ * import { Address, Hex } from 'ox'
31
+ * import { VirtualMaster } from 'ox/tempo'
32
+ *
33
+ * const hash = VirtualMaster.getRegistrationHash({
34
+ * address: Address.from('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'),
35
+ * salt: Hex.from('0x00000000000000000000000000000000000000000000000000000000abf52baf'),
36
+ * })
37
+ *
38
+ * hash
39
+ * // @log: '0x0000000058e21090d8f4bee424b90cddc2378aefa1bbbfa1443631a929ae966d'
40
+ * ```
41
+ *
42
+ * @param value - Master address and salt.
43
+ * @returns The registration hash.
44
+ */
45
+ export function getRegistrationHash(value: getRegistrationHash.Value): Hex.Hex {
46
+ return Hash.keccak256(
47
+ Hex.concat(resolveAddress(value.address), toFixedHex(value.salt, 32)),
48
+ )
49
+ }
50
+
51
+ export declare namespace getRegistrationHash {
52
+ type Value = {
53
+ /** Master address. Accepts both hex and Tempo addresses. */
54
+ address: TempoAddress.Address
55
+ /** 32-byte salt used for registration. */
56
+ salt: Salt
57
+ }
58
+
59
+ type ErrorType =
60
+ | Address.assert.ErrorType
61
+ | Bytes.padLeft.ErrorType
62
+ | Errors.BaseError
63
+ | Hash.keccak256.ErrorType
64
+ | Hex.assert.ErrorType
65
+ | Hex.fromBytes.ErrorType
66
+ | Hex.fromNumber.ErrorType
67
+ | Hex.padLeft.ErrorType
68
+ | TempoAddress.parse.ErrorType
69
+ | Errors.GlobalErrorType
70
+ }
71
+
72
+ /**
73
+ * Derives the 4-byte TIP-1022 `masterId` from a master address and salt.
74
+ *
75
+ * [TIP-1022](https://docs.tempo.xyz/protocol/tips/tip-1022)
76
+ *
77
+ * This returns bytes `[4:8]` of the registration hash, regardless of whether the
78
+ * salt satisfies the proof-of-work requirement.
79
+ *
80
+ * Master addresses must satisfy TIP-1022 registration constraints: they cannot
81
+ * be the zero address, another virtual address, or a TIP-20 token address.
82
+ *
83
+ * @example
84
+ * ```ts twoslash
85
+ * import { Address, Hex } from 'ox'
86
+ * import { VirtualMaster } from 'ox/tempo'
87
+ *
88
+ * const masterId = VirtualMaster.getMasterId({
89
+ * address: Address.from('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'),
90
+ * salt: Hex.from('0x00000000000000000000000000000000000000000000000000000000abf52baf'),
91
+ * })
92
+ *
93
+ * masterId
94
+ * // @log: '0x58e21090'
95
+ * ```
96
+ *
97
+ * @param value - Master address and salt.
98
+ * @returns The derived master identifier.
99
+ */
100
+ export function getMasterId(value: getMasterId.Value): Hex.Hex {
101
+ return Hex.slice(getRegistrationHash(value), 4, 8)
102
+ }
103
+
104
+ export declare namespace getMasterId {
105
+ type Value = getRegistrationHash.Value
106
+ type ErrorType = getRegistrationHash.ErrorType | Errors.GlobalErrorType
107
+ }
108
+
109
+ /**
110
+ * Validates that a salt satisfies the TIP-1022 32-bit proof-of-work requirement.
111
+ *
112
+ * [TIP-1022](https://docs.tempo.xyz/protocol/tips/tip-1022)
113
+ *
114
+ * Returns `false` for invalid master addresses, including the zero address,
115
+ * virtual addresses, and TIP-20 token addresses.
116
+ *
117
+ * @example
118
+ * ```ts twoslash
119
+ * import { Address, Hex } from 'ox'
120
+ * import { VirtualMaster } from 'ox/tempo'
121
+ *
122
+ * const valid = VirtualMaster.validateSalt({
123
+ * address: Address.from('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'),
124
+ * salt: Hex.from('0x00000000000000000000000000000000000000000000000000000000abf52baf'),
125
+ * })
126
+ *
127
+ * valid
128
+ * // @log: true
129
+ * ```
130
+ *
131
+ * @param value - Master address and salt.
132
+ * @returns `true` if the first 4 bytes of the registration hash are zero.
133
+ */
134
+ export function validateSalt(value: validateSalt.Value): boolean {
135
+ try {
136
+ return hasProofOfWork(
137
+ Hash.keccak256(
138
+ Hex.concat(resolveAddress(value.address), toFixedHex(value.salt, 32)),
139
+ { as: 'Bytes' },
140
+ ),
141
+ )
142
+ } catch {
143
+ return false
144
+ }
145
+ }
146
+
147
+ export declare namespace validateSalt {
148
+ type Value = getRegistrationHash.Value
149
+ }
150
+
151
+ /**
152
+ * Searches a bounded range of salts for the first value that satisfies TIP-1022 PoW.
153
+ *
154
+ * [TIP-1022](https://tips.sh/1022)
155
+ *
156
+ * This is intentionally a small, deterministic primitive. It does not coordinate
157
+ * workers or async execution. Callers that need large searches can shard ranges
158
+ * externally.
159
+ *
160
+ * Master addresses must satisfy TIP-1022 registration constraints: they cannot
161
+ * be the zero address, another virtual address, or a TIP-20 token address.
162
+ *
163
+ * :::warning
164
+ *
165
+ * It is strongly recommended to use {@link ox#VirtualMaster.(mineSaltAsync:function)} instead of this
166
+ * function. `mineSaltAsync` uses WASM-accelerated keccak256 with parallel
167
+ * workers and is a lot faster than the pure JS implementation used here.
168
+ *
169
+ * :::
170
+ *
171
+ * @example
172
+ * ```ts twoslash
173
+ * import { Address } from 'ox'
174
+ * import { VirtualMaster } from 'ox/tempo'
175
+ *
176
+ * const result = VirtualMaster.mineSalt({
177
+ * address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
178
+ * })
179
+ *
180
+ * result?.salt
181
+ * // @log: '0x00000000000000000000000000000000000000000000000000000000abf52baf'
182
+ * ```
183
+ *
184
+ * @param value - Search range parameters.
185
+ * @returns The first matching salt in the range, if any.
186
+ */
187
+ export function mineSalt(
188
+ value: mineSalt.Value,
189
+ ): mineSalt.ReturnType | undefined {
190
+ const count = value.count ?? 2 ** 32
191
+
192
+ assertCount(count)
193
+
194
+ const addressBytes = Bytes.fromHex(resolveAddress(value.address))
195
+ const input = new Uint8Array(addressBytes.length + 32)
196
+ input.set(addressBytes)
197
+
198
+ // Salt is a view into input — increment mutates input directly, no copy.
199
+ const saltView = input.subarray(addressBytes.length)
200
+ saltView.set(toFixedBytes(value.start ?? 0n, 32))
201
+
202
+ for (let i = 0; i < count; i++) {
203
+ const hash = keccak_256(input)
204
+
205
+ if (hash[0] === 0 && hash[1] === 0 && hash[2] === 0 && hash[3] === 0) {
206
+ return {
207
+ masterId: Hex.fromBytes(hash.subarray(4, 8)),
208
+ registrationHash: Hex.fromBytes(hash),
209
+ salt: Hex.fromBytes(saltView),
210
+ }
211
+ }
212
+
213
+ if (i < count - 1 && !increment(saltView)) break
214
+ }
215
+
216
+ return undefined
217
+ }
218
+
219
+ export declare namespace mineSalt {
220
+ type Value = {
221
+ /** Master address. Accepts both hex and Tempo addresses. */
222
+ address: TempoAddress.Address
223
+ /** Number of consecutive salts to try. */
224
+ count?: number | undefined
225
+ /** Starting salt value. @default 0n */
226
+ start?: Salt | undefined
227
+ }
228
+
229
+ type ReturnType = {
230
+ /** The 4-byte master identifier derived from the matching salt. */
231
+ masterId: Hex.Hex
232
+ /** The matching registration hash. */
233
+ registrationHash: Hex.Hex
234
+ /** The discovered 32-byte salt. */
235
+ salt: Hex.Hex
236
+ }
237
+
238
+ type ErrorType =
239
+ | Address.assert.ErrorType
240
+ | Bytes.fromHex.ErrorType
241
+ | Bytes.padLeft.ErrorType
242
+ | Errors.BaseError
243
+ | Hash.keccak256.ErrorType
244
+ | Hex.assert.ErrorType
245
+ | Hex.fromBytes.ErrorType
246
+ | Hex.fromNumber.ErrorType
247
+ | Hex.padLeft.ErrorType
248
+ | TempoAddress.parse.ErrorType
249
+ | Errors.GlobalErrorType
250
+ }
251
+
252
+ /**
253
+ * Searches for a salt that satisfies TIP-1022 PoW using parallel workers and
254
+ * WASM-accelerated keccak256.
255
+ *
256
+ * [TIP-1022](https://tips.sh/1022)
257
+ *
258
+ * Uses WASM-accelerated keccak256 with parallel
259
+ * workers when available. Falls back to chunked single-threaded mining in
260
+ * environments without worker support.
261
+ *
262
+ * - **Node.js / Bun / Deno**: Spawns `worker_threads` with inline WASM keccak256.
263
+ * - **Browsers**: Spawns Web Workers via Blob URLs with inline WASM keccak256.
264
+ *
265
+ * @example
266
+ * ```ts twoslash
267
+ * import { Address } from 'ox'
268
+ * import { VirtualMaster } from 'ox/tempo'
269
+ *
270
+ * const result = await VirtualMaster.mineSaltAsync({
271
+ * address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
272
+ * })
273
+ * ```
274
+ *
275
+ * @param parameters - Search parameters.
276
+ * @returns The first matching salt, if any.
277
+ */
278
+ export async function mineSaltAsync(
279
+ parameters: mineSaltAsync.Parameters,
280
+ ): Promise<mineSalt.ReturnType | undefined> {
281
+ const {
282
+ chunkSize = 100_000,
283
+ count = 2 ** 32,
284
+ onProgress,
285
+ signal,
286
+ start: start_ = 0n,
287
+ workers = getDefaultWorkerCount(),
288
+ } = parameters
289
+
290
+ const address = resolveAddress(parameters.address)
291
+ const start = toFixedHex(start_, 32)
292
+
293
+ assertCount(count)
294
+ if (workers !== undefined) assertWorkers(workers)
295
+ throwIfAborted(signal)
296
+
297
+ const workerCount = Math.max(
298
+ 1,
299
+ Math.min(workers, Math.ceil(count / chunkSize)),
300
+ )
301
+
302
+ if (workerCount <= 1)
303
+ return mineSaltAsyncFallback({
304
+ address,
305
+ chunkSize,
306
+ count,
307
+ onProgress,
308
+ signal,
309
+ start,
310
+ })
311
+
312
+ const pool = await VirtualMasterPool.resolve()
313
+ if (!pool)
314
+ return mineSaltAsyncFallback({
315
+ address,
316
+ chunkSize,
317
+ count,
318
+ onProgress,
319
+ signal,
320
+ start,
321
+ })
322
+
323
+ return mineSaltWithWorkerPool({
324
+ address,
325
+ chunkSize,
326
+ count,
327
+ onProgress,
328
+ pool,
329
+ signal,
330
+ start,
331
+ workerCount,
332
+ })
333
+ }
334
+
335
+ export declare namespace mineSaltAsync {
336
+ type Parameters = {
337
+ /** Master address. Accepts both hex and Tempo addresses. */
338
+ address: TempoAddress.Address
339
+ /**
340
+ * Number of salts each worker processes before sending a progress update.
341
+ *
342
+ * @default 100_000
343
+ */
344
+ chunkSize?: number | undefined
345
+ /** Number of consecutive salts to try. @default 2 ** 32 */
346
+ count?: number | undefined
347
+ /** Progress callback invoked after each completed chunk. */
348
+ onProgress?: ((progress: Progress) => void) | undefined
349
+ /** AbortSignal for cancellation. */
350
+ signal?: AbortSignal | undefined
351
+ /** Starting salt value. @default 0n */
352
+ start?: Salt | undefined
353
+ /**
354
+ * Number of workers to use.
355
+ *
356
+ * Set to `0` or `1` to disable worker parallelism.
357
+ *
358
+ * @default os.availableParallelism() - 1
359
+ */
360
+ workers?: number | undefined
361
+ }
362
+
363
+ type Progress = {
364
+ /** Total attempts so far. */
365
+ attempts: number
366
+ /** Configured chunk size. */
367
+ chunkSize: number
368
+ /** Total count requested. */
369
+ count: number
370
+ /** Elapsed time in milliseconds. */
371
+ elapsed: number
372
+ /** Fraction complete (0–1). */
373
+ progress: number
374
+ /** Hashes per second. */
375
+ rate: number
376
+ /** Number of workers in use. */
377
+ workers: number
378
+ }
379
+
380
+ type ErrorType =
381
+ | mineSalt.ErrorType
382
+ | Errors.BaseError
383
+ | Errors.GlobalErrorType
384
+ }
385
+
386
+ /**
387
+ * Runs parallel salt mining across a worker pool.
388
+ *
389
+ * @internal
390
+ */
391
+ // biome-ignore lint/correctness/noUnusedVariables: _
392
+ function mineSaltWithWorkerPool(
393
+ value: mineSaltWithWorkerPool.Options,
394
+ ): Promise<mineSalt.ReturnType | undefined> {
395
+ const startedAt = Date.now()
396
+
397
+ return new Promise<mineSalt.ReturnType | undefined>((resolve, reject) => {
398
+ let settled = false
399
+ let attempts = 0
400
+ let completedWorkers = 0
401
+ const handles: { terminate(): void }[] = []
402
+
403
+ const emitProgress = () => {
404
+ if (!value.onProgress) return
405
+ const elapsed = Date.now() - startedAt
406
+ const seconds = elapsed / 1000
407
+ value.onProgress({
408
+ attempts,
409
+ chunkSize: value.chunkSize,
410
+ count: value.count,
411
+ elapsed,
412
+ progress: Math.min(1, attempts / value.count),
413
+ rate: seconds === 0 ? 0 : attempts / seconds,
414
+ workers: value.workerCount,
415
+ })
416
+ }
417
+
418
+ const terminateAll = () => {
419
+ for (const h of handles) h.terminate()
420
+ }
421
+
422
+ const succeed = (result: mineSalt.ReturnType | undefined) => {
423
+ if (settled) return
424
+ settled = true
425
+ value.signal?.removeEventListener('abort', onAbort)
426
+ terminateAll()
427
+ resolve(result)
428
+ }
429
+
430
+ const fail = (error: unknown) => {
431
+ if (settled) return
432
+ settled = true
433
+ value.signal?.removeEventListener('abort', onAbort)
434
+ terminateAll()
435
+ reject(
436
+ error instanceof Error
437
+ ? error
438
+ : new Errors.BaseError('Failed to mine virtual master salt.'),
439
+ )
440
+ }
441
+
442
+ const onMessage = (msg: VirtualMasterPool.Message) => {
443
+ if (settled) return
444
+ switch (msg.type) {
445
+ case 'found':
446
+ succeed(msg.result as mineSalt.ReturnType)
447
+ return
448
+ case 'progress':
449
+ attempts += msg.attempts
450
+ emitProgress()
451
+ return
452
+ case 'done':
453
+ completedWorkers++
454
+ if (completedWorkers === value.workerCount) succeed(undefined)
455
+ return
456
+ case 'error':
457
+ fail(new Errors.BaseError(msg.message))
458
+ return
459
+ }
460
+ }
461
+
462
+ const onAbort = () => fail(getAbortError(value.signal))
463
+ value.signal?.addEventListener('abort', onAbort, { once: true })
464
+
465
+ for (let i = 0; i < value.workerCount; i++) {
466
+ const handle = value.pool.spawn(i, onMessage, fail)
467
+ handles.push(handle)
468
+ handle.postMessage({
469
+ type: 'start',
470
+ address: value.address,
471
+ chunkSize: value.chunkSize,
472
+ count: value.count,
473
+ start: value.start,
474
+ workerCount: value.workerCount,
475
+ workerIndex: i,
476
+ })
477
+ }
478
+ })
479
+ }
480
+
481
+ declare namespace mineSaltWithWorkerPool {
482
+ type Options = {
483
+ /** Resolved master address. */
484
+ address: Address.Address
485
+ /** Salts per chunk before a progress update. */
486
+ chunkSize: number
487
+ /** Total number of salts to try. */
488
+ count: number
489
+ /** Progress callback. */
490
+ onProgress: mineSaltAsync.Parameters['onProgress']
491
+ /** Worker pool to distribute work across. */
492
+ pool: VirtualMasterPool.Pool
493
+ /** AbortSignal for cancellation. */
494
+ signal: AbortSignal | undefined
495
+ /** Starting salt as a hex string. */
496
+ start: Hex.Hex
497
+ /** Number of workers to use. */
498
+ workerCount: number
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Single-threaded chunked fallback when no worker pool is available.
504
+ *
505
+ * @internal
506
+ */
507
+ // biome-ignore lint/correctness/noUnusedVariables: _
508
+ async function mineSaltAsyncFallback(
509
+ value: mineSaltAsyncFallback.Options,
510
+ ): Promise<mineSalt.ReturnType | undefined> {
511
+ const startedAt = Date.now()
512
+ const startBigInt = BigInt(value.start)
513
+
514
+ for (let offset = 0; offset < value.count; offset += value.chunkSize) {
515
+ throwIfAborted(value.signal)
516
+
517
+ const count = Math.min(value.chunkSize, value.count - offset)
518
+ const result = mineSalt({
519
+ address: value.address,
520
+ count,
521
+ start: startBigInt + BigInt(offset),
522
+ })
523
+
524
+ if (value.onProgress) {
525
+ const attempts = Math.min(value.count, offset + count)
526
+ const elapsed = Date.now() - startedAt
527
+ const seconds = elapsed / 1000
528
+ value.onProgress({
529
+ attempts,
530
+ chunkSize: value.chunkSize,
531
+ count: value.count,
532
+ elapsed,
533
+ progress: Math.min(1, attempts / value.count),
534
+ rate: seconds === 0 ? 0 : attempts / seconds,
535
+ workers: 1,
536
+ })
537
+ }
538
+
539
+ if (result) return result
540
+
541
+ // Yield to the event loop between chunks.
542
+ await new Promise<void>((r) => setTimeout(r, 0))
543
+ }
544
+
545
+ return undefined
546
+ }
547
+
548
+ declare namespace mineSaltAsyncFallback {
549
+ type Options = {
550
+ /** Resolved master address. */
551
+ address: Address.Address
552
+ /** Salts per chunk before yielding to the event loop. */
553
+ chunkSize: number
554
+ /** Total number of salts to try. */
555
+ count: number
556
+ /** Progress callback. */
557
+ onProgress: mineSaltAsync.Parameters['onProgress']
558
+ /** AbortSignal for cancellation. */
559
+ signal: AbortSignal | undefined
560
+ /** Starting salt as a hex string. */
561
+ start: Hex.Hex
562
+ }
563
+ }
564
+
565
+ /**
566
+ * Asserts that `workers` is a non-negative safe integer.
567
+ *
568
+ * @internal
569
+ */
570
+ function assertWorkers(workers: number) {
571
+ if (Number.isSafeInteger(workers) && workers >= 0) return
572
+ throw new Errors.BaseError(
573
+ `Workers "${workers}" is invalid. Expected a non-negative safe integer.`,
574
+ )
575
+ }
576
+
577
+ /**
578
+ * Extracts or creates an error from an `AbortSignal`.
579
+ *
580
+ * @internal
581
+ */
582
+ function getAbortError(signal?: AbortSignal): Error {
583
+ const reason = signal?.reason
584
+ if (reason instanceof Error) return reason
585
+ return new Errors.BaseError('The operation was aborted.')
586
+ }
587
+
588
+ /**
589
+ * Returns the default number of workers for the current platform.
590
+ *
591
+ * @internal
592
+ */
593
+ function getDefaultWorkerCount(): number {
594
+ if (typeof navigator !== 'undefined') {
595
+ const c = navigator.hardwareConcurrency
596
+ if (c && c > 1) return c - 1
597
+ }
598
+ return 1
599
+ }
600
+
601
+ /**
602
+ * Throws the signal's abort reason if the signal is aborted.
603
+ *
604
+ * @internal
605
+ */
606
+ function throwIfAborted(signal?: AbortSignal) {
607
+ if (!signal?.aborted) return
608
+ throw getAbortError(signal)
609
+ }
610
+
611
+ /**
612
+ * Returns `true` if the first 4 bytes of a hash are zero.
613
+ *
614
+ * @internal
615
+ */
616
+ function hasProofOfWork(hash: Bytes.Bytes): boolean {
617
+ return hash[0] === 0 && hash[1] === 0 && hash[2] === 0 && hash[3] === 0
618
+ }
619
+
620
+ /**
621
+ * Asserts that `count` is a positive safe integer.
622
+ *
623
+ * @internal
624
+ */
625
+ function assertCount(count: number) {
626
+ if (Number.isSafeInteger(count) && count > 0) return
627
+
628
+ throw new Errors.BaseError(
629
+ `Count "${count}" is invalid. Expected a positive safe integer.`,
630
+ )
631
+ }
632
+
633
+ /**
634
+ * Increments a big-endian byte array by one. Returns `false` on overflow.
635
+ *
636
+ * @internal
637
+ */
638
+ function increment(bytes: Bytes.Bytes): boolean {
639
+ for (let i = bytes.length - 1; i >= 0; i--) {
640
+ const value = bytes[i]!
641
+ if (value === 0xff) {
642
+ bytes[i] = 0
643
+ continue
644
+ }
645
+
646
+ bytes[i] = value + 1
647
+ return true
648
+ }
649
+
650
+ return false
651
+ }
652
+
653
+ /**
654
+ * Resolves a Tempo or hex address, validates it as a valid master.
655
+ *
656
+ * @internal
657
+ */
658
+ function resolveAddress(address: string): Address.Address {
659
+ const resolved = TempoAddress.resolve(address as TempoAddress.Address)
660
+ Address.assert(resolved, { strict: false })
661
+ assertValidMasterAddress(resolved)
662
+ return resolved
663
+ }
664
+
665
+ /**
666
+ * Throws if the address is zero, virtual, or a TIP-20 token.
667
+ *
668
+ * @internal
669
+ */
670
+ function assertValidMasterAddress(address: Address.Address) {
671
+ const normalized = address.toLowerCase()
672
+
673
+ if (normalized === zeroAddress)
674
+ throw new Errors.BaseError(
675
+ 'Virtual master address cannot be the zero address.',
676
+ )
677
+
678
+ if (VirtualAddress.isVirtual(address))
679
+ throw new Errors.BaseError(
680
+ 'Virtual master address cannot itself be a virtual address.',
681
+ )
682
+
683
+ if (normalized.startsWith(tip20Prefix))
684
+ throw new Errors.BaseError(
685
+ 'Virtual master address cannot be a TIP-20 token address.',
686
+ )
687
+ }
688
+
689
+ /**
690
+ * Converts a salt to a fixed-size byte array.
691
+ *
692
+ * @internal
693
+ */
694
+ function toFixedBytes(value: Salt, size: number): Bytes.Bytes {
695
+ return Bytes.fromHex(toFixedHex(value, size))
696
+ }
697
+
698
+ /**
699
+ * Converts a salt to a zero-padded hex string of the given size.
700
+ *
701
+ * @internal
702
+ */
703
+ function toFixedHex(value: Salt, size: number): Hex.Hex {
704
+ if (typeof value === 'number' || typeof value === 'bigint')
705
+ return Hex.fromNumber(value, { size })
706
+ if (typeof value === 'string') {
707
+ Hex.assert(value, { strict: true })
708
+ return Hex.padLeft(value, size)
709
+ }
710
+ return Hex.fromBytes(Bytes.padLeft(value, size))
711
+ }
package/tempo/e2e.test.ts CHANGED
@@ -3,7 +3,6 @@ import {
3
3
  Address,
4
4
  Hex,
5
5
  P256,
6
- RpcTransport,
7
6
  Secp256k1,
8
7
  Value,
9
8
  WebAuthnP256,
@@ -17,7 +16,6 @@ import {
17
16
  KeyAuthorization,
18
17
  Period,
19
18
  SignatureEnvelope,
20
- ZoneRpcAuthentication,
21
19
  } from './index.js'
22
20
  import * as Transaction from './Transaction.js'
23
21
  import * as TransactionReceipt from './TransactionReceipt.js'