@arkade-os/sdk 0.3.0-alpha.7 → 0.3.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +115 -14
  2. package/dist/cjs/adapters/expo.js +8 -0
  3. package/dist/cjs/arknote/index.js +3 -3
  4. package/dist/cjs/forfeit.js +5 -2
  5. package/dist/cjs/identity/singleKey.js +5 -4
  6. package/dist/cjs/index.js +7 -3
  7. package/dist/cjs/{bip322 → intent}/index.js +37 -55
  8. package/dist/cjs/providers/ark.js +62 -23
  9. package/dist/cjs/providers/expoArk.js +82 -0
  10. package/dist/cjs/providers/expoIndexer.js +105 -0
  11. package/dist/cjs/providers/indexer.js +3 -1
  12. package/dist/cjs/providers/utils.js +122 -0
  13. package/dist/cjs/script/base.js +1 -2
  14. package/dist/cjs/script/tapscript.js +20 -21
  15. package/dist/cjs/script/vhtlc.js +2 -2
  16. package/dist/cjs/tree/signingSession.js +7 -8
  17. package/dist/cjs/tree/txTree.js +3 -4
  18. package/dist/cjs/tree/validation.js +2 -3
  19. package/dist/cjs/utils/arkTransaction.js +117 -12
  20. package/dist/cjs/utils/unknownFields.js +5 -5
  21. package/dist/cjs/wallet/index.js +1 -1
  22. package/dist/cjs/wallet/onchain.js +4 -5
  23. package/dist/cjs/wallet/serviceWorker/utils.js +2 -9
  24. package/dist/cjs/wallet/serviceWorker/wallet.js +4 -8
  25. package/dist/cjs/wallet/serviceWorker/worker.js +25 -23
  26. package/dist/cjs/wallet/unroll.js +6 -7
  27. package/dist/cjs/wallet/utils.js +11 -0
  28. package/dist/cjs/wallet/vtxo-manager.js +381 -0
  29. package/dist/cjs/wallet/wallet.js +130 -143
  30. package/dist/esm/adapters/expo.js +3 -0
  31. package/dist/esm/arknote/index.js +2 -2
  32. package/dist/esm/forfeit.js +4 -1
  33. package/dist/esm/identity/singleKey.js +7 -6
  34. package/dist/esm/index.js +7 -6
  35. package/dist/esm/{bip322 → intent}/index.js +31 -48
  36. package/dist/esm/providers/ark.js +62 -23
  37. package/dist/esm/providers/expoArk.js +78 -0
  38. package/dist/esm/providers/expoIndexer.js +101 -0
  39. package/dist/esm/providers/indexer.js +3 -1
  40. package/dist/esm/providers/utils.js +87 -0
  41. package/dist/esm/script/base.js +1 -2
  42. package/dist/esm/script/tapscript.js +1 -2
  43. package/dist/esm/script/vhtlc.js +1 -1
  44. package/dist/esm/tree/signingSession.js +8 -9
  45. package/dist/esm/tree/txTree.js +3 -4
  46. package/dist/esm/tree/validation.js +2 -3
  47. package/dist/esm/utils/arkTransaction.js +108 -5
  48. package/dist/esm/utils/unknownFields.js +1 -1
  49. package/dist/esm/wallet/index.js +1 -1
  50. package/dist/esm/wallet/onchain.js +1 -2
  51. package/dist/esm/wallet/serviceWorker/utils.js +1 -8
  52. package/dist/esm/wallet/serviceWorker/wallet.js +5 -9
  53. package/dist/esm/wallet/serviceWorker/worker.js +26 -24
  54. package/dist/esm/wallet/unroll.js +2 -3
  55. package/dist/esm/wallet/utils.js +8 -0
  56. package/dist/esm/wallet/vtxo-manager.js +372 -0
  57. package/dist/esm/wallet/wallet.js +124 -137
  58. package/dist/types/adapters/expo.d.ts +4 -0
  59. package/dist/types/arknote/index.d.ts +1 -1
  60. package/dist/types/forfeit.d.ts +2 -2
  61. package/dist/types/identity/index.d.ts +1 -1
  62. package/dist/types/identity/singleKey.d.ts +1 -1
  63. package/dist/types/index.d.ts +8 -7
  64. package/dist/types/intent/index.d.ts +41 -0
  65. package/dist/types/providers/ark.d.ts +190 -22
  66. package/dist/types/providers/expoArk.d.ts +22 -0
  67. package/dist/types/providers/expoIndexer.d.ts +18 -0
  68. package/dist/types/providers/indexer.d.ts +8 -8
  69. package/dist/types/providers/utils.d.ts +18 -0
  70. package/dist/types/script/base.d.ts +3 -2
  71. package/dist/types/tree/signingSession.d.ts +10 -10
  72. package/dist/types/utils/anchor.d.ts +2 -2
  73. package/dist/types/utils/arkTransaction.d.ts +16 -4
  74. package/dist/types/utils/unknownFields.d.ts +2 -2
  75. package/dist/types/wallet/index.d.ts +47 -7
  76. package/dist/types/wallet/onchain.d.ts +1 -1
  77. package/dist/types/wallet/serviceWorker/utils.d.ts +1 -2
  78. package/dist/types/wallet/serviceWorker/wallet.d.ts +2 -2
  79. package/dist/types/wallet/serviceWorker/worker.d.ts +3 -1
  80. package/dist/types/wallet/unroll.d.ts +1 -1
  81. package/dist/types/wallet/utils.d.ts +2 -0
  82. package/dist/types/wallet/vtxo-manager.d.ts +207 -0
  83. package/dist/types/wallet/wallet.d.ts +16 -4
  84. package/package.json +11 -3
  85. package/dist/cjs/bip322/errors.js +0 -13
  86. package/dist/esm/bip322/errors.js +0 -9
  87. package/dist/types/bip322/errors.d.ts +0 -6
  88. package/dist/types/bip322/index.d.ts +0 -57
