@arkade-os/sdk 0.3.8 → 0.3.10

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 (41) hide show
  1. package/README.md +78 -1
  2. package/dist/cjs/identity/singleKey.js +33 -1
  3. package/dist/cjs/index.js +17 -2
  4. package/dist/cjs/intent/index.js +31 -2
  5. package/dist/cjs/providers/ark.js +15 -5
  6. package/dist/cjs/providers/indexer.js +2 -2
  7. package/dist/cjs/wallet/batch.js +183 -0
  8. package/dist/cjs/wallet/index.js +15 -0
  9. package/dist/cjs/wallet/serviceWorker/request.js +0 -2
  10. package/dist/cjs/wallet/serviceWorker/wallet.js +98 -34
  11. package/dist/cjs/wallet/serviceWorker/worker.js +163 -72
  12. package/dist/cjs/wallet/utils.js +2 -2
  13. package/dist/cjs/wallet/vtxo-manager.js +5 -0
  14. package/dist/cjs/wallet/wallet.js +358 -360
  15. package/dist/esm/identity/singleKey.js +31 -0
  16. package/dist/esm/index.js +12 -7
  17. package/dist/esm/intent/index.js +31 -2
  18. package/dist/esm/providers/ark.js +15 -5
  19. package/dist/esm/providers/indexer.js +2 -2
  20. package/dist/esm/wallet/batch.js +180 -0
  21. package/dist/esm/wallet/index.js +14 -0
  22. package/dist/esm/wallet/serviceWorker/request.js +0 -2
  23. package/dist/esm/wallet/serviceWorker/wallet.js +96 -33
  24. package/dist/esm/wallet/serviceWorker/worker.js +165 -74
  25. package/dist/esm/wallet/utils.js +2 -2
  26. package/dist/esm/wallet/vtxo-manager.js +6 -1
  27. package/dist/esm/wallet/wallet.js +359 -363
  28. package/dist/types/identity/index.d.ts +5 -3
  29. package/dist/types/identity/singleKey.d.ts +20 -1
  30. package/dist/types/index.d.ts +11 -8
  31. package/dist/types/intent/index.d.ts +19 -2
  32. package/dist/types/providers/ark.d.ts +9 -8
  33. package/dist/types/providers/indexer.d.ts +2 -2
  34. package/dist/types/wallet/batch.d.ts +87 -0
  35. package/dist/types/wallet/index.d.ts +76 -16
  36. package/dist/types/wallet/serviceWorker/request.d.ts +5 -1
  37. package/dist/types/wallet/serviceWorker/wallet.d.ts +46 -15
  38. package/dist/types/wallet/serviceWorker/worker.d.ts +6 -3
  39. package/dist/types/wallet/utils.d.ts +8 -3
  40. package/dist/types/wallet/wallet.d.ts +87 -36
  41. package/package.json +1 -1
package/README.md CHANGED
@@ -25,13 +25,90 @@ const identity = SingleKey.fromHex('your_private_key_hex')
25
25
  const wallet = await Wallet.create({
26
26
  identity,
27
27
  // Esplora API, can be left empty - mempool.space API will be used
28
- esploraUrl: 'https://mutinynet.com/api',
28
+ esploraUrl: 'https://mutinynet.com/api',
29
29
  arkServerUrl: 'https://mutinynet.arkade.sh',
30
30
  // Optional: specify storage adapter (defaults to InMemoryStorageAdapter)
31
31
  // storage: new LocalStorageAdapter() // for browser persistence
32
32
  })
