@buildonspark/spark-sdk 0.1.38 → 0.1.40

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 (104) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +1 -1
  3. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/arm64-v8a/libspark_frost.so +0 -0
  4. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/arm64-v8a/libuniffi_spark_frost.so +0 -0
  5. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/armeabi-v7a/libspark_frost.so +0 -0
  6. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  7. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86/libspark_frost.so +0 -0
  8. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86/libuniffi_spark_frost.so +0 -0
  9. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86_64/libspark_frost.so +0 -0
  10. package/android/build/intermediates/library_jni/debug/copyDebugJniLibsProjectOnly/jni/x86_64/libuniffi_spark_frost.so +0 -0
  11. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/arm64-v8a/libspark_frost.so +0 -0
  12. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/arm64-v8a/libuniffi_spark_frost.so +0 -0
  13. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/armeabi-v7a/libspark_frost.so +0 -0
  14. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  15. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86/libspark_frost.so +0 -0
  16. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86/libuniffi_spark_frost.so +0 -0
  17. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86_64/libspark_frost.so +0 -0
  18. package/android/build/intermediates/merged_jni_libs/debug/mergeDebugJniLibFolders/out/x86_64/libuniffi_spark_frost.so +0 -0
  19. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/arm64-v8a/libspark_frost.so +0 -0
  20. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/arm64-v8a/libuniffi_spark_frost.so +0 -0
  21. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/armeabi-v7a/libspark_frost.so +0 -0
  22. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/armeabi-v7a/libuniffi_spark_frost.so +0 -0
  23. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86/libspark_frost.so +0 -0
  24. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86/libuniffi_spark_frost.so +0 -0
  25. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86_64/libspark_frost.so +0 -0
  26. package/android/build/intermediates/merged_native_libs/debug/mergeDebugNativeLibs/out/lib/x86_64/libuniffi_spark_frost.so +0 -0
  27. package/dist/{RequestLightningSendInput-B4JdzclX.d.ts → RequestLightningSendInput-CJtcHOnu.d.ts} +1 -1
  28. package/dist/{RequestLightningSendInput-39_zGri6.d.cts → RequestLightningSendInput-DfmfqzZo.d.cts} +1 -1
  29. package/dist/address/index.d.cts +1 -1
  30. package/dist/address/index.d.ts +1 -1
  31. package/dist/address/index.js +2 -2
  32. package/dist/{chunk-W3EC5XSA.js → chunk-5MNQB2T4.js} +2 -2
  33. package/dist/chunk-ED3ZAFDI.js +784 -0
  34. package/dist/{chunk-VJTDG4BQ.js → chunk-HK6LPV6Z.js} +10 -1
  35. package/dist/{chunk-7WRK6WNJ.js → chunk-LHT4QTFK.js} +556 -41
  36. package/dist/{chunk-RAPBVYJY.js → chunk-RFCXPGDM.js} +26 -4
  37. package/dist/{chunk-DI7QXUQJ.js → chunk-W2VXS35Y.js} +4 -4
  38. package/dist/graphql/objects/index.d.cts +5 -4
  39. package/dist/graphql/objects/index.d.ts +5 -4
  40. package/dist/{index-CxAi2L8y.d.ts → index-BDEYgYxP.d.ts} +42 -4
  41. package/dist/{index-Dm17Ggfe.d.cts → index-CLdtdMU4.d.cts} +42 -4
  42. package/dist/index.cjs +1069 -40
  43. package/dist/index.d.cts +6 -6
  44. package/dist/index.d.ts +6 -6
  45. package/dist/index.js +33 -17
  46. package/dist/index.node.cjs +1069 -40
  47. package/dist/index.node.d.cts +6 -6
  48. package/dist/index.node.d.ts +6 -6
  49. package/dist/index.node.js +33 -17
  50. package/dist/native/index.cjs +1069 -40
  51. package/dist/native/index.d.cts +108 -5
  52. package/dist/native/index.d.ts +108 -5
  53. package/dist/native/index.js +1065 -40
  54. package/dist/{network-GFGEHkS4.d.cts → network-B10hBoHp.d.cts} +8 -1
  55. package/dist/{network-DobHpaV6.d.ts → network-CCgyIsGl.d.ts} +8 -1
  56. package/dist/services/config.cjs +29 -12
  57. package/dist/services/config.d.cts +4 -4
  58. package/dist/services/config.d.ts +4 -4
  59. package/dist/services/config.js +5 -5
  60. package/dist/services/connection.d.cts +4 -4
  61. package/dist/services/connection.d.ts +4 -4
  62. package/dist/services/connection.js +2 -2
  63. package/dist/services/index.cjs +30 -13
  64. package/dist/services/index.d.cts +4 -4
  65. package/dist/services/index.d.ts +4 -4
  66. package/dist/services/index.js +8 -8
  67. package/dist/services/lrc-connection.d.cts +4 -4
  68. package/dist/services/lrc-connection.d.ts +4 -4
  69. package/dist/services/lrc-connection.js +1 -1
  70. package/dist/services/token-transactions.cjs +1 -1
  71. package/dist/services/token-transactions.d.cts +4 -4
  72. package/dist/services/token-transactions.d.ts +4 -4
  73. package/dist/services/token-transactions.js +3 -3
  74. package/dist/services/wallet-config.d.cts +4 -4
  75. package/dist/services/wallet-config.d.ts +4 -4
  76. package/dist/signer/signer.cjs +23 -6
  77. package/dist/signer/signer.d.cts +3 -2
  78. package/dist/signer/signer.d.ts +3 -2
  79. package/dist/signer/signer.js +1 -1
  80. package/dist/{signer-DFGw9RRp.d.ts → signer-C5h1DpjF.d.ts} +4 -1
  81. package/dist/{signer-C1t40Wus.d.cts → signer-CYwn7h9U.d.cts} +4 -1
  82. package/dist/types/index.d.cts +4 -3
  83. package/dist/types/index.d.ts +4 -3
  84. package/dist/utils/index.cjs +891 -2
  85. package/dist/utils/index.d.cts +62 -6
  86. package/dist/utils/index.d.ts +62 -6
  87. package/dist/utils/index.js +23 -7
  88. package/package.json +1 -1
  89. package/src/services/deposit.ts +23 -5
  90. package/src/services/token-transactions.ts +1 -1
  91. package/src/services/transfer.ts +218 -11
  92. package/src/services/tree-creation.ts +29 -14
  93. package/src/signer/signer.ts +47 -5
  94. package/src/spark-wallet/spark-wallet.ts +430 -4
  95. package/src/tests/integration/swap.test.ts +225 -0
  96. package/src/tests/integration/tree-creation.test.ts +5 -1
  97. package/src/utils/index.ts +1 -0
  98. package/src/utils/mempool.ts +26 -1
  99. package/src/utils/network.ts +15 -0
  100. package/src/utils/transaction.ts +22 -2
  101. package/src/utils/unilateral-exit.ts +729 -0
  102. package/dist/chunk-E5SL7XTO.js +0 -301
  103. package/dist/{chunk-LIP2K6KR.js → chunk-2CDJZQN4.js} +3 -3
  104. package/dist/{chunk-RGWBSZIO.js → chunk-I4JI6TYN.js} +4 -4
