@arkade-os/sdk 0.4.23 → 0.4.24

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 (40) hide show
  1. package/README.md +21 -1
  2. package/dist/cjs/contracts/contractManager.js +29 -4
  3. package/dist/cjs/contracts/contractWatcher.js +9 -3
  4. package/dist/cjs/contracts/handlers/default.js +3 -2
  5. package/dist/cjs/contracts/handlers/delegate.js +3 -2
  6. package/dist/cjs/contracts/handlers/helpers.js +2 -58
  7. package/dist/cjs/contracts/handlers/vhtlc.js +7 -6
  8. package/dist/cjs/contracts/vtxoOwnership.js +60 -0
  9. package/dist/cjs/index.js +3 -3
  10. package/dist/cjs/script/base.js +12 -47
  11. package/dist/cjs/script/tapscript.js +97 -73
  12. package/dist/cjs/utils/timelock.js +59 -0
  13. package/dist/cjs/utils/unknownFields.js +2 -39
  14. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +59 -9
  15. package/dist/cjs/wallet/unroll.js +79 -67
  16. package/dist/cjs/wallet/wallet.js +78 -8
  17. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +7 -2
  18. package/dist/esm/contracts/contractManager.js +29 -4
  19. package/dist/esm/contracts/contractWatcher.js +9 -3
  20. package/dist/esm/contracts/handlers/default.js +2 -1
  21. package/dist/esm/contracts/handlers/delegate.js +2 -1
  22. package/dist/esm/contracts/handlers/helpers.js +1 -22
  23. package/dist/esm/contracts/handlers/vhtlc.js +2 -1
  24. package/dist/esm/contracts/vtxoOwnership.js +53 -0
  25. package/dist/esm/index.js +1 -1
  26. package/dist/esm/script/base.js +12 -14
  27. package/dist/esm/script/tapscript.js +97 -40
  28. package/dist/esm/utils/timelock.js +22 -0
  29. package/dist/esm/utils/unknownFields.js +2 -6
  30. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +59 -9
  31. package/dist/esm/wallet/unroll.js +78 -67
  32. package/dist/esm/wallet/wallet.js +76 -6
  33. package/dist/esm/worker/expo/processors/contractPollProcessor.js +7 -2
  34. package/dist/types/contracts/handlers/helpers.d.ts +0 -9
  35. package/dist/types/contracts/vtxoOwnership.d.ts +25 -0
  36. package/dist/types/index.d.ts +1 -1
  37. package/dist/types/script/tapscript.d.ts +4 -0
  38. package/dist/types/utils/timelock.d.ts +9 -0
  39. package/dist/types/wallet/unroll.d.ts +10 -0
  40. package/package.json +1 -1
@@ -37,8 +37,9 @@ const delegator_1 = require("./delegator");
37
37
  const repositories_1 = require("../repositories");
38
38
  const contractManager_1 = require("../contracts/contractManager");
39
39
  const handlers_1 = require("../contracts/handlers");
40
- const helpers_1 = require("../contracts/handlers/helpers");
40
+ const timelock_1 = require("../utils/timelock");
41
41
  const syncCursors_1 = require("../utils/syncCursors");
42
+ const vtxoOwnership_1 = require("../contracts/vtxoOwnership");
42
43
  const getArkadeServerUrl = ({ arkServerUrl, }) => arkServerUrl || _1.DEFAULT_ARKADE_SERVER_URL;
43
44
  exports.getArkadeServerUrl = getArkadeServerUrl;
44
45
  // Historical unilateral exit delay for mainnet (~7 days in seconds).
