@buildonspark/spark-sdk 0.3.9 → 0.4.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.
- package/CHANGELOG.md +11 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/uniffi/uniffi/spark_frost/spark_frost.kt +1361 -1367
- 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/bare/index.cjs +322 -142
- package/dist/bare/index.d.cts +13 -3
- package/dist/bare/index.d.ts +13 -3
- package/dist/bare/index.js +321 -142
- package/dist/{chunk-S55NZT4P.js → chunk-27ILUWDJ.js} +1 -1
- package/dist/{chunk-O4C4HGQL.js → chunk-G3LHXHF3.js} +313 -133
- package/dist/{chunk-WRE2T22S.js → chunk-LOXWCMZL.js} +1 -1
- package/dist/{chunk-MFCM6GUD.js → chunk-WICAF6BS.js} +1 -1
- package/dist/debug.cjs +322 -143
- package/dist/debug.d.cts +5 -4
- package/dist/debug.d.ts +5 -4
- package/dist/debug.js +2 -2
- package/dist/index.cjs +323 -143
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +5 -3
- package/dist/index.node.cjs +323 -143
- package/dist/index.node.d.cts +4 -4
- package/dist/index.node.d.ts +4 -4
- package/dist/index.node.js +4 -2
- package/dist/{logging-DDeMLsVN.d.ts → logging-BNGm6dBp.d.ts} +3 -2
- package/dist/{logging-CXhvuqJJ.d.cts → logging-D3IfXfHG.d.cts} +3 -2
- package/dist/native/index.react-native.cjs +466 -145
- package/dist/native/index.react-native.d.cts +21 -3
- package/dist/native/index.react-native.d.ts +21 -3
- package/dist/native/index.react-native.js +462 -144
- package/dist/{spark-wallet.browser-Cz8c4kOW.d.ts → spark-wallet.browser-B2rGwjuM.d.ts} +1 -1
- package/dist/{spark-wallet.browser-CbYo8A_U.d.cts → spark-wallet.browser-Ck9No4Ks.d.cts} +1 -1
- package/dist/{spark-wallet.node-CmIvxtcC.d.ts → spark-wallet.node-BqmKsGPs.d.ts} +1 -1
- package/dist/{spark-wallet.node-4WQgWwB2.d.cts → spark-wallet.node-C2TIkyt4.d.cts} +1 -1
- package/dist/tests/test-utils.cjs +321 -143
- package/dist/tests/test-utils.d.cts +16 -2
- package/dist/tests/test-utils.d.ts +16 -2
- package/dist/tests/test-utils.js +4 -4
- package/dist/{token-transactions-CV8QD3I7.d.cts → token-transactions-Db8mkjnU.d.cts} +1 -1
- package/dist/{token-transactions-Bu023ztN.d.ts → token-transactions-DoMcrxXQ.d.ts} +1 -1
- package/dist/{wallet-config-Bmk2eAn8.d.ts → wallet-config-Bg3kWltL.d.ts} +11 -2
- package/dist/{wallet-config-DQw5llqA.d.cts → wallet-config-CuZKNo9S.d.cts} +11 -2
- 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 +1 -1
- package/src/index.react-native.ts +8 -2
- package/src/services/config.ts +5 -0
- package/src/services/wallet-config.ts +10 -0
- package/src/services/xhr-transport.ts +13 -3
- package/src/signer/signer.react-native.ts +73 -1
- package/src/spark-wallet/spark-wallet.ts +98 -76
- package/src/tests/integration/lightning.test.ts +0 -28
- package/src/tests/integration/static_deposit.test.ts +4 -8
- package/src/tests/integration/unilateral-exit.test.ts +117 -0
- package/src/tests/optimize.test.ts +31 -1
- package/src/tests/utils/signing.ts +33 -0
- package/src/tests/utils/test-faucet.ts +61 -0
- package/src/utils/optimize.ts +42 -0
- package/src/utils/unilateral-exit.ts +1 -40
|
@@ -443,20 +443,6 @@ describe.each(walletTypes)(
|
|
|
443
443
|
for (const leaf of transfer.leaves) {
|
|
444
444
|
const cpfpRefund = getTxFromRawTxBytes(leaf.intermediateRefundTx);
|
|
445
445
|
expectedValue += cpfpRefund.getOutput(0)?.amount || 0n;
|
|
446
|
-
|
|
447
|
-
if ((leaf.intermediateDirectRefundTx.length || 0) > 0) {
|
|
448
|
-
const directRefund = getTxFromRawTxBytes(
|
|
449
|
-
leaf.intermediateDirectRefundTx,
|
|
450
|
-
);
|
|
451
|
-
expectedValue += directRefund.getOutput(0)?.amount || 0n;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
if ((leaf.intermediateDirectFromCpfpRefundTx.length || 0) > 0) {
|
|
455
|
-
const directFromCpfpRefund = getTxFromRawTxBytes(
|
|
456
|
-
leaf.intermediateDirectFromCpfpRefundTx,
|
|
457
|
-
);
|
|
458
|
-
expectedValue += directFromCpfpRefund.getOutput(0)?.amount || 0n;
|
|
459
|
-
}
|
|
460
446
|
}
|
|
461
447
|
|
|
462
448
|
let totalValue = 0n;
|
|
@@ -569,20 +555,6 @@ describe.each(walletTypes)(
|
|
|
569
555
|
for (const leaf of transfer!.leaves) {
|
|
570
556
|
const cpfpRefund = getTxFromRawTxBytes(leaf.intermediateRefundTx);
|
|
571
557
|
expectedValue += cpfpRefund.getOutput(0)?.amount || 0n;
|
|
572
|
-
|
|
573
|
-
if (leaf.intermediateDirectRefundTx.length > 0) {
|
|
574
|
-
const directRefund = getTxFromRawTxBytes(
|
|
575
|
-
leaf.intermediateDirectRefundTx,
|
|
576
|
-
);
|
|
577
|
-
expectedValue += directRefund.getOutput(0)?.amount || 0n;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
if (leaf.intermediateDirectFromCpfpRefundTx.length > 0) {
|
|
581
|
-
const directFromCpfpRefund = getTxFromRawTxBytes(
|
|
582
|
-
leaf.intermediateDirectFromCpfpRefundTx,
|
|
583
|
-
);
|
|
584
|
-
expectedValue += directFromCpfpRefund.getOutput(0)?.amount || 0n;
|
|
585
|
-
}
|
|
586
558
|
}
|
|
587
559
|
|
|
588
560
|
let totalValue = 0n;
|
|
@@ -17,7 +17,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
17
17
|
} = await initTestingWallet(DEPOSIT_AMOUNT, "LOCAL");
|
|
18
18
|
|
|
19
19
|
// Wait for the transaction to be mined
|
|
20
|
-
await faucet.
|
|
20
|
+
await faucet.mineBlocksAndWaitForMiningToComplete(6);
|
|
21
21
|
|
|
22
22
|
const transactionId = signedTx.id;
|
|
23
23
|
|
|
@@ -30,11 +30,9 @@ describe("SSP static deposit address integration", () => {
|
|
|
30
30
|
fee: 301,
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
34
|
-
|
|
35
33
|
await faucet.broadcastTx(txHex);
|
|
36
34
|
|
|
37
|
-
await faucet.
|
|
35
|
+
await faucet.mineBlocksAndWaitForMiningToComplete(6);
|
|
38
36
|
|
|
39
37
|
// Second refund attempt should fail
|
|
40
38
|
console.log(
|
|
@@ -60,7 +58,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
60
58
|
} = await initTestingWallet(DEPOSIT_AMOUNT, "LOCAL");
|
|
61
59
|
|
|
62
60
|
// Wait for the transaction to be mined
|
|
63
|
-
await faucet.
|
|
61
|
+
await faucet.mineBlocksAndWaitForMiningToComplete(6);
|
|
64
62
|
|
|
65
63
|
const transactionId = signedTx.id;
|
|
66
64
|
|
|
@@ -74,8 +72,6 @@ describe("SSP static deposit address integration", () => {
|
|
|
74
72
|
}),
|
|
75
73
|
);
|
|
76
74
|
|
|
77
|
-
await faucet.mineBlocks(6);
|
|
78
|
-
|
|
79
75
|
expect(txId).toBeDefined();
|
|
80
76
|
}, 600000);
|
|
81
77
|
|
|
@@ -91,7 +87,7 @@ describe("SSP static deposit address integration", () => {
|
|
|
91
87
|
} = await initTestingWallet(DEPOSIT_AMOUNT, "LOCAL");
|
|
92
88
|
|
|
93
89
|
// Wait for the transaction to be mined
|
|
94
|
-
await faucet.
|
|
90
|
+
await faucet.mineBlocksAndWaitForMiningToComplete(6);
|
|
95
91
|
|
|
96
92
|
const transactionId = signedTx.id;
|
|
97
93
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { describe, expect, it } from "@jest/globals";
|
|
2
|
+
import { bytesToHex } from "@noble/hashes/utils";
|
|
3
|
+
|
|
4
|
+
import { RPCError } from "../../errors/types.js";
|
|
5
|
+
import { Network } from "../../utils/network.js";
|
|
6
|
+
import { SparkWalletTesting } from "../utils/spark-testing-wallet.js";
|
|
7
|
+
import { BitcoinFaucet } from "../utils/test-faucet.js";
|
|
8
|
+
import { waitForClaim } from "../utils/utils.js";
|
|
9
|
+
import {
|
|
10
|
+
constructUnilateralExitFeeBumpPackages,
|
|
11
|
+
hash160,
|
|
12
|
+
} from "../../utils/unilateral-exit.js";
|
|
13
|
+
import { signPsbtWithExternalKey } from "../utils/signing.js";
|
|
14
|
+
import { TreeNode } from "../../proto/spark.js";
|
|
15
|
+
import { WalletConfigService } from "../../services/config.js";
|
|
16
|
+
import { ConnectionManagerNodeJS } from "../../services/connection/connection.node.js";
|
|
17
|
+
|
|
18
|
+
describe("unilateral exit", () => {
|
|
19
|
+
it("should unilateral exit", async () => {
|
|
20
|
+
const faucet = BitcoinFaucet.getInstance();
|
|
21
|
+
|
|
22
|
+
const { wallet: userWallet } = await SparkWalletTesting.initialize({
|
|
23
|
+
options: {
|
|
24
|
+
network: "LOCAL",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const depositResp = await userWallet.getSingleUseDepositAddress();
|
|
29
|
+
|
|
30
|
+
if (!depositResp) {
|
|
31
|
+
throw new RPCError("Deposit address not found", {
|
|
32
|
+
method: "getDepositAddress",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const signedTx = await faucet.sendToAddress(depositResp, 100_000n);
|
|
37
|
+
|
|
38
|
+
await faucet.mineBlocks(6);
|
|
39
|
+
|
|
40
|
+
await userWallet.claimDeposit(signedTx.id);
|
|
41
|
+
|
|
42
|
+
await waitForClaim({ wallet: userWallet });
|
|
43
|
+
|
|
44
|
+
const leaves = await userWallet.getLeaves();
|
|
45
|
+
expect(leaves.length).toBe(1);
|
|
46
|
+
|
|
47
|
+
const leaf = leaves[0]!;
|
|
48
|
+
|
|
49
|
+
const encodedLeaf = TreeNode.encode(leaf).finish();
|
|
50
|
+
const hexString = bytesToHex(encodedLeaf);
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
address: fundingWalletAddress,
|
|
54
|
+
key: fundingWalletKey,
|
|
55
|
+
pubKey: fundingWalletPubKey,
|
|
56
|
+
} = await faucet.getNewExternalWallet();
|
|
57
|
+
|
|
58
|
+
const fundingTx = await faucet.sendToAddress(fundingWalletAddress, 50_000n);
|
|
59
|
+
|
|
60
|
+
await faucet.mineBlocks(6);
|
|
61
|
+
|
|
62
|
+
const pubKeyHash = hash160(fundingWalletPubKey);
|
|
63
|
+
const p2wpkhScript = new Uint8Array([0x00, 0x14, ...pubKeyHash]);
|
|
64
|
+
|
|
65
|
+
const utxos = [
|
|
66
|
+
{
|
|
67
|
+
txid: fundingTx.id,
|
|
68
|
+
vout: 0,
|
|
69
|
+
value: 50_000n,
|
|
70
|
+
script: bytesToHex(p2wpkhScript),
|
|
71
|
+
publicKey: bytesToHex(fundingWalletPubKey),
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
// Create a spark client to be used for signing fee bump transactions.
|
|
76
|
+
const configService = new WalletConfigService(
|
|
77
|
+
{ network: "LOCAL" },
|
|
78
|
+
userWallet.getSigner(),
|
|
79
|
+
);
|
|
80
|
+
const connectionManager = new ConnectionManagerNodeJS(configService);
|
|
81
|
+
const sparkClient = await connectionManager.createSparkClient(
|
|
82
|
+
configService.getCoordinatorAddress(),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const constructedTx = await constructUnilateralExitFeeBumpPackages(
|
|
86
|
+
[hexString],
|
|
87
|
+
utxos,
|
|
88
|
+
{ satPerVbyte: 5 },
|
|
89
|
+
"http://mempool.minikube.local/api",
|
|
90
|
+
sparkClient,
|
|
91
|
+
Network.LOCAL,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const txPackages = constructedTx[0]?.txPackages;
|
|
95
|
+
|
|
96
|
+
// Broadcast unilateral exit transactions in order
|
|
97
|
+
txPackages?.forEach(async (txPackage) => {
|
|
98
|
+
const startBlock = await faucet.getBlockCount();
|
|
99
|
+
const feeBumpPsbtSigned = await signPsbtWithExternalKey(
|
|
100
|
+
txPackage.feeBumpPsbt!,
|
|
101
|
+
bytesToHex(fundingWalletKey),
|
|
102
|
+
);
|
|
103
|
+
await faucet.submitPackage([txPackage.tx, feeBumpPsbtSigned]);
|
|
104
|
+
|
|
105
|
+
// Mine 1910 blocks to expire time lock.
|
|
106
|
+
await faucet.mineBlocks(1910);
|
|
107
|
+
|
|
108
|
+
// Since we do not depend on the chain watcher, we just need to wait for the blocks to be mined.
|
|
109
|
+
await faucet.waitForBlocksMined({
|
|
110
|
+
startBlock,
|
|
111
|
+
expectedIncrease: 1910,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await connectionManager.closeConnections();
|
|
116
|
+
}, 90000);
|
|
117
|
+
});
|
|
@@ -4,10 +4,12 @@ import {
|
|
|
4
4
|
swapMinimizingLeaves,
|
|
5
5
|
maximizeUnilateralExit,
|
|
6
6
|
minimizeTransferSwap,
|
|
7
|
+
optimize,
|
|
7
8
|
Swap,
|
|
9
|
+
shouldOptimize,
|
|
8
10
|
} from "../utils/optimize.js";
|
|
9
11
|
|
|
10
|
-
describe("
|
|
12
|
+
describe("optimize", () => {
|
|
11
13
|
it("test greedyLeaves", () => {
|
|
12
14
|
expect(greedyLeaves(0)).toEqual([]);
|
|
13
15
|
expect(greedyLeaves(1)).toEqual([1]);
|
|
@@ -42,4 +44,32 @@ describe("keys", () => {
|
|
|
42
44
|
new Swap([100], [1, 1, 2, 4, 4, 8, 16, 32, 32]),
|
|
43
45
|
]);
|
|
44
46
|
});
|
|
47
|
+
|
|
48
|
+
it("test shouldOptimize for unilateral exit", () => {
|
|
49
|
+
expect(shouldOptimize([16], 0)).toEqual(false);
|
|
50
|
+
expect(shouldOptimize([16, 16], 0)).toEqual(false);
|
|
51
|
+
expect(shouldOptimize([16, 16, 16, 16, 16, 16, 16, 16], 0)).toEqual(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("test shouldOptimize for swap minimization", () => {
|
|
55
|
+
expect(shouldOptimize([2], 1)).toEqual(false);
|
|
56
|
+
expect(shouldOptimize([64], 1)).toEqual(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("test optimize for unilateral exit", () => {
|
|
60
|
+
expect(optimize([8], 0)).toEqual([]);
|
|
61
|
+
expect(optimize([16], 0)).toEqual([]);
|
|
62
|
+
expect(optimize([16, 16, 16, 16, 16, 16, 16, 16], 0)).toEqual([
|
|
63
|
+
new Swap([16, 16, 16, 16, 16, 16, 16, 16], [128]),
|
|
64
|
+
]);
|
|
65
|
+
expect(optimize([100000], 0)).toEqual([
|
|
66
|
+
new Swap([100000], [32, 128, 512, 1024, 32768, 65536]),
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("test optimize for swap minimization", () => {
|
|
71
|
+
expect(optimize([8], 1)).toEqual([new Swap([8], [1, 1, 2, 4])]);
|
|
72
|
+
expect(optimize([1, 4], 1)).toEqual([new Swap([4], [2, 2])]);
|
|
73
|
+
expect(optimize([1, 16], 1)).toEqual([new Swap([16], [2, 2, 4, 8])]);
|
|
74
|
+
});
|
|
45
75
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
|
|
2
|
+
import * as btc from "@scure/btc-signer";
|
|
3
|
+
|
|
4
|
+
import { isEphemeralAnchorOutput } from "../../utils/unilateral-exit.js";
|
|
5
|
+
|
|
6
|
+
export async function signPsbtWithExternalKey(
|
|
7
|
+
psbtHex: string,
|
|
8
|
+
privateKeyInput: string,
|
|
9
|
+
): Promise<string> {
|
|
10
|
+
const tx = btc.Transaction.fromPSBT(hexToBytes(psbtHex), {
|
|
11
|
+
allowUnknown: true,
|
|
12
|
+
allowLegacyWitnessUtxo: true,
|
|
13
|
+
version: 3,
|
|
14
|
+
});
|
|
15
|
+
const privateKey = hexToBytes(privateKeyInput);
|
|
16
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
17
|
+
const input = tx.getInput(i);
|
|
18
|
+
if (
|
|
19
|
+
isEphemeralAnchorOutput(
|
|
20
|
+
input?.witnessUtxo?.script,
|
|
21
|
+
input?.witnessUtxo?.amount,
|
|
22
|
+
)
|
|
23
|
+
) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
tx.updateInput(i, {
|
|
27
|
+
witnessScript: input?.witnessUtxo?.script,
|
|
28
|
+
});
|
|
29
|
+
tx.signIdx(privateKey, i);
|
|
30
|
+
tx.finalizeIdx(i);
|
|
31
|
+
}
|
|
32
|
+
return bytesToHex(tx.toBytes(true, true));
|
|
33
|
+
}
|
|
@@ -311,6 +311,17 @@ export class BitcoinFaucet {
|
|
|
311
311
|
return await this.generateToAddress(numBlocks, this.miningAddress);
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
async mineBlocksAndWaitForMiningToComplete(numBlocks: number) {
|
|
315
|
+
const startBlock = await this.getBlockCount();
|
|
316
|
+
|
|
317
|
+
await this.mineBlocks(numBlocks);
|
|
318
|
+
|
|
319
|
+
await this.waitForBlocksMined({
|
|
320
|
+
startBlock,
|
|
321
|
+
expectedIncrease: numBlocks,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
314
325
|
private async call(method: string, params: any[]) {
|
|
315
326
|
try {
|
|
316
327
|
const { fetch, Headers } = getFetch();
|
|
@@ -369,17 +380,67 @@ export class BitcoinFaucet {
|
|
|
369
380
|
return await this.call("getblock", [blockHash, 2]);
|
|
370
381
|
}
|
|
371
382
|
|
|
383
|
+
async getBlockCount(): Promise<number> {
|
|
384
|
+
return await this.call("getblockcount", []);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async waitForBlocksMined({
|
|
388
|
+
startBlock,
|
|
389
|
+
expectedIncrease,
|
|
390
|
+
timeoutMs = 30000,
|
|
391
|
+
intervalMs = 5000,
|
|
392
|
+
}: {
|
|
393
|
+
startBlock: number;
|
|
394
|
+
expectedIncrease: number;
|
|
395
|
+
timeoutMs?: number;
|
|
396
|
+
intervalMs?: number;
|
|
397
|
+
}) {
|
|
398
|
+
const deadline = Date.now() + timeoutMs;
|
|
399
|
+
// Give some time for the blocks to be mined and the chain watcher to catch up.
|
|
400
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
401
|
+
|
|
402
|
+
const start = startBlock;
|
|
403
|
+
const target = start + expectedIncrease;
|
|
404
|
+
while (Date.now() < deadline) {
|
|
405
|
+
const currentBlock = await this.getBlockCount();
|
|
406
|
+
if (currentBlock >= target) return currentBlock;
|
|
407
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
408
|
+
}
|
|
409
|
+
throw new Error(
|
|
410
|
+
`Timed out waiting for ${expectedIncrease} blocks (target height ${target})`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
372
414
|
async broadcastTx(txHex: string) {
|
|
373
415
|
let response = await this.call("sendrawtransaction", [txHex, 0]);
|
|
374
416
|
return response;
|
|
375
417
|
}
|
|
376
418
|
|
|
419
|
+
async submitPackage(txHexs: string[]) {
|
|
420
|
+
let response = await this.call("submitpackage", [txHexs]);
|
|
421
|
+
return response;
|
|
422
|
+
}
|
|
423
|
+
|
|
377
424
|
async getNewAddress(): Promise<string> {
|
|
378
425
|
const key = secp256k1.utils.randomPrivateKey();
|
|
379
426
|
const pubKey = secp256k1.getPublicKey(key);
|
|
380
427
|
return getP2TRAddressFromPublicKey(pubKey, Network.LOCAL);
|
|
381
428
|
}
|
|
382
429
|
|
|
430
|
+
async getNewExternalWallet(): Promise<{
|
|
431
|
+
address: string;
|
|
432
|
+
key: Uint8Array;
|
|
433
|
+
pubKey: Uint8Array;
|
|
434
|
+
}> {
|
|
435
|
+
const key = secp256k1.utils.randomPrivateKey();
|
|
436
|
+
const pubKey = secp256k1.getPublicKey(key);
|
|
437
|
+
return {
|
|
438
|
+
address: getP2TRAddressFromPublicKey(pubKey, Network.LOCAL),
|
|
439
|
+
key,
|
|
440
|
+
pubKey,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
383
444
|
async sendToAddress(
|
|
384
445
|
address: string,
|
|
385
446
|
amount: bigint,
|
package/src/utils/optimize.ts
CHANGED
|
@@ -224,3 +224,45 @@ export function minimizeTransferSwap(
|
|
|
224
224
|
|
|
225
225
|
return swaps;
|
|
226
226
|
}
|
|
227
|
+
|
|
228
|
+
export function shouldOptimize(
|
|
229
|
+
inputLeaves: number[],
|
|
230
|
+
multiplicity: number = 1,
|
|
231
|
+
maxLeavesPerSwap: number = 64,
|
|
232
|
+
): boolean {
|
|
233
|
+
if (multiplicity == 0) {
|
|
234
|
+
// When optimizing for unilateral exits, we should only optimize if it reduces the
|
|
235
|
+
// number of leaves by more than 5x.
|
|
236
|
+
const swaps = maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
|
|
237
|
+
const numInputs = sum(swaps.map((swap) => swap.inLeaves.length));
|
|
238
|
+
const numOutputs = sum(swaps.map((swap) => swap.outLeaves.length));
|
|
239
|
+
return numOutputs * 5 < numInputs;
|
|
240
|
+
} else {
|
|
241
|
+
// When optimizing for swap-minimization, we should only optimize if it changes the
|
|
242
|
+
// number of active denominations by more than 1.
|
|
243
|
+
const swaps = minimizeTransferSwap(
|
|
244
|
+
inputLeaves,
|
|
245
|
+
multiplicity,
|
|
246
|
+
maxLeavesPerSwap,
|
|
247
|
+
);
|
|
248
|
+
const inputCounter = countOccurrences(
|
|
249
|
+
swaps.flatMap((swap) => swap.inLeaves),
|
|
250
|
+
);
|
|
251
|
+
const outputCounter = countOccurrences(
|
|
252
|
+
swaps.flatMap((swap) => swap.outLeaves),
|
|
253
|
+
);
|
|
254
|
+
return Math.abs(inputCounter.size - outputCounter.size) > 1;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function optimize(
|
|
259
|
+
inputLeaves: number[],
|
|
260
|
+
multiplicity: number = 1,
|
|
261
|
+
maxLeavesPerSwap: number = 64,
|
|
262
|
+
): Swap[] {
|
|
263
|
+
if (multiplicity == 0) {
|
|
264
|
+
return maximizeUnilateralExit(inputLeaves, maxLeavesPerSwap);
|
|
265
|
+
} else {
|
|
266
|
+
return minimizeTransferSwap(inputLeaves, multiplicity, maxLeavesPerSwap);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -60,45 +60,6 @@ export interface BroadcastResult {
|
|
|
60
60
|
broadcastedPackages?: number;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// Helper function to convert WIF private key to hex
|
|
64
|
-
function wifToHex(wif: string): string {
|
|
65
|
-
try {
|
|
66
|
-
// WIF decoding using base58 (simplified version)
|
|
67
|
-
const base58Alphabet =
|
|
68
|
-
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
69
|
-
|
|
70
|
-
// Decode base58
|
|
71
|
-
let decoded = BigInt(0);
|
|
72
|
-
for (let i = 0; i < wif.length; i++) {
|
|
73
|
-
const char = wif[i];
|
|
74
|
-
if (!char) {
|
|
75
|
-
throw new Error("Invalid character in WIF at position " + i);
|
|
76
|
-
}
|
|
77
|
-
const index = base58Alphabet.indexOf(char);
|
|
78
|
-
if (index === -1) {
|
|
79
|
-
throw new Error("Invalid character in WIF");
|
|
80
|
-
}
|
|
81
|
-
decoded = decoded * BigInt(58) + BigInt(index);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Convert to hex and pad to ensure proper length
|
|
85
|
-
let hex = decoded.toString(16);
|
|
86
|
-
|
|
87
|
-
// WIF format: [version][32-byte private key][compression flag][4-byte checksum]
|
|
88
|
-
// We want the 32-byte private key part (skip version byte, take 32 bytes)
|
|
89
|
-
if (hex.length >= 74) {
|
|
90
|
-
// 1 + 32 + 1 + 4 = 38 bytes = 76 hex chars minimum
|
|
91
|
-
// Skip version byte (2 hex chars) and take 32 bytes (64 hex chars)
|
|
92
|
-
const privateKeyHex = hex.substring(2, 66);
|
|
93
|
-
return privateKeyHex;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
throw new Error("Invalid WIF length");
|
|
97
|
-
} catch (error) {
|
|
98
|
-
throw new Error(`Failed to convert WIF to hex: ${error}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
63
|
export function isEphemeralAnchorOutput(
|
|
103
64
|
script?: Uint8Array,
|
|
104
65
|
amount?: bigint,
|
|
@@ -542,7 +503,7 @@ export async function constructUnilateralExitFeeBumpPackages(
|
|
|
542
503
|
}
|
|
543
504
|
|
|
544
505
|
// Helper function to create RIPEMD160(SHA256(data)) hash
|
|
545
|
-
function hash160(data: Uint8Array): Uint8Array {
|
|
506
|
+
export function hash160(data: Uint8Array): Uint8Array {
|
|
546
507
|
// Proper implementation using RIPEMD160(SHA256(data))
|
|
547
508
|
const sha256Hash = sha256(data);
|
|
548
509
|
return ripemd160(sha256Hash);
|