@arkade-os/sdk 0.3.0-alpha.8 → 0.3.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 (101) hide show
  1. package/README.md +48 -14
  2. package/dist/cjs/arknote/index.js +3 -3
  3. package/dist/cjs/forfeit.js +2 -2
  4. package/dist/cjs/identity/singleKey.js +8 -8
  5. package/dist/cjs/index.js +13 -5
  6. package/dist/cjs/{bip322 → intent}/index.js +38 -61
  7. package/dist/cjs/musig2/index.js +2 -1
  8. package/dist/cjs/musig2/nonces.js +4 -0
  9. package/dist/cjs/providers/ark.js +76 -45
  10. package/dist/cjs/providers/errors.js +59 -0
  11. package/dist/cjs/providers/expoArk.js +15 -170
  12. package/dist/cjs/providers/expoIndexer.js +22 -111
  13. package/dist/cjs/providers/expoUtils.js +124 -0
  14. package/dist/cjs/providers/onchain.js +19 -20
  15. package/dist/cjs/repositories/walletRepository.js +64 -28
  16. package/dist/cjs/script/base.js +15 -7
  17. package/dist/cjs/script/tapscript.js +20 -21
  18. package/dist/cjs/script/vhtlc.js +2 -2
  19. package/dist/cjs/tree/signingSession.js +44 -11
  20. package/dist/cjs/tree/txTree.js +3 -4
  21. package/dist/cjs/tree/validation.js +2 -3
  22. package/dist/cjs/utils/arkTransaction.js +105 -15
  23. package/dist/cjs/utils/transaction.js +28 -0
  24. package/dist/cjs/utils/unknownFields.js +7 -7
  25. package/dist/cjs/wallet/onchain.js +6 -7
  26. package/dist/cjs/wallet/serviceWorker/response.js +32 -0
  27. package/dist/cjs/wallet/serviceWorker/utils.js +2 -0
  28. package/dist/cjs/wallet/serviceWorker/wallet.js +7 -8
  29. package/dist/cjs/wallet/serviceWorker/worker.js +46 -27
  30. package/dist/cjs/wallet/unroll.js +7 -9
  31. package/dist/cjs/wallet/utils.js +9 -0
  32. package/dist/cjs/wallet/vtxo-manager.js +323 -0
  33. package/dist/cjs/wallet/wallet.js +98 -125
  34. package/dist/esm/arknote/index.js +2 -2
  35. package/dist/esm/forfeit.js +1 -1
  36. package/dist/esm/identity/singleKey.js +9 -9
  37. package/dist/esm/index.js +14 -10
  38. package/dist/esm/{bip322 → intent}/index.js +32 -54
  39. package/dist/esm/musig2/index.js +1 -1
  40. package/dist/esm/musig2/nonces.js +3 -0
  41. package/dist/esm/providers/ark.js +76 -45
  42. package/dist/esm/providers/errors.js +54 -0
  43. package/dist/esm/providers/expoArk.js +15 -137
  44. package/dist/esm/providers/expoIndexer.js +22 -78
  45. package/dist/esm/providers/expoUtils.js +87 -0
  46. package/dist/esm/providers/onchain.js +19 -20
  47. package/dist/esm/repositories/walletRepository.js +64 -28
  48. package/dist/esm/script/base.js +12 -4
  49. package/dist/esm/script/tapscript.js +1 -2
  50. package/dist/esm/script/vhtlc.js +1 -1
  51. package/dist/esm/tree/signingSession.js +45 -12
  52. package/dist/esm/tree/txTree.js +3 -4
  53. package/dist/esm/tree/validation.js +2 -3
  54. package/dist/esm/utils/arkTransaction.js +97 -8
  55. package/dist/esm/utils/transaction.js +24 -0
  56. package/dist/esm/utils/unknownFields.js +3 -3
  57. package/dist/esm/wallet/onchain.js +3 -4
  58. package/dist/esm/wallet/serviceWorker/response.js +32 -0
  59. package/dist/esm/wallet/serviceWorker/utils.js +1 -0
  60. package/dist/esm/wallet/serviceWorker/wallet.js +8 -9
  61. package/dist/esm/wallet/serviceWorker/worker.js +48 -29
  62. package/dist/esm/wallet/unroll.js +5 -7
  63. package/dist/esm/wallet/utils.js +8 -0
  64. package/dist/esm/wallet/vtxo-manager.js +317 -0
  65. package/dist/esm/wallet/wallet.js +92 -119
  66. package/dist/types/arknote/index.d.ts +1 -1
  67. package/dist/types/forfeit.d.ts +2 -2
  68. package/dist/types/identity/index.d.ts +2 -2
  69. package/dist/types/identity/singleKey.d.ts +2 -2
  70. package/dist/types/index.d.ts +9 -7
  71. package/dist/types/intent/index.d.ts +41 -0
  72. package/dist/types/musig2/index.d.ts +1 -1
  73. package/dist/types/musig2/nonces.d.ts +1 -0
  74. package/dist/types/providers/ark.d.ts +62 -26
  75. package/dist/types/providers/errors.d.ts +13 -0
  76. package/dist/types/providers/expoIndexer.d.ts +2 -10
  77. package/dist/types/providers/expoUtils.d.ts +18 -0
  78. package/dist/types/providers/indexer.d.ts +1 -9
  79. package/dist/types/providers/onchain.d.ts +6 -2
  80. package/dist/types/repositories/walletRepository.d.ts +9 -5
  81. package/dist/types/script/base.d.ts +5 -2
  82. package/dist/types/tree/signingSession.d.ts +16 -11
  83. package/dist/types/utils/anchor.d.ts +2 -2
  84. package/dist/types/utils/arkTransaction.d.ts +12 -4
  85. package/dist/types/utils/transaction.d.ts +13 -0
  86. package/dist/types/utils/unknownFields.d.ts +4 -4
  87. package/dist/types/wallet/index.d.ts +6 -4
  88. package/dist/types/wallet/onchain.d.ts +1 -1
  89. package/dist/types/wallet/serviceWorker/response.d.ts +16 -2
  90. package/dist/types/wallet/serviceWorker/utils.d.ts +1 -0
  91. package/dist/types/wallet/serviceWorker/wallet.d.ts +2 -2
  92. package/dist/types/wallet/serviceWorker/worker.d.ts +7 -1
  93. package/dist/types/wallet/unroll.d.ts +1 -1
  94. package/dist/types/wallet/utils.d.ts +2 -1
  95. package/dist/types/wallet/vtxo-manager.d.ts +179 -0
  96. package/dist/types/wallet/wallet.d.ts +8 -4
  97. package/package.json +1 -2
  98. package/dist/cjs/bip322/errors.js +0 -13
  99. package/dist/esm/bip322/errors.js +0 -9
  100. package/dist/types/bip322/errors.d.ts +0 -6
  101. package/dist/types/bip322/index.d.ts +0 -57
