@buildonspark/spark-sdk 0.1.46 → 0.2.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 (171) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/{chunk-BGGEVUJK.js → chunk-2ENZX6LT.js} +241 -7
  3. package/dist/{chunk-LHRD2WT6.js → chunk-4JD4HIAN.js} +23 -3
  4. package/dist/{chunk-I54FARY2.js → chunk-CDLETEDT.js} +11 -3
  5. package/dist/{chunk-OBFKIEMP.js → chunk-TM6CHQXC.js} +1 -1
  6. package/dist/chunk-UDK3EBE5.js +13514 -0
  7. package/dist/chunk-XYTKKLCV.js +7 -0
  8. package/dist/{RequestLightningSendInput-2cSh_In4.d.cts → client-DKbwpcnl.d.ts} +434 -212
  9. package/dist/{RequestLightningSendInput-CN6BNg_g.d.ts → client-Drs5Lapg.d.cts} +434 -212
  10. package/dist/{services/config.cjs → debug.cjs} +31007 -1003
  11. package/dist/debug.d.cts +126 -0
  12. package/dist/debug.d.ts +126 -0
  13. package/dist/debug.js +21 -0
  14. package/dist/graphql/objects/index.d.cts +4 -11
  15. package/dist/graphql/objects/index.d.ts +4 -11
  16. package/dist/graphql/objects/index.js +2 -4
  17. package/dist/index.cjs +18219 -20818
  18. package/dist/index.d.cts +15 -768
  19. package/dist/index.d.ts +15 -768
  20. package/dist/index.js +82 -76
  21. package/dist/index.node.cjs +23831 -26538
  22. package/dist/index.node.d.cts +191 -33
  23. package/dist/index.node.d.ts +191 -33
  24. package/dist/index.node.js +87 -181
  25. package/dist/native/index.cjs +17835 -20519
  26. package/dist/native/index.d.cts +1466 -1546
  27. package/dist/native/index.d.ts +1466 -1546
  28. package/dist/native/index.js +22605 -25286
  29. package/dist/proto/lrc20.d.cts +2 -2
  30. package/dist/proto/lrc20.d.ts +2 -2
  31. package/dist/proto/lrc20.js +3098 -46
  32. package/dist/proto/spark.cjs +241 -7
  33. package/dist/proto/spark.d.cts +1 -1
  34. package/dist/proto/spark.d.ts +1 -1
  35. package/dist/proto/spark.js +5 -1
  36. package/dist/proto/spark_token.cjs +22 -2
  37. package/dist/proto/spark_token.d.cts +8 -1
  38. package/dist/proto/spark_token.d.ts +8 -1
  39. package/dist/proto/spark_token.js +2 -2
  40. package/dist/{sdk-types-CKBsylfW.d.ts → sdk-types-DCIVdKUT.d.ts} +1 -1
  41. package/dist/{sdk-types-Ct8xmN7l.d.cts → sdk-types-DJ2ve9YY.d.cts} +1 -1
  42. package/dist/{spark-DbzGfse6.d.ts → spark-BUOx3U7Q.d.cts} +103 -5
  43. package/dist/{spark-DbzGfse6.d.cts → spark-BUOx3U7Q.d.ts} +103 -5
  44. package/dist/spark-wallet-CF8Oxjqs.d.ts +935 -0
  45. package/dist/spark-wallet-DOLSa3oF.d.cts +935 -0
  46. package/dist/spark_bindings/native/index.d.cts +1 -1
  47. package/dist/spark_bindings/native/index.d.ts +1 -1
  48. package/dist/spark_bindings/wasm/index.d.cts +1 -1
  49. package/dist/spark_bindings/wasm/index.d.ts +1 -1
  50. package/dist/{services/index.cjs → tests/test-utils.cjs} +9788 -10263
  51. package/dist/tests/test-utils.d.cts +79 -0
  52. package/dist/tests/test-utils.d.ts +79 -0
  53. package/dist/tests/test-utils.js +93 -0
  54. package/dist/types/index.cjs +239 -7
  55. package/dist/types/index.d.cts +5 -9
  56. package/dist/types/index.d.ts +5 -9
  57. package/dist/types/index.js +4 -6
  58. package/dist/{types-C-Rp0Oo7.d.ts → types-BADxR3bm.d.cts} +1 -1
  59. package/dist/{types-C-Rp0Oo7.d.cts → types-BADxR3bm.d.ts} +1 -1
  60. package/dist/{index-COm59SPw.d.ts → xchain-address-C2xMs9nz.d.cts} +6 -94
  61. package/dist/{index-CKL5DodV.d.cts → xchain-address-Ckto9oEz.d.ts} +6 -94
  62. package/package.json +9 -33
  63. package/src/debug.ts +13 -0
  64. package/src/graphql/client.ts +59 -20
  65. package/src/index.node.ts +28 -2
  66. package/src/index.ts +31 -1
  67. package/src/native/index.ts +16 -2
  68. package/src/proto/mock.ts +76 -0
  69. package/src/proto/spark.ts +354 -6
  70. package/src/proto/spark_token.ts +34 -2
  71. package/src/services/config.ts +4 -6
  72. package/src/services/connection.ts +131 -64
  73. package/src/services/coop-exit.ts +6 -3
  74. package/src/services/deposit.ts +9 -8
  75. package/src/services/lightning.ts +4 -3
  76. package/src/services/signing.ts +10 -6
  77. package/src/services/token-transactions.ts +100 -85
  78. package/src/services/transfer.ts +88 -60
  79. package/src/services/tree-creation.ts +17 -9
  80. package/src/services/wallet-config.ts +17 -9
  81. package/src/signer/signer.react-native.ts +5 -10
  82. package/src/signer/signer.ts +269 -339
  83. package/src/signer/types.ts +63 -0
  84. package/src/spark-wallet/spark-wallet.ts +226 -149
  85. package/src/spark-wallet/types.ts +22 -8
  86. package/src/tests/integration/adaptor-signature.test.ts +8 -9
  87. package/src/tests/integration/coop-exit.test.ts +214 -202
  88. package/src/tests/integration/lightning.test.ts +128 -103
  89. package/src/tests/integration/swap.test.ts +116 -84
  90. package/src/tests/integration/transfer.test.ts +291 -214
  91. package/src/tests/integration/tree-creation.test.ts +0 -5
  92. package/src/tests/integration/wallet.test.ts +1 -0
  93. package/src/tests/isHermeticTest.ts +3 -24
  94. package/src/tests/{test-util.ts → test-utils.ts} +13 -11
  95. package/src/tests/token-identifier.test.ts +6 -6
  96. package/src/tests/wrapWithOtelSpan.test.ts +1 -1
  97. package/src/{address → utils}/address.ts +1 -1
  98. package/src/utils/crypto.ts +19 -9
  99. package/src/utils/index.ts +2 -0
  100. package/src/utils/network.ts +17 -0
  101. package/src/utils/secret-sharing.ts +1 -2
  102. package/src/utils/signing.ts +1 -1
  103. package/src/utils/token-identifier.ts +27 -21
  104. package/src/utils/token-transaction-validation.ts +34 -0
  105. package/src/utils/token-transactions.ts +12 -8
  106. package/src/utils/unilateral-exit.ts +32 -0
  107. package/src/utils/xchain-address.ts +1 -1
  108. package/dist/BitcoinNetwork-TnABML0T.d.cts +0 -18
  109. package/dist/BitcoinNetwork-TnABML0T.d.ts +0 -18
  110. package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.cts +0 -10
  111. package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.ts +0 -10
  112. package/dist/address/index.cjs +0 -458
  113. package/dist/address/index.d.cts +0 -32
  114. package/dist/address/index.d.ts +0 -32
  115. package/dist/address/index.js +0 -17
  116. package/dist/chunk-4EMV7HHW.js +0 -277
  117. package/dist/chunk-C2S227QR.js +0 -2336
  118. package/dist/chunk-DXR2PXJU.js +0 -1122
  119. package/dist/chunk-GSI4OLXZ.js +0 -117
  120. package/dist/chunk-HHNQ3ZHC.js +0 -170
  121. package/dist/chunk-HMLOC6TE.js +0 -14
  122. package/dist/chunk-HSCLBJEL.js +0 -113
  123. package/dist/chunk-HWJWKEIU.js +0 -75
  124. package/dist/chunk-JB64OQES.js +0 -7095
  125. package/dist/chunk-KMUMFYFX.js +0 -137
  126. package/dist/chunk-N5VZVCGJ.js +0 -622
  127. package/dist/chunk-NSJF5F5O.js +0 -325
  128. package/dist/chunk-NTFKFRQ2.js +0 -3146
  129. package/dist/chunk-OFCJFZ4I.js +0 -24
  130. package/dist/chunk-QNNSEJ4P.js +0 -232
  131. package/dist/chunk-UXDODSDT.js +0 -838
  132. package/dist/chunk-VTUGIIWI.js +0 -0
  133. package/dist/chunk-Z5HIAYFT.js +0 -84
  134. package/dist/network-Css46DAz.d.cts +0 -46
  135. package/dist/network-hynb7iTZ.d.ts +0 -46
  136. package/dist/services/config.d.cts +0 -42
  137. package/dist/services/config.d.ts +0 -42
  138. package/dist/services/config.js +0 -17
  139. package/dist/services/connection.cjs +0 -17691
  140. package/dist/services/connection.d.cts +0 -95
  141. package/dist/services/connection.d.ts +0 -95
  142. package/dist/services/connection.js +0 -11
  143. package/dist/services/index.d.cts +0 -21
  144. package/dist/services/index.d.ts +0 -21
  145. package/dist/services/index.js +0 -58
  146. package/dist/services/lrc-connection.cjs +0 -4713
  147. package/dist/services/lrc-connection.d.cts +0 -34
  148. package/dist/services/lrc-connection.d.ts +0 -34
  149. package/dist/services/lrc-connection.js +0 -11
  150. package/dist/services/token-transactions.cjs +0 -2877
  151. package/dist/services/token-transactions.d.cts +0 -75
  152. package/dist/services/token-transactions.d.ts +0 -75
  153. package/dist/services/token-transactions.js +0 -15
  154. package/dist/services/wallet-config.cjs +0 -340
  155. package/dist/services/wallet-config.d.cts +0 -56
  156. package/dist/services/wallet-config.d.ts +0 -56
  157. package/dist/services/wallet-config.js +0 -33
  158. package/dist/signer/signer.cjs +0 -2004
  159. package/dist/signer/signer.d.cts +0 -10
  160. package/dist/signer/signer.d.ts +0 -10
  161. package/dist/signer/signer.js +0 -24
  162. package/dist/signer-BP6F__oR.d.cts +0 -187
  163. package/dist/signer-BVZJXcq7.d.ts +0 -187
  164. package/dist/utils/index.cjs +0 -2947
  165. package/dist/utils/index.d.cts +0 -18
  166. package/dist/utils/index.d.ts +0 -18
  167. package/dist/utils/index.js +0 -157
  168. package/ios/spark_frost.kt +0 -1900
  169. package/src/address/index.ts +0 -1
  170. package/src/services/lrc-connection.ts +0 -215
  171. /package/dist/{chunk-L3EHBOUX.js → chunk-BYXBJQAS.js} +0 -0
