@arkade-os/sdk 0.4.23 → 0.4.25

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 (60) hide show
  1. package/README.md +21 -1
  2. package/dist/cjs/contracts/contractManager.js +66 -5
  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 +78 -0
  9. package/dist/cjs/index.js +3 -3
  10. package/dist/cjs/repositories/inMemory/walletRepository.js +35 -0
  11. package/dist/cjs/repositories/indexedDB/walletRepository.js +117 -0
  12. package/dist/cjs/repositories/realm/walletRepository.js +28 -0
  13. package/dist/cjs/repositories/sqlite/walletRepository.js +23 -0
  14. package/dist/cjs/script/base.js +12 -47
  15. package/dist/cjs/script/tapscript.js +97 -73
  16. package/dist/cjs/utils/timelock.js +59 -0
  17. package/dist/cjs/utils/unknownFields.js +2 -39
  18. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +71 -10
  19. package/dist/cjs/wallet/serviceWorker/wallet.js +10 -0
  20. package/dist/cjs/wallet/unroll.js +79 -67
  21. package/dist/cjs/wallet/vtxo-manager.js +112 -16
  22. package/dist/cjs/wallet/wallet.js +64 -8
  23. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +7 -2
  24. package/dist/esm/contracts/contractManager.js +66 -5
  25. package/dist/esm/contracts/contractWatcher.js +9 -3
  26. package/dist/esm/contracts/handlers/default.js +2 -1
  27. package/dist/esm/contracts/handlers/delegate.js +2 -1
  28. package/dist/esm/contracts/handlers/helpers.js +1 -22
  29. package/dist/esm/contracts/handlers/vhtlc.js +2 -1
  30. package/dist/esm/contracts/vtxoOwnership.js +69 -0
  31. package/dist/esm/index.js +1 -1
  32. package/dist/esm/repositories/inMemory/walletRepository.js +35 -0
  33. package/dist/esm/repositories/indexedDB/walletRepository.js +117 -0
  34. package/dist/esm/repositories/realm/walletRepository.js +28 -0
  35. package/dist/esm/repositories/sqlite/walletRepository.js +23 -0
  36. package/dist/esm/script/base.js +12 -14
  37. package/dist/esm/script/tapscript.js +97 -40
  38. package/dist/esm/utils/timelock.js +22 -0
  39. package/dist/esm/utils/unknownFields.js +2 -6
  40. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +71 -10
  41. package/dist/esm/wallet/serviceWorker/wallet.js +10 -0
  42. package/dist/esm/wallet/unroll.js +78 -67
  43. package/dist/esm/wallet/vtxo-manager.js +112 -16
  44. package/dist/esm/wallet/wallet.js +62 -6
  45. package/dist/esm/worker/expo/processors/contractPollProcessor.js +7 -2
  46. package/dist/types/contracts/contractManager.d.ts +17 -1
  47. package/dist/types/contracts/handlers/helpers.d.ts +0 -9
  48. package/dist/types/contracts/vtxoOwnership.d.ts +33 -0
  49. package/dist/types/index.d.ts +1 -1
  50. package/dist/types/repositories/inMemory/walletRepository.d.ts +4 -1
  51. package/dist/types/repositories/indexedDB/walletRepository.d.ts +4 -1
  52. package/dist/types/repositories/realm/walletRepository.d.ts +4 -1
  53. package/dist/types/repositories/sqlite/walletRepository.d.ts +4 -1
  54. package/dist/types/repositories/walletRepository.d.ts +21 -0
  55. package/dist/types/script/tapscript.d.ts +4 -0
  56. package/dist/types/utils/timelock.d.ts +9 -0
  57. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +14 -2
  58. package/dist/types/wallet/unroll.d.ts +10 -0
  59. package/dist/types/wallet/vtxo-manager.d.ts +32 -5
  60. package/package.json +1 -1
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SQLiteWalletRepository = void 0;
4
4
  const serialization_1 = require("../serialization");
5
5
  const scriptFromAddress_1 = require("../scriptFromAddress");