33
33
  ```
34
34
 
35
+ ### Readonly Wallets (Watch-Only)
36
+
37
+ The SDK supports readonly wallets that allow you to query wallet state without exposing private keys. This is useful for:
38
+
39
+ - **Watch-only wallets**: Monitor addresses and balances without transaction capabilities
40
+ - **Public interfaces**: Display wallet information safely in public-facing applications
41
+ - **Separate concerns**: Keep signing operations isolated from query operations
42
+
43
+ #### Creating a Readonly Wallet
44
+
45
+ ```typescript
46
+ import { ReadonlySingleKey, ReadonlyWallet } from '@arkade-os/sdk'
47
+
48
+ // Create a readonly identity from a public key
49
+ const identity = SingleKey.fromHex('your_public_key_hex')
50
+ const publicKey = await identity.compressedPublicKey()
51
+ const readonlyIdentity = ReadonlySingleKey.fromPublicKey(publicKey)
52
+
53
+ // Create a readonly wallet
54
+ const readonlyWallet = await ReadonlyWallet.create({
55
+ identity: readonlyIdentity,
56
+ arkServerUrl: 'https://mutinynet.arkade.sh'
57
+ })
58
+
59
+ // Query operations work normally
60
+ const address = await readonlyWallet.getAddress()
61
+ const balance = await readonlyWallet.getBalance()
62
+ const vtxos = await readonlyWallet.getVtxos()
63
+ const history = await readonlyWallet.getTransactionHistory()
64
+
65
+ // Transaction methods are not available (TypeScript will prevent this)
66
+ // await readonlyWallet.sendBitcoin(...) // ❌ Type error!
67
+ ```
68
+
69
+ #### Converting Wallets to Readonly
70
+
71
+ ```typescript
72
+ import { Wallet, SingleKey } from '@arkade-os/sdk'
73
+
74
+ // Create a full wallet
75
+ const identity = SingleKey.fromHex('your_private_key_hex')
76
+ const wallet = await Wallet.create({
77
+ identity,
78
+ arkServerUrl: 'https://mutinynet.arkade.sh'
79
+ })
80
+
81
+ // Convert to readonly wallet (safe to share)
82
+ const readonlyWallet = await wallet.toReadonly()
83
+
84
+ // The readonly wallet can query but not transact
85
+ const balance = await readonlyWallet.getBalance()
86
+ ```
87
+
88
+ #### Converting Identity to Readonly
89
+
90
+ ```typescript
91
+ import { SingleKey } from '@arkade-os/sdk'
92
+
93
+ // Full identity
94
+ const identity = SingleKey.fromHex('your_private_key_hex')
95
+
96
+ // Convert to readonly (no signing capability)
97
+ const readonlyIdentity = await identity.toReadonly()
98
+
99
+ // Use in readonly wallet
100
+ const readonlyWallet = await ReadonlyWallet.create({
101
+ identity: readonlyIdentity,
102
+ arkServerUrl: 'https://mutinynet.arkade.sh'
103
+ })
104
+ ```
105
+
106
+ **Benefits:**
107
+ - ✅ Type-safe: Transaction methods don't exist on readonly types
108
+ - ✅ Secure: Private keys never leave the signing environment
109
+ - ✅ Flexible: Convert between full and readonly wallets as needed
110
+ - ✅ Same API: Query operations work identically on both wallet types
111
+
35
112
  ### Receiving Bitcoin
36
113
 
37
114
  ```typescript
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SingleKey = void 0;
3
+ exports.ReadonlySingleKey = exports.SingleKey = void 0;
4
4
  const utils_js_1 = require("@scure/btc-signer/utils.js");
5
5
  const btc_signer_1 = require("@scure/btc-signer");
6
6
  const base_1 = require("@scure/base");
@@ -86,5 +86,37 @@ class SingleKey {
86
86
  return (0, secp256k1_1.signAsync)(message, this.key, { prehash: false });
87
87
  return secp256k1_1.schnorr.signAsync(message, this.key);
88
88
  }
89
+ async toReadonly() {
90
+ return new ReadonlySingleKey(await this.compressedPublicKey());
91
+ }
89
92
  }
90
93
  exports.SingleKey = SingleKey;