@@ -332,7 +332,16 @@ export interface TokenTransactionConfirmationMetadata {
332
332
  export interface TokenTransactionWithStatus {
333
333
  tokenTransaction: TokenTransaction | undefined;
334
334
  status: TokenTransactionStatus;
335
- confirmationMetadata: TokenTransactionConfirmationMetadata | undefined;
335
+ confirmationMetadata:
336
+ | TokenTransactionConfirmationMetadata
337
+ | undefined;
338
+ /**
339
+ * In rare cases the above reconstructed token transaction may not match the original token transaction due to:
340
+ * a) a pre-empted transfer transaction having its input TTXOs remapped to the newer transaction
341
+ * b) proto migrations or field deprecations resulting in missing/swapped fields (eg. token public key -> token identifier)
342
+ * Include the original hash to ensure clients can reconcile this transaction with the original if needed.
343
+ */
344
+ tokenTransactionHash: Uint8Array;
336
345
  }
337
346
 
338
347
  function createBaseTokenOutputToSpend(): TokenOutputToSpend {
@@ -2674,7 +2683,12 @@ export const TokenTransactionConfirmationMetadata: MessageFns<TokenTransactionCo
2674
2683
  };
2675
2684
 
2676
2685
  function createBaseTokenTransactionWithStatus(): TokenTransactionWithStatus {
2677
- return { tokenTransaction: undefined, status: 0, confirmationMetadata: undefined };
2686
+ return {
2687
+ tokenTransaction: undefined,
2688
+ status: 0,
2689
+ confirmationMetadata: undefined,
2690
+ tokenTransactionHash: new Uint8Array(0),
2691
+ };
2678
2692
  }
2679
2693
 
2680
2694
  export const TokenTransactionWithStatus: MessageFns<TokenTransactionWithStatus> = {
@@ -2688,6 +2702,9 @@ export const TokenTransactionWithStatus: MessageFns<TokenTransactionWithStatus>
2688
2702
  if (message.confirmationMetadata !== undefined) {
2689
2703
  TokenTransactionConfirmationMetadata.encode(message.confirmationMetadata, writer.uint32(26).fork()).join();
2690
2704
  }
2705
+ if (message.tokenTransactionHash.length !== 0) {
2706
+ writer.uint32(34).bytes(message.tokenTransactionHash);
2707
+ }
2691
2708
  return writer;
2692
2709
  },
2693
2710
 
@@ -2722,6 +2739,14 @@ export const TokenTransactionWithStatus: MessageFns<TokenTransactionWithStatus>
2722
2739
  message.confirmationMetadata = TokenTransactionConfirmationMetadata.decode(reader, reader.uint32());
2723
2740
  continue;
2724
2741
  }
