@buildonspark/spark-sdk 0.1.41 → 0.1.43
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.
- package/CHANGELOG.md +15 -0
- package/android/src/main/jniLibs/arm64-v8a/libuniffi_spark_frost.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libuniffi_spark_frost.so +0 -0
- package/android/src/main/jniLibs/x86/libuniffi_spark_frost.so +0 -0
- package/android/src/main/jniLibs/x86_64/libuniffi_spark_frost.so +0 -0
- package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.cts +10 -0
- package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.ts +10 -0
- package/dist/{RequestLightningSendInput-mXUWn_cp.d.ts → RequestLightningSendInput-D7fZdT4A.d.ts} +35 -16
- package/dist/{RequestLightningSendInput-DXcLoiCe.d.cts → RequestLightningSendInput-Na1mHdWg.d.cts} +35 -16
- package/dist/address/index.cjs +38 -7
- package/dist/address/index.d.cts +2 -2
- package/dist/address/index.d.ts +2 -2
- package/dist/address/index.js +3 -3
- package/dist/{chunk-ATEHMLKP.js → chunk-6AFUC5M2.js} +1 -1
- package/dist/{chunk-ZXDE2XMU.js → chunk-BUTZWYBW.js} +9 -6
- package/dist/{chunk-7EFSUADA.js → chunk-DOA6QXYQ.js} +1 -0
- package/dist/{chunk-J5W5Q2ZP.js → chunk-DQYKQJRZ.js} +291 -7
- package/dist/{chunk-TWF35O6M.js → chunk-GSI4OLXZ.js} +32 -1
- package/dist/{chunk-2ZXXLPG2.js → chunk-GYQR4B4P.js} +5 -4
- package/dist/{chunk-ROKY5KS4.js → chunk-HRQRRDSS.js} +53 -15
- package/dist/{chunk-YEZDPUFY.js → chunk-IRW5TWMH.js} +8 -8
- package/dist/{chunk-7VMYMQLF.js → chunk-NSJF5F5O.js} +1 -1
- package/dist/{chunk-TM4TOEOX.js → chunk-O4RYNJNB.js} +3 -3
- package/dist/{chunk-MGPRLH6Q.js → chunk-QNNSEJ4P.js} +1 -1
- package/dist/{chunk-UKT6OFLO.js → chunk-TIUBYNN5.js} +13 -7
- package/dist/{chunk-6YVPOQ2A.js → chunk-TOSP3INR.js} +235 -143
- package/dist/chunk-VFJQNBFX.js +21 -0
- package/dist/{chunk-KKSU7OZO.js → chunk-WWOTVNPP.js} +195 -67
- package/dist/{chunk-HK6LPV6Z.js → chunk-Z5HIAYFT.js} +1 -1
- package/dist/graphql/objects/index.cjs +229 -135
- package/dist/graphql/objects/index.d.cts +54 -9
- package/dist/graphql/objects/index.d.ts +54 -9
- package/dist/graphql/objects/index.js +3 -3
- package/dist/{index-OSDtPMmC.d.ts → index-7RYRH5wc.d.ts} +10 -8
- package/dist/{index-CFh4uWzi.d.cts → index-BJOc8Ur-.d.cts} +10 -8
- package/dist/index.cjs +790 -315
- package/dist/index.d.cts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +26 -26
- package/dist/index.node.cjs +790 -315
- package/dist/index.node.d.cts +9 -8
- package/dist/index.node.d.ts +9 -8
- package/dist/index.node.js +26 -26
- package/dist/native/index.cjs +812 -332
- package/dist/native/index.d.cts +53 -18
- package/dist/native/index.d.ts +53 -18
- package/dist/native/index.js +659 -181
- package/dist/{network-BiwBmoOg.d.cts → network-D5lKssVl.d.cts} +1 -1
- package/dist/{network-BF2GYPye.d.ts → network-xkBSpaTn.d.ts} +1 -1
- package/dist/proto/lrc20.d.cts +1 -1
- package/dist/proto/lrc20.d.ts +1 -1
- package/dist/proto/spark.d.cts +1 -1
- package/dist/proto/spark.d.ts +1 -1
- package/dist/proto/spark_token.d.cts +1 -1
- package/dist/proto/spark_token.d.ts +1 -1
- package/dist/{sdk-types-CfhdFnsA.d.cts → sdk-types-B-q9py_P.d.cts} +1 -1
- package/dist/{sdk-types-MnQrHolg.d.ts → sdk-types-BPoPgzda.d.ts} +1 -1
- package/dist/services/config.cjs +118 -69
- package/dist/services/config.d.cts +6 -4
- package/dist/services/config.d.ts +6 -4
- package/dist/services/config.js +9 -9
- package/dist/services/connection.cjs +95 -15
- package/dist/services/connection.d.cts +6 -4
- package/dist/services/connection.d.ts +6 -4
- package/dist/services/connection.js +3 -3
- package/dist/services/index.cjs +487 -117
- package/dist/services/index.d.cts +5 -4
- package/dist/services/index.d.ts +5 -4
- package/dist/services/index.js +19 -19
- package/dist/services/lrc-connection.cjs +50 -7
- package/dist/services/lrc-connection.d.cts +5 -4
- package/dist/services/lrc-connection.d.ts +5 -4
- package/dist/services/lrc-connection.js +3 -3
- package/dist/services/token-transactions.cjs +351 -36
- package/dist/services/token-transactions.d.cts +5 -4
- package/dist/services/token-transactions.d.ts +5 -4
- package/dist/services/token-transactions.js +6 -6
- package/dist/services/wallet-config.cjs +1 -0
- package/dist/services/wallet-config.d.cts +6 -4
- package/dist/services/wallet-config.d.ts +6 -4
- package/dist/services/wallet-config.js +1 -1
- package/dist/signer/signer.cjs +117 -64
- package/dist/signer/signer.d.cts +4 -3
- package/dist/signer/signer.d.ts +4 -3
- package/dist/signer/signer.js +15 -7
- package/dist/{signer-CylxIujU.d.ts → signer-IO3oMRNj.d.cts} +2 -1
- package/dist/{signer-BhLS7SYR.d.cts → signer-wqesWifN.d.ts} +2 -1
- package/dist/{spark-DjR1b3TC.d.cts → spark-CDm4gqS6.d.cts} +1 -1
- package/dist/{spark-DjR1b3TC.d.ts → spark-CDm4gqS6.d.ts} +1 -1
- package/dist/types/index.cjs +282 -188
- package/dist/types/index.d.cts +7 -6
- package/dist/types/index.d.ts +7 -6
- package/dist/types/index.js +3 -3
- package/dist/utils/index.cjs +90 -58
- package/dist/utils/index.d.cts +6 -5
- package/dist/utils/index.d.ts +6 -5
- package/dist/utils/index.js +16 -16
- package/ios/spark_frostFFI.xcframework/ios-arm64/SparkFrost +0 -0
- package/ios/spark_frostFFI.xcframework/ios-arm64/spark_frostFFI.framework/spark_frostFFI +0 -0
- package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/SparkFrost +0 -0
- package/ios/spark_frostFFI.xcframework/ios-arm64_x86_64-simulator/spark_frostFFI.framework/spark_frostFFI +0 -0
- package/ios/spark_frostFFI.xcframework/macos-arm64_x86_64/spark_frostFFI.framework/spark_frostFFI +0 -0
- package/package.json +4 -4
- package/src/constants.ts +21 -0
- package/src/errors/base.ts +43 -1
- package/src/graphql/client.ts +4 -0
- package/src/graphql/mutations/RequestLightningSend.ts +2 -0
- package/src/graphql/objects/ClaimStaticDepositInput.ts +1 -1
- package/src/graphql/objects/ClaimStaticDepositStatus.ts +4 -2
- package/src/graphql/objects/Connection.ts +7 -7
- package/src/graphql/objects/CoopExitFeeEstimate.ts +1 -1
- package/src/graphql/objects/CoopExitFeeQuote.ts +202 -0
- package/src/graphql/objects/CoopExitFeeQuoteInput.ts +41 -0
- package/src/graphql/objects/CoopExitFeeQuoteOutput.ts +45 -0
- package/src/graphql/objects/CoopExitRequest.ts +21 -0
- package/src/graphql/objects/CurrencyUnit.ts +26 -28
- package/src/graphql/objects/Entity.ts +84 -0
- package/src/graphql/objects/Invoice.ts +2 -2
- package/src/graphql/objects/Leaf.ts +1 -1
- package/src/graphql/objects/LeavesSwapFeeEstimateOutput.ts +1 -1
- package/src/graphql/objects/LeavesSwapRequest.ts +6 -0
- package/src/graphql/objects/LightningReceiveRequest.ts +11 -0
- package/src/graphql/objects/LightningSendFeeEstimateInput.ts +8 -0
- package/src/graphql/objects/LightningSendFeeEstimateOutput.ts +1 -1
- package/src/graphql/objects/LightningSendRequest.ts +3 -0
- package/src/graphql/objects/RequestCoopExitInput.ts +8 -0
- package/src/graphql/objects/RequestLeavesSwapInput.ts +5 -1
- package/src/graphql/objects/RequestLightningReceiveInput.ts +9 -2
- package/src/graphql/objects/RequestLightningSendInput.ts +8 -0
- package/src/graphql/objects/SparkCoopExitRequestStatus.ts +2 -0
- package/src/graphql/objects/SparkUserRequestType.ts +2 -0
- package/src/graphql/objects/SparkWalletUser.ts +20 -0
- package/src/graphql/objects/UserRequest.ts +32 -0
- package/src/graphql/objects/VerifyChallengeInput.ts +1 -1
- package/src/graphql/objects/index.ts +12 -3
- package/src/graphql/queries/LightningSendFeeEstimate.ts +2 -0
- package/src/logger.ts +3 -0
- package/src/native/index.ts +1 -0
- package/src/services/config.ts +4 -0
- package/src/services/connection.ts +68 -29
- package/src/services/coop-exit.ts +1 -1
- package/src/services/lightning.ts +25 -1
- package/src/services/lrc-connection.ts +3 -3
- package/src/services/token-transactions.ts +6 -2
- package/src/services/wallet-config.ts +2 -0
- package/src/signer/signer.ts +4 -1
- package/src/spark-wallet/spark-wallet.ts +51 -15
- package/src/spark-wallet/types.ts +1 -0
- package/src/tests/errors.test.ts +58 -0
- package/src/tests/integration/lightning.test.ts +184 -0
- package/src/tests/integration/ssp/static_deposit.test.ts +1 -2
- package/src/tests/tokens.test.ts +52 -3
- package/src/utils/token-hashing.ts +335 -1
- package/dist/LightningSendFeeEstimateInput-CJvPnCSB.d.cts +0 -5
- package/dist/LightningSendFeeEstimateInput-CJvPnCSB.d.ts +0 -5
- package/dist/chunk-HKAKEKCE.js +0 -8
package/src/native/index.ts
CHANGED
|
@@ -4,4 +4,5 @@ export { ReactNativeSparkSigner } from "../signer/signer.react-native.js";
|
|
|
4
4
|
export { ReactNativeSparkSigner as DefaultSparkSigner } from "../signer/signer.react-native.js";
|
|
5
5
|
export { SparkWallet } from "../spark-wallet/spark-wallet.js";
|
|
6
6
|
export { getLatestDepositTxId } from "../utils/mempool.js";
|
|
7
|
+
export { createDummyTx } from "../spark_bindings/native/index.js";
|
|
7
8
|
export * from "../utils/index.js";
|
package/src/services/config.ts
CHANGED
|
@@ -118,6 +118,10 @@ export class WalletConfigService
|
|
|
118
118
|
return this.config.tokenTransactionVersion;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
public getTokenValidityDurationSeconds(): number {
|
|
122
|
+
return this.config.tokenValidityDurationSeconds;
|
|
123
|
+
}
|
|
124
|
+
|
|
121
125
|
public getElectrsUrl(): string {
|
|
122
126
|
return this.config.electrsUrl;
|
|
123
127
|
}
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
Channel as ChannelWeb,
|
|
8
8
|
ClientFactory as ClientFactoryWeb,
|
|
9
9
|
} from "nice-grpc-web";
|
|
10
|
-
import { isBun, isReactNative } from "../constants.js";
|
|
10
|
+
import { isBun, isReactNative, clientEnv } from "../constants.js";
|
|
11
11
|
import { AuthenticationError, NetworkError } from "../errors/types.js";
|
|
12
12
|
import { MockServiceClient, MockServiceDefinition } from "../proto/mock.js";
|
|
13
13
|
import { SparkServiceClient, SparkServiceDefinition } from "../proto/spark.js";
|
|
@@ -283,13 +283,50 @@ export class ConnectionManager {
|
|
|
283
283
|
certPath?: string,
|
|
284
284
|
): Promise<SparkAuthnServiceClient & { close?: () => void }> {
|
|
285
285
|
const channel = await this.createChannelWithTLS(address, certPath);
|
|
286
|
+
const authnMiddleware = this.createAuthnMiddleware();
|
|
286
287
|
return this.createGrpcClient<SparkAuthnServiceClient>(
|
|
287
288
|
SparkAuthnServiceDefinition,
|
|
288
289
|
channel,
|
|
289
290
|
false,
|
|
291
|
+
authnMiddleware,
|
|
290
292
|
);
|
|
291
293
|
}
|
|
292
294
|
|
|
295
|
+
private createAuthnMiddleware() {
|
|
296
|
+
if (isNode) {
|
|
297
|
+
return async function* (
|
|
298
|
+
this: ConnectionManager,
|
|
299
|
+
call: ClientMiddlewareCall<any, any>,
|
|
300
|
+
options: SparkCallOptions,
|
|
301
|
+
) {
|
|
302
|
+
const metadata = Metadata(options.metadata).set(
|
|
303
|
+
"X-Client-Env",
|
|
304
|
+
clientEnv,
|
|
305
|
+
);
|
|
306
|
+
return yield* call.next(call.request, {
|
|
307
|
+
...options,
|
|
308
|
+
metadata,
|
|
309
|
+
});
|
|
310
|
+
}.bind(this);
|
|
311
|
+
} else {
|
|
312
|
+
return async function* (
|
|
313
|
+
this: ConnectionManager,
|
|
314
|
+
call: ClientMiddlewareCall<any, any>,
|
|
315
|
+
options: SparkCallOptions,
|
|
316
|
+
) {
|
|
317
|
+
const metadata = Metadata(options.metadata)
|
|
318
|
+
.set("X-Requested-With", "XMLHttpRequest")
|
|
319
|
+
.set("X-Grpc-Web", "1")
|
|
320
|
+
.set("X-Client-Env", clientEnv)
|
|
321
|
+
.set("Content-Type", "application/grpc-web+proto");
|
|
322
|
+
return yield* call.next(call.request, {
|
|
323
|
+
...options,
|
|
324
|
+
metadata,
|
|
325
|
+
});
|
|
326
|
+
}.bind(this);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
293
330
|
private createMiddleware(address: string, authToken: string) {
|
|
294
331
|
if (isNode) {
|
|
295
332
|
return this.createNodeMiddleware(address, authToken);
|
|
@@ -304,27 +341,30 @@ export class ConnectionManager {
|
|
|
304
341
|
call: ClientMiddlewareCall<any, any>,
|
|
305
342
|
options: SparkCallOptions,
|
|
306
343
|
) {
|
|
344
|
+
const metadata = Metadata(options.metadata).set(
|
|
345
|
+
"X-Client-Env",
|
|
346
|
+
clientEnv,
|
|
347
|
+
);
|
|
307
348
|
try {
|
|
308
349
|
return yield* call.next(call.request, {
|
|
309
350
|
...options,
|
|
310
|
-
metadata:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
)
|
|
315
|
-
.set("User-Agent", "spark-js-sdk"),
|
|
351
|
+
metadata: metadata.set(
|
|
352
|
+
"Authorization",
|
|
353
|
+
`Bearer ${this.clients.get(address)?.authToken || initialAuthToken}`,
|
|
354
|
+
),
|
|
316
355
|
});
|
|
317
356
|
} catch (error: any) {
|
|
318
357
|
if (error.message?.includes("token has expired")) {
|
|
319
358
|
const newAuthToken = await this.authenticate(address);
|
|
320
|
-
|
|
321
|
-
|
|
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;
|
|
322
364
|
|
|
323
365
|
return yield* call.next(call.request, {
|
|
324
366
|
...options,
|
|
325
|
-
metadata:
|
|
326
|
-
.set("Authorization", `Bearer ${newAuthToken}`)
|
|
327
|
-
.set("User-Agent", "spark-js-sdk"),
|
|
367
|
+
metadata: metadata.set("Authorization", `Bearer ${newAuthToken}`),
|
|
328
368
|
});
|
|
329
369
|
}
|
|
330
370
|
throw error;
|
|
@@ -338,33 +378,32 @@ export class ConnectionManager {
|
|
|
338
378
|
call: ClientMiddlewareCall<any, any>,
|
|
339
379
|
options: SparkCallOptions,
|
|
340
380
|
) {
|
|
381
|
+
const metadata = Metadata(options.metadata)
|
|
382
|
+
.set("X-Requested-With", "XMLHttpRequest")
|
|
383
|
+
.set("X-Grpc-Web", "1")
|
|
384
|
+
.set("X-Client-Env", clientEnv)
|
|
385
|
+
.set("Content-Type", "application/grpc-web+proto");
|
|
386
|
+
|
|
341
387
|
try {
|
|
342
388
|
return yield* call.next(call.request, {
|
|
343
389
|
...options,
|
|
344
|
-
metadata:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
)
|
|
349
|
-
.set("X-Requested-With", "XMLHttpRequest")
|
|
350
|
-
.set("X-Grpc-Web", "1")
|
|
351
|
-
.set("Content-Type", "application/grpc-web+proto")
|
|
352
|
-
.set("User-Agent", "spark-js-sdk"),
|
|
390
|
+
metadata: metadata.set(
|
|
391
|
+
"Authorization",
|
|
392
|
+
`Bearer ${this.clients.get(address)?.authToken || initialAuthToken}`,
|
|
393
|
+
),
|
|
353
394
|
});
|
|
354
395
|
} catch (error: any) {
|
|
355
396
|
if (error.message?.includes("token has expired")) {
|
|
356
397
|
const newAuthToken = await this.authenticate(address);
|
|
357
|
-
|
|
358
|
-
|
|
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;
|
|
359
403
|
|
|
360
404
|
return yield* call.next(call.request, {
|
|
361
405
|
...options,
|
|
362
|
-
metadata:
|
|
363
|
-
.set("Authorization", `Bearer ${newAuthToken}`)
|
|
364
|
-
.set("X-Requested-With", "XMLHttpRequest")
|
|
365
|
-
.set("X-Grpc-Web", "1")
|
|
366
|
-
.set("Content-Type", "application/grpc-web+proto")
|
|
367
|
-
.set("User-Agent", "spark-js-sdk"),
|
|
406
|
+
metadata: metadata.set("Authorization", `Bearer ${newAuthToken}`),
|
|
368
407
|
});
|
|
369
408
|
}
|
|
370
409
|
throw error;
|
|
@@ -50,7 +50,7 @@ export class CoopExitService extends BaseTransferService {
|
|
|
50
50
|
connectorOutputs,
|
|
51
51
|
receiverPubKey,
|
|
52
52
|
);
|
|
53
|
-
const transferTweak = await this.
|
|
53
|
+
const transferTweak = await this.deliverTransferPackage(
|
|
54
54
|
transfer,
|
|
55
55
|
leaves,
|
|
56
56
|
signaturesMap,
|
|
@@ -52,6 +52,7 @@ export type SwapNodesForPreimageParams = {
|
|
|
52
52
|
invoiceString?: string;
|
|
53
53
|
isInboundPayment: boolean;
|
|
54
54
|
feeSats?: number;
|
|
55
|
+
amountSatsToSend?: number;
|
|
55
56
|
};
|
|
56
57
|
|
|
57
58
|
export class LightningService {
|
|
@@ -182,6 +183,7 @@ export class LightningService {
|
|
|
182
183
|
invoiceString,
|
|
183
184
|
isInboundPayment,
|
|
184
185
|
feeSats = 0,
|
|
186
|
+
amountSatsToSend,
|
|
185
187
|
}: SwapNodesForPreimageParams): Promise<InitiatePreimageSwapResponse> {
|
|
186
188
|
const sparkClient = await this.connectionManager.createSparkClient(
|
|
187
189
|
this.config.getCoordinatorAddress(),
|
|
@@ -222,7 +224,29 @@ export class LightningService {
|
|
|
222
224
|
console.error("Error decoding invoice", error);
|
|
223
225
|
}
|
|
224
226
|
|
|
225
|
-
|
|
227
|
+
const isZeroAmountInvoice = !amountMsats;
|
|
228
|
+
|
|
229
|
+
if (isZeroAmountInvoice && amountSatsToSend === undefined) {
|
|
230
|
+
throw new ValidationError(
|
|
231
|
+
"Invalid amount. User must specify amountSatsToSend for 0 amount lightning invoice",
|
|
232
|
+
{
|
|
233
|
+
field: "amountSatsToSend",
|
|
234
|
+
value: amountSatsToSend,
|
|
235
|
+
expected: "positive number",
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
amountSats = isZeroAmountInvoice ? amountSatsToSend! : amountMsats / 1000;
|
|
241
|
+
|
|
242
|
+
if (isNaN(amountSats) || amountSats <= 0) {
|
|
243
|
+
throw new ValidationError("Invalid amount", {
|
|
244
|
+
field: "amountSats",
|
|
245
|
+
value: amountSats,
|
|
246
|
+
expected: "greater than 0",
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
226
250
|
bolt11String = invoiceString;
|
|
227
251
|
}
|
|
228
252
|
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
Channel as ChannelWeb,
|
|
7
7
|
ClientFactory as ClientFactoryWeb,
|
|
8
8
|
} from "nice-grpc-web";
|
|
9
|
-
import { isBun, isReactNative } from "../constants.js";
|
|
9
|
+
import { isBun, isReactNative, clientEnv } from "../constants.js";
|
|
10
10
|
import { NetworkError } from "../errors/types.js";
|
|
11
11
|
import { SparkServiceClient, SparkServiceDefinition } from "../proto/lrc20.js";
|
|
12
12
|
import { RetryOptions, SparkCallOptions } from "../types/grpc.js";
|
|
@@ -126,7 +126,7 @@ export class Lrc20ConnectionManager {
|
|
|
126
126
|
) {
|
|
127
127
|
return yield* call.next(call.request, {
|
|
128
128
|
...options,
|
|
129
|
-
metadata: Metadata(options.metadata).set("
|
|
129
|
+
metadata: Metadata(options.metadata).set("X-Client-Env", clientEnv),
|
|
130
130
|
});
|
|
131
131
|
}.bind(this);
|
|
132
132
|
}
|
|
@@ -143,7 +143,7 @@ export class Lrc20ConnectionManager {
|
|
|
143
143
|
.set("X-Requested-With", "XMLHttpRequest")
|
|
144
144
|
.set("X-Grpc-Web", "1")
|
|
145
145
|
.set("Content-Type", "application/grpc-web+proto")
|
|
146
|
-
.set("
|
|
146
|
+
.set("X-Client-Env", clientEnv),
|
|
147
147
|
});
|
|
148
148
|
}.bind(this);
|
|
149
149
|
}
|
|
@@ -14,6 +14,7 @@ import { secp256k1 } from "@noble/curves/secp256k1";
|
|
|
14
14
|
import { SparkCallOptions } from "../types/grpc.js";
|
|
15
15
|
import {
|
|
16
16
|
hashOperatorSpecificTokenTransactionSignablePayload,
|
|
17
|
+
hashTokenTransactionV0,
|
|
17
18
|
hashTokenTransaction,
|
|
18
19
|
} from "../utils/token-hashing.js";
|
|
19
20
|
import {
|
|
@@ -456,7 +457,7 @@ export class TokenTransactionService {
|
|
|
456
457
|
this.config.getCoordinatorAddress(),
|
|
457
458
|
);
|
|
458
459
|
|
|
459
|
-
const partialTokenTransactionHash =
|
|
460
|
+
const partialTokenTransactionHash = hashTokenTransactionV0(
|
|
460
461
|
tokenTransaction,
|
|
461
462
|
true,
|
|
462
463
|
);
|
|
@@ -547,7 +548,7 @@ export class TokenTransactionService {
|
|
|
547
548
|
);
|
|
548
549
|
|
|
549
550
|
const finalTokenTransaction = startResponse.finalTokenTransaction;
|
|
550
|
-
const finalTokenTransactionHash =
|
|
551
|
+
const finalTokenTransactionHash = hashTokenTransactionV0(
|
|
551
552
|
finalTokenTransaction,
|
|
552
553
|
false,
|
|
553
554
|
);
|
|
@@ -635,6 +636,9 @@ export class TokenTransactionService {
|
|
|
635
636
|
{
|
|
636
637
|
identityPublicKey: await this.config.signer.getIdentityPublicKey(),
|
|
637
638
|
partialTokenTransaction: tokenTransaction,
|
|
639
|
+
validityDurationSeconds:
|
|
640
|
+
await this.config.getTokenValidityDurationSeconds(),
|
|
641
|
+
partialTokenTransactionOwnerSignatures: ownerSignaturesWithIndex,
|
|
638
642
|
},
|
|
639
643
|
{
|
|
640
644
|
retry: true,
|
|
@@ -141,6 +141,7 @@ export type ConfigOptions = MayHaveLrc20WalletApiConfig &
|
|
|
141
141
|
readonly lrc20Address?: string;
|
|
142
142
|
readonly threshold?: number;
|
|
143
143
|
readonly tokenSignatures?: "ECDSA" | "SCHNORR";
|
|
144
|
+
readonly tokenValidityDurationSeconds?: number;
|
|
144
145
|
readonly tokenTransactionVersion?: "V0" | "V1";
|
|
145
146
|
readonly electrsUrl?: string;
|
|
146
147
|
readonly lrc20ApiConfig?: LRC20WalletApiConfig;
|
|
@@ -175,6 +176,7 @@ const BASE_CONFIG: Required<ConfigOptions> = {
|
|
|
175
176
|
signingOperators: getLocalSigningOperators(),
|
|
176
177
|
tokenSignatures: "SCHNORR",
|
|
177
178
|
tokenTransactionVersion: "V0",
|
|
179
|
+
tokenValidityDurationSeconds: 180,
|
|
178
180
|
electrsUrl: getElectrsUrl("LOCAL"),
|
|
179
181
|
expectedWithdrawBondSats: 10000,
|
|
180
182
|
expectedWithdrawRelativeBlockLocktime: 1000,
|
package/src/signer/signer.ts
CHANGED
|
@@ -48,6 +48,9 @@ import { Transaction } from "@scure/btc-signer";
|
|
|
48
48
|
import { taprootTweakPrivKey } from "@scure/btc-signer/utils";
|
|
49
49
|
import type { Psbt } from "bitcoinjs-lib";
|
|
50
50
|
|
|
51
|
+
export { Receipt, PARITY, fromPrivateKey } from "@buildonspark/lrc20-sdk";
|
|
52
|
+
export { MultisigReceiptInput } from "@buildonspark/lrc20-sdk/lrc/types";
|
|
53
|
+
|
|
51
54
|
export type SigningNonce = {
|
|
52
55
|
binding: Uint8Array;
|
|
53
56
|
hiding: Uint8Array;
|
|
@@ -934,4 +937,4 @@ class TaprootSparkSigner extends DefaultSparkSigner {
|
|
|
934
937
|
}
|
|
935
938
|
|
|
936
939
|
export { DefaultSparkSigner, TaprootSparkSigner, TaprootOutputKeysGenerator };
|
|
937
|
-
export type { SparkSigner };
|
|
940
|
+
export type { SparkSigner, TokenSigner };
|
|
@@ -2532,12 +2532,14 @@ export class SparkWallet extends EventEmitter {
|
|
|
2532
2532
|
* @param {Object} params - Parameters for paying the invoice
|
|
2533
2533
|
* @param {string} params.invoice - The BOLT11-encoded Lightning invoice to pay
|
|
2534
2534
|
* @param {boolean} [params.preferSpark] - Whether to prefer a spark transfer over lightning for the payment
|
|
2535
|
+
* @param {number} [params.amountSatsToSend] - The amount in sats to send. This is only valid for 0 amount lightning invoices.
|
|
2535
2536
|
* @returns {Promise<LightningSendRequest>} The Lightning payment request details
|
|
2536
2537
|
*/
|
|
2537
2538
|
public async payLightningInvoice({
|
|
2538
2539
|
invoice,
|
|
2539
2540
|
maxFeeSats,
|
|
2540
2541
|
preferSpark = false,
|
|
2542
|
+
amountSatsToSend,
|
|
2541
2543
|
}: PayLightningInvoiceParams) {
|
|
2542
2544
|
const invoiceNetwork = getNetworkFromInvoice(invoice);
|
|
2543
2545
|
const walletNetwork = this.config.getNetwork();
|
|
@@ -2553,10 +2555,45 @@ export class SparkWallet extends EventEmitter {
|
|
|
2553
2555
|
}
|
|
2554
2556
|
|
|
2555
2557
|
const decodedInvoice = decodeInvoice(invoice);
|
|
2556
|
-
const
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2558
|
+
const amountMSats = decodedInvoice.amountMSats;
|
|
2559
|
+
const isZeroAmountInvoice = !amountMSats;
|
|
2560
|
+
|
|
2561
|
+
// Check if user is trying to send amountSatsToSend for non 0 amount lightning invoice
|
|
2562
|
+
if (!isZeroAmountInvoice && amountSatsToSend !== undefined) {
|
|
2563
|
+
throw new ValidationError(
|
|
2564
|
+
"Invalid amount. User can only specify amountSatsToSend for 0 amount lightning invoice",
|
|
2565
|
+
{
|
|
2566
|
+
field: "amountMSats",
|
|
2567
|
+
value: Number(amountMSats),
|
|
2568
|
+
expected: "0",
|
|
2569
|
+
},
|
|
2570
|
+
);
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// If 0 amount lightning invoice, check that user has specified amountSatsToSend
|
|
2574
|
+
if (isZeroAmountInvoice && amountSatsToSend === undefined) {
|
|
2575
|
+
throw new ValidationError(
|
|
2576
|
+
"Invalid amount. User must specify amountSatsToSend for 0 amount lightning invoice",
|
|
2577
|
+
{
|
|
2578
|
+
field: "amountMSats",
|
|
2579
|
+
value: Number(amountMSats),
|
|
2580
|
+
expected: "0",
|
|
2581
|
+
},
|
|
2582
|
+
);
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
const amountSats = isZeroAmountInvoice
|
|
2586
|
+
? amountSatsToSend!
|
|
2587
|
+
: Number(amountMSats / 1000n);
|
|
2588
|
+
|
|
2589
|
+
if (isNaN(amountSats) || amountSats <= 0) {
|
|
2590
|
+
throw new ValidationError("Invalid amount", {
|
|
2591
|
+
field: "amountSats",
|
|
2592
|
+
value: amountSats,
|
|
2593
|
+
expected: "greater than 0",
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2560
2597
|
const sparkFallbackAddress = decodedInvoice.fallbackAddress;
|
|
2561
2598
|
const paymentHash = decodedInvoice.paymentHash;
|
|
2562
2599
|
|
|
@@ -2586,16 +2623,10 @@ export class SparkWallet extends EventEmitter {
|
|
|
2586
2623
|
return await this.withLeaves(async () => {
|
|
2587
2624
|
const sspClient = this.getSspClient();
|
|
2588
2625
|
|
|
2589
|
-
|
|
2590
|
-
throw new ValidationError("Invalid amount", {
|
|
2591
|
-
field: "amountSats",
|
|
2592
|
-
value: amountSats,
|
|
2593
|
-
expected: "positive number",
|
|
2594
|
-
});
|
|
2595
|
-
}
|
|
2596
|
-
|
|
2626
|
+
// If 0 amount lightning invoice, use amountSatsToSend for fee estimate
|
|
2597
2627
|
const feeEstimate = await this.getLightningSendFeeEstimate({
|
|
2598
2628
|
encodedInvoice: invoice,
|
|
2629
|
+
amountSats: isZeroAmountInvoice ? amountSatsToSend! : undefined,
|
|
2599
2630
|
});
|
|
2600
2631
|
|
|
2601
2632
|
if (maxFeeSats < feeEstimate) {
|
|
@@ -2641,13 +2672,14 @@ export class SparkWallet extends EventEmitter {
|
|
|
2641
2672
|
isInboundPayment: false,
|
|
2642
2673
|
invoiceString: invoice,
|
|
2643
2674
|
feeSats: feeEstimate,
|
|
2675
|
+
amountSatsToSend: amountSatsToSend,
|
|
2644
2676
|
});
|
|
2645
2677
|
|
|
2646
2678
|
if (!swapResponse.transfer) {
|
|
2647
2679
|
throw new Error("Failed to swap nodes for preimage");
|
|
2648
2680
|
}
|
|
2649
2681
|
|
|
2650
|
-
|
|
2682
|
+
await this.transferService.deliverTransferPackage(
|
|
2651
2683
|
swapResponse.transfer,
|
|
2652
2684
|
leavesToSend,
|
|
2653
2685
|
new Map(),
|
|
@@ -2656,6 +2688,7 @@ export class SparkWallet extends EventEmitter {
|
|
|
2656
2688
|
const sspResponse = await sspClient.requestLightningSend({
|
|
2657
2689
|
encodedInvoice: invoice,
|
|
2658
2690
|
idempotencyKey: paymentHash,
|
|
2691
|
+
amountSats: isZeroAmountInvoice ? amountSatsToSend! : undefined,
|
|
2659
2692
|
});
|
|
2660
2693
|
|
|
2661
2694
|
if (!sspResponse) {
|
|
@@ -2677,11 +2710,14 @@ export class SparkWallet extends EventEmitter {
|
|
|
2677
2710
|
*/
|
|
2678
2711
|
public async getLightningSendFeeEstimate({
|
|
2679
2712
|
encodedInvoice,
|
|
2713
|
+
amountSats,
|
|
2680
2714
|
}: LightningSendFeeEstimateInput): Promise<number> {
|
|
2681
2715
|
const sspClient = this.getSspClient();
|
|
2682
2716
|
|
|
2683
|
-
const feeEstimate =
|
|
2684
|
-
|
|
2717
|
+
const feeEstimate = await sspClient.getLightningSendFeeEstimate(
|
|
2718
|
+
encodedInvoice,
|
|
2719
|
+
amountSats,
|
|
2720
|
+
);
|
|
2685
2721
|
|
|
2686
2722
|
if (!feeEstimate) {
|
|
2687
2723
|
throw new Error("Failed to get lightning send fee estimate");
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SparkSDKError } from "../errors/base.js";
|
|
2
|
+
|
|
3
|
+
describe("SparkSDKError", () => {
|
|
4
|
+
it("should not throw and should stringify BigInt values in context", () => {
|
|
5
|
+
const bigNumber = 123n;
|
|
6
|
+
|
|
7
|
+
const err = new SparkSDKError("Test BigInt", { big: bigNumber });
|
|
8
|
+
|
|
9
|
+
expect(err.message).toContain("Context:");
|
|
10
|
+
// BigInt should be converted to a string representation
|
|
11
|
+
expect(err.message).toContain('big: "123"');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should stringify regular primitives correctly", () => {
|
|
15
|
+
const err = new SparkSDKError("Test primitives", {
|
|
16
|
+
num: 1,
|
|
17
|
+
str: "abc",
|
|
18
|
+
bool: true,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(err.message).toContain("num: 1");
|
|
22
|
+
expect(err.message).toContain('str: "abc"');
|
|
23
|
+
expect(err.message).toContain("bool: true");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should include original error message when provided", () => {
|
|
27
|
+
const original = new Error("something broke");
|
|
28
|
+
const err = new SparkSDKError("Wrapper error", {}, original);
|
|
29
|
+
|
|
30
|
+
expect(err.message).toContain("Original Error: something broke");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should stringify Uint8Array values correctly", () => {
|
|
34
|
+
const bytes = new Uint8Array([1, 2, 3]);
|
|
35
|
+
const err = new SparkSDKError("Uint8Array test", { bytes });
|
|
36
|
+
|
|
37
|
+
expect(err.message).toContain("bytes:");
|
|
38
|
+
expect(err.message).toMatch(/Uint8Array\(0x010203\)/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should generate the correct full error message", () => {
|
|
42
|
+
const original = new Error("root cause");
|
|
43
|
+
const context = {
|
|
44
|
+
big: 123n,
|
|
45
|
+
bytes: new Uint8Array([1, 2, 3]),
|
|
46
|
+
num: 42,
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
const err = new SparkSDKError("Something went wrong", context, original);
|
|
50
|
+
|
|
51
|
+
const expectedMessage =
|
|
52
|
+
"SparkSDKError: Something went wrong\n" +
|
|
53
|
+
'Context: big: "123", bytes: "Uint8Array(0x010203)", num: 42\n' +
|
|
54
|
+
"Original Error: root cause";
|
|
55
|
+
|
|
56
|
+
expect(err.message).toBe(expectedMessage);
|
|
57
|
+
});
|
|
58
|
+
});
|