@@ -244,4 +244,229 @@ describe("swap", () => {
244
244
  leavesToClaim_1,
245
245
  );
246
246
  }, 30000);
247
+
248
+ it("test swap v2", async () => {
249
+ const faucet = BitcoinFaucet.getInstance();
250
+ // Initiate sender
251
+ const { wallet: senderWallet } = await SparkWalletTesting.initialize({
252
+ options: {
253
+ network: "LOCAL",
254
+ },
255
+ });
256
+ const senderPubkey = await senderWallet.getIdentityPublicKey();
257
+
258
+ const senderConfig = new WalletConfigService(
259
+ {
260
+ network: "LOCAL",
261
+ },
262
+ senderWallet.getSigner(),
263
+ );
264
+ const senderConnectionManager = new ConnectionManager(senderConfig);
265
+ const senderSigningService = new SigningService(senderConfig);
266
+ const senderTransferService = new TransferService(
267
+ senderConfig,
268
+ senderConnectionManager,
269
+ senderSigningService,
270
+ );
271
+
272
+ // Initiate receiver
273
+ const { wallet: receiverWallet } = await SparkWalletTesting.initialize({
274
+ options: {
275
+ network: "LOCAL",
276
+ },
277
+ });
278
+ const receiverPubkey = await receiverWallet.getIdentityPublicKey();
279
+
280
+ const receiverConfig = new WalletConfigService(
281
+ {
282
+ network: "LOCAL",
283
+ },
284
+ receiverWallet.getSigner(),
285
+ );
286
+ const receiverConnectionManager = new ConnectionManager(receiverConfig);
287
+ const receiverSigningService = new SigningService(receiverConfig);
288
+ const receiverTransferService = new TransferService(
289
+ receiverConfig,
290
+ receiverConnectionManager,
291
+ receiverSigningService,
292
+ );
293
+
294
+ const senderLeafPubKey = await senderWallet.getSigner().generatePublicKey();
295
+ const senderRootNode = await createNewTree(
296
+ senderWallet,
297
+ senderLeafPubKey,
298
+ faucet,
299
+ );
300
+
301
+ const receiverLeafPubKey = await receiverWallet
302
+ .getSigner()
303
+ .generatePublicKey();
304
+ const receiverRootNode = await createNewTree(
305
+ receiverWallet,
306
+ receiverLeafPubKey,
307
+ faucet,
308
+ );
309
+
310
+ // Sender initiates transfer
311
+ const senderNewLeafPubKey = await senderWallet
312
+ .getSigner()
313
+ .generatePublicKey(sha256("1"));
314
+ const senderTransferNode: LeafKeyTweak = {
315
+ leaf: senderRootNode,
316
+ signingPubKey: senderLeafPubKey,
317
+ newSigningPubKey: senderNewLeafPubKey,
318
+ };
319
+ const senderLeavesToTransfer = [senderTransferNode];
320
+
321
+ // Get signature for refunds (normal flow)
322
+ const {
323
+ transfer: senderTransfer,
324
+ signatureMap: senderRefundSignatureMap,
325
+ leafDataMap: senderLeafDataMap,
326
+ } = await senderTransferService.startSwapSignRefund(
327
+ senderLeavesToTransfer,
328
+ hexToBytes(receiverPubkey),
329
+ new Date(Date.now() + 10 * 60 * 1000),
330
+ );
331
+
332
+ expect(senderRefundSignatureMap.size).toBe(1);
333
+ const senderSignature = senderRefundSignatureMap.get(senderRootNode.id);
334
+ expect(senderSignature).toBeDefined();
335
+ expect(senderLeafDataMap.size).toBe(1);
336
+
337
+ const { adaptorPrivateKey } = generateAdaptorFromSignature(
338
+ senderSignature!,
339
+ );
340
+ const adaptorPubKey = secp256k1.getPublicKey(adaptorPrivateKey);
341
+
342
+ const receiverNewLeafPubKey = await receiverWallet
343
+ .getSigner()
344
+ .generatePublicKey(sha256("1"));
345
+
346
+ const receiverTransferNode: LeafKeyTweak = {
347
+ leaf: receiverRootNode,
348
+ signingPubKey: receiverLeafPubKey,
349
+ newSigningPubKey: receiverNewLeafPubKey,
350
+ };
351
+ const receiverLeavesToTransfer = [receiverTransferNode];
352
+
353
+ const {
354
+ transfer: receiverTransfer,
355
+ signatureMap: receiverRefundSignatureMap,
356
+ leafDataMap: receiverLeafDataMap,
357
+ signingResults: operatorSigningResults,
358
+ } = await receiverTransferService.counterSwapSignRefund(
359
+ receiverLeavesToTransfer,
360
+ hexToBytes(senderPubkey),
361
+ new Date(Date.now() + 10 * 60 * 1000),
362
+ adaptorPubKey,
363
+ );
364
+
365
+ const newReceiverRefundSignatureMap = new Map<string, Uint8Array>();
366
+ for (const [nodeId, signature] of receiverRefundSignatureMap.entries()) {
367
+ const leafData = receiverLeafDataMap.get(nodeId);
368
+ if (!leafData?.refundTx) {
369
+ throw new ValidationError("Refund transaction not found", {
370
+ field: "refundTx",
371
+ value: leafData,
372
+ });
373
+ }
374
+ const sighash = getSigHashFromTx(
375
+ leafData.refundTx,
376
+ 0,
377
+ leafData.tx.getOutput(leafData.vout),
378
+ );
379
+ let verifyingPubkey: Uint8Array | undefined;
380
+ for (const signingResult of operatorSigningResults) {
381
+ if (signingResult.leafId === nodeId) {
382
+ verifyingPubkey = signingResult.verifyingKey;
383
+ }
384
+ }
385
+ expect(verifyingPubkey).toBeDefined();
386
+ const taprootKey = computeTaprootKeyNoScript(
387
+ verifyingPubkey!.slice(1, 33),
388
+ );
389
+ const adaptorSig = applyAdaptorToSignature(
390
+ taprootKey.slice(1, 33),
391
+ sighash,
392
+ signature,
393
+ adaptorPrivateKey,
394
+ );
395
+ newReceiverRefundSignatureMap.set(nodeId, adaptorSig);
396
+ }
397
+ const senderTransferTweakKey =
398
+ await senderTransferService.deliverTransferPackage(
399
+ senderTransfer,
400
+ senderLeavesToTransfer,
401
+ senderRefundSignatureMap,
402
+ );
403
+
404
+ const pendingTransfer =
405
+ await receiverTransferService.queryPendingTransfers();
406
+ expect(pendingTransfer.transfers.length).toBe(1);
407
+ const receiverPendingTransfer = pendingTransfer.transfers[0];
408
+ expect(receiverPendingTransfer!.id).toBe(senderTransferTweakKey.id);
409
+
410
+ const leafPrivKeyMap = await receiverTransferService.verifyPendingTransfer(
411
+ receiverPendingTransfer!,
412
+ );
413
+
414
+ expect(leafPrivKeyMap.size).toBe(1);
415
+ expect(leafPrivKeyMap.get(senderRootNode.id)).toBeDefined();
416
+ const bytesEqual = equalBytes(
417
+ leafPrivKeyMap.get(senderRootNode.id)!,
418
+ senderNewLeafPubKey,
419
+ );
420
+ expect(bytesEqual).toBe(true);
421
+ expect(receiverPendingTransfer!.leaves[0]!.leaf).toBeDefined();
422
+ const finalLeafPubKey = await receiverWallet
423
+ .getSigner()
424
+ .generatePublicKey(sha256("2"));
425
+ const claimingNode: LeafKeyTweak = {
426
+ leaf: receiverPendingTransfer!.leaves[0]!.leaf!,
427
+ signingPubKey: senderNewLeafPubKey,
428
+ newSigningPubKey: finalLeafPubKey,
429
+ };
430
+ const leavesToClaim = [claimingNode];
431
+ await receiverTransferService.claimTransfer(
432
+ receiverPendingTransfer!,
433
+ leavesToClaim,
434
+ );
435
+ await receiverTransferService.deliverTransferPackage(
436
+ receiverTransfer,
437
+ receiverLeavesToTransfer,
438
+ newReceiverRefundSignatureMap,
439
+ );
440
+
441
+ const sPendingTransfer =
442
+ await senderTransferService.queryPendingTransfers();
443
+ expect(sPendingTransfer.transfers.length).toBe(1);
444
+ const senderPendingTransfer = sPendingTransfer.transfers[0];
445
+ expect(senderPendingTransfer!.id).toBe(receiverTransfer.id);
446
+
447
+ const senderLeafPrivKeyMap =
448
+ await senderTransferService.verifyPendingTransfer(senderPendingTransfer!);
449
+ expect(senderLeafPrivKeyMap.size).toBe(1);
450
+ expect(senderLeafPrivKeyMap.get(receiverRootNode.id)).toBeDefined();
451
+ const bytesEqual_1 = equalBytes(
452
+ senderLeafPrivKeyMap.get(receiverRootNode.id)!,
453
+ receiverNewLeafPubKey,
454
+ );
455
+ expect(bytesEqual_1).toBe(true);
456
+ expect(senderPendingTransfer!.leaves[0]!.leaf).toBeDefined();
457
+
458
+ const finalLeafPubKey_1 = await senderWallet
459
+ .getSigner()
460
+ .generatePublicKey(sha256("3"));
461
+ const claimingNode_1: LeafKeyTweak = {
462
+ leaf: senderPendingTransfer!.leaves[0]!.leaf!,
463
+ signingPubKey: receiverNewLeafPubKey,
464
+ newSigningPubKey: finalLeafPubKey_1,
465
+ };
466
+ const leavesToClaim_1 = [claimingNode_1];
467
+ await senderTransferService.claimTransfer(
468
+ senderPendingTransfer!,
469
+ leavesToClaim_1,
470
+ );
471
+ }, 30000);
247
472
  });