2742
+ case 4: {
2743
+ if (tag !== 34) {
2744
+ break;
2745
+ }
2746
+
2747
+ message.tokenTransactionHash = reader.bytes();
2748
+ continue;
2749
+ }
2725
2750
  }
2726
2751
  if ((tag & 7) === 4 || tag === 0) {
2727
2752
  break;
@@ -2738,6 +2763,9 @@ export const TokenTransactionWithStatus: MessageFns<TokenTransactionWithStatus>
2738
2763
  confirmationMetadata: isSet(object.confirmationMetadata)
2739
2764
  ? TokenTransactionConfirmationMetadata.fromJSON(object.confirmationMetadata)
2740
2765
  : undefined,
2766
+ tokenTransactionHash: isSet(object.tokenTransactionHash)
2767
+ ? bytesFromBase64(object.tokenTransactionHash)
2768
+ : new Uint8Array(0),
2741
2769
  };
2742
2770
  },
2743
2771
 
@@ -2752,6 +2780,9 @@ export const TokenTransactionWithStatus: MessageFns<TokenTransactionWithStatus>
2752
2780
  if (message.confirmationMetadata !== undefined) {
2753
2781
  obj.confirmationMetadata = TokenTransactionConfirmationMetadata.toJSON(message.confirmationMetadata);
2754
2782
  }