@@ -38,8 +38,8 @@ exports.waitForIncomingFunds = waitForIncomingFunds;
38
38
  const base_1 = require("@scure/base");
39
39
  const bip68 = __importStar(require("bip68"));
40
40
  const payment_js_1 = require("@scure/btc-signer/payment.js");
41
- const transaction_js_1 = require("@scure/btc-signer/transaction.js");
42
- const psbt_js_1 = require("@scure/btc-signer/psbt.js");
41
+ const btc_signer_1 = require("@scure/btc-signer");
42
+ const utils_js_1 = require("@scure/btc-signer/utils.js");
43
43
  const transactionHistory_1 = require("../utils/transactionHistory");
44
44
  const address_1 = require("../script/address");
45
45
  const default_1 = require("../script/default");
@@ -49,18 +49,19 @@ const ark_1 = require("../providers/ark");
49
49
  const forfeit_1 = require("../forfeit");
50
50
  const validation_1 = require("../tree/validation");
51
51
  const _1 = require(".");
52
- const utils_js_1 = require("@scure/btc-signer/utils.js");
53
52
  const base_2 = require("../script/base");
54
53
  const tapscript_1 = require("../script/tapscript");
55
54
  const arkTransaction_1 = require("../utils/arkTransaction");
55
+ const vtxo_manager_1 = require("./vtxo-manager");
56
56
  const arknote_1 = require("../arknote");
57
- const bip322_1 = require("../bip322");
57
+ const intent_1 = require("../intent");
58
58
  const indexer_1 = require("../providers/indexer");
59
59
  const txTree_1 = require("../tree/txTree");
60
+ const unknownFields_1 = require("../utils/unknownFields");
60
61
  const inMemory_1 = require("../storage/inMemory");
61
62
  const walletRepository_1 = require("../repositories/walletRepository");
62
63
  const contractRepository_1 = require("../repositories/contractRepository");
63
- const utils_1 = require("./serviceWorker/utils");
64
+ const utils_1 = require("./utils");
64
65
  /**
65
66
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
66
67
  * The wallet does not store any data locally and relies on Ark and onchain
@@ -68,13 +69,21 @@ const utils_1 = require("./serviceWorker/utils");
68
69
  *
69
70
  * @example
70
71
  * ```typescript
71
- * // Create a wallet
72
+ * // Create a wallet with URL configuration
72
73
  * const wallet = await Wallet.create({
73
74
  * identity: SingleKey.fromHex('your_private_key'),
74
75
  * arkServerUrl: 'https://ark.example.com',
75
76
  * esploraUrl: 'https://mempool.space/api'
76
77
  * });
77
78
  *
79
+ * // Or with custom provider instances (e.g., for Expo/React Native)
80
+ * const wallet = await Wallet.create({
81
+ * identity: SingleKey.fromHex('your_private_key'),
82
+ * arkProvider: new ExpoArkProvider('https://ark.example.com'),
83
+ * indexerProvider: new ExpoIndexerProvider('https://ark.example.com'),
84
+ * esploraUrl: 'https://mempool.space/api'
85
+ * });
86
+ *
78
87
  * // Get addresses
79
88
  * const arkAddress = await wallet.getAddress();
80
89
  * const boardingAddress = await wallet.getBoardingAddress();
@@ -87,7 +96,7 @@ const utils_1 = require("./serviceWorker/utils");
87
96
  * ```
88
97
  */
