@arkade-os/sdk 0.3.1-alpha.7 → 0.3.2

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.
@@ -31,6 +31,7 @@ const deserializeTapLeaf = (t) => {
31
31
  };
32
32
  const deserializeVtxo = (o) => ({
33
33
  ...o,
34
+ createdAt: new Date(o.createdAt),
34
35
  tapTree: fromHex(o.tapTree),
35
36
  forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
36
37
  intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
@@ -112,7 +112,7 @@ function hasBoardingTxExpired(coin, boardingTimelock) {
112
112
  return false;
113
113
  if (boardingTimelock.value === 0n)
114
114
  return true;
115
- if (boardingTimelock.type !== "blocks")
115
+ if (boardingTimelock.type === "blocks")
116
116
  return false; // TODO: handle get chain tip
117
117
  // validate expiry in terms of seconds
118
118
  const now = BigInt(Math.floor(Date.now() / 1000));
@@ -69,6 +69,33 @@ class Worker {
69
69
  const address = await this.wallet.getBoardingAddress();
70
70
  return await this.walletRepository.getUtxos(address);
71
71
  }
72
+ async getTransactionHistory() {
73
+ if (!this.wallet)
74
+ return [];
75
+ let txs = [];
76
+ try {
77
+ const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
78
+ const { spendable, spent } = await this.getAllVtxos();
79
+ // convert VTXOs to offchain transactions
80
+ console.log("getTransactionHistory - vtxosToTxs:", spendable);
81
+ const offchainTxs = (0, transactionHistory_1.vtxosToTxs)(spendable, spent, roundsToIgnore);
82
+ txs = [...boardingTxs, ...offchainTxs];
83
+ // sort transactions by creation time in descending order (newest first)
84
+ txs.sort(
85
+ // place createdAt = 0 (unconfirmed txs) first, then descending
86
+ (a, b) => {
87
+ if (a.createdAt === 0)
88
+ return -1;
89
+ if (b.createdAt === 0)
90
+ return 1;
91
+ return b.createdAt - a.createdAt;
92
+ });
93
+ }
94
+ catch (error) {
95
+ console.error("Error getting transaction history:", error);
96
+ }
97
+ return txs;
98
+ }
72
99
  async start(withServiceWorkerUpdate = true) {
73
100
  self.addEventListener("message", async (event) => {
74
101
  await this.handleMessage(event);
@@ -116,7 +143,7 @@ class Worker {
116
143
  const address = await this.wallet.getAddress();
117
144
  await this.walletRepository.saveVtxos(address, vtxos);
118
145
  // Get transaction history to cache boarding txs
119
- const txs = await this.wallet.getTransactionHistory();
146
+ const txs = await this.getTransactionHistory();
120
147
  if (txs)
121
148
  await this.walletRepository.saveTransactions(address, txs);
122
149
  // unsubscribe previous subscription if any
@@ -139,19 +166,16 @@ class Worker {
139
166
  ...spentVtxos,
140
167
  ]);
141
168
  // notify all clients about the vtxo update
142
- this.sendMessageToAllClients(response_1.Response.vtxoUpdate(newVtxos, spentVtxos));
169
+ await this.sendMessageToAllClients(response_1.Response.vtxoUpdate(newVtxos, spentVtxos));
143
170
  }
144
171
  if (funds.type === "utxo") {
145
- const newUtxos = funds.coins.map((utxo) => (0, utils_1.extendCoin)(this.wallet, utxo));
146
- if (newUtxos.length === 0) {
147
- this.sendMessageToAllClients(response_1.Response.utxoUpdate([]));
148
- return;
149
- }
172
+ const utxos = funds.coins.map((utxo) => (0, utils_1.extendCoin)(this.wallet, utxo));
150
173
  const boardingAddress = await this.wallet?.getBoardingAddress();
151
174
  // save utxos using unified repository
152
- await this.walletRepository.saveUtxos(boardingAddress, newUtxos);
175
+ await this.walletRepository.clearUtxos(boardingAddress);
176
+ await this.walletRepository.saveUtxos(boardingAddress, utxos);
153
177
  // notify all clients about the utxo update
154
- this.sendMessageToAllClients(response_1.Response.utxoUpdate(funds.coins));
178
+ await this.sendMessageToAllClients(response_1.Response.utxoUpdate(utxos));
155
179
  }
156
180
  });
157
181
  }
@@ -437,21 +461,7 @@ class Worker {
437
461
  return;
438
462
  }
439
463
  try {
440
- const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
441
- const { spendable, spent } = await this.getAllVtxos();
442
- // convert VTXOs to offchain transactions
443
- const offchainTxs = (0, transactionHistory_1.vtxosToTxs)(spendable, spent, roundsToIgnore);
444
- const txs = [...boardingTxs, ...offchainTxs];
445
- // sort transactions by creation time in descending order (newest first)
446
- txs.sort(
447
- // place createdAt = 0 (unconfirmed txs) first, then descending
448
- (a, b) => {
449
- if (a.createdAt === 0)
450
- return -1;
451
- if (b.createdAt === 0)
452
- return 1;
453
- return b.createdAt - a.createdAt;
454
- });
464
+ const txs = await this.getTransactionHistory();
455
465
  event.source?.postMessage(response_1.Response.transactionHistory(message.id, txs));
456
466
  }
457
467
  catch (error) {
@@ -512,8 +512,8 @@ class Wallet {
512
512
  ...params.inputs.map((input) => `${input.txid}:${input.vout}`),
513
513
  ];
514
514
  const settlementStream = this.arkProvider.getEventStream(abortController.signal, topics);
515
- // roundId, sweepTapTreeRoot and forfeitOutputScript are set once the BatchStarted event is received
516
- let roundId;
515
+ // batchId, sweepTapTreeRoot and forfeitOutputScript are set once the BatchStarted event is received
516
+ let batchId;
517
517
  let sweepTapTreeRoot;
518
518
  const vtxoChunks = [];
519
519
  const connectorsChunks = [];
@@ -526,11 +526,7 @@ class Wallet {
526
526
  switch (event.type) {
527
527
  // the settlement failed
528
528
  case ark_1.SettlementEventType.BatchFailed:
529
- // fail if the roundId is the one joined
530
- if (event.id === roundId) {
531
- throw new Error(event.reason);
532
- }
533
- break;
529
+ throw new Error(event.reason);
534
530
  case ark_1.SettlementEventType.BatchStarted:
535
531
  if (step !== undefined) {
536
532
  continue;
@@ -539,7 +535,7 @@ class Wallet {
539
535
  if (!res.skip) {
540
536
  step = event.type;
541
537
  sweepTapTreeRoot = res.sweepTapTreeRoot;
542
- roundId = res.roundId;
538
+ batchId = res.roundId;
543
539
  if (!hasOffchainOutputs) {
544
540
  // if there are no offchain outputs, we don't have to handle musig2 tree signatures
545
541
  // we can directly advance to the finalization step
@@ -644,8 +640,10 @@ class Wallet {
644
640
  if (step !== ark_1.SettlementEventType.BatchFinalization) {
645
641
  continue;
646
642
  }
647
- abortController.abort();
648
- return event.commitmentTxid;
643
+ if (event.id === batchId) {
644
+ abortController.abort();
645
+ return event.commitmentTxid;
646
+ }
649
647
  }
650
648
  }
651
649
  }
@@ -28,6 +28,7 @@ const deserializeTapLeaf = (t) => {
28
28
  };
29
29
  const deserializeVtxo = (o) => ({
30
30
  ...o,
31
+ createdAt: new Date(o.createdAt),
31
32
  tapTree: fromHex(o.tapTree),
32
33
  forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
33
34
  intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
@@ -107,7 +107,7 @@ export function hasBoardingTxExpired(coin, boardingTimelock) {
107
107
  return false;
108
108
  if (boardingTimelock.value === 0n)
109
109
  return true;
110
- if (boardingTimelock.type !== "blocks")
110
+ if (boardingTimelock.type === "blocks")
111
111
  return false; // TODO: handle get chain tip
112
112
  // validate expiry in terms of seconds
113
113
  const now = BigInt(Math.floor(Date.now() / 1000));
@@ -66,6 +66,33 @@ export class Worker {
66
66
  const address = await this.wallet.getBoardingAddress();
67
67
  return await this.walletRepository.getUtxos(address);
68
68
  }
69
+ async getTransactionHistory() {
70
+ if (!this.wallet)
71
+ return [];
72
+ let txs = [];
73
+ try {
74
+ const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
75
+ const { spendable, spent } = await this.getAllVtxos();
76
+ // convert VTXOs to offchain transactions
77
+ console.log("getTransactionHistory - vtxosToTxs:", spendable);
78
+ const offchainTxs = vtxosToTxs(spendable, spent, roundsToIgnore);
79
+ txs = [...boardingTxs, ...offchainTxs];
80
+ // sort transactions by creation time in descending order (newest first)
81
+ txs.sort(
82
+ // place createdAt = 0 (unconfirmed txs) first, then descending
83
+ (a, b) => {
84
+ if (a.createdAt === 0)
85
+ return -1;
86
+ if (b.createdAt === 0)
87
+ return 1;
88
+ return b.createdAt - a.createdAt;
89
+ });
90
+ }
91
+ catch (error) {
92
+ console.error("Error getting transaction history:", error);
93
+ }
94
+ return txs;
95
+ }
69
96
  async start(withServiceWorkerUpdate = true) {
70
97
  self.addEventListener("message", async (event) => {
71
98
  await this.handleMessage(event);
@@ -113,7 +140,7 @@ export class Worker {
113
140
  const address = await this.wallet.getAddress();
114
141
  await this.walletRepository.saveVtxos(address, vtxos);
115
142
  // Get transaction history to cache boarding txs
116
- const txs = await this.wallet.getTransactionHistory();
143
+ const txs = await this.getTransactionHistory();
117
144
  if (txs)
118
145
  await this.walletRepository.saveTransactions(address, txs);
119
146
  // unsubscribe previous subscription if any
@@ -136,19 +163,16 @@ export class Worker {
136
163
  ...spentVtxos,
137
164
  ]);
138
165
  // notify all clients about the vtxo update
139
- this.sendMessageToAllClients(Response.vtxoUpdate(newVtxos, spentVtxos));
166
+ await this.sendMessageToAllClients(Response.vtxoUpdate(newVtxos, spentVtxos));
140
167
  }
141
168
  if (funds.type === "utxo") {
142
- const newUtxos = funds.coins.map((utxo) => extendCoin(this.wallet, utxo));
143
- if (newUtxos.length === 0) {
144
- this.sendMessageToAllClients(Response.utxoUpdate([]));
145
- return;
146
- }
169
+ const utxos = funds.coins.map((utxo) => extendCoin(this.wallet, utxo));
147
170
  const boardingAddress = await this.wallet?.getBoardingAddress();
148
171
  // save utxos using unified repository
149
- await this.walletRepository.saveUtxos(boardingAddress, newUtxos);
172
+ await this.walletRepository.clearUtxos(boardingAddress);
173
+ await this.walletRepository.saveUtxos(boardingAddress, utxos);
150
174
  // notify all clients about the utxo update
151
- this.sendMessageToAllClients(Response.utxoUpdate(funds.coins));
175
+ await this.sendMessageToAllClients(Response.utxoUpdate(utxos));
152
176
  }
153
177
  });
154
178
  }
@@ -434,21 +458,7 @@ export class Worker {
434
458
  return;
435
459
  }
436
460
  try {
437
- const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
438
- const { spendable, spent } = await this.getAllVtxos();
439
- // convert VTXOs to offchain transactions
440
- const offchainTxs = vtxosToTxs(spendable, spent, roundsToIgnore);
441
- const txs = [...boardingTxs, ...offchainTxs];
442
- // sort transactions by creation time in descending order (newest first)
443
- txs.sort(
444
- // place createdAt = 0 (unconfirmed txs) first, then descending
445
- (a, b) => {
446
- if (a.createdAt === 0)
447
- return -1;
448
- if (b.createdAt === 0)
449
- return 1;
450
- return b.createdAt - a.createdAt;
451
- });
461
+ const txs = await this.getTransactionHistory();
452
462
  event.source?.postMessage(Response.transactionHistory(message.id, txs));
453
463
  }
454
464
  catch (error) {
@@ -475,8 +475,8 @@ export class Wallet {
475
475
  ...params.inputs.map((input) => `${input.txid}:${input.vout}`),
476
476
  ];
477
477
  const settlementStream = this.arkProvider.getEventStream(abortController.signal, topics);
478
- // roundId, sweepTapTreeRoot and forfeitOutputScript are set once the BatchStarted event is received
479
- let roundId;
478
+ // batchId, sweepTapTreeRoot and forfeitOutputScript are set once the BatchStarted event is received
479
+ let batchId;
480
480
  let sweepTapTreeRoot;
481
481
  const vtxoChunks = [];
482
482
  const connectorsChunks = [];
@@ -489,11 +489,7 @@ export class Wallet {
489
489
  switch (event.type) {
490
490
  // the settlement failed
491
491
  case SettlementEventType.BatchFailed:
492
- // fail if the roundId is the one joined
493
- if (event.id === roundId) {
494
- throw new Error(event.reason);
495
- }
496
- break;
492
+ throw new Error(event.reason);
497
493
  case SettlementEventType.BatchStarted:
498
494
  if (step !== undefined) {
499
495
  continue;
@@ -502,7 +498,7 @@ export class Wallet {
502
498
  if (!res.skip) {
503
499
  step = event.type;
504
500
  sweepTapTreeRoot = res.sweepTapTreeRoot;
505
- roundId = res.roundId;
501
+ batchId = res.roundId;
506
502
  if (!hasOffchainOutputs) {
507
503
  // if there are no offchain outputs, we don't have to handle musig2 tree signatures
508
504
  // we can directly advance to the finalization step
@@ -607,8 +603,10 @@ export class Wallet {
607
603
  if (step !== SettlementEventType.BatchFinalization) {
608
604
  continue;
609
605
  }
610
- abortController.abort();
611
- return event.commitmentTxid;
606
+ if (event.id === batchId) {
607
+ abortController.abort();
608
+ return event.commitmentTxid;
609
+ }
612
610
  }
613
611
  }
614
612
  }
@@ -1,4 +1,4 @@
1
- import { WalletBalance, VirtualCoin, ArkTransaction, IWallet, Coin } from "..";
1
+ import { WalletBalance, VirtualCoin, ArkTransaction, IWallet, ExtendedCoin } from "..";
2
2
  import { ExtendedVirtualCoin } from "../..";
3
3
  import { SettlementEvent } from "../../providers/ark";
4
4
  /**
@@ -116,8 +116,8 @@ export declare namespace Response {
116
116
  function vtxoUpdate(newVtxos: ExtendedVirtualCoin[], spentVtxos: ExtendedVirtualCoin[]): VtxoUpdate;
117
117
  interface UtxoUpdate extends Base {
118
118
  type: "UTXO_UPDATE";
119
- coins: Coin[];
119
+ coins: ExtendedCoin[];
120
120
  }
121
121
  function isUtxoUpdate(response: Base): response is UtxoUpdate;
122
- function utxoUpdate(coins: Coin[]): UtxoUpdate;
122
+ function utxoUpdate(coins: ExtendedCoin[]): UtxoUpdate;
123
123
  }
@@ -29,6 +29,7 @@ export declare class Worker {
29
29
  * Get all boarding utxos from wallet repository
30
30
  */
31
31
  private getAllBoardingUtxos;
32
+ private getTransactionHistory;
32
33
  start(withServiceWorkerUpdate?: boolean): Promise<void>;
33
34
  clear(): Promise<void>;
34
35
  reload(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.3.1-alpha.7",
3
+ "version": "0.3.2",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",