@@ -1,22 +1,20 @@
1
- import { OP } from "@scure/btc-signer/script.js";
2
- import { Transaction, SigHash } from "@scure/btc-signer/transaction.js";
3
- import { Script } from "@scure/btc-signer/script.js";
4
- import { ErrMissingData, ErrMissingInputs, ErrMissingWitnessUtxo, } from './errors.js';
1
+ import { OP, Script, SigHash } from "@scure/btc-signer";
5
2
  import { schnorr } from "@noble/curves/secp256k1.js";
6
- import { base64 } from "@scure/base";
3
+ import { Transaction } from '../utils/transaction.js';
7
4
  /**
8
- * BIP-322 signature implementation for Bitcoin message signing.
5
+ * Intent proof implementation for Bitcoin message signing.
9
6
  *
10
- * BIP-322 defines a standard for signing Bitcoin messages as well as proving
7
+ * Intent proof defines a standard for signing Bitcoin messages as well as proving
11
8
  * ownership of coins. This namespace provides utilities for creating and
12
- * validating BIP-322.
9
+ * validating Intent proof.
13
10
  *
11
+ * it is greatly inspired by BIP322.
14
12
  * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
15
13
  *
16
14
  * @example
17
15
  * ```typescript
18
- * // Create a BIP-322 proof
19
- * const proof = BIP322.create(
16
+ * // Create a Intent proof
17
+ * const proof = Intent.create(
20
18
  * "Hello Bitcoin!",
21
19
  * [input],
22
20
  * [output]
@@ -25,65 +23,46 @@ import { base64 } from "@scure/base";
25
23
  * // Sign the proof
26
24
  * const signedProof = await identity.sign(proof);
27
25
  *
28
- * // Extract the signature
29
- * const signature = BIP322.signature(signedProof);
30
- * ```
31
26
  */
