@buildonspark/spark-sdk 0.3.6 → 0.3.7
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 +8 -0
- package/dist/bare/index.cjs +9240 -9125
- package/dist/bare/index.d.cts +14 -11
- package/dist/bare/index.d.ts +14 -11
- package/dist/bare/index.js +3917 -3802
- package/dist/{chunk-XPHYQ2L6.js → chunk-KDEVNW7C.js} +4895 -4779
- package/dist/{chunk-LIZFXQWK.js → chunk-P4HYYSMU.js} +1 -1
- package/dist/{chunk-EHKP3Y65.js → chunk-SRPKOCG4.js} +1 -2
- package/dist/{chunk-FJ7LTA2O.js → chunk-UYTT3C6H.js} +1 -1
- package/dist/{client-AHn11NHe.d.cts → client-Bcb7TUIp.d.cts} +11 -9
- package/dist/{client-GOlkXliC.d.ts → client-D9T58OY8.d.ts} +11 -9
- package/dist/debug.cjs +1852 -1738
- package/dist/debug.d.cts +4 -4
- package/dist/debug.d.ts +4 -4
- package/dist/debug.js +2 -2
- package/dist/graphql/objects/index.d.cts +2 -2
- package/dist/graphql/objects/index.d.ts +2 -2
- package/dist/index.cjs +174 -58
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/index.node.cjs +174 -57
- package/dist/index.node.d.cts +5 -5
- package/dist/index.node.d.ts +5 -5
- package/dist/index.node.js +2 -2
- package/dist/{logging-D7ukPwRA.d.ts → logging-JIaZZIbR.d.ts} +2 -2
- package/dist/{logging-CW3kwBaM.d.cts → logging-zkr4UlOi.d.cts} +2 -2
- package/dist/native/{index.cjs → index.react-native.cjs} +182 -62
- package/dist/native/{index.d.cts → index.react-native.d.cts} +19 -15
- package/dist/native/{index.d.ts → index.react-native.d.ts} +19 -15
- package/dist/native/{index.js → index.react-native.js} +179 -60
- package/dist/{spark-wallet-NxG55m7K.d.cts → spark-wallet-BuFrUWeE.d.cts} +4 -3
- package/dist/{spark-wallet-jwNvWvpK.d.ts → spark-wallet-CE5PYiIb.d.ts} +4 -3
- package/dist/{spark-wallet.browser-Cg4fB-Nm.d.ts → spark-wallet.browser-BwYkkOBU.d.ts} +1 -1
- package/dist/{spark-wallet.browser-Db7Y95Kt.d.cts → spark-wallet.browser-DC3jdQPW.d.cts} +1 -1
- package/dist/{spark-wallet.node-DB3ZqtJG.d.ts → spark-wallet.node-C9d2W-Nb.d.ts} +1 -1
- package/dist/{spark-wallet.node-HEG2ahNd.d.cts → spark-wallet.node-CR_zNxmy.d.cts} +1 -1
- package/dist/tests/test-utils.cjs +168 -51
- package/dist/tests/test-utils.d.cts +4 -4
- package/dist/tests/test-utils.d.ts +4 -4
- package/dist/tests/test-utils.js +4 -4
- package/dist/{token-transactions-B2-BO7Oz.d.ts → token-transactions-BZoJuvuE.d.ts} +1 -1
- package/dist/{token-transactions-BAN68xwg.d.cts → token-transactions-I_OFIoNH.d.cts} +1 -1
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +14 -4
- package/src/graphql/client.ts +6 -9
- package/src/graphql/mutations/CompleteCoopExit.ts +1 -1
- package/src/graphql/mutations/RequestCoopExit.ts +3 -1
- package/src/graphql/mutations/RequestLightningSend.ts +3 -1
- package/src/graphql/objects/CompleteCoopExitInput.ts +22 -33
- package/src/graphql/objects/RequestCoopExitInput.ts +39 -45
- package/src/graphql/objects/RequestLightningSendInput.ts +31 -39
- package/src/index.react-native.ts +21 -0
- package/src/services/config.ts +2 -2
- package/src/services/connection/connection.ts +10 -0
- package/src/services/coop-exit.ts +5 -1
- package/src/services/token-transactions.ts +8 -8
- package/src/spark-wallet/spark-wallet.browser.ts +0 -1
- package/src/spark-wallet/spark-wallet.react-native.ts +5 -3
- package/src/spark-wallet/spark-wallet.ts +56 -9
- package/src/tests/integration/coop-exit.test.ts +2 -0
- package/src/tests/integration/lightning.test.ts +5 -1
- package/src/tests/integration/ssp/coop-exit-validation.test.ts +5 -6
- package/src/tests/integration/ssp/static_deposit.test.ts +45 -35
- package/src/tests/optimize.test.ts +45 -0
- package/src/tests/token-outputs.test.ts +60 -1
- package/src/tests/utils/test-faucet.ts +12 -8
- package/src/utils/optimize.ts +226 -0
- package/src/native/index.ts +0 -21
|
@@ -14,6 +14,7 @@ import { Mutex } from "async-mutex";
|
|
|
14
14
|
import { uuidv7 } from "uuidv7";
|
|
15
15
|
import {
|
|
16
16
|
ConfigurationError,
|
|
17
|
+
InternalValidationError,
|
|
17
18
|
NetworkError,
|
|
18
19
|
NotImplementedError,
|
|
19
20
|
RPCError,
|
|
@@ -106,7 +107,7 @@ import {
|
|
|
106
107
|
isValidSparkFallback,
|
|
107
108
|
} from "../services/bolt11-spark.js";
|
|
108
109
|
import { SigningService } from "../services/signing.js";
|
|
109
|
-
import { SparkSigner } from "../signer/signer.js";
|
|
110
|
+
import { DefaultSparkSigner, SparkSigner } from "../signer/signer.js";
|
|
110
111
|
import { KeyDerivation, KeyDerivationType } from "../signer/types.js";
|
|
111
112
|
import { BitcoinFaucet } from "../tests/utils/test-faucet.js";
|
|
112
113
|
import {
|
|
@@ -125,6 +126,7 @@ import {
|
|
|
125
126
|
import { chunkArray } from "../utils/chunkArray.js";
|
|
126
127
|
import { getFetch } from "../utils/fetch.js";
|
|
127
128
|
import { addPublicKeys } from "../utils/keys.js";
|
|
129
|
+
import { maximizeUnilateralExit } from "../utils/optimize.js";
|
|
128
130
|
import { RetryContext, withRetry } from "../utils/retry.js";
|
|
129
131
|
import {
|
|
130
132
|
Bech32mTokenIdentifier,
|
|
@@ -193,9 +195,10 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
193
195
|
|
|
194
196
|
private tracer: Tracer | null = null;
|
|
195
197
|
|
|
196
|
-
constructor(options?: ConfigOptions,
|
|
198
|
+
constructor(options?: ConfigOptions, signerArg?: SparkSigner) {
|
|
197
199
|
super();
|
|
198
200
|
|
|
201
|
+
const signer = signerArg || this.buildSigner();
|
|
199
202
|
this.config = new WalletConfigService(options, signer);
|
|
200
203
|
this.connectionManager = this.buildConnectionManager(this.config);
|
|
201
204
|
this.signingService = new SigningService(this.config);
|
|
@@ -263,6 +266,10 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
263
266
|
return this.sspClient;
|
|
264
267
|
}
|
|
265
268
|
|
|
269
|
+
protected buildSigner() {
|
|
270
|
+
return new DefaultSparkSigner();
|
|
271
|
+
}
|
|
272
|
+
|
|
266
273
|
protected buildConnectionManager(config: WalletConfigService) {
|
|
267
274
|
return new ConnectionManager(config);
|
|
268
275
|
}
|
|
@@ -744,9 +751,40 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
744
751
|
await this.withLeaves(async () => {
|
|
745
752
|
this.optimizationInProgress = true;
|
|
746
753
|
try {
|
|
747
|
-
|
|
748
|
-
|
|
754
|
+
this.leaves = await this.getLeaves();
|
|
755
|
+
const swaps = maximizeUnilateralExit(
|
|
756
|
+
this.leaves.map((leaf) => leaf.value),
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
// Build a map from the denomination to the nodes
|
|
760
|
+
const valueToNodes = new Map<number, TreeNode[]>();
|
|
761
|
+
this.leaves.forEach((leaf) => {
|
|
762
|
+
if (!valueToNodes.has(leaf.value)) {
|
|
763
|
+
valueToNodes.set(leaf.value, []);
|
|
764
|
+
}
|
|
765
|
+
valueToNodes.get(leaf.value)!.push(leaf);
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
// Select the leaves to send for each swap.
|
|
769
|
+
for (const swap of swaps) {
|
|
770
|
+
const leavesToSend: TreeNode[] = [];
|
|
771
|
+
for (const leafValue of swap.inLeaves) {
|
|
772
|
+
const nodes = valueToNodes.get(leafValue);
|
|
773
|
+
if (nodes && nodes.length > 0) {
|
|
774
|
+
const node = nodes.shift()!;
|
|
775
|
+
leavesToSend.push(node);
|
|
776
|
+
} else {
|
|
777
|
+
throw new InternalValidationError(
|
|
778
|
+
`No unused leaf with value ${leafValue} found in leaves`,
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
await this.requestLeavesSwap({
|
|
783
|
+
leaves: leavesToSend,
|
|
784
|
+
targetAmounts: swap.outLeaves,
|
|
785
|
+
});
|
|
749
786
|
}
|
|
787
|
+
|
|
750
788
|
this.leaves = await this.getLeaves();
|
|
751
789
|
} finally {
|
|
752
790
|
this.optimizationInProgress = false;
|
|
@@ -1430,6 +1468,14 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
1430
1468
|
directSignatureMap,
|
|
1431
1469
|
directFromCpfpSignatureMap,
|
|
1432
1470
|
);
|
|
1471
|
+
|
|
1472
|
+
// At this point the leaves are considered outgoing.
|
|
1473
|
+
// Remove them from internal state so we don't select them again
|
|
1474
|
+
const leavesToRemove = new Set(leavesBatch.map((leaf) => leaf.id));
|
|
1475
|
+
this.leaves = [
|
|
1476
|
+
...this.leaves.filter((leaf) => !leavesToRemove.has(leaf.id)),
|
|
1477
|
+
];
|
|
1478
|
+
|
|
1433
1479
|
const completeResponse = await sspClient.completeLeaveSwap({
|
|
1434
1480
|
adaptorSecretKey: bytesToHex(cpfpAdaptorPrivateKey),
|
|
1435
1481
|
directAdaptorSecretKey: bytesToHex(directAdaptorPrivateKey),
|
|
@@ -3537,14 +3583,14 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
3537
3583
|
|
|
3538
3584
|
const sspResponse = await sspClient.requestLightningSend({
|
|
3539
3585
|
encodedInvoice: invoice,
|
|
3540
|
-
idempotencyKey: uuidv7(),
|
|
3541
3586
|
amountSats: isZeroAmountInvoice ? amountSatsToSend! : undefined,
|
|
3587
|
+
userOutboundTransferExternalId: swapResponse.transfer.id,
|
|
3542
3588
|
});
|
|
3543
3589
|
|
|
3544
3590
|
if (!sspResponse) {
|
|
3545
3591
|
throw new Error("Failed to contact SSP");
|
|
3546
3592
|
}
|
|
3547
|
-
|
|
3593
|
+
|
|
3548
3594
|
const leavesToRemove = new Set(leavesToSend.map((leaf) => leaf.leaf.id));
|
|
3549
3595
|
this.leaves = this.leaves.filter((leaf) => !leavesToRemove.has(leaf.id));
|
|
3550
3596
|
|
|
@@ -3912,12 +3958,14 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
3912
3958
|
})),
|
|
3913
3959
|
);
|
|
3914
3960
|
|
|
3961
|
+
const transferId = uuidv7();
|
|
3962
|
+
|
|
3915
3963
|
const requestCoopExitParams: RequestCoopExitInput = {
|
|
3916
3964
|
leafExternalIds: leavesToSendToSsp.map((leaf) => leaf.id),
|
|
3917
3965
|
withdrawalAddress: onchainAddress,
|
|
3918
|
-
idempotencyKey: uuidv7(),
|
|
3919
3966
|
exitSpeed,
|
|
3920
3967
|
withdrawAll: deductFeeFromWithdrawalAmount,
|
|
3968
|
+
userOutboundTransferExternalId: transferId,
|
|
3921
3969
|
};
|
|
3922
3970
|
|
|
3923
3971
|
if (!deductFeeFromWithdrawalAmount) {
|
|
@@ -3962,11 +4010,11 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
3962
4010
|
exitTxId: coopExitTxId,
|
|
3963
4011
|
connectorOutputs,
|
|
3964
4012
|
receiverPubKey: sspPubIdentityKey,
|
|
4013
|
+
transferId,
|
|
3965
4014
|
});
|
|
3966
4015
|
|
|
3967
4016
|
const completeResponse = await sspClient.completeCoopExit({
|
|
3968
4017
|
userOutboundTransferExternalId: transfer.transfer.id,
|
|
3969
|
-
coopExitRequestId: coopExitRequest.id,
|
|
3970
4018
|
});
|
|
3971
4019
|
|
|
3972
4020
|
return completeResponse;
|
|
@@ -4887,7 +4935,6 @@ export abstract class SparkWallet extends EventEmitter<SparkWalletEvents> {
|
|
|
4887
4935
|
const consoleOptions = wallet.config.getConsoleOptions();
|
|
4888
4936
|
const spanProcessors: SpanProcessor[] = [];
|
|
4889
4937
|
if (consoleOptions.otel) {
|
|
4890
|
-
console.log("OpenTelemetry client logging enabled.");
|
|
4891
4938
|
spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter()));
|
|
4892
4939
|
}
|
|
4893
4940
|
const traceUrls = this.getOtelTraceUrls();
|
|
@@ -156,11 +156,13 @@ describe.each(walletTypes)("coop exit", ({ name, Signer, createTree }) => {
|
|
|
156
156
|
newKeyDerivation: newLeafDerivationPath,
|
|
157
157
|
};
|
|
158
158
|
|
|
159
|
+
const transferId = uuidv7();
|
|
159
160
|
const senderTransfer = await coopExitService.getConnectorRefundSignatures({
|
|
160
161
|
leaves: [transferNode],
|
|
161
162
|
exitTxId: hexToBytes(getTxIdNoReverse(exitTx)),
|
|
162
163
|
connectorOutputs,
|
|
163
164
|
receiverPubKey: hexToBytes(sspPubkey),
|
|
165
|
+
transferId,
|
|
164
166
|
});
|
|
165
167
|
|
|
166
168
|
const receiverTransfer = await sspTransferService.queryTransfer(
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { getTestWalletConfig, walletTypes } from "../test-utils.js";
|
|
25
25
|
import { SparkWalletTesting } from "../utils/spark-testing-wallet.js";
|
|
26
26
|
import { BitcoinFaucet } from "../utils/test-faucet.js";
|
|
27
|
+
import { DefaultSparkSigner } from "../../signer/signer.js";
|
|
27
28
|
|
|
28
29
|
async function cleanUp() {
|
|
29
30
|
const config = getTestWalletConfig();
|
|
@@ -33,7 +34,10 @@ async function cleanUp() {
|
|
|
33
34
|
);
|
|
34
35
|
const paymentHash = sha256(preimage);
|
|
35
36
|
|
|
36
|
-
const configService = new WalletConfigService(
|
|
37
|
+
const configService = new WalletConfigService(
|
|
38
|
+
config,
|
|
39
|
+
new DefaultSparkSigner(),
|
|
40
|
+
);
|
|
37
41
|
const connectionManager = new ConnectionManagerNodeJS(configService);
|
|
38
42
|
for (const operator of Object.values(config.signingOperators!)) {
|
|
39
43
|
const client = await connectionManager.createMockClient(operator!.address);
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { expect } from "@jest/globals";
|
|
2
|
+
import { ValidationError } from "../../../errors/types.js";
|
|
3
|
+
import { ExitSpeed } from "../../../types/index.js";
|
|
4
|
+
import { getNewAddress } from "../../utils/regtest-test-faucet.js";
|
|
2
5
|
import {
|
|
3
6
|
initTestingWallet,
|
|
4
7
|
SparkWalletTesting,
|
|
5
8
|
} from "../../utils/spark-testing-wallet.js";
|
|
6
|
-
import { expect } from "@jest/globals";
|
|
7
|
-
import { ExitSpeed } from "../../../types/index.js";
|
|
8
|
-
import { ValidationError } from "../../../errors/types.js";
|
|
9
|
-
import { getNewAddress } from "../../utils/regtest-test-faucet.js";
|
|
10
9
|
|
|
11
10
|
const DEPOSIT_AMOUNT = 50_000n;
|
|
12
11
|
|
|
@@ -200,7 +199,7 @@ describe("SSP coop exit basic validation", () => {
|
|
|
200
199
|
exitSpeed: ExitSpeed.FAST,
|
|
201
200
|
deductFeeFromWithdrawalAmount: true,
|
|
202
201
|
}),
|
|
203
|
-
).rejects.toThrow("
|
|
202
|
+
).rejects.toThrow("Total target amount exceeds available balance");
|
|
204
203
|
}, 600000);
|
|
205
204
|
|
|
206
205
|
// it("should correctly update balance after successful withdrawal", async () => {
|
|
@@ -46,18 +46,20 @@ describe("SSP static deposit address integration", () => {
|
|
|
46
46
|
vout!,
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
50
|
-
|
|
51
49
|
const quoteAmount = quote!.creditAmountSats;
|
|
52
50
|
const sspSignature = quote!.signature;
|
|
53
51
|
|
|
54
|
-
await
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
await retryUntilSuccess(
|
|
53
|
+
async () =>
|
|
54
|
+
await userWallet.claimStaticDeposit({
|
|
55
|
+
transactionId,
|
|
56
|
+
creditAmountSats: quoteAmount,
|
|
57
|
+
sspSignature,
|
|
58
|
+
outputIndex: vout!,
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
await waitForClaim({ wallet: userWallet });
|
|
61
63
|
const { balance } = await userWallet.getBalance();
|
|
62
64
|
expect(balance).toBe(BigInt(quoteAmount));
|
|
63
65
|
|
|
@@ -79,7 +81,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
79
81
|
creditAmountSats: quoteAmount2,
|
|
80
82
|
sspSignature: sspSignature2,
|
|
81
83
|
});
|
|
82
|
-
await
|
|
84
|
+
await waitForClaim({ wallet: userWallet });
|
|
83
85
|
const { balance: balance2 } = await userWallet.getBalance();
|
|
84
86
|
expect(balance2).toBe(BigInt(quoteAmount + quoteAmount2));
|
|
85
87
|
|
|
@@ -99,7 +101,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
99
101
|
transactionId: transactionId3,
|
|
100
102
|
maxFee: 1000,
|
|
101
103
|
});
|
|
102
|
-
await
|
|
104
|
+
await waitForClaim({ wallet: userWallet });
|
|
103
105
|
const { balance: balance3 } = await userWallet.getBalance();
|
|
104
106
|
expect(balance3).toBe(BigInt(quoteAmount + quoteAmount2 + quoteAmount3));
|
|
105
107
|
// Get transfers should include static deposit transfers.
|
|
@@ -203,18 +205,20 @@ describe("SSP static deposit address integration", () => {
|
|
|
203
205
|
vout!,
|
|
204
206
|
);
|
|
205
207
|
|
|
206
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
207
|
-
|
|
208
208
|
const quoteAmount = quote!.creditAmountSats;
|
|
209
209
|
const sspSignature = quote!.signature;
|
|
210
210
|
|
|
211
|
-
await
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
211
|
+
await retryUntilSuccess(
|
|
212
|
+
async () =>
|
|
213
|
+
await userWallet.claimStaticDeposit({
|
|
214
|
+
transactionId,
|
|
215
|
+
creditAmountSats: quoteAmount,
|
|
216
|
+
sspSignature,
|
|
217
|
+
outputIndex: vout!,
|
|
218
|
+
}),
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
await waitForClaim({ wallet: userWallet });
|
|
218
222
|
const { balance } = await userWallet.getBalance();
|
|
219
223
|
expect(balance).toBe(BigInt(quoteAmount));
|
|
220
224
|
|
|
@@ -231,12 +235,15 @@ describe("SSP static deposit address integration", () => {
|
|
|
231
235
|
await userWallet.getClaimStaticDepositQuote(transactionId2);
|
|
232
236
|
const quoteAmount2 = quote2!.creditAmountSats;
|
|
233
237
|
const sspSignature2 = quote2!.signature;
|
|
234
|
-
await
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
await retryUntilSuccess(
|
|
239
|
+
async () =>
|
|
240
|
+
await userWallet.claimStaticDeposit({
|
|
241
|
+
transactionId: transactionId2,
|
|
242
|
+
creditAmountSats: quoteAmount2,
|
|
243
|
+
sspSignature: sspSignature2,
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
await waitForClaim({ wallet: userWallet });
|
|
240
247
|
const { balance: balance2 } = await userWallet.getBalance();
|
|
241
248
|
expect(balance2).toBe(BigInt(quoteAmount + quoteAmount2));
|
|
242
249
|
|
|
@@ -252,11 +259,14 @@ describe("SSP static deposit address integration", () => {
|
|
|
252
259
|
const quote3 =
|
|
253
260
|
await userWallet.getClaimStaticDepositQuote(transactionId3);
|
|
254
261
|
const quoteAmount3 = quote3!.creditAmountSats;
|
|
255
|
-
await
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
262
|
+
await retryUntilSuccess(
|
|
263
|
+
async () =>
|
|
264
|
+
await userWallet.claimStaticDepositWithMaxFee({
|
|
265
|
+
transactionId: transactionId3,
|
|
266
|
+
maxFee: 1000,
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
await waitForClaim({ wallet: userWallet });
|
|
260
270
|
const { balance: balance3 } = await userWallet.getBalance();
|
|
261
271
|
expect(balance3).toBe(BigInt(quoteAmount + quoteAmount2 + quoteAmount3));
|
|
262
272
|
// Get transfers should include static deposit transfers.
|
|
@@ -533,7 +543,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
533
543
|
creditAmountSats: quote.creditAmountSats,
|
|
534
544
|
sspSignature: quote.signature,
|
|
535
545
|
});
|
|
536
|
-
await
|
|
546
|
+
await waitForClaim({ wallet: aliceWallet });
|
|
537
547
|
|
|
538
548
|
const { balance } = await aliceWallet.getBalance();
|
|
539
549
|
|
|
@@ -619,7 +629,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
619
629
|
outputIndex: vout!,
|
|
620
630
|
});
|
|
621
631
|
|
|
622
|
-
await
|
|
632
|
+
await waitForClaim({ wallet: userWallet });
|
|
623
633
|
|
|
624
634
|
expect(outputs).toBeDefined();
|
|
625
635
|
|
|
@@ -669,7 +679,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
669
679
|
outputIndex: vout!,
|
|
670
680
|
});
|
|
671
681
|
|
|
672
|
-
await
|
|
682
|
+
await waitForClaim({ wallet: userWallet });
|
|
673
683
|
|
|
674
684
|
console.log("Fetching wallet balance after claim...");
|
|
675
685
|
const { balance } = await userWallet.getBalance();
|
|
@@ -686,7 +696,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
686
696
|
|
|
687
697
|
expect(transfer).toBeDefined();
|
|
688
698
|
|
|
689
|
-
await
|
|
699
|
+
await waitForClaim({ wallet: userWallet });
|
|
690
700
|
|
|
691
701
|
// Try to refund the deposit after claiming and transfer
|
|
692
702
|
console.log("Attempting refund of claimed deposit...");
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, it } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
greedyLeaves,
|
|
4
|
+
swapMinimizingLeaves,
|
|
5
|
+
maximizeUnilateralExit,
|
|
6
|
+
minimizeTransferSwap,
|
|
7
|
+
Swap,
|
|
8
|
+
} from "../utils/optimize.js";
|
|
9
|
+
|
|
10
|
+
describe("keys", () => {
|
|
11
|
+
it("test greedyLeaves", () => {
|
|
12
|
+
expect(greedyLeaves(0)).toEqual([]);
|
|
13
|
+
expect(greedyLeaves(1)).toEqual([1]);
|
|
14
|
+
expect(greedyLeaves(100)).toEqual([4, 32, 64]);
|
|
15
|
+
expect(greedyLeaves(255)).toEqual([1, 2, 4, 8, 16, 32, 64, 128]);
|
|
16
|
+
expect(greedyLeaves(256)).toEqual([256]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("test swapMinimizingLeaves", () => {
|
|
20
|
+
expect(swapMinimizingLeaves(0)).toEqual([]);
|
|
21
|
+
expect(swapMinimizingLeaves(1)).toEqual([1]);
|
|
22
|
+
expect(swapMinimizingLeaves(100)).toEqual([1, 1, 2, 4, 4, 8, 16, 32, 32]);
|
|
23
|
+
expect(swapMinimizingLeaves(255)).toEqual([1, 2, 4, 8, 16, 32, 64, 128]);
|
|
24
|
+
expect(swapMinimizingLeaves(256)).toEqual([1, 1, 2, 4, 8, 16, 32, 64, 128]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("test maximizeUnilateralExit", () => {
|
|
28
|
+
expect(maximizeUnilateralExit([100, 64, 28, 1, 1])).toEqual([
|
|
29
|
+
new Swap([1, 1, 28, 64, 100], [2, 64, 128]),
|
|
30
|
+
]);
|
|
31
|
+
expect(maximizeUnilateralExit([1, 1, 1, 1, 1, 1, 1, 1], 2)).toEqual([
|
|
32
|
+
new Swap([1, 1], [2]),
|
|
33
|
+
new Swap([1, 1], [2]),
|
|
34
|
+
new Swap([1, 1], [2]),
|
|
35
|
+
new Swap([1, 1], [2]),
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("test minimizeTransferSwap", () => {
|
|
40
|
+
expect(minimizeTransferSwap([8])).toEqual([new Swap([8], [1, 1, 2, 4])]);
|
|
41
|
+
expect(minimizeTransferSwap([100])).toEqual([
|
|
42
|
+
new Swap([100], [1, 1, 2, 4, 4, 8, 16, 32, 32]),
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { numberToBytesBE } from "@noble/curves/utils";
|
|
1
|
+
import { numberToBytesBE, bytesToNumberBE } from "@noble/curves/utils";
|
|
2
2
|
import { ValidationError } from "../errors/types.js";
|
|
3
3
|
import { OutputWithPreviousTransactionData } from "../proto/spark.js";
|
|
4
4
|
import { WalletConfigService } from "../services/config.js";
|
|
@@ -17,6 +17,18 @@ describe("select token outputs", () => {
|
|
|
17
17
|
);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
// Helper to access the private sorting method
|
|
21
|
+
const sortTokenOutputsByStrategy = (
|
|
22
|
+
tokenOutputs: OutputWithPreviousTransactionData[],
|
|
23
|
+
strategy: "SMALL_FIRST" | "LARGE_FIRST",
|
|
24
|
+
) => {
|
|
25
|
+
// TypeScript bracket notation to access private method
|
|
26
|
+
(tokenTransactionService as any)["sortTokenOutputsByStrategy"](
|
|
27
|
+
tokenOutputs,
|
|
28
|
+
strategy,
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
20
32
|
const createMockTokenOutput = (
|
|
21
33
|
id: string,
|
|
22
34
|
tokenAmount: bigint,
|
|
@@ -191,4 +203,51 @@ describe("select token outputs", () => {
|
|
|
191
203
|
// Total: 600n >= 600n
|
|
192
204
|
});
|
|
193
205
|
});
|
|
206
|
+
|
|
207
|
+
describe("sorting with large amounts", () => {
|
|
208
|
+
it("should sort correctly when all amounts are above 2^60", () => {
|
|
209
|
+
const base = 2n ** 60n;
|
|
210
|
+
const amounts = [
|
|
211
|
+
base + 5000n,
|
|
212
|
+
base + 100n,
|
|
213
|
+
base + 1n,
|
|
214
|
+
base + 10000n,
|
|
215
|
+
base + 500n,
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
const tokenOutputs = amounts.map((amount, i) =>
|
|
219
|
+
createMockTokenOutput(`output${i}`, amount),
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// SMALL_FIRST
|
|
223
|
+
const smallFirstSorted = [...tokenOutputs];
|
|
224
|
+
sortTokenOutputsByStrategy(smallFirstSorted, "SMALL_FIRST");
|
|
225
|
+
|
|
226
|
+
const smallFirstAmounts = smallFirstSorted.map((o) =>
|
|
227
|
+
bytesToNumberBE(o.output!.tokenAmount!),
|
|
228
|
+
);
|
|
229
|
+
expect(smallFirstAmounts).toEqual([
|
|
230
|
+
base + 1n,
|
|
231
|
+
base + 100n,
|
|
232
|
+
base + 500n,
|
|
233
|
+
base + 5000n,
|
|
234
|
+
base + 10000n,
|
|
235
|
+
]);
|
|
236
|
+
|
|
237
|
+
// LARGE_FIRST
|
|
238
|
+
const largeFirstSorted = [...tokenOutputs];
|
|
239
|
+
sortTokenOutputsByStrategy(largeFirstSorted, "LARGE_FIRST");
|
|
240
|
+
|
|
241
|
+
const largeFirstAmounts = largeFirstSorted.map((o) =>
|
|
242
|
+
bytesToNumberBE(o.output!.tokenAmount!),
|
|
243
|
+
);
|
|
244
|
+
expect(largeFirstAmounts).toEqual([
|
|
245
|
+
base + 10000n,
|
|
246
|
+
base + 5000n,
|
|
247
|
+
base + 500n,
|
|
248
|
+
base + 100n,
|
|
249
|
+
base + 1n,
|
|
250
|
+
]);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
194
253
|
});
|
|
@@ -42,9 +42,9 @@ export class BitcoinFaucet {
|
|
|
42
42
|
private lock: Promise<void> = Promise.resolve();
|
|
43
43
|
|
|
44
44
|
private constructor(
|
|
45
|
-
private url: string
|
|
46
|
-
private username: string
|
|
47
|
-
private password: string
|
|
45
|
+
private url: string,
|
|
46
|
+
private username: string,
|
|
47
|
+
private password: string,
|
|
48
48
|
) {
|
|
49
49
|
this.miningAddress = getP2TRAddressFromPublicKey(
|
|
50
50
|
secp256k1.getPublicKey(STATIC_MINING_KEY),
|
|
@@ -52,12 +52,16 @@ export class BitcoinFaucet {
|
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
static getInstance(
|
|
56
|
-
url: string = "http://127.0.0.1:8332",
|
|
57
|
-
username: string = "testutil",
|
|
58
|
-
password: string = "testutilpassword",
|
|
59
|
-
): BitcoinFaucet {
|
|
55
|
+
static getInstance(): BitcoinFaucet {
|
|
60
56
|
if (!BitcoinFaucet.instance) {
|
|
57
|
+
const url =
|
|
58
|
+
process.env.BITCOIN_RPC_URL ||
|
|
59
|
+
(process.env.MINIKUBE_IP
|
|
60
|
+
? `http://${process.env.MINIKUBE_IP}:8332`
|
|
61
|
+
: "http://127.0.0.1:8332");
|
|
62
|
+
const username = process.env.BITCOIN_RPC_USER || "testutil";
|
|
63
|
+
const password = process.env.BITCOIN_RPC_PASSWORD || "testutilpassword";
|
|
64
|
+
|
|
61
65
|
BitcoinFaucet.instance = new BitcoinFaucet(url, username, password);
|
|
62
66
|
}
|
|
63
67
|
return BitcoinFaucet.instance;
|