2783
+ if (message.tokenTransactionHash.length !== 0) {
2784
+ obj.tokenTransactionHash = base64FromBytes(message.tokenTransactionHash);
2785
+ }
2755
2786
  return obj;
2756
2787
  },
2757
2788
 
@@ -2767,6 +2798,7 @@ export const TokenTransactionWithStatus: MessageFns<TokenTransactionWithStatus>
2767
2798
  message.confirmationMetadata = (object.confirmationMetadata !== undefined && object.confirmationMetadata !== null)
2768
2799
  ? TokenTransactionConfirmationMetadata.fromPartial(object.confirmationMetadata)
2769
2800
  : undefined;
2801
+ message.tokenTransactionHash = object.tokenTransactionHash ?? new Uint8Array(0);
2770
2802
  return message;
2771
2803
  },
2772
2804
  };
@@ -8,9 +8,7 @@ import { DefaultSparkSigner, SparkSigner } from "../signer/signer.js";
8
8
  import { Network, NetworkToProto, NetworkType } from "../utils/network.js";
9
9
  import {
10
10
  ConfigOptions,
11
- LOCAL_WALLET_CONFIG,
12
- MAINNET_WALLET_CONFIG,
13
- REGTEST_WALLET_CONFIG,
11
+ WalletConfig,
14
12
  SigningOperator,
15
13
  } from "./wallet-config.js";
16
14
  import { ConfigurationError } from "../errors/types.js";
@@ -39,11 +37,11 @@ export class WalletConfigService
39
37
  private getDefaultConfig(network: Network): Required<ConfigOptions> {
40
38
  switch (network) {
41
39
  case Network.MAINNET:
42
- return MAINNET_WALLET_CONFIG;
40
+ return WalletConfig.MAINNET;
43
41
  case Network.REGTEST:
44
- return REGTEST_WALLET_CONFIG;
42
+ return WalletConfig.REGTEST;
45
43
  default:
46
- return LOCAL_WALLET_CONFIG;
44
+ return WalletConfig.LOCAL;
47
45
  }
48
46
  }
49
47
 
@@ -1,4 +1,4 @@
1
- import { isNode } from "@lightsparkdev/core";
1
+ import { isError, isNode } from "@lightsparkdev/core";
2
2
  import { sha256 } from "@noble/hashes/sha2";