89
98
  class Wallet {
90
- constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, dustAmount, walletRepository, contractRepository) {
99
+ constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, renewalConfig) {
91
100
  this.identity = identity;
92
101
  this.network = network;
93
102
  this.networkName = networkName;
@@ -99,20 +108,45 @@ class Wallet {
99
108
  this.boardingTapscript = boardingTapscript;
100
109
  this.serverUnrollScript = serverUnrollScript;
101
110
  this.forfeitOutputScript = forfeitOutputScript;
111
+ this.forfeitPubkey = forfeitPubkey;
102
112
  this.dustAmount = dustAmount;
103
113
  this.walletRepository = walletRepository;
104
114
  this.contractRepository = contractRepository;
115
+ this.renewalConfig = {
116
+ enabled: renewalConfig?.enabled ?? false,
117
+ ...vtxo_manager_1.DEFAULT_RENEWAL_CONFIG,
118
+ ...renewalConfig,
119
+ };
105
120
  }
106
121
  static async create(config) {
107
122
  const pubkey = await config.identity.xOnlyPublicKey();
108
123
  if (!pubkey) {
109
124
  throw new Error("Invalid configured public key");
110
125
  }
111
- const arkProvider = new ark_1.RestArkProvider(config.arkServerUrl);
112
- const indexerProvider = new indexer_1.RestIndexerProvider(config.arkServerUrl);
126
+ // Use provided arkProvider instance or create a new one from arkServerUrl
127
+ const arkProvider = config.arkProvider ||
128
+ (() => {
129
+ if (!config.arkServerUrl) {
130
+ throw new Error("Either arkProvider or arkServerUrl must be provided");
131
+ }
132
+ return new ark_1.RestArkProvider(config.arkServerUrl);
133
+ })();
134
+ // Extract arkServerUrl from provider if not explicitly provided
135
+ const arkServerUrl = config.arkServerUrl || arkProvider.serverUrl;
136
+ if (!arkServerUrl) {
137
+ throw new Error("Could not determine arkServerUrl from provider");
138
+ }
139
+ // Use provided indexerProvider instance or create a new one
140
+ // indexerUrl defaults to arkServerUrl if not provided
141
+ const indexerUrl = config.indexerUrl || arkServerUrl;
142
+ const indexerProvider = config.indexerProvider || new indexer_1.RestIndexerProvider(indexerUrl);
113
143
  const info = await arkProvider.getInfo();
114
144
  const network = (0, networks_1.getNetwork)(info.network);
115
- const onchainProvider = new onchain_1.EsploraProvider(config.esploraUrl || onchain_1.ESPLORA_URL[info.network]);
145
+ // Extract esploraUrl from provider if not explicitly provided
146
+ const esploraUrl = config.esploraUrl || onchain_1.ESPLORA_URL[info.network];
147
+ // Use provided onchainProvider instance or create a new one
148
+ const onchainProvider = config.onchainProvider || new onchain_1.EsploraProvider(esploraUrl);
149
+ // Generate timelocks
116
150
  const exitTimelock = {
117
151
  value: info.unilateralExitDelay,
118
152
  type: info.unilateralExitDelay < 512n ? "blocks" : "seconds",
@@ -136,17 +170,24 @@ class Wallet {
136
170
  // Save tapscripts
137
171
  const offchainTapscript = bareVtxoTapscript;
138
172
  // the serverUnrollScript is the one used to create output scripts of the checkpoint transactions
139
- const rawCheckpointExitClosure = base_1.hex.decode(info.checkpointExitClosure);
140
- const serverUnrollScript = tapscript_1.CSVMultisigTapscript.decode(rawCheckpointExitClosure);
173
+ let serverUnrollScript;
174
+ try {
175
+ const raw = base_1.hex.decode(info.checkpointTapscript);
176
+ serverUnrollScript = tapscript_1.CSVMultisigTapscript.decode(raw);
177
+ }
178
+ catch (e) {
179
+ throw new Error("Invalid checkpointTapscript from server");
180
+ }
141
181
  // parse the server forfeit address
142
182
  // server is expecting funds to be sent to this address
143
- const forfeitAddress = (0, payment_js_1.Address)(network).decode(info.forfeitAddress);
144
- const forfeitOutputScript = payment_js_1.OutScript.encode(forfeitAddress);
183
+ const forfeitPubkey = base_1.hex.decode(info.forfeitPubkey).slice(1);
184
+ const forfeitAddress = (0, btc_signer_1.Address)(network).decode(info.forfeitAddress);
185
+ const forfeitOutputScript = btc_signer_1.OutScript.encode(forfeitAddress);
145
186
  // Set up storage and repositories
146
187
  const storage = config.storage || new inMemory_1.InMemoryStorageAdapter();
147
188
  const walletRepository = new walletRepository_1.WalletRepositoryImpl(storage);
148
189
  const contractRepository = new contractRepository_1.ContractRepositoryImpl(storage);
149
- return new Wallet(config.identity, network, info.network, onchainProvider, arkProvider, indexerProvider, serverPubKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, info.dust, walletRepository, contractRepository);
190
+ return new Wallet(config.identity, network, info.network, onchainProvider, arkProvider, indexerProvider, serverPubKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, info.dust, walletRepository, contractRepository, config.renewalConfig);
150
191
  }
151
192
  get arkAddress() {
152
193
  return this.offchainTapscript.address(this.network.hrp, this.arkServerPublicKey);
@@ -208,40 +249,24 @@ class Wallet {
208
249
  // if (cachedVtxos.length) return cachedVtxos;
209
250
  // For now, always fetch fresh data from provider and update cache
210
251
  // In future, we can add cache invalidation logic based on timestamps
211
- const spendableVtxos = await this.getVirtualCoins(filter);
212
- const encodedOffchainTapscript = this.offchainTapscript.encode();
213
- const forfeit = this.offchainTapscript.forfeit();
214
- const exit = this.offchainTapscript.exit();
215
- const extendedVtxos = spendableVtxos.map((vtxo) => ({
216
- ...vtxo,
217
- forfeitTapLeafScript: forfeit,
218
- intentTapLeafScript: exit,
219
- tapTree: encodedOffchainTapscript,
220
- }));
252
+ const vtxos = await this.getVirtualCoins(filter);
253
+ const extendedVtxos = vtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this, vtxo));
221
254
  // Update cache with fresh data
222
255
  await this.walletRepository.saveVtxos(address, extendedVtxos);
223
256
  return extendedVtxos;
224
257
  }
225
258
  async getVirtualCoins(filter = { withRecoverable: true, withUnrolled: false }) {
226
259
  const scripts = [base_1.hex.encode(this.offchainTapscript.pkScript)];
227
- const response = await this.indexerProvider.getVtxos({
228
- scripts,
229
- spendableOnly: true,
230
- });
231
- const vtxos = response.vtxos;
232
- if (filter.withRecoverable) {
233
- const response = await this.indexerProvider.getVtxos({
234
- scripts,
235
- recoverableOnly: true,
236
- });
237
- vtxos.push(...response.vtxos);
260
+ const response = await this.indexerProvider.getVtxos({ scripts });
261
+ const allVtxos = response.vtxos;
262
+ let vtxos = allVtxos.filter(_1.isSpendable);
263
+ // all recoverable vtxos are spendable by definition
264
+ if (!filter.withRecoverable) {
265
+ vtxos = vtxos.filter((vtxo) => !(0, _1.isRecoverable)(vtxo));
238
266
  }
239
267
  if (filter.withUnrolled) {
240
- const response = await this.indexerProvider.getVtxos({
241
- scripts,
242
- spentOnly: true,
243
- });
244
- vtxos.push(...response.vtxos.filter((vtxo) => vtxo.isUnrolled));
268
+ const spentVtxos = allVtxos.filter((vtxo) => !(0, _1.isSpendable)(vtxo));
269
+ vtxos.push(...spentVtxos.filter((vtxo) => vtxo.isUnrolled));
245
270
  }
246
271
  return vtxos;
247
272
  }
@@ -279,10 +304,10 @@ class Wallet {
279
304
  return txs;
280
305
  }
281
306
  async getBoardingTxs() {
282
- const boardingAddress = await this.getBoardingAddress();
283
- const txs = await this.onchainProvider.getTransactions(boardingAddress);
284
307
  const utxos = [];
285
308
  const commitmentsToIgnore = new Set();
309
+ const boardingAddress = await this.getBoardingAddress();
310
+ const txs = await this.onchainProvider.getTransactions(boardingAddress);
286
311
  for (const tx of txs) {
287
312
  for (let i = 0; i < tx.vout.length; i++) {
288
313
  const vout = tx.vout[i];
@@ -402,7 +427,7 @@ class Wallet {
402
427
  // TODO persist final virtual tx and checkpoints to repository
403
428
  // sign the checkpoints
404
429
  const finalCheckpoints = await Promise.all(signedCheckpointTxs.map(async (c) => {
405
- const tx = transaction_js_1.Transaction.fromPSBT(base_1.base64.decode(c));
430
+ const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(c));
406
431
  const signedCheckpoint = await this.identity.sign(tx);
407
432
  return base_1.base64.encode(signedCheckpoint.toPSBT());
408
433
  }));
@@ -423,13 +448,15 @@ class Wallet {
423
448
  }
424
449
  }
425
450
  }
426
- // if no params are provided, use all boarding and offchain utxos as inputs
451
+ // if no params are provided, use all non expired boarding utxos and offchain vtxos as inputs
427
452
  // and send all to the offchain address
428
453
  if (!params) {
429
454
  let amount = 0;
430
- const boardingUtxos = await this.getBoardingUtxos();
455
+ const exitScript = tapscript_1.CSVMultisigTapscript.decode(base_1.hex.decode(this.boardingTapscript.exitScript));
456
+ const boardingTimelock = exitScript.params.timelock;
457
+ const boardingUtxos = (await this.getBoardingUtxos()).filter((utxo) => !(0, arkTransaction_1.hasBoardingTxExpired)(utxo, boardingTimelock));
431
458
  amount += boardingUtxos.reduce((sum, input) => sum + input.value, 0);
432
- const vtxos = await this.getVtxos();
459
+ const vtxos = await this.getVtxos({ withRecoverable: true });
433
460
  amount += vtxos.reduce((sum, input) => sum + input.value, 0);
434
461
  const inputs = [...boardingUtxos, ...vtxos];
435
462
  if (inputs.length === 0) {
@@ -458,8 +485,8 @@ class Wallet {
458
485
  }
459
486
  catch {
460
487
  // onchain
461
- const addr = (0, payment_js_1.Address)(this.network).decode(output.address);
462
- script = payment_js_1.OutScript.encode(addr);
488
+ const addr = (0, btc_signer_1.Address)(this.network).decode(output.address);
489
+ script = btc_signer_1.OutScript.encode(addr);
463
490
  onchainOutputIndexes.push(index);
464
491
  }
465
492
  outputs.push({
@@ -472,7 +499,7 @@ class Wallet {
472
499
  const signingPublicKeys = [];
473
500
  if (hasOffchainOutputs) {
474
501
  session = this.identity.signerSession();
475
- signingPublicKeys.push(base_1.hex.encode(session.getPublicKey()));
502
+ signingPublicKeys.push(base_1.hex.encode(await session.getPublicKey()));
476
503
  }
477
504
  const [intent, deleteIntent] = await Promise.all([
478
505
  this.makeRegisterIntentSignature(params.inputs, outputs, onchainOutputIndexes, signingPublicKeys),
@@ -511,7 +538,7 @@ class Wallet {
511
538
  if (step !== undefined) {
512
539
  continue;
513
540
  }
514
- const res = await this.handleBatchStartedEvent(event, intentId, this.arkServerPublicKey, this.forfeitOutputScript);
541
+ const res = await this.handleBatchStartedEvent(event, intentId, this.forfeitPubkey, this.forfeitOutputScript);
515
542
  if (!res.skip) {
516
543
  step = event.type;
517
544
  sweepTapTreeRoot = res.sweepTapTreeRoot;
@@ -639,22 +666,22 @@ class Wallet {
639
666
  let onchainStopFunc;
640
667
  let indexerStopFunc;
641
668
  if (this.onchainProvider && boardingAddress) {
669
+ const findVoutOnTx = (tx) => {
670
+ return tx.vout.findIndex((v) => v.scriptpubkey_address === boardingAddress);
671
+ };
642
672
  onchainStopFunc = await this.onchainProvider.watchAddresses([boardingAddress], (txs) => {
673
+ // find all utxos belonging to our boarding address
643
674
  const coins = txs
675
+ // filter txs where address is in output
676
+ .filter((tx) => findVoutOnTx(tx) !== -1)
677
+ // return utxo as Coin
644
678
  .map((tx) => {
645
- const vout = tx.vout.findIndex((v) => v.scriptpubkey_address === boardingAddress);
646
- if (vout === -1) {
647
- console.warn(`No vout found for address ${boardingAddress} in transaction ${tx.txid}`);
648
- return null;
649
- }
650
- return {
651
- txid: tx.txid,
652
- vout,
653
- value: Number(tx.vout[vout].value),
654
- status: tx.status,
655
- };
656
- })
657
- .filter((coin) => coin !== null);
679
+ const { txid, status } = tx;
680
+ const vout = findVoutOnTx(tx);
681
+ const value = Number(tx.vout[vout].value);
682
+ return { txid, vout, value, status };
683
+ });
684
+ // and notify via callback
658
685
  eventCallback({
659
686
  type: "utxo",
660
687
  coins,
@@ -696,10 +723,10 @@ class Wallet {
696
723
  };
697
724
  return stopFunc;
698
725
  }
699
- async handleBatchStartedEvent(event, intentId, serverPubKey, forfeitOutputScript) {
726
+ async handleBatchStartedEvent(event, intentId, forfeitPubKey, forfeitOutputScript) {
700
727
  const utf8IntentId = new TextEncoder().encode(intentId);
701
728
  const intentIdHash = (0, utils_js_1.sha256)(utf8IntentId);
702
- const intentIdHashStr = base_1.hex.encode(new Uint8Array(intentIdHash));
729
+ const intentIdHashStr = base_1.hex.encode(intentIdHash);
703
730
  let skip = true;
704
731
  // check if our intent ID hash matches any in the event
705
732
  for (const idHash of event.intentIdHashes) {
@@ -719,7 +746,7 @@ class Wallet {
719
746
  value: event.batchExpiry,
720
747
  type: event.batchExpiry >= 512n ? "seconds" : "blocks",
721
748
  },
722
- pubkeys: [serverPubKey],
749
+ pubkeys: [forfeitPubKey],
723
750
  }).script;
724
751
  const sweepTapTreeRoot = (0, payment_js_1.tapLeafHash)(sweepTapscript);
725
752
  return {
@@ -732,7 +759,7 @@ class Wallet {
732
759
  // validates the vtxo tree, creates a signing session and generates the musig2 nonces
733
760
  async handleSettlementSigningEvent(event, sweepTapTreeRoot, session, vtxoGraph) {
734
761
  // validate the unsigned vtxo tree
735
- const commitmentTx = transaction_js_1.Transaction.fromPSBT(base_1.base64.decode(event.unsignedCommitmentTx));
762
+ const commitmentTx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.unsignedCommitmentTx));
736
763
  (0, validation_1.validateVtxoTxGraph)(vtxoGraph, commitmentTx, sweepTapTreeRoot);
737
764
  // TODO check if our registered outputs are in the vtxo tree
738
765
  const sharedOutput = commitmentTx.getOutput(0);
@@ -740,18 +767,21 @@ class Wallet {
740
767
  throw new Error("Shared output not found");
741
768
  }
742
769
  session.init(vtxoGraph, sweepTapTreeRoot, sharedOutput.amount);
743
- await this.arkProvider.submitTreeNonces(event.id, base_1.hex.encode(session.getPublicKey()), session.getNonces());
770
+ const pubkey = base_1.hex.encode(await session.getPublicKey());
771
+ const nonces = await session.getNonces();
772
+ await this.arkProvider.submitTreeNonces(event.id, pubkey, nonces);
744
773
  }
745
774
  async handleSettlementSigningNoncesGeneratedEvent(event, session) {
746
775
  session.setAggregatedNonces(event.treeNonces);
747
- const signatures = session.sign();
748
- await this.arkProvider.submitTreeSignatures(event.id, base_1.hex.encode(session.getPublicKey()), signatures);
776
+ const signatures = await session.sign();
777
+ const pubkey = base_1.hex.encode(await session.getPublicKey());
778
+ await this.arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
749
779
  }
750
780
  async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
751
781
  // the signed forfeits transactions to submit
752
782
  const signedForfeits = [];
753
783
  const vtxos = await this.getVirtualCoins();
754
- let settlementPsbt = transaction_js_1.Transaction.fromPSBT(base_1.base64.decode(event.commitmentTx));
784
+ let settlementPsbt = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.commitmentTx));
755
785
  let hasBoardingUtxos = false;
756
786
  let connectorIndex = 0;
757
787
  const connectorsLeaves = connectorsGraph?.leaves() || [];
@@ -793,7 +823,7 @@ class Wallet {
793
823
  throw new Error("not enough connectors received");
794
824
  }
795
825
  const connectorLeaf = connectorsLeaves[connectorIndex];
796
- const connectorTxId = base_1.hex.encode((0, utils_js_1.sha256x2)(connectorLeaf.toBytes(true)).reverse());
826
+ const connectorTxId = connectorLeaf.id;
797
827
  const connectorOutput = connectorLeaf.getOutput(0);
798
828
  if (!connectorOutput) {
799
829
  throw new Error("connector output not found");
@@ -812,7 +842,7 @@ class Wallet {
812
842
  amount: BigInt(vtxo.value),
813
843
  script: base_2.VtxoScript.decode(input.tapTree).pkScript,
814
844
  },
815
- sighashType: transaction_js_1.SigHash.DEFAULT,
845
+ sighashType: btc_signer_1.SigHash.DEFAULT,
816
846
  tapLeafScript: [input.forfeitTapLeafScript],
817
847
  },
818
848
  {
@@ -834,112 +864,69 @@ class Wallet {
834
864
  : undefined);
835
865
  }
836
866
  }
837
- async makeRegisterIntentSignature(bip322Inputs, outputs, onchainOutputsIndexes, cosignerPubKeys) {
867
+ async makeRegisterIntentSignature(coins, outputs, onchainOutputsIndexes, cosignerPubKeys) {
838
868
  const nowSeconds = Math.floor(Date.now() / 1000);
839
- const { inputs, inputTapTrees, finalizer } = this.prepareBIP322Inputs(bip322Inputs);
869
+ const inputs = this.prepareIntentProofInputs(coins);
840
870
  const message = {
841
871
  type: "register",
842
- input_tap_trees: inputTapTrees,
843
872
  onchain_output_indexes: onchainOutputsIndexes,
844
873
  valid_at: nowSeconds,
845
874
  expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
846
875
  cosigners_public_keys: cosignerPubKeys,
847
876
  };
848
877
  const encodedMessage = JSON.stringify(message, null, 0);
849
- const signature = await this.makeBIP322Signature(encodedMessage, inputs, finalizer, outputs);
878
+ const proof = intent_1.Intent.create(encodedMessage, inputs, outputs);
879
+ const signedProof = await this.identity.sign(proof);
850
880
  return {
851
- signature,
881
+ proof: base_1.base64.encode(signedProof.toPSBT()),
852
882
  message: encodedMessage,
853
883
  };
854
884
  }
855
- async makeDeleteIntentSignature(bip322Inputs) {
885
+ async makeDeleteIntentSignature(coins) {
856
886
  const nowSeconds = Math.floor(Date.now() / 1000);
857
- const { inputs, finalizer } = this.prepareBIP322Inputs(bip322Inputs);
887
+ const inputs = this.prepareIntentProofInputs(coins);
858
888
  const message = {
859
889
  type: "delete",
860
890
  expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
861
891
  };
862
892
  const encodedMessage = JSON.stringify(message, null, 0);
863
- const signature = await this.makeBIP322Signature(encodedMessage, inputs, finalizer);
893
+ const proof = intent_1.Intent.create(encodedMessage, inputs, []);
894
+ const signedProof = await this.identity.sign(proof);
864
895
  return {
865
- signature,
896
+ proof: base_1.base64.encode(signedProof.toPSBT()),
866
897
  message: encodedMessage,
867
898
  };
868
899
  }
869
- prepareBIP322Inputs(bip322Inputs) {
900
+ prepareIntentProofInputs(coins) {
870
901
  const inputs = [];
871
- const inputTapTrees = [];
872
- const inputExtraWitnesses = [];
873
- for (const bip322Input of bip322Inputs) {
874
- const vtxoScript = base_2.VtxoScript.decode(bip322Input.tapTree);
875
- const sequence = getSequence(bip322Input);
902
+ for (const input of coins) {
903
+ const vtxoScript = base_2.VtxoScript.decode(input.tapTree);
904
+ const sequence = getSequence(input);
905
+ const unknown = [unknownFields_1.VtxoTaprootTree.encode(input.tapTree)];
906
+ if (input.extraWitness) {
907
+ unknown.push(unknownFields_1.ConditionWitness.encode(input.extraWitness));
908
+ }
876
909
  inputs.push({
877
- txid: base_1.hex.decode(bip322Input.txid),
878
- index: bip322Input.vout,
910
+ txid: base_1.hex.decode(input.txid),
911
+ index: input.vout,
879
912
  witnessUtxo: {
880
- amount: BigInt(bip322Input.value),
913
+ amount: BigInt(input.value),
881
914
  script: vtxoScript.pkScript,
882
915
  },
883
916
  sequence,
884
- tapLeafScript: [bip322Input.intentTapLeafScript],
917
+ tapLeafScript: [input.intentTapLeafScript],
918
+ unknown,
885
919
  });
886
- inputTapTrees.push(base_1.hex.encode(bip322Input.tapTree));
887
- inputExtraWitnesses.push(bip322Input.extraWitness || []);
888
920
  }
889
- return {
890
- inputs,
891
- inputTapTrees,
892
- finalizer: finalizeWithExtraWitnesses(inputExtraWitnesses),
893
- };
894
- }
895
- async makeBIP322Signature(message, inputs, finalizer, outputs) {
896
- const proof = bip322_1.BIP322.create(message, inputs, outputs);
897
- const signedProof = await this.identity.sign(proof);
898
- return bip322_1.BIP322.signature(signedProof, finalizer);
921
+ return inputs;
899
922
  }
900
923
  }
901
924
  exports.Wallet = Wallet;
902
925
  Wallet.MIN_FEE_RATE = 1; // sats/vbyte
903
- function finalizeWithExtraWitnesses(inputExtraWitnesses) {
904
- return function (tx) {
905
- for (let i = 0; i < tx.inputsLength; i++) {
906
- try {
907
- tx.finalizeIdx(i);
908
- }
909
- catch (e) {
910
- // handle empty witness error
911
- if (e instanceof Error &&
912
- e.message.includes("finalize/taproot: empty witness")) {
913
- const tapLeaves = tx.getInput(i).tapLeafScript;
914
- if (!tapLeaves || tapLeaves.length <= 0)
915
- throw e;
916
- const [cb, s] = tapLeaves[0];
917
- const script = s.slice(0, -1);
918
- tx.updateInput(i, {
919
- finalScriptWitness: [
920
- script,
921
- psbt_js_1.TaprootControlBlock.encode(cb),
922
- ],
923
- });
924
- }
925
- }
926
- const finalScriptWitness = tx.getInput(i).finalScriptWitness;
927
- if (!finalScriptWitness)
928
- throw new Error("input not finalized");
929
- // input 0 and 1 spend the same pkscript
930
- const extra = inputExtraWitnesses[i === 0 ? 0 : i - 1];
931
- if (extra && extra.length > 0) {
932
- tx.updateInput(i, {
933
- finalScriptWitness: [...extra, ...finalScriptWitness],
934
- });
935
- }
936
- }
937
- };
938
- }
939
- function getSequence(bip322Input) {
926
+ function getSequence(coin) {
940
927
  let sequence = undefined;
941
928
  try {
942
- const scriptWithLeafVersion = bip322Input.intentTapLeafScript[1];
929
+ const scriptWithLeafVersion = coin.intentTapLeafScript[1];
943
930
  const script = scriptWithLeafVersion.subarray(0, scriptWithLeafVersion.length - 1);
944
931
  const params = tapscript_1.CSVMultisigTapscript.decode(script).params;
945
932
  sequence = bip68.encode(params.timelock.type === "blocks"
@@ -0,0 +1,3 @@
1
+ // Expo adapter for React Native/Expo environments
2
+ export { ExpoArkProvider } from '../providers/expoArk.js';
3
+ export { ExpoIndexerProvider } from '../providers/expoIndexer.js';
@@ -1,7 +1,7 @@
1
1
  import { base58, hex } from "@scure/base";
2
- import { VtxoScript } from '../script/base.js';
3
2
  import { sha256 } from "@scure/btc-signer/utils.js";
4
- import { Script } from "@scure/btc-signer/script.js";
3
+ import { Script } from "@scure/btc-signer";
4
+ import { VtxoScript } from '../script/base.js';
5
5
  /**
6
6
  * ArkNotes are special virtual coins in the Ark protocol that can be created
7
7
  * and spent without requiring any transactions. The server mints them, and they
@@ -1,9 +1,12 @@
1
- import { Transaction } from "@scure/btc-signer/transaction.js";
1
+ import { Transaction } from "@scure/btc-signer";
2
2
  import { P2A } from './utils/anchor.js';
3
3
  export function buildForfeitTx(inputs, forfeitPkScript, txLocktime) {
4
4
  const tx = new Transaction({
5
5
  version: 3,
6
6
  lockTime: txLocktime,
7
+ allowUnknownOutputs: true,
8
+ allowUnknown: true,
9
+ allowUnknownInputs: true,
7
10
  });
8
11
  let amount = 0n;
9
12
  for (const input of inputs) {
@@ -1,8 +1,8 @@
1
- import { pubECDSA, pubSchnorr, randomPrivateKeyBytes, sha256, } from "@scure/btc-signer/utils.js";
2
- import { hex } from "@scure/base";
1
+ import { pubECDSA, pubSchnorr, randomPrivateKeyBytes, } from "@scure/btc-signer/utils.js";
3
2
  import { SigHash } from "@scure/btc-signer/transaction.js";
3
+ import { hex } from "@scure/base";
4
4
  import { TreeSignerSession } from '../tree/signingSession.js';
5
- import { schnorr } from "@noble/secp256k1";
5
+ import { schnorr, sign } from "@noble/secp256k1";
6
6
  const ZERO_32 = new Uint8Array(32).fill(0);
7
7
  const ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
8
8
  /**
@@ -79,8 +79,9 @@ export class SingleKey {
79
79
  signerSession() {
80
80
  return TreeSignerSession.random();
81
81
  }
82
- async signMessage(message) {
83
- const msgBytes = new TextEncoder().encode(message);
84
- return schnorr.sign(sha256(msgBytes), this.key);
82
+ async signMessage(message, signatureType = "schnorr") {
83
+ if (signatureType === "ecdsa")
84
+ return sign(message, this.key, { prehash: false });
85
+ return schnorr.sign(message, this.key);
85
86
  }
86
87
  }
package/dist/esm/index.js CHANGED
@@ -8,6 +8,7 @@ import { TxType, } from './wallet/index.js';
8
8
  import { Wallet, waitForIncomingFunds } from './wallet/wallet.js';
9
9
  import { TxTree } from './tree/txTree.js';
10
10
  import { Ramps } from './wallet/ramps.js';
11
+ import { VtxoManager } from './wallet/vtxo-manager.js';
11
12
  import { ServiceWorkerWallet } from './wallet/serviceWorker/wallet.js';
12
13
  import { OnchainWallet } from './wallet/onchain.js';
13
14
  import { setupServiceWorker } from './wallet/serviceWorker/utils.js';
@@ -17,9 +18,9 @@ import { Response } from './wallet/serviceWorker/response.js';
17
18
  import { ESPLORA_URL, EsploraProvider, } from './providers/onchain.js';
18
19
  import { RestArkProvider, SettlementEventType, } from './providers/ark.js';
19
20
  import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CSVMultisigTapscript, decodeTapscript, MultisigTapscript, } from './script/tapscript.js';
20
- import { buildOffchainTx, } from './utils/arkTransaction.js';
21
+ import { hasBoardingTxExpired, buildOffchainTx, verifyTapscriptSignatures, } from './utils/arkTransaction.js';
21
22
  import { VtxoTaprootTree, ConditionWitness, getArkPsbtFields, setArkPsbtField, ArkPsbtFieldKey, ArkPsbtFieldKeyType, CosignerPublicKey, VtxoTreeExpiry, } from './utils/unknownFields.js';
22
- import { BIP322 } from './bip322/index.js';
23
+ import { Intent } from './intent/index.js';
23
24
  import { ArkNote } from './arknote/index.js';
24
25
  import { networks } from './networks.js';
25
26
  import { RestIndexerProvider, IndexerTxType, ChainTxType, } from './providers/indexer.js';
@@ -29,7 +30,7 @@ import { WalletRepositoryImpl } from './repositories/walletRepository.js';
29
30
  import { ContractRepositoryImpl } from './repositories/contractRepository.js';
30
31
  export {
31
32
  // Wallets
32
- Wallet, SingleKey, OnchainWallet, Ramps,
33
+ Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager,
33
34
  // Providers
34
35
  ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider,
35
36
  // Script-related
@@ -43,15 +44,15 @@ decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTa
43
44
  // Ark PSBT fields
44
45
  ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness,
45
46
  // Utils
46
- buildOffchainTx, waitForIncomingFunds,
47
+ buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired,
47
48
  // Arknote
48
49
  ArkNote,
49
50
  // Network
50
51
  networks,
51
52
  // Repositories
52
53
  WalletRepositoryImpl, ContractRepositoryImpl,
53
- // BIP322
54
- BIP322,
54
+ // Intent proof
55
+ Intent,
55
56
  // TxTree
56
57
  TxTree,
57
58
  // Anchor