6
+ const vtxoOwnership_1 = require("../../contracts/vtxoOwnership");
6
7
  /**
7
8
  * SQLite-based implementation of WalletRepository.
8
9
  *
@@ -245,6 +246,28 @@ class SQLiteWalletRepository {
245
246
  await this.ensureInit();
246
247
  await this.db.run(`DELETE FROM ${this.tables.vtxos} WHERE address = ?`, [address]);
247
248
  }
249
+ async getVtxosForScript(script) {
250
+ await this.ensureInit();
251
+ const rows = await this.db.all(`SELECT * FROM ${this.tables.vtxos} WHERE script = ?`, [script]);
252
+ return rows.map(vtxoRowToDomain);
253
+ }
254
+ async saveVtxosForScript(key, vtxos) {
255
+ if (!key.address) {
256
+ throw new Error("SQLiteWalletRepository requires an address");
257
+ }
258
+ for (const vtxo of vtxos) {
259
+ if (!(0, vtxoOwnership_1.isVtxoForScript)(vtxo, key.script)) {
260
+ throw new Error(`VTXO ${vtxo.txid}:${vtxo.vout} script mismatch: expected ${key.script}, got ${vtxo.script}`);
261
+ }
262
+ }
263
+ return this.saveVtxos(key.address, vtxos);
264
+ }
265
+ async deleteVtxosForScript(script) {
266
+ await this.ensureInit();
267
+ await this.db.run(`DELETE FROM ${this.tables.vtxos} WHERE script = ?`, [
268
+ script,
269
+ ]);
270
+ }
248
271
  // ── UTXO management ────────────────────────────────────────────────
249
272
  async getUtxos(address) {
250
273
  await this.ensureInit();
@@ -1,47 +1,14 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.VtxoScript = exports.TapTreeCoder = void 0;
37
4
  exports.scriptFromTapLeafScript = scriptFromTapLeafScript;
38
5
  exports.getSequence = getSequence;
39
6
  const btc_signer_1 = require("@scure/btc-signer");
40
- const bip68 = __importStar(require("bip68"));
41
7
  const payment_js_1 = require("@scure/btc-signer/payment.js");
42
8
  const psbt_js_1 = require("@scure/btc-signer/psbt.js");
43
9
  const base_1 = require("@scure/base");
44
10
  const address_1 = require("./address");
11
+ const timelock_1 = require("../utils/timelock");
45
12
  const tapscript_1 = require("./tapscript");
46
13
  exports.TapTreeCoder = psbt_js_1.PSBTOutput.tapTree[2];
47
14
  function scriptFromTapLeafScript(leaf) {
@@ -163,19 +130,19 @@ class VtxoScript {
163
130
  const paths = [];
164
131
  for (const leaf of this.leaves) {
165
132
  try {
166
- const tapscript = tapscript_1.CSVMultisigTapscript.decode(scriptFromTapLeafScript(leaf));
167
- paths.push(tapscript);
168
- continue;
169
- }
170
- catch (e) {
171
- try {
172
- const tapscript = tapscript_1.ConditionCSVMultisigTapscript.decode(scriptFromTapLeafScript(leaf));
173
- paths.push(tapscript);
133
+ const script = scriptFromTapLeafScript(leaf);
134
+ if (tapscript_1.CSVMultisigTapscript.isScriptValid(script) === true) {
135
+ const tapScript = tapscript_1.CSVMultisigTapscript.decode(script);
136
+ paths.push(tapScript);
174
137
  }
175
- catch (e) {
176
- continue;
138
+ else if (tapscript_1.ConditionCSVMultisigTapscript.isScriptValid(script) === true) {
139
+ const tapScript = tapscript_1.ConditionCSVMultisigTapscript.decode(script);
140
+ paths.push(tapScript);
177
141
  }
178
142
  }
143
+ catch (e) {
144
+ console.debug("Failed to decode script", e);
145
+ }
179
146
  }
180
147
  return paths;
181
148
  }
@@ -206,9 +173,7 @@ function getSequence(tapLeafScript) {
206
173
  const script = scriptWithLeafVersion.subarray(0, scriptWithLeafVersion.length - 1);
207
174
  try {
208
175
  const params = tapscript_1.CSVMultisigTapscript.decode(script).params;
209
- sequence = bip68.encode(params.timelock.type === "blocks"
210
- ? { blocks: Number(params.timelock.value) }
211
- : { seconds: Number(params.timelock.value) });
176
+ sequence = (0, timelock_1.timelockToSequence)(params.timelock);
212
177
  }
213
178
  catch {
214
179
  const params = tapscript_1.CLTVMultisigTapscript.decode(script).params;
@@ -1,43 +1,10 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.TapscriptType = void 0;
37
4
  exports.decodeTapscript = decodeTapscript;
38
- const bip68 = __importStar(require("bip68"));
39
5
  const btc_signer_1 = require("@scure/btc-signer");
40
6
  const base_1 = require("@scure/base");
7
+ const timelock_1 = require("../utils/timelock");
41
8
  const MinimalScriptNum = (0, btc_signer_1.ScriptNum)(undefined, true);
42
9
  var TapscriptType;
43
10
  (function (TapscriptType) {
@@ -271,9 +238,7 @@ var CSVMultisigTapscript;
271
238
  throw new Error(`Invalid pubkey length: expected 32, got ${pubkey.length}`);
272
239
  }
273
240
  }
274
- const sequence = MinimalScriptNum.encode(BigInt(bip68.encode(params.timelock.type === "blocks"
275
- ? { blocks: Number(params.timelock.value) }
276
- : { seconds: Number(params.timelock.value) })));
241
+ const sequence = MinimalScriptNum.encode(BigInt((0, timelock_1.timelockToSequence)(params.timelock)));
277
242
  const asm = [
278
243
  sequence.length === 1 ? sequence[0] : sequence,
279
244
  "CHECKSEQUENCEVERIFY",
@@ -296,17 +261,12 @@ var CSVMultisigTapscript;
296
261
  if (script.length === 0) {
297
262
  throw new Error("Failed to decode: script is empty");
298
263
  }
299
- const asm = btc_signer_1.Script.decode(script);
300
- if (asm.length < 3) {
301
- throw new Error(`Invalid script: too short (expected at least 3)`);
264
+ const isValid = isScriptValid(script);
265
+ if (isValid instanceof Error) {
266
+ throw isValid;
302
267
  }
268
+ const asm = btc_signer_1.Script.decode(script);
303
269
  const sequence = asm[0];
304
- if (typeof sequence === "string") {
305
- throw new Error("Invalid script: expected sequence number");
306
- }
307
- if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
308
- throw new Error("Invalid script: expected CHECKSEQUENCEVERIFY DROP");
309
- }
310
270
  const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(3)));
311
271
  let multisig;
312
272
  try {
@@ -322,10 +282,7 @@ var CSVMultisigTapscript;
322
282
  else {
323
283
  sequenceNum = Number(MinimalScriptNum.decode(sequence));
324
284
  }
325
- const decodedTimelock = bip68.decode(sequenceNum);
326
- const timelock = decodedTimelock.blocks !== undefined
327
- ? { type: "blocks", value: BigInt(decodedTimelock.blocks) }
328
- : { type: "seconds", value: BigInt(decodedTimelock.seconds) };
285
+ const timelock = (0, timelock_1.sequenceToTimelock)(sequenceNum);
329
286
  const reconstructed = encode({
330
287
  timelock,
331
288
  ...multisig.params,
@@ -348,6 +305,21 @@ var CSVMultisigTapscript;
348
305
  return tapscript.type === TapscriptType.CSVMultisig;
349
306
  }
350
307
  CSVMultisigTapscript.is = is;
308
+ function isScriptValid(script) {
309
+ const asm = btc_signer_1.Script.decode(script);
310
+ if (asm.length < 3) {
311
+ return new Error(`Invalid script: too short (expected at least 3)`);
312
+ }
313
+ const sequence = asm[0];
314
+ if (typeof sequence === "string") {
315
+ return new Error("Invalid script: expected sequence number");
316
+ }
317
+ if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
318
+ return new Error("Invalid script: expected CHECKSEQUENCEVERIFY DROP");
319
+ }
320
+ return true;
321
+ }
322
+ CSVMultisigTapscript.isScriptValid = isScriptValid;
351
323
  })(CSVMultisigTapscript || (exports.CSVMultisigTapscript = CSVMultisigTapscript = {}));
352
324
  /**
353
325
  * Combines a condition script with an exit closure. The resulting script requires
@@ -382,18 +354,14 @@ var ConditionCSVMultisigTapscript;
382
354
  if (script.length === 0) {
383
355
  throw new Error("Failed to decode: script is empty");
384
356
  }
385
- const asm = btc_signer_1.Script.decode(script);
386
- if (asm.length < 1) {
387
- throw new Error(`Invalid script: too short (expected at least 1)`);
388
- }
389
- let verifyIndex = -1;
390
- for (let i = asm.length - 1; i >= 0; i--) {
391
- if (asm[i] === "VERIFY") {
392
- verifyIndex = i;
393
- }
357
+ const isValid = isScriptValid(script);
358
+ if (isValid instanceof Error) {
359
+ throw isValid;
394
360
  }
361
+ const asm = btc_signer_1.Script.decode(script);
362
+ let verifyIndex = getVerifyIndex(asm);
395
363
  if (verifyIndex === -1) {
396
- throw new Error("Invalid script: missing VERIFY operation");
364
+ throw Error("Invalid script: missing VERIFY operation");
397
365
  }
398
366
  const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
399
367
  const csvMultisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
@@ -426,6 +394,28 @@ var ConditionCSVMultisigTapscript;
426
394
  return tapscript.type === TapscriptType.ConditionCSVMultisig;
427
395
  }
428
396
  ConditionCSVMultisigTapscript.is = is;
397
+ function getVerifyIndex(asm) {
398
+ let verifyIndex = -1;
399
+ for (let i = asm.length - 1; i >= 0; i--) {
400
+ if (asm[i] === "VERIFY") {
401
+ verifyIndex = i;
402
+ return verifyIndex;
403
+ }
404
+ }
405
+ return verifyIndex;
406
+ }
407
+ function isScriptValid(script) {
408
+ const asm = btc_signer_1.Script.decode(script);
409
+ if (asm.length < 1) {
410
+ return new Error(`Invalid script: too short (expected at least 1)`);
411
+ }
412
+ let verifyIndex = getVerifyIndex(asm);
413
+ if (verifyIndex === -1) {
414
+ return new Error("Invalid script: missing VERIFY operation");
415
+ }
416
+ return true;
417
+ }
418
+ ConditionCSVMultisigTapscript.isScriptValid = isScriptValid;
429
419
  })(ConditionCSVMultisigTapscript || (exports.ConditionCSVMultisigTapscript = ConditionCSVMultisigTapscript = {}));
430
420
  /**
431
421
  * Combines a condition script with a forfeit closure. The resulting script requires
@@ -460,18 +450,14 @@ var ConditionMultisigTapscript;
460
450
  if (script.length === 0) {
461
451
  throw new Error("Failed to decode: script is empty");
462
452
  }
463
- const asm = btc_signer_1.Script.decode(script);
464
- if (asm.length < 1) {
465
- throw new Error(`Invalid script: too short (expected at least 1)`);
466
- }
467
- let verifyIndex = -1;
468
- for (let i = asm.length - 1; i >= 0; i--) {
469
- if (asm[i] === "VERIFY") {
470
- verifyIndex = i;
471
- }
453
+ const isValid = isScriptValid(script);
454
+ if (isValid instanceof Error) {
455
+ throw isValid;
472
456
  }
457
+ const asm = btc_signer_1.Script.decode(script);
458
+ let verifyIndex = getVerifyIndex(asm);
473
459
  if (verifyIndex === -1) {
474
- throw new Error("Invalid script: missing VERIFY operation");
460
+ throw Error("Invalid script: missing VERIFY operation");
475
461
  }
476
462
  const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
477
463
  const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
@@ -504,6 +490,28 @@ var ConditionMultisigTapscript;
504
490
  return tapscript.type === TapscriptType.ConditionMultisig;
505
491
  }
506
492
  ConditionMultisigTapscript.is = is;
493
+ function getVerifyIndex(asm) {
494
+ let verifyIndex = -1;
495
+ for (let i = asm.length - 1; i >= 0; i--) {
496
+ if (asm[i] === "VERIFY") {
497
+ verifyIndex = i;
498
+ return verifyIndex;
499
+ }
500
+ }
501
+ return verifyIndex;
502
+ }
503
+ function isScriptValid(script) {
504
+ const asm = btc_signer_1.Script.decode(script);
505
+ if (asm.length < 1) {
506
+ return new Error(`Invalid script: too short (expected at least 1)`);
507
+ }
508
+ let verifyIndex = getVerifyIndex(asm);
509
+ if (verifyIndex === -1) {
510
+ return new Error("Invalid script: missing VERIFY operation");
511
+ }
512
+ return true;
513
+ }
514
+ ConditionMultisigTapscript.isScriptValid = isScriptValid;
507
515
  })(ConditionMultisigTapscript || (exports.ConditionMultisigTapscript = ConditionMultisigTapscript = {}));
508
516
  /**
509
517
  * Implements an absolute timelock (CLTV) script combined with a forfeit closure.
@@ -544,10 +552,11 @@ var CLTVMultisigTapscript;
544
552
  if (script.length === 0) {
545
553
  throw new Error("Failed to decode: script is empty");
546
554
  }
547
- const asm = btc_signer_1.Script.decode(script);
548
- if (asm.length < 3) {
549
- throw new Error(`Invalid script: too short (expected at least 3)`);
555
+ const isValid = isScriptValid(script);
556
+ if (isValid instanceof Error) {
557
+ throw isValid;
550
558
  }
559
+ const asm = btc_signer_1.Script.decode(script);
551
560
  const locktime = asm[0];
552
561
  if (typeof locktime === "string") {
553
562
  throw new Error("Invalid script: expected locktime number");
@@ -592,4 +601,19 @@ var CLTVMultisigTapscript;
592
601
  return tapscript.type === TapscriptType.CLTVMultisig;
593
602
  }
594
603
  CLTVMultisigTapscript.is = is;
604
+ function isScriptValid(script) {
605
+ const asm = btc_signer_1.Script.decode(script);
606
+ if (asm.length < 3) {
607
+ return new Error(`Invalid script: too short (expected at least 3)`);
608
+ }
609
+ const locktime = asm[0];
610
+ if (typeof locktime === "string") {
611
+ return new Error("Invalid script: expected locktime as number or bytes");
612
+ }
613
+ if (asm[1] !== "CHECKLOCKTIMEVERIFY" || asm[2] !== "DROP") {
614
+ return new Error("Invalid script: expected CHECKLOCKTIMEVERIFY DROP");
615
+ }
616
+ return true;
617
+ }
618
+ CLTVMultisigTapscript.isScriptValid = isScriptValid;
595
619
  })(CLTVMultisigTapscript || (exports.CLTVMultisigTapscript = CLTVMultisigTapscript = {}));
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.timelockToSequence = timelockToSequence;
37
+ exports.sequenceToTimelock = sequenceToTimelock;
38
+ const bip68 = __importStar(require("bip68"));
39
+ /**
40
+ * Convert RelativeTimelock to BIP68 sequence number.
41
+ */
42
+ function timelockToSequence(timelock) {
43
+ return bip68.encode(timelock.type === "blocks"
44
+ ? { blocks: Number(timelock.value) }
45
+ : { seconds: Number(timelock.value) });
46
+ }
47
+ /**
48
+ * Convert BIP68 sequence number back to RelativeTimelock.
49
+ */
50
+ function sequenceToTimelock(sequence) {
51
+ const decoded = bip68.decode(sequence);
52
+ if ("blocks" in decoded && decoded.blocks !== undefined) {
53
+ return { type: "blocks", value: BigInt(decoded.blocks) };
54
+ }
55
+ if ("seconds" in decoded && decoded.seconds !== undefined) {
56
+ return { type: "seconds", value: BigInt(decoded.seconds) };
57
+ }
58
+ throw new Error(`Invalid BIP68 sequence: ${sequence}`);
59
+ }
@@ -1,44 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.ConditionWitness = exports.VtxoTaprootTree = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = void 0;
37
4
  exports.setArkPsbtField = setArkPsbtField;