@@ -1,10 +1,14 @@
1
1
  // @ts-nocheck
2
2
  import { describe, expect, it } from "@jest/globals";
3
3
  import { bytesToHex } from "@noble/curves/abstract/utils";
4
+ import { secp256k1 } from "@noble/curves/secp256k1";
4
5
  import { ValidationError } from "../../errors/types.js";
5
6
  import { getTxFromRawTxBytes, getTxId } from "../../utils/bitcoin.js";
6
7
  import { Network } from "../../utils/network.js";
8
+ import { DEFAULT_FEE_SATS } from "../../utils/transaction.js";
9
+ import { createNewTreeWithLevels } from "../test-util.js";
7
10
  import { SparkWalletTesting } from "../utils/spark-testing-wallet.js";
11
+ import { BitcoinFaucet } from "../utils/test-faucet.js";
8
12
 
9
13
  describe("Tree Creation", () => {
10
14
  it.skip("test tree creation address generation", async () => {
@@ -19,7 +23,7 @@ describe("Tree Creation", () => {
19
23
 
20
24
  const dummyTx = createDummyTx({
21
25
  address: depositResp.depositAddress!.address,
22
- amountSats: 65536n,
26
+ amountSats: 65536n + DEFAULT_FEE_SATS,
23
27
  });
24
28
 
25
29
  const depositTxHex = bytesToHex(dummyTx.tx);
@@ -10,3 +10,4 @@ export * from "./signing.js";
10
10
  export * from "./token-transactions.js";
11
11
  export * from "./transfer_package.js";
12
12
  export * from "./transaction.js";
13
+ export * from "./unilateral-exit.js";
@@ -3,7 +3,7 @@ import {
3
3
  getElectrsUrl,
4
4
  } from "../services/wallet-config.js";
5
5
  import { BitcoinNetwork } from "../types/index.js";
6
- import { getNetworkFromAddress } from "./network.js";
6
+ import { Network, getNetworkFromAddress } from "./network.js";
7
7
 
8
8
  export async function getLatestDepositTxId(
9
9
  address: string,
@@ -42,3 +42,28 @@ export async function getLatestDepositTxId(
42
42
  }
43
43
  return null;
44
44
  }
45
+
46
+ export async function isTxBroadcast(
47
+ txid: string,
48
+ baseUrl: string,
49
+ network?: any,
50
+ ): Promise<boolean> {
51
+ const headers: Record<string, string> = {};
52
+
53
+ if (network === Network.REGTEST) {
54
+ const auth = btoa(
55
+ `${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`,
56
+ );
57
+ headers["Authorization"] = `Basic ${auth}`;
58
+ }
59
+ const response = await fetch(`${baseUrl}/tx/${txid}`, {
60
+ headers,
61
+ });
62
+
63
+ const tx = await response.json();
64
+ if (tx.error) {
65
+ return false;
66
+ }
67
+
68
+ return true;
69
+ }
@@ -78,3 +78,18 @@ export function getNetworkFromAddress(address: string) {
78
78
  }
79
79
  return null;
80
80
  }
81
+
82
+ /**
83
+ * Utility function to get the network enum value from a string.
84
+ *
85
+ * @param {string} network - The Bitcoin network to turn into an enum value
86
+ * @returns {Network} The enum value matching the string
87
+ */
88
+ export function getNetworkFromString(network?: string) {
89
+ const net = (network ?? "REGTEST").toUpperCase();
90
+ if (net === "MAINNET") return Network.MAINNET;
91
+ if (net === "TESTNET") return Network.TESTNET;
92
+ if (net === "SIGNET") return Network.SIGNET;
93
+ if (net === "LOCAL") return Network.LOCAL;
94
+ return Network.REGTEST; // default
95
+ }
@@ -6,6 +6,22 @@ import { Network } from "./network.js";
6
6
 
7
7
  const TIME_LOCK_INTERVAL = 100;
8
8
 
9
+ // Default fee constants matching Go implementation
10
+ const ESTIMATED_TX_SIZE = 191;
11
+ const DEFAULT_SATS_PER_VBYTE = 5;
12
+ export const DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
13
+
14
+ /**
15
+ * Subtracts the default fee from the amount if it's greater than the fee.
16
+ * Returns the original amount if it's less than or equal to the fee.
17
+ */
18
+ export function maybeApplyFee(amount: bigint): bigint {
19
+ if (amount > BigInt(DEFAULT_FEE_SATS)) {
20
+ return amount - BigInt(DEFAULT_FEE_SATS);
21
+ }
22
+ return amount;
23
+ }
24
+
9
25
  export function createRefundTx(
10
26
  sequence: number,
11
27
  nodeOutPoint: TransactionInput,
@@ -13,7 +29,10 @@ export function createRefundTx(
13
29
  receivingPubkey: Uint8Array,
14
30
  network: Network,
15
31
  ): Transaction {
16
- const newRefundTx = new Transaction({ allowUnknownOutputs: true });
32
+ const newRefundTx = new Transaction({
33
+ version: 3,
34
+ allowUnknownOutputs: true,
35
+ });
17
36
  newRefundTx.addInput({
18
37
  ...nodeOutPoint,
19
38
  sequence,
@@ -25,6 +44,7 @@ export function createRefundTx(
25
44
  script: refundPkScript,
26
45
  amount: amountSats,
27
46
  });
47
+
28
48
  newRefundTx.addOutput(getEphemeralAnchorOutput());
29
49
 
30
50
  return newRefundTx;
@@ -55,7 +75,7 @@ export function getNextTransactionSequence(
55
75
  };
56
76
  }
57
77
 
58
- if (nextTimelock <= 0) {
78
+ if (nextTimelock < 0) {
59
79
  throw new ValidationError("timelock interval is less than or equal to 0", {
60
80
  field: "nextTimelock",
61
81
  value: nextTimelock,