3
3
  import type { Channel, ClientFactory } from "nice-grpc";
4
4
  import { retryMiddleware } from "nice-grpc-client-middleware-retry";
@@ -23,6 +23,10 @@ import {
23
23
  SparkTokenServiceDefinition,
24
24
  } from "../proto/spark_token.js";
25
25
 
26
+ type SparkAuthnServiceClientWithClose = SparkAuthnServiceClient & {
27
+ close?: () => void;
28
+ };
29
+
26
30
  export class ConnectionManager {
27
31
  private config: WalletConfigService;
28
32
  private clients: Map<
@@ -52,6 +56,9 @@ export class ConnectionManager {
52
56
  }
53
57
  > = new Map();
54
58
 
59
+ // Tracks in-flight authenticate() promises so concurrent callers share one
60
+ private authPromises: Map<string, Promise<string>> = new Map();
61
+
55
62
  constructor(config: WalletConfigService) {
56
63
  this.config = config;
57
64
  }
@@ -231,57 +238,105 @@ export class ConnectionManager {
231
238
  }
232
239
 
233
240
  private async authenticate(address: string, certPath?: string) {
234
- try {
235
- const identityPublicKey = await this.config.signer.getIdentityPublicKey();
236
- const sparkAuthnClient = await this.createSparkAuthnGrpcConnection(
237
- address,
238
- certPath,
239
- );
241
+ const existing = this.authPromises.get(address);
242
+ if (existing) {
243
+ return existing;
244
+ }
245
+
246
+ const authPromise = (async () => {
247
+ const MAX_ATTEMPTS = 3;
248
+ let lastError: Error | undefined;
249
+
250
+ /* React Native can cause some outgoing requests to be paused which can result
251
+ in challenges expiring, so we'll retry any authentication failures: */
252
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
253
+ let sparkAuthnClient: SparkAuthnServiceClientWithClose | undefined;
254
+ try {
255
+ const identityPublicKey =
256
+ await this.config.signer.getIdentityPublicKey();
257
+ sparkAuthnClient = await this.createSparkAuthnGrpcConnection(
258
+ address,
259
+ certPath,
260
+ );
240
261
 
241
- const challengeResp = await sparkAuthnClient.get_challenge({
242
- publicKey: identityPublicKey,
243
- });
262
+ const challengeResp = await sparkAuthnClient.get_challenge({
263
+ publicKey: identityPublicKey,
264
+ });
244
265
 
245
- if (!challengeResp.protectedChallenge?.challenge) {
246
- throw new AuthenticationError("Invalid challenge response", {
247
- endpoint: "get_challenge",
248
- reason: "Missing challenge in response",
249
- });
250
- }
266
+ if (!challengeResp.protectedChallenge?.challenge) {
267
+ throw new AuthenticationError("Invalid challenge response", {
268
+ endpoint: "get_challenge",
269
+ reason: "Missing challenge in response",
270
+ });
271
+ }
251
272
 
252
- const challengeBytes = Challenge.encode(
253
- challengeResp.protectedChallenge.challenge,
254
- ).finish();
255
- const hash = sha256(challengeBytes);
273
+ const challengeBytes = Challenge.encode(
274
+ challengeResp.protectedChallenge.challenge,
275
+ ).finish();
276
+ const hash = sha256(challengeBytes);
256
277
 
257
- const derSignatureBytes =
258
- await this.config.signer.signMessageWithIdentityKey(hash);
278
+ const derSignatureBytes =
279
+ await this.config.signer.signMessageWithIdentityKey(hash);
259
280
 
260
- const verifyResp = await sparkAuthnClient.verify_challenge({
261
- protectedChallenge: challengeResp.protectedChallenge,
262
- signature: derSignatureBytes,
263
- publicKey: identityPublicKey,
264
- });
281
+ const verifyResp = await sparkAuthnClient.verify_challenge({
282
+ protectedChallenge: challengeResp.protectedChallenge,
283
+ signature: derSignatureBytes,
284
+ publicKey: identityPublicKey,
285
+ });
286
+
287
+ sparkAuthnClient.close?.();
288
+ return verifyResp.sessionToken;
289
+ } catch (error: unknown) {
290
+ if (isError(error)) {
291
+ sparkAuthnClient?.close?.();
292
+
293
+ if (error.message.includes("challenge expired")) {
294
+ console.warn(
295
+ `Authentication attempt ${attempt + 1} failed due to expired challenge, retrying...`,
296
+ );
297
+ lastError = error;
298
+ continue;
299
+ }
300
+
301
+ throw new AuthenticationError(
302
+ "Authentication failed",
303
+ {
304
+ endpoint: "authenticate",
305
+ reason: error.message,
306
+ },
307
+ error,
308
+ );
309
+ } else {
310
+ lastError = new Error(
311
+ `Unknown error during authentication: ${String(error)}`,
312
+ );
313
+ }
314
+ }
315
+ }
265
316
 
266
- sparkAuthnClient.close?.();
267
- return verifyResp.sessionToken;
268
- } catch (error: any) {
269
- console.error("Authentication error:", error);
270
317
  throw new AuthenticationError(
271
- "Authentication failed",
318
+ "Authentication failed after retrying expired challenges",
272
319
  {
273
320
  endpoint: "authenticate",
274
- reason: error.message,
321
+ reason: lastError?.message ?? "Unknown error",
275
322
  },
276
- error,
323
+ lastError,
277
324
  );
325
+ })();
326
+
327
+ this.authPromises.set(address, authPromise);
328
+
329
+ try {
330
+ return await authPromise;
331
+ } finally {
332
+ this.authPromises.delete(address);
278
333
  }
279
334
  }
280
335
 
281
336
  private async createSparkAuthnGrpcConnection(
282
337
  address: string,
283
338
  certPath?: string,
284
- ): Promise<SparkAuthnServiceClient & { close?: () => void }> {
339
+ ): Promise<SparkAuthnServiceClientWithClose> {
285
340
  const channel = await this.createChannelWithTLS(address, certPath);
286
341
  const authnMiddleware = this.createAuthnMiddleware();
287
342
  return this.createGrpcClient<SparkAuthnServiceClient>(
@@ -335,6 +390,32 @@ export class ConnectionManager {
335
390
  }
336
391
  }
337
392
 
393
+ private async *handleMiddlewareError(
394
+ error: unknown,
395
+ address: string,
396
+ call: ClientMiddlewareCall<any, any>,
397
+ metadata: Metadata,
398
+ options: SparkCallOptions,
399
+ ) {
400
+ if (isError(error)) {
401
+ if (error.message.includes("token has expired")) {
402
+ const newAuthToken = await this.authenticate(address);
403
+ const clientData = this.clients.get(address);
404
+ if (!clientData) {
405
+ throw new Error(`No client found for address: ${address}`);
406
+ }
407
+ clientData.authToken = newAuthToken;
408
+
409
+ return yield* call.next(call.request, {
410
+ ...options,
411
+ metadata: metadata.set("Authorization", `Bearer ${newAuthToken}`),
412
+ });
413
+ }
414
+ }
415
+
416
+ throw error;
417
+ }
418
+
338
419
  private createNodeMiddleware(address: string, initialAuthToken: string) {
339
420
  return async function* (
340
421
  this: ConnectionManager,
@@ -353,21 +434,14 @@ export class ConnectionManager {
353
434
  `Bearer ${this.clients.get(address)?.authToken || initialAuthToken}`,
354
435
  ),
355
436
  });
356
- } catch (error: any) {
357
- if (error.message?.includes("token has expired")) {
358
- const newAuthToken = await this.authenticate(address);
359
- const clientData = this.clients.get(address);
360
- if (!clientData) {
361
- throw new Error(`No client found for address: ${address}`);
362
- }
363
- clientData.authToken = newAuthToken;
364
-
365
- return yield* call.next(call.request, {
366
- ...options,
367
- metadata: metadata.set("Authorization", `Bearer ${newAuthToken}`),
368
- });
369
- }
370
- throw error;
437
+ } catch (error: unknown) {
438
+ return yield* this.handleMiddlewareError(
439
+ error,
440
+ address,
441
+ call,
442
+ metadata,
443
+ options,
444
+ );
371
445
  }
372
446
  }.bind(this);