32
- export var BIP322;
33
- (function (BIP322) {
27
+ export var Intent;
28
+ (function (Intent) {
34
29
  /**
35
- * Creates a new BIP-322 "full" proof of funds unsigned transaction.
30
+ * Creates a new Intent proof unsigned transaction.
36
31
  *
37
32
  * This function constructs a special transaction that can be signed to prove
38
33
  * ownership of VTXOs and UTXOs. The proof includes the message to be
39
34
  * signed and the inputs/outputs that demonstrate ownership.
40
35
  *
41
- * @param message - The BIP-322 message to be signed
36
+ * @param message - The Intent message to be signed
42
37
  * @param inputs - Array of transaction inputs to prove ownership of
43
38
  * @param outputs - Optional array of transaction outputs
44
- * @returns An unsigned BIP-322 proof transaction
39
+ * @returns An unsigned Intent proof transaction
45
40
  */
46
41
  function create(message, inputs, outputs = []) {
47
42
  if (inputs.length == 0)
48
- throw ErrMissingInputs;
43
+ throw new Error("intent proof requires at least one input");
49
44
  if (!validateInputs(inputs))
50
- throw ErrMissingData;
45
+ throw new Error("invalid inputs");
51
46
  if (!validateOutputs(outputs))
52
- throw ErrMissingData;
47
+ throw new Error("invalid outputs");
53
48
  // create the initial transaction to spend
54
49
  const toSpend = craftToSpendTx(message, inputs[0].witnessUtxo.script);
55
50
  // create the transaction to sign
56
51
  return craftToSignTx(toSpend, inputs, outputs);
57
52
  }
58
- BIP322.create = create;
59
- /**
60
- * Finalizes and extracts the FullProof transaction into a BIP-322 signature.
61
- *
62
- * This function takes a signed proof transaction and converts it into a
63
- * base64-encoded signature string. If the proof's inputs have special
64
- * spending conditions, a custom finalizer can be provided.
65
- *
66
- * @param signedProof - The signed BIP-322 proof transaction
67
- * @param finalizer - Optional custom finalizer function
68
- * @returns Base64-encoded BIP-322 signature
69
- */
70
- function signature(signedProof, finalizer = (tx) => tx.finalize()) {
71
- finalizer(signedProof);
72
- return base64.encode(signedProof.extract());
73
- }
74
- BIP322.signature = signature;
75
- })(BIP322 || (BIP322 = {}));
53
+ Intent.create = create;
54
+ })(Intent || (Intent = {}));
76
55
  const OP_RETURN_EMPTY_PKSCRIPT = new Uint8Array([OP.RETURN]);
77
56
  const ZERO_32 = new Uint8Array(32).fill(0);
78
57
  const MAX_INDEX = 0xffffffff;
79
- const TAG_BIP322 = "BIP0322-signed-message";
58
+ const TAG_INTENT_PROOF = "ark-intent-proof-message";
80
59
  function validateInput(input) {
81
60
  if (input.index === undefined)
82
- throw ErrMissingData;
61
+ throw new Error("intent proof input requires index");
83
62
  if (input.txid === undefined)
84
- throw ErrMissingData;
63
+ throw new Error("intent proof input requires txid");
85
64
  if (input.witnessUtxo === undefined)
86
- throw ErrMissingWitnessUtxo;
65
+ throw new Error("intent proof input requires witness utxo");
87
66
  return true;
88
67
  }