94
+ class ReadonlySingleKey {
95
+ constructor(publicKey) {
96
+ this.publicKey = publicKey;
97
+ if (publicKey.length !== 33) {
98
+ throw new Error("Invalid public key length");
99
+ }
100
+ }
101
+ /**
102
+ * Create a ReadonlySingleKey from a compressed public key.
103
+ *
104
+ * @param publicKey - 33-byte compressed public key (02/03 prefix + 32-byte x coordinate)
105
+ * @returns A new ReadonlySingleKey instance
106
+ * @example
107
+ * ```typescript
108
+ * const pubkey = new Uint8Array(33); // your compressed public key
109
+ * const readonlyKey = ReadonlySingleKey.fromPublicKey(pubkey);
110
+ * ```
111
+ */
112
+ static fromPublicKey(publicKey) {
113
+ return new ReadonlySingleKey(publicKey);
114
+ }
115
+ xOnlyPublicKey() {
116
+ return Promise.resolve(this.publicKey.slice(1));
117
+ }
118
+ compressedPublicKey() {
119
+ return Promise.resolve(this.publicKey);
120
+ }
121
+ }
122
+ exports.ReadonlySingleKey = ReadonlySingleKey;
package/dist/cjs/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.P2A = exports.TxTree = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
4
- exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = void 0;
3
+ exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerReadonlyWallet = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.ReadonlySingleKey = exports.SingleKey = exports.ReadonlyWallet = exports.Wallet = void 0;
4
+ exports.getSequence = exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.Intent = void 0;
5
5
  const transaction_1 = require("./utils/transaction");
6
6
  Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } });
7
7
  const singleKey_1 = require("./identity/singleKey");
8
8
  Object.defineProperty(exports, "SingleKey", { enumerable: true, get: function () { return singleKey_1.SingleKey; } });
9
+ Object.defineProperty(exports, "ReadonlySingleKey", { enumerable: true, get: function () { return singleKey_1.ReadonlySingleKey; } });
9
10
  const address_1 = require("./script/address");
10
11
  Object.defineProperty(exports, "ArkAddress", { enumerable: true, get: function () { return address_1.ArkAddress; } });
11
12
  const vhtlc_1 = require("./script/vhtlc");