@@ -55,7 +56,7 @@ function dedupeTimelocks(timelocks) {
55
56
  const seen = new Set();
56
57
  const deduped = [];
57
58
  for (const timelock of timelocks) {
58
- const sequence = (0, helpers_1.timelockToSequence)(timelock).toString();
59
+ const sequence = (0, timelock_1.timelockToSequence)(timelock).toString();
59
60
  if (seen.has(sequence))
60
61
  continue;
61
62
  seen.add(sequence);
@@ -648,7 +649,7 @@ class ReadonlyWallet {
648
649
  watcherConfig: this.watcherConfig,
649
650
  });
650
651
  for (const csvTimelock of this.walletContractTimelocks) {
651
- const csvTimelockStr = (0, helpers_1.timelockToSequence)(csvTimelock).toString();
652
+ const csvTimelockStr = (0, timelock_1.timelockToSequence)(csvTimelock).toString();
652
653
  const defaultScript = new default_1.DefaultVtxo.Script({
653
654
  pubKey: this.offchainTapscript.options.pubKey,
654
655
  serverPubKey: this.offchainTapscript.options.serverPubKey,
@@ -1830,7 +1831,7 @@ class Wallet extends ReadonlyWallet {
1830
1831
  }
1831
1832
  }
1832
1833
  const createdAt = Date.now();
1833
- const addr = this.arkAddress.encode();
1834
+ const primaryAddr = this.arkAddress.encode();
1834
1835
  // Only save a change virtual output for preconfirmed coins (those with a batchExpiry).
1835
1836
  // Inputs without a batchExpiry are already settled/unrolled and don't need tracking.
1836
1837
  let changeVtxo;
@@ -1857,8 +1858,45 @@ class Wallet extends ReadonlyWallet {
1857
1858
  script: base_1.hex.encode(this.offchainTapscript.pkScript),
1858
1859
  };
1859
1860
  }
1860
- await this.walletRepository.saveVtxos(addr, changeVtxo ? [...spentVtxos, changeVtxo] : spentVtxos);
1861
- await this.walletRepository.saveTransactions(addr, [
1861
+ // Route spent rows to their owning contract bucket. The wallet's
1862
+ // primary contract is registered with the manager at boot, so
1863
+ // `addrByScript` already includes it; in a multi-contract spend
1864
+ // each input may belong to a different contract.
1865
+ const contracts = await cm.getContracts();
1866
+ const addrByScript = new Map(contracts.map((c) => [c.script, c.address]));
1867
+ const spentByScript = new Map();
1868
+ for (const v of spentVtxos) {
1869
+ if (!v.script) {
1870
+ throw new Error(`Wallet.updateDbAfterOffchainTx: spent VTXO ${v.txid}:${v.vout} has no script`);
1871
+ }
1872
+ const arr = spentByScript.get(v.script) ?? [];
1873
+ arr.push(v);
1874
+ spentByScript.set(v.script, arr);
1875
+ }
1876
+ const byAddress = new Map();
1877
+ for (const [script, vtxos] of spentByScript) {
1878
+ // User-initiated send path: a wrong-script row here means the
1879
+ // wallet is about to record ownership against the wrong
1880
+ // contract — fail loudly rather than persist inconsistent state.
1881
+ (0, vtxoOwnership_1.validateVtxosForScript)(vtxos, script, "Wallet.updateDbAfterOffchainTx");
1882
+ const targetAddr = addrByScript.get(script);
1883
+ if (!targetAddr) {
1884
+ throw new Error(`Wallet.updateDbAfterOffchainTx: no contract owns script ${script}`);
1885
+ }
1886
+ const bucket = byAddress.get(targetAddr) ?? [];
1887
+ bucket.push(...vtxos);
1888
+ byAddress.set(targetAddr, bucket);
1889
+ }
1890
+ // Change is always primary-script by construction.
1891
+ if (changeVtxo) {
1892
+ const bucket = byAddress.get(primaryAddr) ?? [];
1893
+ bucket.push(changeVtxo);
1894
+ byAddress.set(primaryAddr, bucket);
1895
+ }
1896
+ for (const [addr, vtxos] of byAddress) {
1897
+ await this.walletRepository.saveVtxos(addr, vtxos);
1898
+ }
1899
+ await this.walletRepository.saveTransactions(primaryAddr, [
1862
1900
  {
1863
1901
  key: {
1864
1902
  boardingTxid: "",
@@ -1874,12 +1912,12 @@ class Wallet extends ReadonlyWallet {
1874
1912
  }
1875
1913
  catch (e) {
1876
1914
  console.warn("error saving offchain tx to repository", e);
1915
+ throw e;
1877
1916
  }
1878
1917
  }
1879
1918
  // mark virtual outputs as spent/settled, remove boarding inputs
1880
1919
  async updateDbAfterSettle(inputs, commitmentTxid) {
1881
1920
  try {
1882
- const addr = this.arkAddress.encode();
1883
1921
  const boardingAddress = await this.getBoardingAddress();
1884
1922
  const spentVtxos = [];
1885
1923
  const inputArkTxIds = new Set();
@@ -1912,7 +1950,38 @@ class Wallet extends ReadonlyWallet {
1912
1950
  }
1913
1951
  }
1914
1952
  if (spentVtxos.length > 0) {
1915
- await this.walletRepository.saveVtxos(addr, spentVtxos);
1953
+ // Route settled rows to their owning contract bucket. In a
1954
+ // multi-contract settle the inputs may belong to several
1955
+ // contracts; the wallet's primary contract is registered with
1956
+ // the manager at boot, so its address is in `addrByScript`
1957
+ // alongside the rest.
1958
+ const contracts = await cm.getContracts();
1959
+ const addrByScript = new Map(contracts.map((c) => [c.script, c.address]));
1960
+ const byAddress = new Map();
1961
+ const byScript = new Map();
1962
+ for (const v of spentVtxos) {
1963
+ if (!v.script) {
1964
+ throw new Error(`Wallet.updateDbAfterSettle: spent VTXO ${v.txid}:${v.vout} has no script`);
1965
+ }
1966
+ const arr = byScript.get(v.script) ?? [];
1967
+ arr.push(v);
1968
+ byScript.set(v.script, arr);
1969
+ }
1970
+ for (const [script, vtxos] of byScript) {
1971
+ // User-initiated settle path: refuse to record a settle
1972
+ // against the wrong script.
1973
+ (0, vtxoOwnership_1.validateVtxosForScript)(vtxos, script, "Wallet.updateDbAfterSettle");
1974
+ const targetAddr = addrByScript.get(script);
1975
+ if (!targetAddr) {
1976
+ throw new Error(`Wallet.updateDbAfterSettle: no contract owns script ${script}`);
1977
+ }
1978
+ const bucket = byAddress.get(targetAddr) ?? [];
1979
+ bucket.push(...vtxos);
1980
+ byAddress.set(targetAddr, bucket);
1981
+ }
1982
+ for (const [bucketAddr, vtxos] of byAddress) {
1983
+ await this.walletRepository.saveVtxos(bucketAddr, vtxos);
1984
+ }
1916
1985
  }
1917
1986
  if (boardingUtxoToRemove.size > 0) {
1918
1987
  const currentUtxos = await this.walletRepository.getUtxos(boardingAddress);
@@ -1926,6 +1995,7 @@ class Wallet extends ReadonlyWallet {
1926
1995
  }
1927
1996
  catch (e) {
1928
1997
  console.warn("error updating repository after settle", e);
1998
+ throw e;
1929
1999
  }
1930
2000
  }
1931
2001
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.contractPollProcessor = exports.CONTRACT_POLL_TASK_TYPE = void 0;
4
+ const vtxoOwnership_1 = require("../../../contracts/vtxoOwnership");
4
5
  exports.CONTRACT_POLL_TASK_TYPE = "contract-poll";
5
6
  /**
6
7
  * Polls the indexer for the latest VTXO state of every contract and
@@ -43,8 +44,12 @@ exports.contractPollProcessor = {
43
44
  hasMore = page ? vtxos.length === pageSize : false;
44
45
  pageIndex++;
45
46
  }
46
- await walletRepository.saveVtxos(contract.address, allVtxos);
47
- vtxosSaved += allVtxos.length;
47
+ // Skip wrong-script rows (legacy duplicates or indexer drift)
48
+ // before persisting; the loop must keep going for the remaining
49
+ // contracts even when one row is rejected.
50
+ const filtered = (0, vtxoOwnership_1.warnAndFilterVtxosForScript)(allVtxos, contract.script, "contractPollProcessor");
51
+ await walletRepository.saveVtxos(contract.address, filtered);
52
+ vtxosSaved += filtered.length;
48
53
  contractsProcessed++;
49
54
  }
50
55
  return {
@@ -3,6 +3,7 @@ import { ContractWatcher } from './contractWatcher.js';
3
3
  import { contractHandlers } from './handlers/index.js';
4
4
  import { extendVirtualCoinForContract } from '../wallet/utils.js';
5
5
  import { advanceSyncCursor, computeSyncWindow, cursorCutoff, getSyncCursor, } from '../utils/syncCursors.js';
6
+ import { filterVtxosForScript, warnAndFilterVtxosForScript, } from './vtxoOwnership.js';
6
7
  const DEFAULT_PAGE_SIZE = 500;
7
8
  /**
8
9
  * Central manager for contract lifecycle and operations.
@@ -354,9 +355,19 @@ export class ContractManager {
354
355
  const contracts = opts?.scripts
355
356
  ? await this.getContracts({ script: opts.scripts })
356
357
  : undefined;
358
+ // Only forward an explicit window when the caller supplied one. An
359
+ // empty `{ after: undefined, before: undefined }` would short-circuit
360
+ // both the cursor-derived `?after=` query in `syncContracts` (because
361
+ // `??` doesn't fire on a non-nullish object) AND the cursor-advance
362
+ // gate (which requires `options.window === undefined`), turning every
363
+ // `refreshVtxos()` call into an unbounded full re-scan whose cursor
364
+ // never moves forward.
365
+ const hasExplicitWindow = opts?.after !== undefined || opts?.before !== undefined;
357
366
  await this.syncContracts({
358
367
  contracts,
359
- window: { after: opts?.after, before: opts?.before },
368
+ window: hasExplicitWindow
369
+ ? { after: opts?.after, before: opts?.before }
370
+ : undefined,
360
371
  });
361
372
  }
362
373
  /**
@@ -399,7 +410,11 @@ export class ContractManager {
399
410
  this.emitEvent(event);
400
411
  }
401
412
  async getVtxosForContracts(contracts) {
402
- const res = await Promise.all(contracts.map(({ script, address }) => this.config.walletRepository.getVtxos(address).then((vtxos) => vtxos.map((vtxo) => ({
413
+ const res = await Promise.all(contracts.map(({ script, address }) => this.config.walletRepository.getVtxos(address).then((vtxos) =>
414
+ // Address buckets may carry legacy duplicate rows from
415
+ // other contracts that once shared the same address —
416
+ // gate by script so the wrong-script row never wins.
417
+ filterVtxosForScript(vtxos, script).map((vtxo) => ({
403
418
  ...vtxo,
404
419
  contractScript: script,
405
420
  })))));
@@ -467,7 +482,14 @@ export class ContractManager {
467
482
  });
468
483
  }
469
484
  for (const [addr, contractVtxos] of byContract) {
470
- await this.config.walletRepository.saveVtxos(addr, contractVtxos);
485
+ // The bucket is keyed by contract address, so the script filter
486
+ // here is the same as the contract's. Skip wrong-script rows
487
+ // rather than crash the reconcile loop.
488
+ const contract = contracts.find((c) => c.address === addr);
489
+ const filtered = warnAndFilterVtxosForScript(contractVtxos, contract.script, "ContractManager.reconcilePendingFrontier");
490
+ if (filtered.length === 0)
491
+ continue;
492
+ await this.config.walletRepository.saveVtxos(addr, filtered);
471
493
  }
472
494
  }
473
495
  async fetchContractVxosFromIndexer(contracts, pageSize, syncWindow) {
@@ -477,7 +499,10 @@ export class ContractManager {
477
499
  result.set(contractScript, vtxos);
478
500
  const contract = contracts.find((c) => c.script === contractScript);
479
501
  if (contract) {
480
- await this.config.walletRepository.saveVtxos(contract.address, vtxos);
502
+ const filtered = warnAndFilterVtxosForScript(vtxos, contract.script, "ContractManager.fetchContractVxosFromIndexer");
503
+ if (filtered.length === 0)
504
+ continue;
505
+ await this.config.walletRepository.saveVtxos(contract.address, filtered);
481
506
  }
482
507
  }
483
508
  return result;
@@ -1,5 +1,6 @@
1
1
  import { extendVirtualCoinForContract } from '../wallet/utils.js';
2
2
  import { isEventSourceError } from '../providers/utils.js';
3
+ import { filterVtxosForScript } from './vtxoOwnership.js';
3
4
  /**
4
5
  * Watches multiple contracts for virtual output state changes with resilient connection handling.
5
6
  *
@@ -87,7 +88,10 @@ export class ContractWatcher {
87
88
  */
88
89
  async seedLastKnownVtxos(state) {
89
90
  try {
90
- const cached = await this.config.walletRepository.getVtxos(state.contract.address);
91
+ // Apply the same script gate used by getContractVtxos so a legacy
92
+ // wrong-script row in the address bucket can't seed the baseline
93
+ // and then look "spent" on the first poll.
94
+ const cached = filterVtxosForScript(await this.config.walletRepository.getVtxos(state.contract.address), state.contract.script);
91
95
  for (const vtxo of cached) {
92
96
  if (vtxo.isSpent)
93
97
  continue;
@@ -166,8 +170,10 @@ export class ContractWatcher {
166
170
  return true;
167
171
  })
168
172
  .map(async (state) => {
169
- // Use contract address as cache key
170
- const cached = await repo.getVtxos(state.contract.address);
173
+ // Use contract address as cache key. Legacy address buckets
174
+ // can contain rows from other contracts; gate by script before
175
+ // converting so a wrong-script row never reaches the watcher.
176
+ const cached = filterVtxosForScript(await repo.getVtxos(state.contract.address), state.contract.script);
171
177
  if (cached.length > 0) {
172
178
  // Convert to ContractVtxo with contractScript
173
179
  const contractVtxos = cached.map((v) => ({
@@ -1,6 +1,7 @@
1
1
  import { hex } from "@scure/base";
2
2
  import { DefaultVtxo } from '../../script/default.js';
3
- import { isCsvSpendable, sequenceToTimelock, timelockToSequence, } from './helpers.js';
3
+ import { isCsvSpendable } from './helpers.js';
4
+ import { sequenceToTimelock, timelockToSequence } from '../../utils/timelock.js';
4
5
  import { normalizeToDescriptor, extractPubKey, } from '../../identity/descriptor.js';
5
6
  /**
6
7
  * Extract pubkey bytes from a descriptor or hex string.
@@ -1,7 +1,8 @@
1
1
  import { hex } from "@scure/base";
2
2
  import { DelegateVtxo } from '../../script/delegate.js';
3
3
  import { DefaultVtxo } from '../../script/default.js';
4
- import { isCsvSpendable, sequenceToTimelock, timelockToSequence, } from './helpers.js';
4
+ import { isCsvSpendable } from './helpers.js';
5
+ import { sequenceToTimelock, timelockToSequence } from '../../utils/timelock.js';
5
6
  /**
6
7
  * Handler for delegate wallet virtual outputs.
7
8
  *
@@ -1,4 +1,4 @@
1
- import * as bip68 from "bip68";
1
+ import { sequenceToTimelock } from '../../utils/timelock.js';
2
2
  import { isDescriptor, extractPubKey } from '../../identity/descriptor.js';
3
3
  /**
4
4
  * Extract raw hex pubkey from a value that may be a descriptor or raw hex.
@@ -16,27 +16,6 @@ function extractRawPubKey(value) {
16
16
  return undefined;
17
17
  }
18
18
  }
19
- /**
20
- * Convert RelativeTimelock to BIP68 sequence number.
21
- */
22
- export function timelockToSequence(timelock) {
23
- return bip68.encode(timelock.type === "blocks"
24
- ? { blocks: Number(timelock.value) }
25
- : { seconds: Number(timelock.value) });
26
- }
27
- /**
28
- * Convert BIP68 sequence number back to RelativeTimelock.
29
- */
30
- export function sequenceToTimelock(sequence) {
31
- const decoded = bip68.decode(sequence);
32
- if ("blocks" in decoded && decoded.blocks !== undefined) {
33
- return { type: "blocks", value: BigInt(decoded.blocks) };
34
- }
35
- if ("seconds" in decoded && decoded.seconds !== undefined) {
36
- return { type: "seconds", value: BigInt(decoded.seconds) };
37
- }
38
- throw new Error(`Invalid BIP68 sequence: ${sequence}`);
39
- }
40
19
  /**
41
20
  * Resolve wallet's role from explicit role or by matching descriptor/pubkey.
42
21
  */
@@ -1,6 +1,7 @@
1
1
  import { hex } from "@scure/base";
2
2
  import { VHTLC } from '../../script/vhtlc.js';
3
- import { isCltvSatisfied, isCsvSpendable, resolveRole, sequenceToTimelock, timelockToSequence, } from './helpers.js';
3
+ import { isCltvSatisfied, isCsvSpendable, resolveRole } from './helpers.js';
4
+ import { sequenceToTimelock, timelockToSequence } from '../../utils/timelock.js';
4
5
  /**
5
6
  * Handler for Virtual Hash Time Lock Contract (VHTLC).
6
7
  *
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Tier 1 helpers that enforce VTXO ownership at call sites that already know
3
+ * the intended contract script. Address-keyed repositories may still hand back
4
+ * legacy duplicate rows under the wrong bucket; these helpers gate reads and
5
+ * writes so a wrong-script row never wins.
6
+ *
7
+ * `script` is the authoritative ownership key. Equality is strict: a missing
8
+ * or empty `vtxo.script` never matches.
9
+ */
10
+ export function vtxoOutpoint(vtxo) {
11
+ return `${vtxo.txid}:${vtxo.vout}`;
12
+ }
13
+ export function isVtxoForScript(vtxo, script) {
14
+ return !!vtxo.script && vtxo.script === script;
15
+ }
16
+ export function filterVtxosForScript(vtxos, script) {
17
+ return vtxos.filter((v) => isVtxoForScript(v, script));
18
+ }
19
+ /**
20
+ * Background/indexer sync flavour: drop wrong-script rows and log enough
21
+ * context to identify each rejection. Returns only matching rows so the
22
+ * caller can keep going.
23
+ */
24
+ export function warnAndFilterVtxosForScript(vtxos, script, context) {
25
+ const matches = [];
26
+ const rejected = [];
27
+ for (const v of vtxos) {
28
+ if (isVtxoForScript(v, script)) {
29
+ matches.push(v);
30
+ }
31
+ else {
32
+ rejected.push(`${vtxoOutpoint(v)}(script=${v.script ?? ""})`);
33
+ }
34
+ }
35
+ if (rejected.length > 0) {
36
+ console.warn(`${context}: dropped ${rejected.length} wrong-script VTXO(s) for script ${script}: ${rejected.join(", ")}`);
37
+ }
38
+ return matches;
39
+ }
40
+ /**
41
+ * User-initiated transaction/signing flavour: throw before persisting or
42
+ * signing inconsistent ownership state. Silently skipping here would hide a
43
+ * serious bug in the wallet's spend path.
44
+ */
45
+ export function validateVtxosForScript(vtxos, script, context) {
46
+ const mismatches = vtxos.filter((v) => !isVtxoForScript(v, script));
47
+ if (mismatches.length === 0)
48
+ return;
49
+ const detail = mismatches
50
+ .map((v) => `${vtxoOutpoint(v)}(script=${v.script ?? ""})`)
51
+ .join(", ");
52
+ throw new Error(`${context}: refusing to persist ${mismatches.length} VTXO(s) whose script does not match ${script}: ${detail}`);
53
+ }
package/dist/esm/index.js CHANGED
@@ -42,7 +42,7 @@ export * from './arkfee/index.js';
42
42
  export * as asset from './extension/asset/index.js';
43
43
  // Contracts
44
44
  import { ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract, } from './contracts/index.js';
45
- import { timelockToSequence, sequenceToTimelock, } from './contracts/handlers/helpers.js';
45
+ import { timelockToSequence, sequenceToTimelock } from './utils/timelock.js';
46
46
  import { closeDatabase, openDatabase } from './repositories/indexedDB/manager.js';
47
47
  import { WalletMessageHandler, WalletNotInitializedError, ReadonlyWalletError, DelegatorNotConfiguredError, } from './wallet/serviceWorker/wallet-message-handler.js';
48
48
  import { MESSAGE_BUS_NOT_INITIALIZED, MessageBusNotInitializedError, ServiceWorkerTimeoutError, } from './worker/errors.js';
@@ -1,9 +1,9 @@
1
1
  import { Script, Address, p2tr, taprootListToTree, TAPROOT_UNSPENDABLE_KEY, } from "@scure/btc-signer";
2
- import * as bip68 from "bip68";
3
2
  import { TAP_LEAF_VERSION } from "@scure/btc-signer/payment.js";
4
3
  import { PSBTOutput } from "@scure/btc-signer/psbt.js";
5
4
  import { hex } from "@scure/base";
6
5
  import { ArkAddress } from './address.js';
6
+ import { timelockToSequence } from '../utils/timelock.js';
7
7
  import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, CSVMultisigTapscript, } from './tapscript.js';
8
8
  export const TapTreeCoder = PSBTOutput.tapTree[2];
9
9
  export function scriptFromTapLeafScript(leaf) {
@@ -125,19 +125,19 @@ export class VtxoScript {
125
125
  const paths = [];
126
126
  for (const leaf of this.leaves) {
127
127
  try {
128
- const tapscript = CSVMultisigTapscript.decode(scriptFromTapLeafScript(leaf));
129
- paths.push(tapscript);
130
- continue;
131
- }
132
- catch (e) {
133
- try {
134
- const tapscript = ConditionCSVMultisigTapscript.decode(scriptFromTapLeafScript(leaf));
135
- paths.push(tapscript);
128
+ const script = scriptFromTapLeafScript(leaf);
129
+ if (CSVMultisigTapscript.isScriptValid(script) === true) {
130
+ const tapScript = CSVMultisigTapscript.decode(script);
131
+ paths.push(tapScript);
136
132
  }
137
- catch (e) {
138
- continue;
133
+ else if (ConditionCSVMultisigTapscript.isScriptValid(script) === true) {
134
+ const tapScript = ConditionCSVMultisigTapscript.decode(script);
135
+ paths.push(tapScript);
139
136
  }
140
137
  }
138
+ catch (e) {
139
+ console.debug("Failed to decode script", e);
140
+ }
141
141
  }
142
142
  return paths;
143
143
  }
@@ -167,9 +167,7 @@ export function getSequence(tapLeafScript) {
167
167
  const script = scriptWithLeafVersion.subarray(0, scriptWithLeafVersion.length - 1);
168
168
  try {
169
169
  const params = CSVMultisigTapscript.decode(script).params;
170
- sequence = bip68.encode(params.timelock.type === "blocks"
171
- ? { blocks: Number(params.timelock.value) }
172
- : { seconds: Number(params.timelock.value) });
170
+ sequence = timelockToSequence(params.timelock);
173
171
  }
174
172
  catch {
175
173
  const params = CLTVMultisigTapscript.decode(script).params;