38
5
  exports.getArkPsbtFields = getArkPsbtFields;
39
- const bip68 = __importStar(require("bip68"));
40
6
  const btc_signer_1 = require("@scure/btc-signer");
41
7
  const base_1 = require("@scure/base");
8
+ const timelock_1 = require("./timelock");
42
9
  /**
43
10
  * ArkPsbtFieldKey are the available key names for the Arkade PSBT custom fields.
44
11
  */
@@ -184,11 +151,7 @@ exports.VtxoTreeExpiry = {
184
151
  const v = (0, btc_signer_1.ScriptNum)(6, true).decode(unknown[1]);
185
152
  if (!v)
186
153
  return null;
187
- const { blocks, seconds } = bip68.decode(Number(v));
188
- return {
189
- type: blocks ? "blocks" : "seconds",
190
- value: BigInt(blocks ?? seconds ?? 0),
191
- };
154
+ return (0, timelock_1.sequenceToTimelock)(Number(v));
192
155
  }),
193
156
  };
194
157
  const encodedPsbtFieldKey = Object.fromEntries(Object.values(ArkPsbtFieldKey).map((key) => [
@@ -5,6 +5,8 @@ const indexer_1 = require("../../providers/indexer");
5
5
  const index_1 = require("../index");
6
6
  const utils_1 = require("../utils");
7
7
  const transactionHistory_1 = require("../../utils/transactionHistory");
8
+ const vtxoOwnership_1 = require("../../contracts/vtxoOwnership");
9
+ const scriptFromAddress_1 = require("../../repositories/scriptFromAddress");
8
10
  class WalletNotInitializedError extends Error {
9
11
  constructor() {
10
12
  super("Wallet handler not initialized");
@@ -317,6 +319,16 @@ class WalletMessageHandler {
317
319
  type: "REFRESH_VTXOS_SUCCESS",
318
320
  });
319
321
  }
322
+ case "REFRESH_OUTPOINTS": {
323
+ const manager = await this.readonlyWallet.getContractManager();
324
+ const { outpoints } = message
325
+ .payload;
326
+ await manager.refreshOutpoints(outpoints);
327
+ return this.tagged({
328
+ id,
329
+ type: "REFRESH_OUTPOINTS_SUCCESS",
330
+ });
331
+ }
320
332
  case "SEND": {
321
333
  const { recipients } = message.payload;
322
334
  const txid = await this.wallet.send(...recipients);
@@ -587,11 +599,47 @@ class WalletMessageHandler {
587
599
  const { newVtxos, spentVtxos } = funds;
588
600
  if (newVtxos.length + spentVtxos.length === 0)
589
601
  return;
590
- // save virtual outputs using unified repository
591
- await this.walletRepository?.saveVtxos(address, [
592
- ...newVtxos,
593
- ...spentVtxos,
594
- ]);
602
+ // Save virtual outputs using unified repository. The
603
+ // event may carry rows for several scripts (other
604
+ // contracts the wallet watches), so split by script and
605
+ // save each bucket under its own contract address rather
606
+ // than saving a mixed-script array under one address.
607
+ const byScript = new Map();
608
+ for (const v of [...newVtxos, ...spentVtxos]) {
609
+ if (!v.script) {
610
+ // Without a script we can't route the row to the
611
+ // right contract bucket; surface the drop instead
612
+ // of silently losing the VTXO.
613
+ console.warn(`WalletMessageHandler.notifyIncomingFunds: dropping VTXO without script ${v.txid}:${v.vout}`);
614
+ continue;
615
+ }
616
+ const arr = byScript.get(v.script) ?? [];
617
+ arr.push(v);
618
+ byScript.set(v.script, arr);
619
+ }
620
+ let walletScript;
621
+ try {
622
+ walletScript = (0, scriptFromAddress_1.scriptFromArkAddress)(address);
623
+ }
624
+ catch {
625
+ walletScript = undefined;
626
+ }
627
+ const cm = await this.readonlyWallet.getContractManager();
628
+ const contracts = await cm.getContracts();
629
+ const addrByScript = new Map(contracts.map((c) => [c.script, c.address]));
630
+ for (const [script, vtxos] of byScript) {
631
+ const filtered = (0, vtxoOwnership_1.warnAndFilterVtxosForScript)(vtxos, script, "WalletMessageHandler.notifyIncomingFunds");
632
+ if (filtered.length === 0)
633
+ continue;
634
+ const targetAddress = script === walletScript
635
+ ? address
636
+ : addrByScript.get(script);
637
+ if (!targetAddress)
638
+ continue;
639
+ if (this.walletRepository) {
640
+ await (0, vtxoOwnership_1.saveVtxosForContract)(this.walletRepository, { script, address: targetAddress }, filtered);
641
+ }
642
+ }
595
643
  // notify all clients about the virtual output state update
596
644
  this.scheduleForNextTick(() => this.tagged({
597
645
  type: "VTXO_UPDATE",
@@ -803,17 +851,30 @@ class WalletMessageHandler {
803
851
  }
804
852
  }
805
853
  };
806
- // Aggregate virtual outputs from all contract addresses
854
+ // Aggregate virtual outputs from all contract addresses. Address
855
+ // buckets may carry legacy duplicate rows from other contracts; gate
856
+ // each bucket by its owning contract script before deduplication so a
857
+ // wrong-script row never wins the txid:vout race.
807
858
  const manager = await this.readonlyWallet.getContractManager();
808
859
  const contracts = await manager.getContracts();
809
860
  for (const contract of contracts) {
810
- const vtxos = await this.walletRepository.getVtxos(contract.address);
811
- addVtxos(vtxos);
861
+ addVtxos(await (0, vtxoOwnership_1.getVtxosForContract)(this.walletRepository, contract));
812
862
  }
813
- // Also check the wallet's primary address
863
+ // Also check the wallet's primary address. Decode it to its script
864
+ // and apply the same script gate. Failing to decode the wallet's own
865
+ // address is a structural bug — surfacing the error is safer than
866
+ // silently dropping the primary bucket and zeroing the user's
867
+ // visible balance.
814
868
  const walletAddress = await this.readonlyWallet.getAddress();
869
+ let walletScript;
870
+ try {
871
+ walletScript = (0, scriptFromAddress_1.scriptFromArkAddress)(walletAddress);
872
+ }
873
+ catch (e) {
874
+ throw new Error(`WalletMessageHandler.getVtxosFromRepo: failed to derive script from wallet address ${walletAddress}: ${e instanceof Error ? e.message : String(e)}`);
875
+ }
815
876
  const walletVtxos = await this.walletRepository.getVtxos(walletAddress);
816
- addVtxos(walletVtxos);
877
+ addVtxos((0, vtxoOwnership_1.filterVtxosForScript)(walletVtxos, walletScript));
817
878
  return allVtxos;
818
879
  }
819
880
  /**
@@ -60,6 +60,7 @@ exports.DEFAULT_MESSAGE_TIMEOUTS = {
60
60
  UPDATE_CONTRACT: 30000,
61
61
  DELETE_CONTRACT: 10000,
62
62
  REFRESH_VTXOS: 30000,
63
+ REFRESH_OUTPOINTS: 30000,
63
64
  };
64
65
  const DEDUPABLE_REQUEST_TYPES = new Set([
65
66
  "GET_ADDRESS",
@@ -824,6 +825,15 @@ class ServiceWorkerReadonlyWallet {
824
825
  };
825
826
  await sendContractMessage(message);
826
827
  },
828
+ async refreshOutpoints(outpoints) {
829
+ const message = {
830
+ type: "REFRESH_OUTPOINTS",
831
+ id: (0, utils_2.getRandomId)(),
832
+ tag: messageTag,
833
+ payload: { outpoints },
834
+ };
835
+ await sendContractMessage(message);
836
+ },
827
837
  async isWatching() {
828
838
  const message = {
829
839
  type: "IS_CONTRACT_MANAGER_WATCHING",