89
68
  function validateInputs(inputs) {
@@ -92,9 +71,9 @@ function validateInputs(inputs) {
92
71
  }
93
72
  function validateOutput(output) {
94
73
  if (output.amount === undefined)
95
- throw ErrMissingData;
74
+ throw new Error("intent proof output requires amount");
96
75
  if (output.script === undefined)
97
- throw ErrMissingData;
76
+ throw new Error("intent proof output requires script");
98
77
  return true;
99
78
  }
100
79
  function validateOutputs(outputs) {
@@ -102,13 +81,10 @@ function validateOutputs(outputs) {
102
81
  return true;
103
82
  }
104
83
  // craftToSpendTx creates the initial transaction that will be spent in the proof
105
- export function craftToSpendTx(message, pkScript) {
84
+ function craftToSpendTx(message, pkScript) {
106
85
  const messageHash = hashMessage(message);
107
86
  const tx = new Transaction({
108
87
  version: 0,
109
- allowUnknownOutputs: true,
110
- allowUnknown: true,
111
- allowUnknownInputs: true,
112
88
  });
113
89
  // add input with zero hash and max index
114
90
  tx.addInput({
@@ -131,9 +107,6 @@ function craftToSignTx(toSpend, inputs, outputs) {
131
107
  const firstInput = inputs[0];
132
108
  const tx = new Transaction({
133
109
  version: 2,
134
- allowUnknownOutputs: outputs.length === 0,
135
- allowUnknown: true,
136
- allowUnknownInputs: true,
137
110
  lockTime: 0,
138
111
  });
139
112
  // add the first "toSpend" input
@@ -148,11 +121,16 @@ function craftToSignTx(toSpend, inputs, outputs) {
148
121
  sighashType: SigHash.ALL,
149
122
  });
150
123
  // add other inputs
151
- for (const input of inputs) {
124
+ for (const [i, input] of inputs.entries()) {
152
125
  tx.addInput({
153
126
  ...input,
154
127
  sighashType: SigHash.ALL,
155
128
  });
129
+ if (input.unknown?.length) {
130
+ tx.updateInput(i + 1, {
131
+ unknown: input.unknown,
132
+ });
133
+ }
156
134
  }
157
135
  // add the special OP_RETURN output if no outputs are provided
158
136
  if (outputs.length === 0) {
@@ -172,5 +150,5 @@ function craftToSignTx(toSpend, inputs, outputs) {
172
150
  return tx;
173
151
  }
174
152
  function hashMessage(message) {
175
- return schnorr.utils.taggedHash(TAG_BIP322, new TextEncoder().encode(message));
153
+ return schnorr.utils.taggedHash(TAG_INTENT_PROOF, new TextEncoder().encode(message));
176
154
  }
@@ -1,3 +1,3 @@
1
- export { generateNonces } from './nonces.js';
1
+ export { generateNonces, aggregateNonces } from './nonces.js';
2
2
  export { PartialSig, sign } from './sign.js';
3
3
  export { aggregateKeys } from './keys.js';
@@ -6,3 +6,6 @@ export function generateNonces(publicKey) {
6
6
  const nonces = musig.nonceGen(publicKey);
7
7
  return { secNonce: nonces.secret, pubNonce: nonces.public };
8
8
  }
9
+ export function aggregateNonces(pubNonces) {
10
+ return musig.nonceAggregate(pubNonces);
11
+ }
@@ -1,5 +1,6 @@
1
1
  import { hex } from "@scure/base";
2
2
  import { eventSourceIterator } from './utils.js';
3
+ import { maybeArkError } from './errors.js';
3
4
  export var SettlementEventType;
4
5
  (function (SettlementEventType) {
5
6
  SettlementEventType["BatchStarted"] = "batch_started";
@@ -7,7 +8,7 @@ export var SettlementEventType;
7
8
  SettlementEventType["BatchFinalized"] = "batch_finalized";
8
9
  SettlementEventType["BatchFailed"] = "batch_failed";
9
10
  SettlementEventType["TreeSigningStarted"] = "tree_signing_started";
10
- SettlementEventType["TreeNoncesAggregated"] = "tree_nonces_aggregated";
11
+ SettlementEventType["TreeNonces"] = "tree_nonces";
11
12
  SettlementEventType["TreeTx"] = "tree_tx";
12
13
  SettlementEventType["TreeSignature"] = "tree_signature";
13
14
  })(SettlementEventType || (SettlementEventType = {}));
@@ -28,29 +29,41 @@ export class RestArkProvider {
28
29
  const url = `${this.serverUrl}/v1/info`;
29
30
  const response = await fetch(url);
30
31
  if (!response.ok) {
31
- throw new Error(`Failed to get server info: ${response.statusText}`);
32
+ const errorText = await response.text();
33
+ handleError(errorText, `Failed to get server info: ${response.statusText}`);
32
34
  }
33
35
  const fromServer = await response.json();
34
36
  return {
35
- ...fromServer,
36
- vtxoTreeExpiry: BigInt(fromServer.vtxoTreeExpiry ?? 0),
37
- unilateralExitDelay: BigInt(fromServer.unilateralExitDelay ?? 0),
38
- roundInterval: BigInt(fromServer.roundInterval ?? 0),
39
- dust: BigInt(fromServer.dust ?? 0),
40
- utxoMinAmount: BigInt(fromServer.utxoMinAmount ?? 0),
41
- utxoMaxAmount: BigInt(fromServer.utxoMaxAmount ?? -1),
42
- vtxoMinAmount: BigInt(fromServer.vtxoMinAmount ?? 0),
43
- vtxoMaxAmount: BigInt(fromServer.vtxoMaxAmount ?? -1),
44
37
  boardingExitDelay: BigInt(fromServer.boardingExitDelay ?? 0),
45
- checkpointExitClosure: fromServer.checkpointTapscript ?? "",
46
- marketHour: "marketHour" in fromServer && fromServer.marketHour != null
38
+ checkpointTapscript: fromServer.checkpointTapscript ?? "",
39
+ deprecatedSigners: fromServer.deprecatedSigners?.map((signer) => ({
40
+ cutoffDate: BigInt(signer.cutoffDate ?? 0),
41
+ pubkey: signer.pubkey ?? "",
42
+ })) ?? [],
43
+ digest: fromServer.digest ?? "",
44
+ dust: BigInt(fromServer.dust ?? 0),
45
+ fees: fromServer.fees,
46
+ forfeitAddress: fromServer.forfeitAddress ?? "",
47
+ forfeitPubkey: fromServer.forfeitPubkey ?? "",
48
+ network: fromServer.network ?? "",
49
+ scheduledSession: "scheduledSession" in fromServer &&
50
+ fromServer.scheduledSession != null
47
51
  ? {
48
- nextStartTime: BigInt(fromServer.marketHour.nextStartTime ?? 0),
49
- nextEndTime: BigInt(fromServer.marketHour.nextEndTime ?? 0),
50
- period: BigInt(fromServer.marketHour.period ?? 0),
51
- roundInterval: BigInt(fromServer.marketHour.roundInterval ?? 0),
52
+ duration: BigInt(fromServer.scheduledSession.duration ?? 0),
53
+ nextStartTime: BigInt(fromServer.scheduledSession.nextStartTime ?? 0),
54
+ nextEndTime: BigInt(fromServer.scheduledSession.nextEndTime ?? 0),
55
+ period: BigInt(fromServer.scheduledSession.period ?? 0),
52
56
  }
53
57
  : undefined,
58
+ serviceStatus: fromServer.serviceStatus ?? {},
59
+ sessionDuration: BigInt(fromServer.sessionDuration ?? 0),
60
+ signerPubkey: fromServer.signerPubkey ?? "",
61
+ unilateralExitDelay: BigInt(fromServer.unilateralExitDelay ?? 0),
62
+ utxoMaxAmount: BigInt(fromServer.utxoMaxAmount ?? -1),
63
+ utxoMinAmount: BigInt(fromServer.utxoMinAmount ?? 0),
64
+ version: fromServer.version ?? "",
65
+ vtxoMaxAmount: BigInt(fromServer.vtxoMaxAmount ?? -1),
66
+ vtxoMinAmount: BigInt(fromServer.vtxoMinAmount ?? 0),
54
67
  };
55
68
  }
56
69
  async submitTx(signedArkTx, checkpointTxs) {
@@ -61,22 +74,13 @@ export class RestArkProvider {
61
74
  "Content-Type": "application/json",
62
75
  },
63
76
  body: JSON.stringify({
64
- signedArkTx: signedArkTx,
65
- checkpointTxs: checkpointTxs,
77
+ signedArkTx,
78
+ checkpointTxs,
66
79
  }),
67
80
  });
68
81
  if (!response.ok) {
69
82
  const errorText = await response.text();
70
- try {
71
- const grpcError = JSON.parse(errorText);
72
- // gRPC errors usually have a message and code field
73
- throw new Error(`Failed to submit virtual transaction: ${grpcError.message || grpcError.error || errorText}`);
74
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
75
- }
76
- catch (_) {
77
- // If JSON parse fails, use the raw error text
78
- throw new Error(`Failed to submit virtual transaction: ${errorText}`);
79
- }
83
+ handleError(errorText, `Failed to submit virtual transaction: ${errorText}`);
80
84
  }
81
85
  const data = await response.json();
82
86
  return {
@@ -99,7 +103,7 @@ export class RestArkProvider {
99
103
  });
100
104
  if (!response.ok) {
101
105
  const errorText = await response.text();
102
- throw new Error(`Failed to finalize offchain transaction: ${errorText}`);
106
+ handleError(errorText, `Failed to finalize offchain transaction: ${errorText}`);
103
107
  }
104
108
  }
105
109
  async registerIntent(intent) {
@@ -111,14 +115,14 @@ export class RestArkProvider {
111
115
  },
112
116
  body: JSON.stringify({
113
117
  intent: {
114
- signature: intent.signature,
118
+ proof: intent.proof,
115
119
  message: intent.message,
116
120
  },
117
121
  }),
118
122
  });
119
123
  if (!response.ok) {
120
124
  const errorText = await response.text();
121
- throw new Error(`Failed to register intent: ${errorText}`);
125
+ handleError(errorText, `Failed to register intent: ${errorText}`);
122
126
  }
123
127
  const data = await response.json();
124
128
  return data.intentId;
@@ -132,14 +136,14 @@ export class RestArkProvider {
132
136
  },
133
137
  body: JSON.stringify({
134
138
  proof: {
135
- signature: intent.signature,
139
+ proof: intent.proof,
136
140
  message: intent.message,
137
141
  },
138
142
  }),
139
143
  });
140
144
  if (!response.ok) {
141
145
  const errorText = await response.text();
142
- throw new Error(`Failed to delete intent: ${errorText}`);
146
+ handleError(errorText, `Failed to delete intent: ${errorText}`);
143
147
  }
144
148
  }
145
149
  async confirmRegistration(intentId) {
@@ -155,7 +159,7 @@ export class RestArkProvider {
155
159
  });
156
160
  if (!response.ok) {
157
161
  const errorText = await response.text();
158
- throw new Error(`Failed to confirm registration: ${errorText}`);
162
+ handleError(errorText, `Failed to confirm registration: ${errorText}`);
159
163
  }
160
164
  }
161
165
  async submitTreeNonces(batchId, pubkey, nonces) {
@@ -173,7 +177,7 @@ export class RestArkProvider {
173
177
  });
174
178
  if (!response.ok) {
175
179
  const errorText = await response.text();
176
- throw new Error(`Failed to submit tree nonces: ${errorText}`);
180
+ handleError(errorText, `Failed to submit tree nonces: ${errorText}`);
177
181
  }
178
182
  }