373
447
  }
@@ -393,20 +467,13 @@ export class ConnectionManager {
393
467
  ),
394
468
  });
395
469
  } catch (error: any) {
396
- if (error.message?.includes("token has expired")) {
397
- const newAuthToken = await this.authenticate(address);
398
- const clientData = this.clients.get(address);
399
- if (!clientData) {
400
- throw new Error(`No client found for address: ${address}`);
401
- }
402
- clientData.authToken = newAuthToken;
403
-
404
- return yield* call.next(call.request, {
405
- ...options,
406
- metadata: metadata.set("Authorization", `Bearer ${newAuthToken}`),
407
- });
408
- }
409
- throw error;
470
+ return yield* this.handleMiddlewareError(
471
+ error,
472
+ address,
473
+ call,
474
+ metadata,
475
+ options,
476
+ );
410
477
  }
411
478
  }.bind(this);
412
479
  }
@@ -16,8 +16,8 @@ import { getNextTransactionSequence } from "../utils/transaction.js";
16
16
  import { WalletConfigService } from "./config.js";
17
17
  import { ConnectionManager } from "./connection.js";
18
18
  import { SigningService } from "./signing.js";
19
- import { BaseTransferService, LeafRefundSigningData } from "./transfer.js";
20
19
  import type { LeafKeyTweak } from "./transfer.js";
