@buildonspark/spark-sdk 0.0.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/dist/graphql/client.d.ts +24 -0
- package/dist/graphql/client.js +177 -0
- package/dist/graphql/client.js.map +1 -0
- package/dist/graphql/mutations/CompleteCoopExit.d.ts +1 -0
- package/dist/graphql/mutations/CompleteCoopExit.js +19 -0
- package/dist/graphql/mutations/CompleteCoopExit.js.map +1 -0
- package/dist/graphql/mutations/CompleteLeavesSwap.d.ts +1 -0
- package/dist/graphql/mutations/CompleteLeavesSwap.js +17 -0
- package/dist/graphql/mutations/CompleteLeavesSwap.js.map +1 -0
- package/dist/graphql/mutations/RequestCoopExit.d.ts +1 -0
- package/dist/graphql/mutations/RequestCoopExit.js +20 -0
- package/dist/graphql/mutations/RequestCoopExit.js.map +1 -0
- package/dist/graphql/mutations/RequestLightningReceive.d.ts +1 -0
- package/dist/graphql/mutations/RequestLightningReceive.js +26 -0
- package/dist/graphql/mutations/RequestLightningReceive.js.map +1 -0
- package/dist/graphql/mutations/RequestLightningSend.d.ts +1 -0
- package/dist/graphql/mutations/RequestLightningSend.js +18 -0
- package/dist/graphql/mutations/RequestLightningSend.js.map +1 -0
- package/dist/graphql/mutations/RequestSwapLeaves.d.ts +1 -0
- package/dist/graphql/mutations/RequestSwapLeaves.js +26 -0
- package/dist/graphql/mutations/RequestSwapLeaves.js.map +1 -0
- package/dist/graphql/objects/BitcoinNetwork.d.ts +17 -0
- package/dist/graphql/objects/BitcoinNetwork.js +20 -0
- package/dist/graphql/objects/BitcoinNetwork.js.map +1 -0
- package/dist/graphql/objects/CompleteCoopExitInput.d.ts +7 -0
- package/dist/graphql/objects/CompleteCoopExitInput.js +14 -0
- package/dist/graphql/objects/CompleteCoopExitInput.js.map +1 -0
- package/dist/graphql/objects/CompleteCoopExitOutput.d.ts +7 -0
- package/dist/graphql/objects/CompleteCoopExitOutput.js +19 -0
- package/dist/graphql/objects/CompleteCoopExitOutput.js.map +1 -0
- package/dist/graphql/objects/CompleteLeavesSwapInput.d.ts +8 -0
- package/dist/graphql/objects/CompleteLeavesSwapInput.js +16 -0
- package/dist/graphql/objects/CompleteLeavesSwapInput.js.map +1 -0
- package/dist/graphql/objects/CompleteLeavesSwapOutput.d.ts +7 -0
- package/dist/graphql/objects/CompleteLeavesSwapOutput.js +19 -0
- package/dist/graphql/objects/CompleteLeavesSwapOutput.js.map +1 -0
- package/dist/graphql/objects/Connection.d.ts +16 -0
- package/dist/graphql/objects/Connection.js +56 -0
- package/dist/graphql/objects/Connection.js.map +1 -0
- package/dist/graphql/objects/CoopExitFeeEstimateInput.d.ts +7 -0
- package/dist/graphql/objects/CoopExitFeeEstimateInput.js +14 -0
- package/dist/graphql/objects/CoopExitFeeEstimateInput.js.map +1 -0
- package/dist/graphql/objects/CoopExitFeeEstimateOutput.d.ts +8 -0
- package/dist/graphql/objects/CoopExitFeeEstimateOutput.js +26 -0
- package/dist/graphql/objects/CoopExitFeeEstimateOutput.js.map +1 -0
- package/dist/graphql/objects/CoopExitRequest.d.ts +32 -0
- package/dist/graphql/objects/CoopExitRequest.js +64 -0
- package/dist/graphql/objects/CoopExitRequest.js.map +1 -0
- package/dist/graphql/objects/CurrencyAmount.d.ts +24 -0
- package/dist/graphql/objects/CurrencyAmount.js +30 -0
- package/dist/graphql/objects/CurrencyAmount.js.map +1 -0
- package/dist/graphql/objects/CurrencyUnit.d.ts +27 -0
- package/dist/graphql/objects/CurrencyUnit.js +30 -0
- package/dist/graphql/objects/CurrencyUnit.js.map +1 -0
- package/dist/graphql/objects/Entity.d.ts +16 -0
- package/dist/graphql/objects/Entity.js +141 -0
- package/dist/graphql/objects/Entity.js.map +1 -0
- package/dist/graphql/objects/Invoice.d.ts +15 -0
- package/dist/graphql/objects/Invoice.js +45 -0
- package/dist/graphql/objects/Invoice.js.map +1 -0
- package/dist/graphql/objects/Leaf.d.ts +11 -0
- package/dist/graphql/objects/Leaf.js +29 -0
- package/dist/graphql/objects/Leaf.js.map +1 -0
- package/dist/graphql/objects/LeavesSwapFeeEstimateInput.d.ts +6 -0
- package/dist/graphql/objects/LeavesSwapFeeEstimateInput.js +12 -0
- package/dist/graphql/objects/LeavesSwapFeeEstimateInput.js.map +1 -0
- package/dist/graphql/objects/LeavesSwapFeeEstimateOutput.d.ts +8 -0
- package/dist/graphql/objects/LeavesSwapFeeEstimateOutput.js +26 -0
- package/dist/graphql/objects/LeavesSwapFeeEstimateOutput.js.map +1 -0
- package/dist/graphql/objects/LeavesSwapRequest.d.ts +32 -0
- package/dist/graphql/objects/LeavesSwapRequest.js +87 -0
- package/dist/graphql/objects/LeavesSwapRequest.js.map +1 -0
- package/dist/graphql/objects/LightningReceiveFeeEstimateInput.d.ts +8 -0
- package/dist/graphql/objects/LightningReceiveFeeEstimateInput.js +15 -0
- package/dist/graphql/objects/LightningReceiveFeeEstimateInput.js.map +1 -0
- package/dist/graphql/objects/LightningReceiveFeeEstimateOutput.d.ts +8 -0
- package/dist/graphql/objects/LightningReceiveFeeEstimateOutput.js +26 -0
- package/dist/graphql/objects/LightningReceiveFeeEstimateOutput.js.map +1 -0
- package/dist/graphql/objects/LightningReceiveRequest.d.ts +31 -0
- package/dist/graphql/objects/LightningReceiveRequest.js +93 -0
- package/dist/graphql/objects/LightningReceiveRequest.js.map +1 -0
- package/dist/graphql/objects/LightningReceiveRequestStatus.d.ts +16 -0
- package/dist/graphql/objects/LightningReceiveRequestStatus.js +19 -0
- package/dist/graphql/objects/LightningReceiveRequestStatus.js.map +1 -0
- package/dist/graphql/objects/LightningSendFeeEstimateInput.d.ts +6 -0
- package/dist/graphql/objects/LightningSendFeeEstimateInput.js +12 -0
- package/dist/graphql/objects/LightningSendFeeEstimateInput.js.map +1 -0
- package/dist/graphql/objects/LightningSendFeeEstimateOutput.d.ts +8 -0
- package/dist/graphql/objects/LightningSendFeeEstimateOutput.js +26 -0
- package/dist/graphql/objects/LightningSendFeeEstimateOutput.js.map +1 -0
- package/dist/graphql/objects/LightningSendRequest.d.ts +32 -0
- package/dist/graphql/objects/LightningSendRequest.js +78 -0
- package/dist/graphql/objects/LightningSendRequest.js.map +1 -0
- package/dist/graphql/objects/LightningSendRequestStatus.d.ts +15 -0
- package/dist/graphql/objects/LightningSendRequestStatus.js +18 -0
- package/dist/graphql/objects/LightningSendRequestStatus.js.map +1 -0
- package/dist/graphql/objects/PageInfo.d.ts +11 -0
- package/dist/graphql/objects/PageInfo.js +26 -0
- package/dist/graphql/objects/PageInfo.js.map +1 -0
- package/dist/graphql/objects/RequestCoopExitInput.d.ts +7 -0
- package/dist/graphql/objects/RequestCoopExitInput.js +14 -0
- package/dist/graphql/objects/RequestCoopExitInput.js.map +1 -0
- package/dist/graphql/objects/RequestCoopExitOutput.d.ts +7 -0
- package/dist/graphql/objects/RequestCoopExitOutput.js +19 -0
- package/dist/graphql/objects/RequestCoopExitOutput.js.map +1 -0
- package/dist/graphql/objects/RequestLeavesSwapInput.d.ts +13 -0
- package/dist/graphql/objects/RequestLeavesSwapInput.js +25 -0
- package/dist/graphql/objects/RequestLeavesSwapInput.js.map +1 -0
- package/dist/graphql/objects/RequestLeavesSwapOutput.d.ts +7 -0
- package/dist/graphql/objects/RequestLeavesSwapOutput.js +19 -0
- package/dist/graphql/objects/RequestLeavesSwapOutput.js.map +1 -0
- package/dist/graphql/objects/RequestLightningReceiveInput.d.ts +16 -0
- package/dist/graphql/objects/RequestLightningReceiveInput.js +21 -0
- package/dist/graphql/objects/RequestLightningReceiveInput.js.map +1 -0
- package/dist/graphql/objects/RequestLightningReceiveOutput.d.ts +7 -0
- package/dist/graphql/objects/RequestLightningReceiveOutput.js +19 -0
- package/dist/graphql/objects/RequestLightningReceiveOutput.js.map +1 -0
- package/dist/graphql/objects/RequestLightningSendInput.d.ts +7 -0
- package/dist/graphql/objects/RequestLightningSendInput.js +14 -0
- package/dist/graphql/objects/RequestLightningSendInput.js.map +1 -0
- package/dist/graphql/objects/RequestLightningSendOutput.d.ts +7 -0
- package/dist/graphql/objects/RequestLightningSendOutput.js +19 -0
- package/dist/graphql/objects/RequestLightningSendOutput.js.map +1 -0
- package/dist/graphql/objects/SparkCoopExitRequestStatus.d.ts +11 -0
- package/dist/graphql/objects/SparkCoopExitRequestStatus.js +14 -0
- package/dist/graphql/objects/SparkCoopExitRequestStatus.js.map +1 -0
- package/dist/graphql/objects/SparkLeavesSwapRequestStatus.d.ts +11 -0
- package/dist/graphql/objects/SparkLeavesSwapRequestStatus.js +14 -0
- package/dist/graphql/objects/SparkLeavesSwapRequestStatus.js.map +1 -0
- package/dist/graphql/objects/SparkTransferToLeavesConnection.d.ts +19 -0
- package/dist/graphql/objects/SparkTransferToLeavesConnection.js +45 -0
- package/dist/graphql/objects/SparkTransferToLeavesConnection.js.map +1 -0
- package/dist/graphql/objects/SwapLeaf.d.ts +9 -0
- package/dist/graphql/objects/SwapLeaf.js +23 -0
- package/dist/graphql/objects/SwapLeaf.js.map +1 -0
- package/dist/graphql/objects/Transfer.d.ts +24 -0
- package/dist/graphql/objects/Transfer.js +82 -0
- package/dist/graphql/objects/Transfer.js.map +1 -0
- package/dist/graphql/objects/UserLeafInput.d.ts +8 -0
- package/dist/graphql/objects/UserLeafInput.js +16 -0
- package/dist/graphql/objects/UserLeafInput.js.map +1 -0
- package/dist/graphql/objects/WalletUser.d.ts +21 -0
- package/dist/graphql/objects/WalletUser.js +48 -0
- package/dist/graphql/objects/WalletUser.js.map +1 -0
- package/dist/graphql/objects/index.d.ts +41 -0
- package/dist/graphql/objects/index.js +13 -0
- package/dist/graphql/objects/index.js.map +1 -0
- package/dist/graphql/queries/CoopExitFeeEstimate.d.ts +1 -0
- package/dist/graphql/queries/CoopExitFeeEstimate.js +18 -0
- package/dist/graphql/queries/CoopExitFeeEstimate.js.map +1 -0
- package/dist/graphql/queries/CurrentUser.d.ts +1 -0
- package/dist/graphql/queries/CurrentUser.js +10 -0
- package/dist/graphql/queries/CurrentUser.js.map +1 -0
- package/dist/graphql/queries/LightningReceiveFeeEstimate.d.ts +1 -0
- package/dist/graphql/queries/LightningReceiveFeeEstimate.js +18 -0
- package/dist/graphql/queries/LightningReceiveFeeEstimate.js.map +1 -0
- package/dist/graphql/queries/LightningSendFeeEstimate.d.ts +1 -0
- package/dist/graphql/queries/LightningSendFeeEstimate.js +16 -0
- package/dist/graphql/queries/LightningSendFeeEstimate.js.map +1 -0
- package/dist/proto/common.d.ts +58 -0
- package/dist/proto/common.js +350 -0
- package/dist/proto/common.js.map +1 -0
- package/dist/proto/google/protobuf/descriptor.d.ts +1228 -0
- package/dist/proto/google/protobuf/descriptor.js +5070 -0
- package/dist/proto/google/protobuf/descriptor.js.map +1 -0
- package/dist/proto/google/protobuf/duration.d.ts +99 -0
- package/dist/proto/google/protobuf/duration.js +90 -0
- package/dist/proto/google/protobuf/duration.js.map +1 -0
- package/dist/proto/google/protobuf/empty.d.ts +33 -0
- package/dist/proto/google/protobuf/empty.js +46 -0
- package/dist/proto/google/protobuf/empty.js.map +1 -0
- package/dist/proto/google/protobuf/timestamp.d.ts +128 -0
- package/dist/proto/google/protobuf/timestamp.js +90 -0
- package/dist/proto/google/protobuf/timestamp.js.map +1 -0
- package/dist/proto/mock.d.ts +48 -0
- package/dist/proto/mock.js +103 -0
- package/dist/proto/mock.js.map +1 -0
- package/dist/proto/spark.d.ts +1101 -0
- package/dist/proto/spark.js +9565 -0
- package/dist/proto/spark.js.map +1 -0
- package/dist/proto/spark_authn.d.ts +111 -0
- package/dist/proto/spark_authn.js +517 -0
- package/dist/proto/spark_authn.js.map +1 -0
- package/dist/proto/validate/validate.d.ts +1087 -0
- package/dist/proto/validate/validate.js +4437 -0
- package/dist/proto/validate/validate.js.map +1 -0
- package/dist/services/config.d.ts +24 -0
- package/dist/services/config.js +29 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/connection.d.ts +21 -0
- package/dist/services/connection.js +154 -0
- package/dist/services/connection.js.map +1 -0
- package/dist/services/coop-exit.d.ts +20 -0
- package/dist/services/coop-exit.js +102 -0
- package/dist/services/coop-exit.js.map +1 -0
- package/dist/services/deposit.d.ts +21 -0
- package/dist/services/deposit.js +214 -0
- package/dist/services/deposit.js.map +1 -0
- package/dist/services/lightning.d.ts +31 -0
- package/dist/services/lightning.js +196 -0
- package/dist/services/lightning.js.map +1 -0
- package/dist/services/token-transactions.d.ts +17 -0
- package/dist/services/token-transactions.js +297 -0
- package/dist/services/token-transactions.js.map +1 -0
- package/dist/services/transfer.d.ts +63 -0
- package/dist/services/transfer.js +499 -0
- package/dist/services/transfer.js.map +1 -0
- package/dist/services/tree-creation.d.ts +30 -0
- package/dist/services/tree-creation.js +404 -0
- package/dist/services/tree-creation.js.map +1 -0
- package/dist/signer/signer.d.ts +97 -0
- package/dist/signer/signer.js +239 -0
- package/dist/signer/signer.js.map +1 -0
- package/dist/spark-sdk.d.ts +87 -0
- package/dist/spark-sdk.js +675 -0
- package/dist/spark-sdk.js.map +1 -0
- package/dist/tests/adaptor-signature.test.d.ts +1 -0
- package/dist/tests/adaptor-signature.test.js +34 -0
- package/dist/tests/adaptor-signature.test.js.map +1 -0
- package/dist/tests/bitcoin.test.d.ts +1 -0
- package/dist/tests/bitcoin.test.js +77 -0
- package/dist/tests/bitcoin.test.js.map +1 -0
- package/dist/tests/coop-exit.test.d.ts +1 -0
- package/dist/tests/coop-exit.test.js +140 -0
- package/dist/tests/coop-exit.test.js.map +1 -0
- package/dist/tests/deposit.test.d.ts +1 -0
- package/dist/tests/deposit.test.js +57 -0
- package/dist/tests/deposit.test.js.map +1 -0
- package/dist/tests/keys.test.d.ts +1 -0
- package/dist/tests/keys.test.js +53 -0
- package/dist/tests/keys.test.js.map +1 -0
- package/dist/tests/lightning.test.d.ts +1 -0
- package/dist/tests/lightning.test.js +175 -0
- package/dist/tests/lightning.test.js.map +1 -0
- package/dist/tests/secret-sharing.test.d.ts +1 -0
- package/dist/tests/secret-sharing.test.js +41 -0
- package/dist/tests/secret-sharing.test.js.map +1 -0
- package/dist/tests/swap.test.d.ts +1 -0
- package/dist/tests/swap.test.js +131 -0
- package/dist/tests/swap.test.js.map +1 -0
- package/dist/tests/test-util.d.ts +24 -0
- package/dist/tests/test-util.js +137 -0
- package/dist/tests/test-util.js.map +1 -0
- package/dist/tests/tokens.test.d.ts +1 -0
- package/dist/tests/tokens.test.js +42 -0
- package/dist/tests/tokens.test.js.map +1 -0
- package/dist/tests/transfer.test.d.ts +1 -0
- package/dist/tests/transfer.test.js +175 -0
- package/dist/tests/transfer.test.js.map +1 -0
- package/dist/tests/tree-creation.test.d.ts +1 -0
- package/dist/tests/tree-creation.test.js +32 -0
- package/dist/tests/tree-creation.test.js.map +1 -0
- package/dist/tests/utils/spark-testing-wallet.d.ts +14 -0
- package/dist/tests/utils/spark-testing-wallet.js +13 -0
- package/dist/tests/utils/spark-testing-wallet.js.map +1 -0
- package/dist/tests/utils/test-faucet.d.ts +22 -0
- package/dist/tests/utils/test-faucet.js +148 -0
- package/dist/tests/utils/test-faucet.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/adaptor-signature.d.ts +7 -0
- package/dist/utils/adaptor-signature.js +114 -0
- package/dist/utils/adaptor-signature.js.map +1 -0
- package/dist/utils/bitcoin.d.ts +12 -0
- package/dist/utils/bitcoin.js +87 -0
- package/dist/utils/bitcoin.js.map +1 -0
- package/dist/utils/crypto.d.ts +1 -0
- package/dist/utils/crypto.js +14 -0
- package/dist/utils/crypto.js.map +1 -0
- package/dist/utils/index.d.ts +14 -0
- package/dist/utils/index.js +15 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/keys.d.ts +7 -0
- package/dist/utils/keys.js +68 -0
- package/dist/utils/keys.js.map +1 -0
- package/dist/utils/network.d.ts +11 -0
- package/dist/utils/network.js +26 -0
- package/dist/utils/network.js.map +1 -0
- package/dist/utils/proof.d.ts +1 -0
- package/dist/utils/proof.js +12 -0
- package/dist/utils/proof.js.map +1 -0
- package/dist/utils/response-validation.d.ts +1 -0
- package/dist/utils/response-validation.js +16 -0
- package/dist/utils/response-validation.js.map +1 -0
- package/dist/utils/secret-sharing.d.ts +26 -0
- package/dist/utils/secret-sharing.js +175 -0
- package/dist/utils/secret-sharing.js.map +1 -0
- package/dist/utils/signing.d.ts +12 -0
- package/dist/utils/signing.js +67 -0
- package/dist/utils/signing.js.map +1 -0
- package/dist/utils/token-hashing.d.ts +3 -0
- package/dist/utils/token-hashing.js +117 -0
- package/dist/utils/token-hashing.js.map +1 -0
- package/dist/utils/token-keyshares.d.ts +5 -0
- package/dist/utils/token-keyshares.js +17 -0
- package/dist/utils/token-keyshares.js.map +1 -0
- package/dist/utils/token-transactions.d.ts +5 -0
- package/dist/utils/token-transactions.js +40 -0
- package/dist/utils/token-transactions.js.map +1 -0
- package/dist/utils/transaction.d.ts +8 -0
- package/dist/utils/transaction.js +33 -0
- package/dist/utils/transaction.js.map +1 -0
- package/dist/utils/wasm-wrapper.d.ts +2 -0
- package/dist/utils/wasm-wrapper.js +36 -0
- package/dist/utils/wasm-wrapper.js.map +1 -0
- package/dist/utils/wasm.d.ts +54 -0
- package/dist/utils/wasm.js +26 -0
- package/dist/utils/wasm.js.map +1 -0
- package/dist/wasm/spark_bindings.d.ts +229 -0
- package/dist/wasm/spark_bindings.js +1097 -0
- package/dist/wasm/spark_bindings.js.map +1 -0
- package/dist/wasm/spark_bindings_bg.wasm +0 -0
- package/package.json +140 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
import { bytesToHex, hexToBytes, } from "@noble/curves/abstract/utils";
|
|
2
|
+
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
3
|
+
import { sha256 } from "@scure/btc-signer/utils";
|
|
4
|
+
import { decode } from "light-bolt11-decoder";
|
|
5
|
+
import SspClient from "./graphql/client.js";
|
|
6
|
+
import { BitcoinNetwork, } from "./graphql/objects/index.js";
|
|
7
|
+
import { TransferStatus, } from "./proto/spark.js";
|
|
8
|
+
import { WalletConfigService } from "./services/config.js";
|
|
9
|
+
import { ConnectionManager } from "./services/connection.js";
|
|
10
|
+
import { CoopExitService } from "./services/coop-exit.js";
|
|
11
|
+
import { DepositService } from "./services/deposit.js";
|
|
12
|
+
import { LightningService } from "./services/lightning.js";
|
|
13
|
+
import { TokenTransactionService } from "./services/token-transactions.js";
|
|
14
|
+
import { TransferService } from "./services/transfer.js";
|
|
15
|
+
import { TreeCreationService, } from "./services/tree-creation.js";
|
|
16
|
+
import { applyAdaptorToSignature, generateAdaptorFromSignature, generateSignatureFromExistingAdaptor, } from "./utils/adaptor-signature.js";
|
|
17
|
+
import { computeTaprootKeyNoScript, getSigHashFromTx, getTxFromRawTxBytes, getTxFromRawTxHex, getTxId, } from "./utils/bitcoin.js";
|
|
18
|
+
import { calculateAvailableTokenAmount, checkIfSelectedLeavesAreAvailable, } from "./utils/token-transactions.js";
|
|
19
|
+
import { initWasm } from "./utils/wasm-wrapper.js";
|
|
20
|
+
// Add this constant at the file level
|
|
21
|
+
const MAX_TOKEN_LEAVES = 100;
|
|
22
|
+
export class SparkWallet {
|
|
23
|
+
config;
|
|
24
|
+
connectionManager;
|
|
25
|
+
depositService;
|
|
26
|
+
transferService;
|
|
27
|
+
treeCreationService;
|
|
28
|
+
lightningService;
|
|
29
|
+
coopExitService;
|
|
30
|
+
tokenTransactionService;
|
|
31
|
+
sspClient = null;
|
|
32
|
+
wasmModule = null;
|
|
33
|
+
leaves = [];
|
|
34
|
+
tokenLeaves = new Map();
|
|
35
|
+
constructor(network, signer) {
|
|
36
|
+
this.config = new WalletConfigService(network, signer);
|
|
37
|
+
this.connectionManager = new ConnectionManager(this.config);
|
|
38
|
+
this.depositService = new DepositService(this.config, this.connectionManager);
|
|
39
|
+
this.transferService = new TransferService(this.config, this.connectionManager);
|
|
40
|
+
this.treeCreationService = new TreeCreationService(this.config, this.connectionManager);
|
|
41
|
+
this.tokenTransactionService = new TokenTransactionService(this.config, this.connectionManager);
|
|
42
|
+
this.lightningService = new LightningService(this.config, this.connectionManager);
|
|
43
|
+
this.coopExitService = new CoopExitService(this.config, this.connectionManager);
|
|
44
|
+
}
|
|
45
|
+
async initWasm() {
|
|
46
|
+
try {
|
|
47
|
+
this.wasmModule = await initWasm();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
console.error("Failed to initialize Wasm module", e);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async initializeWallet(identityPublicKey) {
|
|
54
|
+
this.sspClient = new SspClient(identityPublicKey);
|
|
55
|
+
await this.initWasm();
|
|
56
|
+
// TODO: Better leaf management?
|
|
57
|
+
this.leaves = await this.getLeaves();
|
|
58
|
+
this.config.signer.restoreSigningKeysFromLeafs(this.leaves);
|
|
59
|
+
// await this.syncTokenLeaves();
|
|
60
|
+
}
|
|
61
|
+
async selectLeaves(targetAmount) {
|
|
62
|
+
if (targetAmount <= 0) {
|
|
63
|
+
throw new Error("Target amount must be positive");
|
|
64
|
+
}
|
|
65
|
+
const leaves = await this.getLeaves();
|
|
66
|
+
if (leaves.length === 0) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
leaves.sort((a, b) => b.value - a.value);
|
|
70
|
+
let amount = 0;
|
|
71
|
+
let nodes = [];
|
|
72
|
+
for (const leaf of leaves) {
|
|
73
|
+
if (targetAmount - amount >= leaf.value) {
|
|
74
|
+
amount += leaf.value;
|
|
75
|
+
nodes.push(leaf);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (amount !== targetAmount) {
|
|
79
|
+
await this.requestLeavesSwap({ targetAmount });
|
|
80
|
+
amount = 0;
|
|
81
|
+
nodes = [];
|
|
82
|
+
const newLeaves = await this.getLeaves();
|
|
83
|
+
newLeaves.sort((a, b) => b.value - a.value);
|
|
84
|
+
for (const leaf of newLeaves) {
|
|
85
|
+
if (targetAmount - amount >= leaf.value) {
|
|
86
|
+
amount += leaf.value;
|
|
87
|
+
nodes.push(leaf);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return nodes;
|
|
92
|
+
}
|
|
93
|
+
async selectLeavesForSwap(targetAmount) {
|
|
94
|
+
if (targetAmount == 0) {
|
|
95
|
+
throw new Error("Target amount needs to > 0");
|
|
96
|
+
}
|
|
97
|
+
const leaves = await this.getLeaves();
|
|
98
|
+
leaves.sort((a, b) => a.value - b.value);
|
|
99
|
+
let amount = 0;
|
|
100
|
+
const nodes = [];
|
|
101
|
+
for (const leaf of leaves) {
|
|
102
|
+
if (amount < targetAmount) {
|
|
103
|
+
amount += leaf.value;
|
|
104
|
+
nodes.push(leaf);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (amount < targetAmount) {
|
|
108
|
+
throw new Error("You don't have enough nodes to swap for the target amount");
|
|
109
|
+
}
|
|
110
|
+
return nodes;
|
|
111
|
+
}
|
|
112
|
+
async getLeaves() {
|
|
113
|
+
const sparkClient = await this.connectionManager.createSparkClient(this.config.getCoordinatorAddress());
|
|
114
|
+
const leaves = await sparkClient.query_nodes({
|
|
115
|
+
source: {
|
|
116
|
+
$case: "ownerIdentityPubkey",
|
|
117
|
+
ownerIdentityPubkey: await this.config.signer.getIdentityPublicKey(),
|
|
118
|
+
},
|
|
119
|
+
includeParents: true,
|
|
120
|
+
});
|
|
121
|
+
return Object.entries(leaves.nodes)
|
|
122
|
+
.filter(([_, node]) => node.status === "AVAILABLE")
|
|
123
|
+
.map(([_, node]) => node);
|
|
124
|
+
}
|
|
125
|
+
async optimizeLeaves() {
|
|
126
|
+
if (this.leaves.length > 0) {
|
|
127
|
+
await this.requestLeavesSwap({ leaves: this.leaves });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async syncWallet() {
|
|
131
|
+
await this.claimTransfers();
|
|
132
|
+
// TODO: This is broken. Uncomment when fixed
|
|
133
|
+
// await this.syncTokenLeaves();
|
|
134
|
+
this.leaves = await this.getLeaves();
|
|
135
|
+
await this.optimizeLeaves();
|
|
136
|
+
}
|
|
137
|
+
isInitialized() {
|
|
138
|
+
return this.sspClient !== null && this.wasmModule !== null;
|
|
139
|
+
}
|
|
140
|
+
async getIdentityPublicKey() {
|
|
141
|
+
return bytesToHex(await this.config.signer.getIdentityPublicKey());
|
|
142
|
+
}
|
|
143
|
+
async initWalletFromMnemonic(mnemonic) {
|
|
144
|
+
if (!mnemonic) {
|
|
145
|
+
mnemonic = await this.config.signer.generateMnemonic();
|
|
146
|
+
}
|
|
147
|
+
const identityPublicKey = await this.config.signer.createSparkWalletFromMnemonic(mnemonic);
|
|
148
|
+
await this.initializeWallet(identityPublicKey);
|
|
149
|
+
return mnemonic;
|
|
150
|
+
}
|
|
151
|
+
async initWallet(seed) {
|
|
152
|
+
const identityPublicKey = await this.config.signer.createSparkWalletFromSeed(seed);
|
|
153
|
+
await this.initializeWallet(identityPublicKey);
|
|
154
|
+
return identityPublicKey;
|
|
155
|
+
}
|
|
156
|
+
async requestLeavesSwap({ targetAmount, leaves, }) {
|
|
157
|
+
if (targetAmount && targetAmount <= 0) {
|
|
158
|
+
throw new Error("targetAmount must be positive");
|
|
159
|
+
}
|
|
160
|
+
await this.claimTransfers();
|
|
161
|
+
let leavesToSwap;
|
|
162
|
+
if (targetAmount && leaves && leaves.length > 0) {
|
|
163
|
+
if (targetAmount < leaves.reduce((acc, leaf) => acc + leaf.value, 0)) {
|
|
164
|
+
throw new Error("targetAmount is less than the sum of leaves");
|
|
165
|
+
}
|
|
166
|
+
leavesToSwap = leaves;
|
|
167
|
+
}
|
|
168
|
+
else if (targetAmount) {
|
|
169
|
+
leavesToSwap = await this.selectLeavesForSwap(targetAmount);
|
|
170
|
+
}
|
|
171
|
+
else if (leaves && leaves.length > 0) {
|
|
172
|
+
leavesToSwap = leaves;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
throw new Error("targetAmount or leaves must be provided");
|
|
176
|
+
}
|
|
177
|
+
const leafKeyTweaks = await Promise.all(leavesToSwap.map(async (leaf) => ({
|
|
178
|
+
leaf,
|
|
179
|
+
signingPubKey: await this.config.signer.generatePublicKey(sha256(leaf.id)),
|
|
180
|
+
newSigningPubKey: await this.config.signer.generatePublicKey(),
|
|
181
|
+
})));
|
|
182
|
+
const { transfer, signatureMap } = await this.transferService.sendTransferSignRefund(leafKeyTweaks, await this.config.signer.getSspIdentityPublicKey(), new Date(Date.now() + 10 * 60 * 1000));
|
|
183
|
+
if (!transfer.leaves[0]?.leaf) {
|
|
184
|
+
throw new Error("Failed to get leaf");
|
|
185
|
+
}
|
|
186
|
+
const refundSignature = signatureMap.get(transfer.leaves[0].leaf.id);
|
|
187
|
+
if (!refundSignature) {
|
|
188
|
+
throw new Error("Failed to get refund signature");
|
|
189
|
+
}
|
|
190
|
+
const { adaptorPrivateKey, adaptorSignature } = generateAdaptorFromSignature(refundSignature);
|
|
191
|
+
if (!transfer.leaves[0].leaf) {
|
|
192
|
+
throw new Error("Failed to get leaf");
|
|
193
|
+
}
|
|
194
|
+
const userLeaves = [];
|
|
195
|
+
userLeaves.push({
|
|
196
|
+
leaf_id: transfer.leaves[0].leaf.id,
|
|
197
|
+
raw_unsigned_refund_transaction: bytesToHex(transfer.leaves[0].intermediateRefundTx),
|
|
198
|
+
adaptor_added_signature: bytesToHex(adaptorSignature),
|
|
199
|
+
});
|
|
200
|
+
for (let i = 1; i < transfer.leaves.length; i++) {
|
|
201
|
+
const leaf = transfer.leaves[i];
|
|
202
|
+
if (!leaf?.leaf) {
|
|
203
|
+
throw new Error("Failed to get leaf");
|
|
204
|
+
}
|
|
205
|
+
const refundSignature = signatureMap.get(leaf.leaf.id);
|
|
206
|
+
if (!refundSignature) {
|
|
207
|
+
throw new Error("Failed to get refund signature");
|
|
208
|
+
}
|
|
209
|
+
const signature = generateSignatureFromExistingAdaptor(refundSignature, adaptorPrivateKey);
|
|
210
|
+
userLeaves.push({
|
|
211
|
+
leaf_id: leaf.leaf.id,
|
|
212
|
+
raw_unsigned_refund_transaction: bytesToHex(leaf.intermediateRefundTx),
|
|
213
|
+
adaptor_added_signature: bytesToHex(signature),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const adaptorPubkey = bytesToHex(secp256k1.getPublicKey(adaptorPrivateKey));
|
|
217
|
+
let request = null;
|
|
218
|
+
try {
|
|
219
|
+
request = await this.sspClient?.requestLeaveSwap({
|
|
220
|
+
userLeaves,
|
|
221
|
+
adaptorPubkey,
|
|
222
|
+
targetAmountSats: targetAmount ||
|
|
223
|
+
leavesToSwap.reduce((acc, leaf) => acc + leaf.value, 0),
|
|
224
|
+
totalAmountSats: leavesToSwap.reduce((acc, leaf) => acc + leaf.value, 0),
|
|
225
|
+
// TODO: Request fee from SSP
|
|
226
|
+
feeSats: 0,
|
|
227
|
+
// TODO: Map config network to proto network
|
|
228
|
+
network: BitcoinNetwork.REGTEST,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
await this.transferService.cancelSendTransfer(transfer);
|
|
233
|
+
console.log("Cancelled send transfer", transfer.id);
|
|
234
|
+
throw new Error(`Failed to request leaves swap: ${e}`);
|
|
235
|
+
}
|
|
236
|
+
if (!request) {
|
|
237
|
+
throw new Error("Failed to request leaves swap. No response returned.");
|
|
238
|
+
}
|
|
239
|
+
const sparkClient = await this.connectionManager.createSparkClient(this.config.getCoordinatorAddress());
|
|
240
|
+
for (const leaf of request.swapLeaves) {
|
|
241
|
+
const response = await sparkClient.query_nodes({
|
|
242
|
+
source: {
|
|
243
|
+
$case: "nodeIds",
|
|
244
|
+
nodeIds: {
|
|
245
|
+
nodeIds: [leaf.leafId],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
const nodesLength = Object.values(response.nodes).length;
|
|
250
|
+
if (nodesLength !== 1) {
|
|
251
|
+
throw new Error(`Expected 1 node, got ${nodesLength}`);
|
|
252
|
+
}
|
|
253
|
+
if (!response.nodes[leaf.leafId]?.nodeTx) {
|
|
254
|
+
throw new Error(`Node tx not found for leaf ${leaf.leafId}`);
|
|
255
|
+
}
|
|
256
|
+
if (!response.nodes[leaf.leafId]?.verifyingPublicKey) {
|
|
257
|
+
throw new Error(`Node public key not found for leaf ${leaf.leafId}`);
|
|
258
|
+
}
|
|
259
|
+
// @ts-ignore - We do a null check above
|
|
260
|
+
const nodeTx = getTxFromRawTxBytes(response.nodes[leaf.leafId].nodeTx);
|
|
261
|
+
const refundTxBytes = hexToBytes(leaf.rawUnsignedRefundTransaction);
|
|
262
|
+
const refundTx = getTxFromRawTxBytes(refundTxBytes);
|
|
263
|
+
const sighash = getSigHashFromTx(refundTx, 0, nodeTx.getOutput(0));
|
|
264
|
+
// @ts-ignore - We do a null check above
|
|
265
|
+
const nodePublicKey = response.nodes[leaf.leafId].verifyingPublicKey;
|
|
266
|
+
const taprootKey = computeTaprootKeyNoScript(nodePublicKey.slice(1));
|
|
267
|
+
const adaptorSignatureBytes = hexToBytes(leaf.adaptorSignedSignature);
|
|
268
|
+
applyAdaptorToSignature(taprootKey.slice(1), sighash, adaptorSignatureBytes, adaptorPrivateKey);
|
|
269
|
+
}
|
|
270
|
+
await this.transferService.sendTransferTweakKey(transfer, leafKeyTweaks, signatureMap);
|
|
271
|
+
const completeResponse = await this.sspClient?.completeLeaveSwap({
|
|
272
|
+
adaptorSecretKey: bytesToHex(adaptorPrivateKey),
|
|
273
|
+
userOutboundTransferExternalId: transfer.id,
|
|
274
|
+
leavesSwapRequestId: request.id,
|
|
275
|
+
});
|
|
276
|
+
if (!completeResponse) {
|
|
277
|
+
throw new Error("Failed to complete leaves swap");
|
|
278
|
+
}
|
|
279
|
+
await this.claimTransfers();
|
|
280
|
+
return completeResponse;
|
|
281
|
+
}
|
|
282
|
+
async getBalance() {
|
|
283
|
+
await this.claimTransfers();
|
|
284
|
+
// await this.syncTokenLeaves();
|
|
285
|
+
const leaves = await this.getLeaves();
|
|
286
|
+
return leaves.reduce((acc, leaf) => acc + BigInt(leaf.value), 0n);
|
|
287
|
+
}
|
|
288
|
+
async generatePublicKey() {
|
|
289
|
+
return bytesToHex(await this.config.signer.generatePublicKey());
|
|
290
|
+
}
|
|
291
|
+
// ***** Deposit Flow *****
|
|
292
|
+
async generateDepositAddress(signingPubkey) {
|
|
293
|
+
return await this.depositService.generateDepositAddress({ signingPubkey });
|
|
294
|
+
}
|
|
295
|
+
async finalizeDeposit({ signingPubKey, verifyingKey, depositTx, vout, }) {
|
|
296
|
+
const response = await this.depositService.createTreeRoot({
|
|
297
|
+
signingPubKey,
|
|
298
|
+
verifyingKey,
|
|
299
|
+
depositTx,
|
|
300
|
+
vout,
|
|
301
|
+
});
|
|
302
|
+
return await this.transferDepositToSelf(response.nodes, signingPubKey);
|
|
303
|
+
}
|
|
304
|
+
async claimDeposits() {
|
|
305
|
+
const sparkClient = await this.connectionManager.createSparkClient(this.config.getCoordinatorAddress());
|
|
306
|
+
const identityPublicKey = await this.config.signer.getIdentityPublicKey();
|
|
307
|
+
const deposits = await sparkClient.query_unused_deposit_addresses({
|
|
308
|
+
identityPublicKey,
|
|
309
|
+
});
|
|
310
|
+
const depositNodes = [];
|
|
311
|
+
for (const deposit of deposits.depositAddresses) {
|
|
312
|
+
const tx = await this.queryMempoolTxs(deposit.depositAddress);
|
|
313
|
+
if (!tx) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const { depositTx, vout } = tx;
|
|
317
|
+
const nodes = await this.finalizeDeposit({
|
|
318
|
+
signingPubKey: deposit.userSigningPublicKey,
|
|
319
|
+
verifyingKey: deposit.verifyingPublicKey,
|
|
320
|
+
depositTx,
|
|
321
|
+
vout,
|
|
322
|
+
});
|
|
323
|
+
if (nodes) {
|
|
324
|
+
depositNodes.push(...nodes);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return depositNodes;
|
|
328
|
+
}
|
|
329
|
+
async queryMempoolTxs(address) {
|
|
330
|
+
const baseUrl = "https://regtest-mempool.dev.dev.sparkinfra.net/api";
|
|
331
|
+
const auth = btoa("lightspark:TFNR6ZeLdxF9HejW");
|
|
332
|
+
const response = await fetch(`${baseUrl}/address/${address}/txs`, {
|
|
333
|
+
headers: {
|
|
334
|
+
Authorization: `Basic ${auth}`,
|
|
335
|
+
"Content-Type": "application/json",
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
const addressTxs = await response.json();
|
|
339
|
+
if (addressTxs && addressTxs.length > 0) {
|
|
340
|
+
console.log("Found transaction");
|
|
341
|
+
const latestTx = addressTxs[0];
|
|
342
|
+
const outputIndex = latestTx.vout.findIndex((output) => output.scriptpubkey_address === address);
|
|
343
|
+
if (outputIndex === -1) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
const txResponse = await fetch(`${baseUrl}/tx/${latestTx.txid}/hex`, {
|
|
347
|
+
headers: {
|
|
348
|
+
Authorization: `Basic ${auth}`,
|
|
349
|
+
"Content-Type": "application/json",
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
const txHex = await txResponse.text();
|
|
353
|
+
const depositTx = getTxFromRawTxHex(txHex);
|
|
354
|
+
return {
|
|
355
|
+
depositTx,
|
|
356
|
+
vout: outputIndex,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
async transferDepositToSelf(leaves, signingPubKey) {
|
|
362
|
+
const leafKeyTweaks = await Promise.all(leaves.map(async (leaf) => ({
|
|
363
|
+
leaf,
|
|
364
|
+
signingPubKey,
|
|
365
|
+
newSigningPubKey: await this.config.signer.generatePublicKey(),
|
|
366
|
+
})));
|
|
367
|
+
await this.transferService.sendTransfer(leafKeyTweaks, await this.config.signer.getIdentityPublicKey(), new Date(Date.now() + 10 * 60 * 1000));
|
|
368
|
+
const pendingTransfers = await this.transferService.queryPendingTransfers();
|
|
369
|
+
if (pendingTransfers.transfers.length > 0) {
|
|
370
|
+
// @ts-ignore - We check the length, so the first element is guaranteed to exist
|
|
371
|
+
return (await this.claimTransfer(pendingTransfers.transfers[0])).nodes;
|
|
372
|
+
}
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
// ***** Transfer Flow *****
|
|
376
|
+
async sendTransfer({ amount, receiverPubKey, leaves, expiryTime = new Date(Date.now() + 10 * 60 * 1000), }) {
|
|
377
|
+
let leavesToSend = [];
|
|
378
|
+
if (leaves) {
|
|
379
|
+
leavesToSend = leaves.map((leaf) => ({
|
|
380
|
+
...leaf,
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
383
|
+
else if (amount) {
|
|
384
|
+
leavesToSend = await this.selectLeaves(amount);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
throw new Error("Must provide amount or leaves");
|
|
388
|
+
}
|
|
389
|
+
const leafKeyTweaks = await Promise.all(leavesToSend.map(async (leaf) => ({
|
|
390
|
+
leaf,
|
|
391
|
+
signingPubKey: await this.config.signer.generatePublicKey(sha256(leaf.id)),
|
|
392
|
+
newSigningPubKey: await this.config.signer.generatePublicKey(),
|
|
393
|
+
})));
|
|
394
|
+
const transfer = await this.transferService.sendTransfer(leafKeyTweaks, receiverPubKey, expiryTime);
|
|
395
|
+
const leavesToRemove = new Set(leavesToSend.map((leaf) => leaf.id));
|
|
396
|
+
this.leaves = this.leaves.filter((leaf) => !leavesToRemove.has(leaf.id));
|
|
397
|
+
return transfer;
|
|
398
|
+
}
|
|
399
|
+
async claimTransfer(transfer) {
|
|
400
|
+
const leafPubKeyMap = await this.transferService.verifyPendingTransfer(transfer);
|
|
401
|
+
let leavesToClaim = [];
|
|
402
|
+
for (const leaf of transfer.leaves) {
|
|
403
|
+
if (leaf.leaf) {
|
|
404
|
+
const leafPubKey = leafPubKeyMap.get(leaf.leaf.id);
|
|
405
|
+
if (leafPubKey) {
|
|
406
|
+
leavesToClaim.push({
|
|
407
|
+
leaf: leaf.leaf,
|
|
408
|
+
signingPubKey: leafPubKey,
|
|
409
|
+
newSigningPubKey: await this.config.signer.generatePublicKey(sha256(leaf.leaf.id)),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return await this.transferService.claimTransfer(transfer, leavesToClaim);
|
|
415
|
+
}
|
|
416
|
+
async claimTransfers() {
|
|
417
|
+
const transfers = await this.transferService.queryPendingTransfers();
|
|
418
|
+
let claimed = false;
|
|
419
|
+
for (const transfer of transfers.transfers) {
|
|
420
|
+
if (transfer.status !== TransferStatus.TRANSFER_STATUS_SENDER_KEY_TWEAKED &&
|
|
421
|
+
transfer.status !==
|
|
422
|
+
TransferStatus.TRANSFER_STATUS_RECEIVER_KEY_TWEAKED &&
|
|
423
|
+
transfer.status !==
|
|
424
|
+
TransferStatus.TRANSFER_STATUSR_RECEIVER_REFUND_SIGNED) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
await this.claimTransfer(transfer);
|
|
428
|
+
claimed = true;
|
|
429
|
+
}
|
|
430
|
+
return claimed;
|
|
431
|
+
}
|
|
432
|
+
async cancelAllSenderInitiatedTransfers() {
|
|
433
|
+
const transfers = await this.transferService.queryPendingTransfersBySender();
|
|
434
|
+
for (const transfer of transfers.transfers) {
|
|
435
|
+
if (transfer.status === TransferStatus.TRANSFER_STATUS_SENDER_KEY_TWEAKED) {
|
|
436
|
+
await this.transferService.cancelSendTransfer(transfer);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// ***** Lightning Flow *****
|
|
441
|
+
async createLightningInvoice({ amountSats, memo, expirySeconds,
|
|
442
|
+
// TODO: This should default to lightspark ssp
|
|
443
|
+
invoiceCreator = () => Promise.resolve(""), }) {
|
|
444
|
+
if (!this.sspClient) {
|
|
445
|
+
throw new Error("SSP client not initialized");
|
|
446
|
+
}
|
|
447
|
+
const requestLightningInvoice = async (amountSats, paymentHash, memo) => {
|
|
448
|
+
const invoice = await this.sspClient.requestLightningReceive({
|
|
449
|
+
amountSats,
|
|
450
|
+
// TODO: Map config network to ssp network
|
|
451
|
+
network: BitcoinNetwork.REGTEST,
|
|
452
|
+
paymentHash: bytesToHex(paymentHash),
|
|
453
|
+
expirySecs: expirySeconds,
|
|
454
|
+
memo,
|
|
455
|
+
});
|
|
456
|
+
return invoice?.invoice.encodedEnvoice;
|
|
457
|
+
};
|
|
458
|
+
return this.lightningService.createLightningInvoice({
|
|
459
|
+
amountSats,
|
|
460
|
+
memo,
|
|
461
|
+
invoiceCreator: requestLightningInvoice,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
async payLightningInvoice({ invoice, amountSats, }) {
|
|
465
|
+
if (!this.sspClient) {
|
|
466
|
+
throw new Error("SSP client not initialized");
|
|
467
|
+
}
|
|
468
|
+
// TODO: Get fee
|
|
469
|
+
const decodedInvoice = decode(invoice);
|
|
470
|
+
amountSats =
|
|
471
|
+
Number(decodedInvoice.sections.find((section) => section.name === "amount")
|
|
472
|
+
?.value) / 1000;
|
|
473
|
+
if (isNaN(amountSats) || amountSats <= 0) {
|
|
474
|
+
throw new Error("Invalid amount");
|
|
475
|
+
}
|
|
476
|
+
const paymentHash = decodedInvoice.sections.find((section) => section.name === "payment_hash")?.value;
|
|
477
|
+
if (!paymentHash) {
|
|
478
|
+
throw new Error("No payment hash found in invoice");
|
|
479
|
+
}
|
|
480
|
+
// fetch leaves for amount
|
|
481
|
+
const leaves = await this.selectLeaves(amountSats);
|
|
482
|
+
const leavesToSend = await Promise.all(leaves.map(async (leaf) => ({
|
|
483
|
+
leaf,
|
|
484
|
+
signingPubKey: await this.config.signer.generatePublicKey(sha256(leaf.id)),
|
|
485
|
+
newSigningPubKey: await this.config.signer.generatePublicKey(),
|
|
486
|
+
})));
|
|
487
|
+
const swapResponse = await this.lightningService.swapNodesForPreimage({
|
|
488
|
+
leaves: leavesToSend,
|
|
489
|
+
receiverIdentityPubkey: await this.config.signer.getSspIdentityPublicKey(),
|
|
490
|
+
paymentHash: hexToBytes(paymentHash),
|
|
491
|
+
isInboundPayment: false,
|
|
492
|
+
invoiceString: invoice,
|
|
493
|
+
});
|
|
494
|
+
if (!swapResponse.transfer) {
|
|
495
|
+
throw new Error("Failed to swap nodes for preimage");
|
|
496
|
+
}
|
|
497
|
+
const transfer = await this.transferService.sendTransferTweakKey(swapResponse.transfer, leavesToSend, new Map());
|
|
498
|
+
const sspResponse = await this.sspClient.requestLightningSend({
|
|
499
|
+
encodedInvoice: invoice,
|
|
500
|
+
idempotencyKey: paymentHash,
|
|
501
|
+
});
|
|
502
|
+
if (!sspResponse) {
|
|
503
|
+
throw new Error("Failed to contact SSP");
|
|
504
|
+
}
|
|
505
|
+
const leavesToRemove = new Set(leavesToSend.map((leaf) => leaf.leaf.id));
|
|
506
|
+
this.leaves = this.leaves.filter((leaf) => !leavesToRemove.has(leaf.id));
|
|
507
|
+
return sspResponse;
|
|
508
|
+
}
|
|
509
|
+
async getLightningReceiveFeeEstimate({ amountSats, network, }) {
|
|
510
|
+
if (!this.sspClient) {
|
|
511
|
+
throw new Error("SSP client not initialized");
|
|
512
|
+
}
|
|
513
|
+
return await this.sspClient.getLightningReceiveFeeEstimate(amountSats, network);
|
|
514
|
+
}
|
|
515
|
+
async getLightningSendFeeEstimate({ encodedInvoice, }) {
|
|
516
|
+
if (!this.sspClient) {
|
|
517
|
+
throw new Error("SSP client not initialized");
|
|
518
|
+
}
|
|
519
|
+
return await this.sspClient.getLightningSendFeeEstimate(encodedInvoice);
|
|
520
|
+
}
|
|
521
|
+
// ***** Tree Creation Flow *****
|
|
522
|
+
async generateDepositAddressForTree(vout, parentSigningPubKey, parentTx, parentNode) {
|
|
523
|
+
return await this.treeCreationService.generateDepositAddressForTree(vout, parentSigningPubKey, parentTx, parentNode);
|
|
524
|
+
}
|
|
525
|
+
async createTree(vout, root, createLeaves, parentTx, parentNode) {
|
|
526
|
+
return await this.treeCreationService.createTree(vout, root, createLeaves, parentTx, parentNode);
|
|
527
|
+
}
|
|
528
|
+
// ***** Cooperative Exit Flow *****
|
|
529
|
+
async coopExit(onchainAddress, targetAmountSats) {
|
|
530
|
+
let leavesToSend = [];
|
|
531
|
+
if (targetAmountSats) {
|
|
532
|
+
leavesToSend = await this.selectLeaves(targetAmountSats);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
leavesToSend = this.leaves.map((leaf) => ({
|
|
536
|
+
...leaf,
|
|
537
|
+
}));
|
|
538
|
+
}
|
|
539
|
+
const leafKeyTweaks = await Promise.all(leavesToSend.map(async (leaf) => ({
|
|
540
|
+
leaf,
|
|
541
|
+
signingPubKey: await this.config.signer.generatePublicKey(sha256(leaf.id)),
|
|
542
|
+
newSigningPubKey: await this.config.signer.generatePublicKey(),
|
|
543
|
+
})));
|
|
544
|
+
const coopExitRequest = await this.sspClient?.requestCoopExit({
|
|
545
|
+
leafExternalIds: leavesToSend.map((leaf) => leaf.id),
|
|
546
|
+
withdrawalAddress: onchainAddress,
|
|
547
|
+
});
|
|
548
|
+
if (!coopExitRequest?.rawConnectorTransaction) {
|
|
549
|
+
throw new Error("Failed to request coop exit");
|
|
550
|
+
}
|
|
551
|
+
const connectorTx = getTxFromRawTxHex(coopExitRequest.rawConnectorTransaction);
|
|
552
|
+
const coopExitTxId = getTxId(connectorTx);
|
|
553
|
+
const connectorOutputs = [];
|
|
554
|
+
for (let i = 0; i < connectorTx.outputsLength - 1; i++) {
|
|
555
|
+
connectorOutputs.push({
|
|
556
|
+
txid: hexToBytes(coopExitTxId),
|
|
557
|
+
index: i,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
const sspPubIdentityKey = await this.config.signer.getSspIdentityPublicKey();
|
|
561
|
+
const transfer = await this.coopExitService.getConnectorRefundSignatures({
|
|
562
|
+
leaves: leafKeyTweaks,
|
|
563
|
+
exitTxId: hexToBytes(coopExitTxId),
|
|
564
|
+
connectorOutputs,
|
|
565
|
+
receiverPubKey: sspPubIdentityKey,
|
|
566
|
+
});
|
|
567
|
+
const completeResponse = await this.sspClient?.completeCoopExit({
|
|
568
|
+
userOutboundTransferExternalId: transfer.transfer.id,
|
|
569
|
+
coopExitRequestId: coopExitRequest.id,
|
|
570
|
+
});
|
|
571
|
+
return completeResponse;
|
|
572
|
+
}
|
|
573
|
+
async getCoopExitFeeEstimate({ leafExternalIds, withdrawalAddress, }) {
|
|
574
|
+
if (!this.sspClient) {
|
|
575
|
+
throw new Error("SSP client not initialized");
|
|
576
|
+
}
|
|
577
|
+
return await this.sspClient.getCoopExitFeeEstimate({
|
|
578
|
+
leafExternalIds,
|
|
579
|
+
withdrawalAddress,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
// ***** Token Flow *****
|
|
583
|
+
async syncTokenLeaves() {
|
|
584
|
+
this.tokenLeaves.clear();
|
|
585
|
+
const trackedPublicKeys = await this.config.signer.getTrackedPublicKeys();
|
|
586
|
+
const unsortedTokenLeaves = await this.tokenTransactionService.fetchOwnedTokenLeaves([...trackedPublicKeys, await this.config.signer.getIdentityPublicKey()], []);
|
|
587
|
+
// Group leaves by token key
|
|
588
|
+
const groupedLeaves = new Map();
|
|
589
|
+
unsortedTokenLeaves.forEach((leaf) => {
|
|
590
|
+
const tokenKey = bytesToHex(leaf.leaf.tokenPublicKey);
|
|
591
|
+
const index = leaf.previousTransactionVout;
|
|
592
|
+
if (!groupedLeaves.has(tokenKey)) {
|
|
593
|
+
groupedLeaves.set(tokenKey, []);
|
|
594
|
+
}
|
|
595
|
+
groupedLeaves.get(tokenKey).push({
|
|
596
|
+
...leaf,
|
|
597
|
+
previousTransactionVout: index,
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
this.tokenLeaves = groupedLeaves;
|
|
601
|
+
}
|
|
602
|
+
async getAllTokenLeaves() {
|
|
603
|
+
await this.syncTokenLeaves();
|
|
604
|
+
return this.tokenLeaves;
|
|
605
|
+
}
|
|
606
|
+
async getAllTokenBalances() {
|
|
607
|
+
await this.syncTokenLeaves();
|
|
608
|
+
const balances = new Map();
|
|
609
|
+
for (const [tokenPublicKey, leaves] of this.tokenLeaves.entries()) {
|
|
610
|
+
balances.set(tokenPublicKey, calculateAvailableTokenAmount(leaves));
|
|
611
|
+
}
|
|
612
|
+
return balances;
|
|
613
|
+
}
|
|
614
|
+
async getTokenBalance(tokenPublicKey) {
|
|
615
|
+
await this.syncTokenLeaves();
|
|
616
|
+
if (!this.tokenLeaves.has(tokenPublicKey)) {
|
|
617
|
+
return 0n;
|
|
618
|
+
}
|
|
619
|
+
return calculateAvailableTokenAmount(this.tokenLeaves.get(tokenPublicKey));
|
|
620
|
+
}
|
|
621
|
+
async transferTokens(tokenPublicKey, tokenAmount, recipientPublicKey, selectedLeaves) {
|
|
622
|
+
await this.syncTokenLeaves();
|
|
623
|
+
if (!this.tokenLeaves.has(tokenPublicKey)) {
|
|
624
|
+
throw new Error("No token leaves with the given tokenPublicKey");
|
|
625
|
+
}
|
|
626
|
+
const tokenPublicKeyBytes = hexToBytes(tokenPublicKey);
|
|
627
|
+
const recipientPublicKeyBytes = hexToBytes(recipientPublicKey);
|
|
628
|
+
if (selectedLeaves) {
|
|
629
|
+
if (!checkIfSelectedLeavesAreAvailable(selectedLeaves, this.tokenLeaves, tokenPublicKeyBytes)) {
|
|
630
|
+
throw new Error("One or more selected leaves are not available");
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
selectedLeaves = this.selectTokenLeaves(tokenPublicKey, tokenAmount);
|
|
635
|
+
}
|
|
636
|
+
if (selectedLeaves.length > MAX_TOKEN_LEAVES) {
|
|
637
|
+
throw new Error("Too many leaves selected");
|
|
638
|
+
}
|
|
639
|
+
const tokenTransaction = await this.tokenTransactionService.constructTransferTokenTransaction(selectedLeaves, recipientPublicKeyBytes, tokenPublicKeyBytes, tokenAmount);
|
|
640
|
+
await this.tokenTransactionService.broadcastTokenTransaction(tokenTransaction, selectedLeaves.map((leaf) => leaf.leaf.ownerPublicKey), selectedLeaves.map((leaf) => leaf.leaf.revocationPublicKey));
|
|
641
|
+
}
|
|
642
|
+
selectTokenLeaves(tokenPublicKey, tokenAmount) {
|
|
643
|
+
return this.tokenTransactionService.selectTokenLeaves(this.tokenLeaves.get(tokenPublicKey), tokenAmount);
|
|
644
|
+
}
|
|
645
|
+
// If no leaves are passed in, it will take all the leaves available for the given tokenPublicKey
|
|
646
|
+
async consolidateTokenLeaves(tokenPublicKey, selectedLeaves) {
|
|
647
|
+
await this.syncTokenLeaves();
|
|
648
|
+
const tokenPublicKeyBytes = hexToBytes(tokenPublicKey);
|
|
649
|
+
if (!this.tokenLeaves.has(tokenPublicKey)) {
|
|
650
|
+
throw new Error("No token leaves with the given tokenPublicKey");
|
|
651
|
+
}
|
|
652
|
+
if (selectedLeaves) {
|
|
653
|
+
if (!checkIfSelectedLeavesAreAvailable(selectedLeaves, this.tokenLeaves, tokenPublicKeyBytes)) {
|
|
654
|
+
throw new Error("One or more selected leaves are not available");
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
// Get all available leaves
|
|
659
|
+
selectedLeaves = this.tokenLeaves.get(tokenPublicKey);
|
|
660
|
+
// Limit to MAX_TOKEN_LEAVES if there are too many
|
|
661
|
+
if (selectedLeaves.length > MAX_TOKEN_LEAVES) {
|
|
662
|
+
selectedLeaves = selectedLeaves.slice(0, MAX_TOKEN_LEAVES);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (selectedLeaves.length === 1) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (selectedLeaves.length > MAX_TOKEN_LEAVES) {
|
|
669
|
+
throw new Error("Too many leaves selected");
|
|
670
|
+
}
|
|
671
|
+
const partialTokenTransaction = await this.tokenTransactionService.constructConsolidateTokenTransaction(selectedLeaves, tokenPublicKeyBytes);
|
|
672
|
+
await this.tokenTransactionService.broadcastTokenTransaction(partialTokenTransaction, selectedLeaves.map((leaf) => leaf.leaf.ownerPublicKey), selectedLeaves.map((leaf) => leaf.leaf.revocationPublicKey));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
//# sourceMappingURL=spark-sdk.js.map
|