179
183
  async submitTreeSignatures(batchId, pubkey, signatures) {
@@ -191,7 +195,7 @@ export class RestArkProvider {
191
195
  });
192
196
  if (!response.ok) {
193
197
  const errorText = await response.text();
194
- throw new Error(`Failed to submit tree signatures: ${errorText}`);
198
+ handleError(errorText, `Failed to submit tree signatures: ${errorText}`);
195
199
  }
196
200
  }
197
201
  async submitSignedForfeitTxs(signedForfeitTxs, signedCommitmentTx) {
@@ -207,7 +211,8 @@ export class RestArkProvider {
207
211
  }),
208
212
  });
209
213
  if (!response.ok) {
210
- throw new Error(`Failed to submit forfeit transactions: ${response.statusText}`);
214
+ const errorText = await response.text();
215
+ handleError(errorText, `Failed to submit forfeit transactions: ${response.statusText}`);
211
216
  }
212
217
  }
213
218
  async *getEventStream(signal, topics) {
@@ -305,6 +310,22 @@ export class RestArkProvider {
305
310
  }
306
311
  }
307
312
  }
313
+ async getPendingTxs(intent) {
314
+ const url = `${this.serverUrl}/v1/tx/pending`;
315
+ const response = await fetch(url, {
316
+ method: "POST",
317
+ headers: {
318
+ "Content-Type": "application/json",
319
+ },
320
+ body: JSON.stringify({ intent }),
321
+ });
322
+ if (!response.ok) {
323
+ const errorText = await response.text();
324
+ handleError(errorText, `Failed to get pending transactions: ${errorText}`);
325
+ }
326
+ const data = await response.json();
327
+ return data.pendingTxs;
328
+ }
308
329
  parseSettlementEvent(data) {
309
330
  // Check for BatchStarted event
310
331
  if (data.batchStarted) {
@@ -350,10 +371,16 @@ export class RestArkProvider {
350
371
  }
351
372
  // Check for TreeNoncesAggregated event
352
373
  if (data.treeNoncesAggregated) {
374
+ // skip treeNoncesAggregated event, deprecated
375
+ return null;
376
+ }
377
+ if (data.treeNonces) {
353
378
  return {
354
- type: SettlementEventType.TreeNoncesAggregated,
355
- id: data.treeNoncesAggregated.id,
356
- treeNonces: decodeMusig2Nonces(data.treeNoncesAggregated.treeNonces),
379
+ type: SettlementEventType.TreeNonces,
380
+ id: data.treeNonces.id,
381
+ topic: data.treeNonces.topic,
382
+ txid: data.treeNonces.txid,
383
+ nonces: decodeMusig2Nonces(data.treeNonces.nonces), // pubkey -> public nonce
357
384
  };
358
385
  }
359
386
  // Check for TreeTx event
@@ -426,17 +453,16 @@ function encodeMusig2Nonces(nonces) {
426
453
  for (const [txid, nonce] of nonces) {
427
454
  noncesObject[txid] = hex.encode(nonce.pubNonce);
428
455
  }
429
- return JSON.stringify(noncesObject);
456
+ return noncesObject;
430
457
  }
431
458
  function encodeMusig2Signatures(signatures) {
432
459
  const sigObject = {};
433
460
  for (const [txid, sig] of signatures) {
434
461
  sigObject[txid] = hex.encode(sig.encode());
435
462
  }
436
- return JSON.stringify(sigObject);
463
+ return sigObject;
437
464
  }
438
- function decodeMusig2Nonces(str) {
439
- const noncesObject = JSON.parse(str);
465
+ function decodeMusig2Nonces(noncesObject) {
440
466
  return new Map(Object.entries(noncesObject).map(([txid, nonce]) => {
441
467
  if (typeof nonce !== "string") {
442
468
  throw new Error("invalid nonce");
@@ -478,3 +504,8 @@ function mapVtxo(vtxo) {
478
504
  arkTxid: vtxo.arkTxid,
479
505
  };
480
506
  }
507
+ function handleError(errorText, defaultMessage) {
508
+ const error = new Error(errorText);
509
+ const arkError = maybeArkError(error);
510
+ throw arkError ?? new Error(defaultMessage);
511
+ }
@@ -0,0 +1,54 @@
1
+ export class ArkError extends Error {
2
+ constructor(code, message, name, metadata) {
3
+ super(message);
4
+ this.code = code;
5
+ this.message = message;
6
+ this.name = name;
7
+ this.metadata = metadata;
8
+ }
9
+ }
10
+ /**
11
+ * Try to convert an error to an ArkError class, returning undefined if the error is not an ArkError
12
+ * @param error - The error to parse
13
+ * @returns The parsed ArkError, or undefined if the error is not an ArkError
14
+ */
15
+ export function maybeArkError(error) {
16
+ try {
17
+ if (!(error instanceof Error))
18
+ return undefined;
19
+ const decoded = JSON.parse(error.message);
20
+ if (!("details" in decoded))
21
+ return undefined;
22
+ if (!Array.isArray(decoded.details))
23
+ return undefined;
24
+ // search for a valid details object with the correct type
25
+ for (const details of decoded.details) {
26
+ if (!("@type" in details))
27
+ continue;
28
+ const type = details["@type"];
29
+ if (type !== "type.googleapis.com/ark.v1.ErrorDetails")
30
+ continue;
31
+ if (!("code" in details))
32
+ continue;
33
+ const code = details.code;
34
+ if (!("message" in details))
35
+ continue;
36
+ const message = details.message;
37
+ if (!("name" in details))
38
+ continue;
39
+ const name = details.name;
40
+ let metadata;
41
+ if ("metadata" in details && isMetadata(details.metadata)) {
42
+ metadata = details.metadata;
43
+ }
44
+ return new ArkError(code, message, name, metadata);
45
+ }
46
+ return undefined;
47
+ }
48
+ catch (e) {
49
+ return undefined;
50
+ }
51
+ }
52
+ function isMetadata(value) {
53
+ return typeof value === "object" && value !== null && !Array.isArray(value);
54
+ }