20
+ import { BaseTransferService, LeafRefundSigningData } from "./transfer.js";
21
21
 
22
22
  export type GetConnectorRefundSignaturesParams = {
23
23
  leaves: LeafKeyTweak[];
@@ -50,6 +50,7 @@ export class CoopExitService extends BaseTransferService {
50
50
  connectorOutputs,
51
51
  receiverPubKey,
52
52
  );
53
+
53
54
  const transferTweak = await this.deliverTransferPackage(
54
55
  transfer,
55
56
  leaves,
@@ -152,7 +153,9 @@ export class CoopExitService extends BaseTransferService {
152
153
  const signingJob: LeafRefundTxSigningJob = {
153
154
  leafId: leaf.leaf.id,
154
155
  refundTxSigningJob: {
155
- signingPublicKey: leaf.signingPubKey,
156
+ signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
157
+ leaf.keyDerivation,
158
+ ),
156
159
  rawTx: refundTx.toBytes(),
157
160
  signingNonceCommitment: signingNonceCommitment,
158
161
  },
@@ -164,7 +167,7 @@ export class CoopExitService extends BaseTransferService {
164
167
  signingJobs.push(signingJob);
165
168
  const tx = getTxFromRawTxBytes(leaf.leaf.nodeTx);
166
169
  leafDataMap.set(leaf.leaf.id, {
167
- signingPubKey: leaf.signingPubKey,
170
+ keyDerivation: leaf.keyDerivation,
168
171
  refundTx,
169
172
  signingNonceCommitment,
170
173
  tx,
@@ -12,6 +12,7 @@ import {
12
12
  GenerateDepositAddressResponse,
13
13
  StartDepositTreeCreationResponse,
14
14
  } from "../proto/spark.js";
15
+ import { KeyDerivation } from "../signer/types.js";
15
16
  import {
16
17
  getP2TRAddressFromPublicKey,
17
18
  getSigHashFromTx,
@@ -20,10 +21,7 @@ import {
20
21
  import { subtractPublicKeys } from "../utils/keys.js";
21
22
  import { getNetwork } from "../utils/network.js";
22
23
  import { proofOfPossessionMessageHashForDepositAddress } from "../utils/proof.js";
23
- import {
24
- DEFAULT_FEE_SATS,
25
- getEphemeralAnchorOutput,
26
- } from "../utils/transaction.js";
24
+ import { getEphemeralAnchorOutput } from "../utils/transaction.js";
27
25
  import { WalletConfigService } from "./config.js";
28
26
  import { ConnectionManager } from "./connection.js";
29
27
 
@@ -39,7 +37,7 @@ export type GenerateDepositAddressParams = {
39
37
  };
40
38
 
41
39
  export type CreateTreeRootParams = {
42
- signingPubKey: Uint8Array;
40
+ keyDerivation: KeyDerivation;
43
41
  verifyingKey: Uint8Array;
44
42
  depositTx: Transaction;
45
43
  vout: number;
@@ -182,7 +180,7 @@ export class DepositService {
182
180
  }
183
181
 
184
182
  async createTreeRoot({
185
- signingPubKey,
183
+ keyDerivation,
186
184
  verifyingKey,
187
185
  depositTx,
188
186
  vout,
@@ -242,6 +240,9 @@ export class DepositService {
242
240
  sequence,
243
241
  });
244
242
 
243
+ const signingPubKey =
244
+ await this.config.signer.getPublicKeyFromDerivation(keyDerivation);
245
+
245
246
  const refundP2trAddress = getP2TRAddressFromPublicKey(
246
247
  signingPubKey,
247
248
  this.config.getNetwork(),
@@ -346,7 +347,7 @@ export class DepositService {
346
347
  const rootSignature = await this.config.signer.signFrost({
347
348
  message: rootTxSighash,
348
349
  publicKey: signingPubKey,
349
- privateAsPubKey: signingPubKey,
350
+ keyDerivation,
350
351
  verifyingKey,
351
352
  selfCommitment: rootNonceCommitment,
352
353
  statechainCommitments:
@@ -358,7 +359,7 @@ export class DepositService {
358
359
  const refundSignature = await this.config.signer.signFrost({
359
360
  message: refundTxSighash,
360
361
  publicKey: signingPubKey,
361
- privateAsPubKey: signingPubKey,
362
+ keyDerivation,
362
363
  verifyingKey,
363
364
  selfCommitment: refundNonceCommitment,
364
365
  statechainCommitments:
@@ -25,8 +25,6 @@ import { SigningService } from "./signing.js";
25
25
  import type { LeafKeyTweak } from "./transfer.js";
26
26
  import { decodeInvoice } from "./bolt11-spark.js";
27
27
 
28
- const crypto = getCrypto();
29
-
30
28
  export type CreateLightningInvoiceParams = {
31
29
  invoiceCreator: (
32
30
  amountSats: number,
@@ -76,6 +74,7 @@ export class LightningService {
76
74
  receiverIdentityPubkey,
77
75
  descriptionHash,
78
76
  }: CreateLightningInvoiceParams): Promise<LightningReceiveRequest> {
77
+ const crypto = getCrypto();
79
78
  const randBytes = crypto.getRandomValues(new Uint8Array(32));
80
79
  const preimage = numberToBytesBE(
81
80
  bytesToNumberBE(randBytes) % secp256k1.CURVE.n,
@@ -237,7 +236,9 @@ export class LightningService {
237
236
  );
238
237
  }
239
238
 
240
- amountSats = isZeroAmountInvoice ? amountSatsToSend! : amountMsats / 1000;
239
+ amountSats = isZeroAmountInvoice
240
+ ? amountSatsToSend!
241
+ : Math.ceil(amountMsats / 1000);
241
242
 
242
243
  if (isNaN(amountSats) || amountSats <= 0) {
243
244
  throw new ValidationError("Invalid amount", {
@@ -1,12 +1,10 @@
1
- import { TransactionInput } from "@scure/btc-signer/psbt";
2
1
  import { hexToBytes } from "@noble/curves/abstract/utils";
2
+ import { TransactionInput } from "@scure/btc-signer/psbt";
3
3
  import { ValidationError } from "../errors/types.js";
4
4
  import {
5
5
  RequestedSigningCommitments,
6
6
  UserSignedTxSigningJob,
7
7
  } from "../proto/spark.js";
8
- import type { LeafKeyTweak } from "./transfer.js";
9
- import { WalletConfigService } from "./config.js";
10
8
  import {
11
9
  getSigHashFromTx,
12
10
  getTxFromRawTxBytes,
@@ -16,6 +14,8 @@ import {
16
14
  createRefundTx,
17
15
  getNextTransactionSequence,
18
16
  } from "../utils/transaction.js";
17
+ import { WalletConfigService } from "./config.js";
18
+ import type { LeafKeyTweak } from "./transfer.js";
19
19
 
20
20
  export class SigningService {
21
21
  private readonly config: WalletConfigService;
@@ -83,8 +83,10 @@ export class SigningService {
83
83
  }
84
84
  const signingResult = await this.config.signer.signFrost({
85
85
  message: sighash,
86
- publicKey: leaf.signingPubKey,
87
- privateAsPubKey: leaf.signingPubKey,
86
+ keyDerivation: leaf.keyDerivation,
87
+ publicKey: await this.config.signer.getPublicKeyFromDerivation(
88
+ leaf.keyDerivation,
89
+ ),
88
90
  selfCommitment: signingCommitment,
89
91
  statechainCommitments: signingNonceCommitments,
90
92
  adaptorPubKey: new Uint8Array(),
@@ -93,7 +95,9 @@ export class SigningService {
93
95
 
94
96
  leafSigningJobs.push({
95
97
  leafId: leaf.leaf.id,
96
- signingPublicKey: leaf.signingPubKey,
98
+ signingPublicKey: await this.config.signer.getPublicKeyFromDerivation(
99
+ leaf.keyDerivation,
100
+ ),
97
101
  rawTx: refundTx.toBytes(),
98
102
  signingNonceCommitment: signingCommitment,
99
103
  userSignature: signingResult,