@@ -17,9 +18,17 @@ Object.defineProperty(exports, "VtxoScript", { enumerable: true, get: function (
17
18
  Object.defineProperty(exports, "TapTreeCoder", { enumerable: true, get: function () { return base_1.TapTreeCoder; } });
18
19
  const wallet_1 = require("./wallet");
19
20
  Object.defineProperty(exports, "TxType", { enumerable: true, get: function () { return wallet_1.TxType; } });
21
+ Object.defineProperty(exports, "isSpendable", { enumerable: true, get: function () { return wallet_1.isSpendable; } });
22
+ Object.defineProperty(exports, "isSubdust", { enumerable: true, get: function () { return wallet_1.isSubdust; } });
23
+ Object.defineProperty(exports, "isRecoverable", { enumerable: true, get: function () { return wallet_1.isRecoverable; } });
24
+ Object.defineProperty(exports, "isExpired", { enumerable: true, get: function () { return wallet_1.isExpired; } });
25
+ const batch_1 = require("./wallet/batch");
26
+ Object.defineProperty(exports, "Batch", { enumerable: true, get: function () { return batch_1.Batch; } });
20
27
  const wallet_2 = require("./wallet/wallet");
21
28
  Object.defineProperty(exports, "Wallet", { enumerable: true, get: function () { return wallet_2.Wallet; } });
29
+ Object.defineProperty(exports, "ReadonlyWallet", { enumerable: true, get: function () { return wallet_2.ReadonlyWallet; } });
22
30
  Object.defineProperty(exports, "waitForIncomingFunds", { enumerable: true, get: function () { return wallet_2.waitForIncomingFunds; } });
31
+ Object.defineProperty(exports, "getSequence", { enumerable: true, get: function () { return wallet_2.getSequence; } });
23
32
  const txTree_1 = require("./tree/txTree");
24
33
  Object.defineProperty(exports, "TxTree", { enumerable: true, get: function () { return txTree_1.TxTree; } });
25
34
  const ramps_1 = require("./wallet/ramps");
@@ -29,6 +38,7 @@ Object.defineProperty(exports, "isVtxoExpiringSoon", { enumerable: true, get: fu
29
38
  Object.defineProperty(exports, "VtxoManager", { enumerable: true, get: function () { return vtxo_manager_1.VtxoManager; } });
30
39
  const wallet_3 = require("./wallet/serviceWorker/wallet");
31
40
  Object.defineProperty(exports, "ServiceWorkerWallet", { enumerable: true, get: function () { return wallet_3.ServiceWorkerWallet; } });
41
+ Object.defineProperty(exports, "ServiceWorkerReadonlyWallet", { enumerable: true, get: function () { return wallet_3.ServiceWorkerReadonlyWallet; } });
32
42
  const onchain_1 = require("./wallet/onchain");
33
43
  Object.defineProperty(exports, "OnchainWallet", { enumerable: true, get: function () { return onchain_1.OnchainWallet; } });
34
44
  const utils_1 = require("./wallet/serviceWorker/utils");
@@ -87,3 +97,8 @@ Object.defineProperty(exports, "ContractRepositoryImpl", { enumerable: true, get
87
97
  const errors_1 = require("./providers/errors");
88
98
  Object.defineProperty(exports, "ArkError", { enumerable: true, get: function () { return errors_1.ArkError; } });
89
99
  Object.defineProperty(exports, "maybeArkError", { enumerable: true, get: function () { return errors_1.maybeArkError; } });
100
+ const validation_1 = require("./tree/validation");
101
+ Object.defineProperty(exports, "validateVtxoTxGraph", { enumerable: true, get: function () { return validation_1.validateVtxoTxGraph; } });
102
+ Object.defineProperty(exports, "validateConnectorsTxGraph", { enumerable: true, get: function () { return validation_1.validateConnectorsTxGraph; } });
103
+ const forfeit_1 = require("./forfeit");
104
+ Object.defineProperty(exports, "buildForfeitTx", { enumerable: true, get: function () { return forfeit_1.buildForfeitTx; } });
@@ -36,12 +36,15 @@ var Intent;
36
36
  * ownership of VTXOs and UTXOs. The proof includes the message to be
37
37
  * signed and the inputs/outputs that demonstrate ownership.
38
38
  *
39
- * @param message - The Intent message to be signed
39
+ * @param message - The Intent message to be signed, either raw string of Message object
40
40
  * @param inputs - Array of transaction inputs to prove ownership of
41
41
  * @param outputs - Optional array of transaction outputs
42
42
  * @returns An unsigned Intent proof transaction
43
43
  */
44
44
  function create(message, inputs, outputs = []) {
45
+ if (typeof message !== "string") {
46
+ message = encodeMessage(message);
47
+ }
45
48
  if (inputs.length == 0)
46
49
  throw new Error("intent proof requires at least one input");
47
50
  if (!validateInputs(inputs))
@@ -54,6 +57,29 @@ var Intent;
54
57
  return craftToSignTx(toSpend, inputs, outputs);
55
58
  }
56
59
  Intent.create = create;
60
+ function encodeMessage(message) {
61
+ switch (message.type) {
62
+ case "register":
63
+ return JSON.stringify({
64
+ type: "register",
65
+ onchain_output_indexes: message.onchain_output_indexes,
66
+ valid_at: message.valid_at,
67
+ expire_at: message.expire_at,
68
+ cosigners_public_keys: message.cosigners_public_keys,
69
+ });
70
+ case "delete":
71
+ return JSON.stringify({
72
+ type: "delete",
73
+ expire_at: message.expire_at,
74
+ });
75
+ case "get-pending-tx":
76
+ return JSON.stringify({
77
+ type: "get-pending-tx",
78
+ expire_at: message.expire_at,
79
+ });
80
+ }
81
+ }
82
+ Intent.encodeMessage = encodeMessage;
57
83
  })(Intent || (exports.Intent = Intent = {}));
58
84
  const OP_RETURN_EMPTY_PKSCRIPT = new Uint8Array([btc_signer_1.OP.RETURN]);
59
85
  const ZERO_32 = new Uint8Array(32).fill(0);
@@ -108,9 +134,12 @@ function craftToSpendTx(message, pkScript) {
108
134
  // craftToSignTx creates the transaction that will be signed for the proof
109
135
  function craftToSignTx(toSpend, inputs, outputs) {
110
136
  const firstInput = inputs[0];
137
+ const lockTime = inputs
138
+ .map((input) => input.sequence || 0)
139
+ .reduce((a, b) => Math.max(a, b), 0);
111
140
  const tx = new transaction_1.Transaction({
112
141
  version: 2,
113
- lockTime: 0,
142
+ lockTime,
114
143
  });
115
144
  // add the first "toSpend" input
116
145
  tx.addInput({
@@ -5,6 +5,7 @@ exports.isFetchTimeoutError = isFetchTimeoutError;
5
5
  const base_1 = require("@scure/base");
6
6
  const utils_1 = require("./utils");
7
7
  const errors_1 = require("./errors");
8
+ const intent_1 = require("../intent");
8
9
  var SettlementEventType;
9
10
  (function (SettlementEventType) {
10
11
  SettlementEventType["BatchStarted"] = "batch_started";
@@ -49,8 +50,12 @@ class RestArkProvider {
49
50
  fees: {
50
51
  intentFee: {
51
52
  ...fromServer.fees?.intentFee,
52
- onchainInput: BigInt(fromServer.fees?.intentFee?.onchainInput ?? 0),
53
- onchainOutput: BigInt(fromServer.fees?.intentFee?.onchainOutput ?? 0),
53
+ onchainInput: BigInt(
54
+ // split(".")[0] to remove the decimal part
55
+ (fromServer.fees?.intentFee?.onchainInput ?? "0").split(".")[0] ?? 0),
56
+ onchainOutput: BigInt(
57
+ // split(".")[0] to remove the decimal part
58
+ (fromServer.fees?.intentFee?.onchainOutput ?? "0").split(".")[0] ?? 0),
54
59
  },
55
60
  txFeeRate: fromServer?.fees?.txFeeRate ?? "",
56
61
  },
@@ -127,7 +132,7 @@ class RestArkProvider {
127
132
  body: JSON.stringify({
128
133
  intent: {
129
134
  proof: intent.proof,
130
- message: intent.message,
135
+ message: intent_1.Intent.encodeMessage(intent.message),
131
136
  },
132
137
  }),
133
138
  });
@@ -148,7 +153,7 @@ class RestArkProvider {
148
153
  body: JSON.stringify({
149
154
  intent: {
150
155
  proof: intent.proof,
151
- message: intent.message,
156
+ message: intent_1.Intent.encodeMessage(intent.message),
152
157
  },
153
158
  }),
154
159
  });
@@ -328,7 +333,12 @@ class RestArkProvider {
328
333
  headers: {
329
334
  "Content-Type": "application/json",
330
335
  },
331
- body: JSON.stringify({ intent }),
336
+ body: JSON.stringify({
337
+ intent: {
338
+ proof: intent.proof,
339
+ message: intent_1.Intent.encodeMessage(intent.message),
340
+ },
341
+ }),
332
342
  });
333
343
  if (!response.ok) {
334
344
  const errorText = await response.text();
@@ -365,7 +365,7 @@ function convertVtxo(vtxo) {
365
365
  // Unexported namespace for type guards only
366
366
  var Response;
367
367
  (function (Response) {
368
- function isBatch(data) {
368
+ function isBatchInfo(data) {
369
369
  return (typeof data === "object" &&
370
370
  typeof data.totalOutputAmount === "string" &&
371
371
  typeof data.totalOutputVtxos === "number" &&
@@ -389,7 +389,7 @@ var Response;
389
389
  typeof data.totalOutputAmount === "string" &&
390
390
  typeof data.totalOutputVtxos === "number" &&
391
391
  typeof data.batches === "object" &&
392
- Object.values(data.batches).every(isBatch));
392
+ Object.values(data.batches).every(isBatchInfo));
393
393
  }
394
394
  Response.isCommitmentTx = isCommitmentTx;
395
395
  function isOutpoint(data) {
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Batch = void 0;
4
+ const ark_1 = require("../providers/ark");
5
+ const txTree_1 = require("../tree/txTree");
6
+ const base_1 = require("@scure/base");
7
+ /**
8
+ * Batch namespace provides utilities for joining and processing batch session.
9
+ * The batch settlement process involves multiple events, this namespace provides abstractions and types to handle them.
10
+ * @see https://docs.arkadeos.com/learn/pillars/batch-swaps
11
+ * @example
12
+ * ```typescript
13
+ * // use wallet handler or create a custom one
14
+ * const handler = wallet.createBatchHandler(intentId, inputs, musig2session);
15
+ *
16
+ * const abortController = new AbortController();
17
+ * // Get event stream from Ark provider
18
+ * const eventStream = arkProvider.getEventStream(
19
+ * abortController.signal,
20
+ * ['your-topic-1', 'your-topic-2']
21
+ * );
22
+ *
23
+ * // Join the batch and process events
24
+ * try {
25
+ * const commitmentTxid = await Batch.join(eventStream, handler);
26
+ * console.log('Batch completed with commitment:', commitmentTxid);
27
+ * } catch (error) {
28
+ * console.error('Batch processing failed:', error);
29
+ * } finally {
30
+ * abortController.abort();
31
+ * }
32
+ * ```
33
+ */
34
+ var Batch;
35
+ (function (Batch) {
36
+ // State machine steps for batch session
37
+ let Step;
38
+ (function (Step) {
39
+ Step["Start"] = "start";
40
+ Step["BatchStarted"] = "batch_started";
41
+ Step["TreeSigningStarted"] = "tree_signing_started";
42
+ Step["TreeNoncesAggregated"] = "tree_nonces_aggregated";
43
+ Step["BatchFinalization"] = "batch_finalization";
44
+ })(Step || (Step = {}));
45
+ /**
46
+ * Start the state machine that will process the batch events and join a batch.
47
+ * @param eventIterator - The events stream to process.
48
+ * @param handler - How to react to events.
49
+ * @param options - Options.
50
+ */
51
+ async function join(eventIterator, handler, options = {}) {
52
+ const { abortController, skipVtxoTreeSigning = false, eventCallback, } = options;
53
+ let step = Step.Start;
54
+ // keep track of tree transactions as they arrive
55
+ const flatVtxoTree = [];
56
+ const flatConnectorTree = [];
57
+ // once everything is collected, the TxTree objects are created
58
+ let vtxoTree = undefined;
59
+ let connectorTree = undefined;
60
+ for await (const event of eventIterator) {
61
+ if (abortController?.signal.aborted) {
62
+ throw new Error("canceled");
63
+ }
64
+ if (eventCallback) {
65
+ // don't wait for the callback to complete and ignore errors
66
+ eventCallback(event).catch(() => { });
67
+ }
68
+ switch (event.type) {
69
+ case ark_1.SettlementEventType.BatchStarted: {
70
+ const e = event;
71
+ const { skip } = await handler.onBatchStarted(e);
72
+ if (!skip) {
73
+ step = Step.BatchStarted;
74
+ if (skipVtxoTreeSigning) {
75
+ // skip TxTree events and musig2 signatures and nonces
76
+ step = Step.TreeNoncesAggregated;
77
+ }
78
+ }
79
+ continue;
80
+ }
81
+ case ark_1.SettlementEventType.BatchFinalized: {
82
+ if (step !== Step.BatchFinalization) {
83
+ continue;
84
+ }
85
+ if (handler.onBatchFinalized) {
86
+ await handler.onBatchFinalized(event);
87
+ }
88
+ return event.commitmentTxid;
89
+ }
90
+ case ark_1.SettlementEventType.BatchFailed: {
91
+ if (handler.onBatchFailed) {
92
+ await handler.onBatchFailed(event);
93
+ continue;
94
+ }
95
+ throw new Error(event.reason);
96
+ }
97
+ case ark_1.SettlementEventType.TreeTx: {
98
+ if (step !== Step.BatchStarted &&
99
+ step !== Step.TreeNoncesAggregated) {
100
+ continue;
101
+ }
102
+ // batchIndex 0 = vtxo tree, batchIndex 1 = connector tree
103
+ if (event.batchIndex === 0) {
104
+ flatVtxoTree.push(event.chunk);
105
+ }
106
+ else {
107
+ flatConnectorTree.push(event.chunk);
108
+ }
109
+ if (handler.onTreeTxEvent) {
110
+ await handler.onTreeTxEvent(event);
111
+ }
112
+ continue;
113
+ }
114
+ case ark_1.SettlementEventType.TreeSignature: {
115
+ if (step !== Step.TreeNoncesAggregated) {
116
+ continue;
117
+ }
118
+ if (!vtxoTree) {
119
+ throw new Error("vtxo tree not initialized");
120
+ }
121
+ // push signature to the vtxo tree
122
+ const tapKeySig = base_1.hex.decode(event.signature);
123
+ vtxoTree.update(event.txid, (tx) => {
124
+ tx.updateInput(0, {
125
+ tapKeySig,
126
+ });
127
+ });
128
+ if (handler.onTreeSignatureEvent) {
129
+ await handler.onTreeSignatureEvent(event);
130
+ }
131
+ continue;
132
+ }
133
+ case ark_1.SettlementEventType.TreeSigningStarted: {
134
+ if (step !== Step.BatchStarted) {
135
+ continue;
136
+ }
137
+ // create vtxo tree from collected chunks
138
+ vtxoTree = txTree_1.TxTree.create(flatVtxoTree);
139
+ const { skip } = await handler.onTreeSigningStarted(event, vtxoTree);
140
+ if (!skip) {
141
+ step = Step.TreeSigningStarted;
142
+ }
143
+ continue;
144
+ }
145
+ case ark_1.SettlementEventType.TreeNonces: {
146
+ if (step !== Step.TreeSigningStarted) {
147
+ continue;
148
+ }
149
+ const { fullySigned } = await handler.onTreeNonces(event);
150
+ if (fullySigned) {
151
+ step = Step.TreeNoncesAggregated;
152
+ }
153
+ continue;
154
+ }
155
+ case ark_1.SettlementEventType.BatchFinalization: {
156
+ if (step !== Step.TreeNoncesAggregated) {
157
+ continue;
158
+ }
159
+ // Build vtxo tree if it hasn't been built yet
160
+ if (!vtxoTree && flatVtxoTree.length > 0) {
161
+ vtxoTree = txTree_1.TxTree.create(flatVtxoTree);
162
+ }
163
+ if (!vtxoTree && !skipVtxoTreeSigning) {
164
+ throw new Error("vtxo tree not initialized");
165
+ }
166
+ // Build connector tree if we have chunks
167
+ if (flatConnectorTree.length > 0) {
168
+ connectorTree = txTree_1.TxTree.create(flatConnectorTree);
169
+ }
170
+ await handler.onBatchFinalization(event, vtxoTree, connectorTree);
171
+ step = Step.BatchFinalization;
172
+ continue;
173
+ }
174
+ default:
175
+ // unknown event type, continue
176
+ continue;
177
+ }
178
+ }
179
+ // iterator closed without finalization, something went wrong
180
+ throw new Error("event stream closed");
181
+ }
182
+ Batch.join = join;
183
+ })(Batch || (exports.Batch = Batch = {}));
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TxType = void 0;
4
4
  exports.isSpendable = isSpendable;
5
5
  exports.isRecoverable = isRecoverable;
6
+ exports.isExpired = isExpired;
6
7
  exports.isSubdust = isSubdust;
7
8
  var TxType;
8
9
  (function (TxType) {
@@ -15,6 +16,20 @@ function isSpendable(vtxo) {
15
16
  function isRecoverable(vtxo) {
16
17
  return vtxo.virtualStatus.state === "swept" && isSpendable(vtxo);
17
18
  }
19
+ function isExpired(vtxo) {
20
+ if (vtxo.virtualStatus.state === "swept")
21
+ return true; // swept by server = expired
22
+ const expiry = vtxo.virtualStatus.batchExpiry;
23
+ if (!expiry)
24
+ return false;
25
+ // we use this as a workaround to avoid issue on regtest where expiry date is expressed in blockheight instead of timestamp
26
+ // if expiry, as Date, is before 2025, then we admit it's too small to be a timestamp
27
+ // TODO: API should return the expiry unit
28
+ const expireAt = new Date(expiry);
29
+ if (expireAt.getFullYear() < 2025)
30
+ return false;
31
+ return expiry <= Date.now();
32
+ }
18
33
  function isSubdust(vtxo, dust) {
19
34
  return vtxo.value < dust;
20
35
  }
@@ -14,8 +14,6 @@ var Request;
14
14
  return (message.type === "INIT_WALLET" &&
15
15
  "arkServerUrl" in message &&
16
16
  typeof message.arkServerUrl === "string" &&
17
- "privateKey" in message &&
18
- typeof message.privateKey === "string" &&
19
17
  ("arkServerPublicKey" in message
20
18
  ? message.arkServerPublicKey === undefined ||
21
19
  typeof message.arkServerPublicKey === "string"