@buildonspark/spark-sdk 0.1.45 → 0.1.47
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 +22 -0
- package/dist/{chunk-I54FARY2.js → chunk-EAP3U3CW.js} +14 -14
- package/dist/chunk-GWFQ7EBA.js +3773 -0
- package/dist/{chunk-J2IE4Z7Y.js → chunk-NNX4OK44.js} +3487 -934
- package/dist/{RequestLightningSendInput-Du0z7Om7.d.cts → client-CvpTRpcw.d.cts} +422 -212
- package/dist/{RequestLightningSendInput-DEPd_fPO.d.ts → client-D7KgLN44.d.ts} +422 -212
- package/dist/graphql/objects/index.d.cts +5 -9
- package/dist/graphql/objects/index.d.ts +5 -9
- package/dist/graphql/objects/index.js +1 -1
- package/dist/index.cjs +20461 -23377
- package/dist/index.d.cts +15 -769
- package/dist/index.d.ts +15 -769
- package/dist/index.js +81 -71
- package/dist/index.node.cjs +21994 -25018
- package/dist/index.node.d.cts +312 -34
- package/dist/index.node.d.ts +312 -34
- package/dist/index.node.js +82 -176
- package/dist/native/index.cjs +22847 -25841
- package/dist/native/index.d.cts +974 -1138
- package/dist/native/index.d.ts +974 -1138
- package/dist/native/index.js +10604 -13592
- package/dist/proto/lrc20.d.cts +2 -2
- package/dist/proto/lrc20.d.ts +2 -2
- package/dist/proto/lrc20.js +3098 -46
- package/dist/proto/spark.d.cts +1 -1
- package/dist/proto/spark.d.ts +1 -1
- package/dist/proto/spark_token.d.cts +1 -1
- package/dist/proto/spark_token.d.ts +1 -1
- package/dist/{sdk-types-Cc4l4kb1.d.ts → sdk-types-BGCeea0G.d.ts} +1 -1
- package/dist/{sdk-types-B0SwjolI.d.cts → sdk-types-XUeQMLFP.d.cts} +1 -1
- package/dist/{spark-dM7EYXYQ.d.cts → spark-BbUrbvZz.d.cts} +1 -1
- package/dist/{spark-dM7EYXYQ.d.ts → spark-BbUrbvZz.d.ts} +1 -1
- package/dist/spark-wallet-BAFPpPtY.d.cts +923 -0
- package/dist/spark-wallet-CJkQW8pK.d.ts +923 -0
- package/dist/spark_bindings/native/index.d.cts +1 -1
- package/dist/spark_bindings/native/index.d.ts +1 -1
- package/dist/spark_bindings/wasm/index.d.cts +1 -1
- package/dist/spark_bindings/wasm/index.d.ts +1 -1
- package/dist/{services/index.cjs → tests/test-utils.cjs} +2512 -4380
- package/dist/tests/test-utils.d.cts +79 -0
- package/dist/tests/test-utils.d.ts +79 -0
- package/dist/tests/test-utils.js +85 -0
- package/dist/types/index.d.cts +5 -9
- package/dist/types/index.d.ts +5 -9
- package/dist/types/index.js +5 -5
- package/dist/{types-C-Rp0Oo7.d.cts → types-BADxR3bm.d.cts} +1 -1
- package/dist/{types-C-Rp0Oo7.d.ts → types-BADxR3bm.d.ts} +1 -1
- package/package.json +7 -35
- package/src/graphql/client.ts +59 -20
- package/src/index.node.ts +28 -2
- package/src/index.ts +31 -1
- package/src/native/index.ts +16 -2
- package/src/services/config.ts +4 -6
- package/src/services/connection.ts +131 -64
- package/src/services/lightning.ts +1 -2
- package/src/services/token-transactions.ts +7 -7
- package/src/services/transfer.ts +1 -1
- package/src/services/tree-creation.ts +1 -1
- package/src/services/wallet-config.ts +18 -10
- package/src/signer/signer.react-native.ts +2 -5
- package/src/signer/signer.ts +138 -64
- package/src/signer/types.ts +52 -0
- package/src/spark-wallet/spark-wallet.ts +79 -36
- package/src/spark-wallet/types.ts +4 -4
- package/src/tests/integration/coop-exit.test.ts +2 -1
- package/src/tests/integration/lightning.test.ts +2 -2
- package/src/tests/integration/swap.test.ts +1 -1
- package/src/tests/integration/transfer.test.ts +5 -5
- package/src/tests/integration/tree-creation.test.ts +1 -1
- package/src/tests/integration/wallet.test.ts +1 -0
- package/src/tests/isHermeticTest.ts +3 -24
- package/src/tests/{test-util.ts → test-utils.ts} +3 -7
- package/src/tests/wrapWithOtelSpan.test.ts +1 -1
- package/src/{address → utils}/address.ts +1 -1
- package/src/utils/crypto.ts +19 -9
- package/src/utils/index.ts +2 -0
- package/src/utils/network.ts +17 -0
- package/src/utils/secret-sharing.ts +1 -2
- package/src/utils/signing.ts +1 -1
- package/src/utils/token-transactions.ts +3 -3
- package/src/utils/unilateral-exit.ts +32 -0
- package/src/utils/xchain-address.ts +1 -1
- package/dist/BitcoinNetwork-TnABML0T.d.cts +0 -18
- package/dist/BitcoinNetwork-TnABML0T.d.ts +0 -18
- package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.cts +0 -10
- package/dist/LightningSendFeeEstimateInput-BgOhEAI-.d.ts +0 -10
- package/dist/address/index.cjs +0 -458
- package/dist/address/index.d.cts +0 -32
- package/dist/address/index.d.ts +0 -32
- package/dist/address/index.js +0 -17
- package/dist/chunk-5FUB65LX.js +0 -838
- package/dist/chunk-6264CGDM.js +0 -113
- package/dist/chunk-7V6N75CC.js +0 -24
- package/dist/chunk-C2S227QR.js +0 -2336
- package/dist/chunk-GSI4OLXZ.js +0 -117
- package/dist/chunk-GZ5IPPJ2.js +0 -170
- package/dist/chunk-HWJWKEIU.js +0 -75
- package/dist/chunk-KMUMFYFX.js +0 -137
- package/dist/chunk-L3EHBOUX.js +0 -0
- package/dist/chunk-NSJF5F5O.js +0 -325
- package/dist/chunk-NTFKFRQ2.js +0 -3146
- package/dist/chunk-PQN3C2MF.js +0 -1122
- package/dist/chunk-QNNSEJ4P.js +0 -232
- package/dist/chunk-R5PXJZQS.js +0 -277
- package/dist/chunk-VTUGIIWI.js +0 -0
- package/dist/chunk-YUPMXTCJ.js +0 -622
- package/dist/chunk-Z5HIAYFT.js +0 -84
- package/dist/index-B2AwKW5J.d.cts +0 -214
- package/dist/index-CJDi1HWc.d.ts +0 -214
- package/dist/network-BTJl-Sul.d.ts +0 -46
- package/dist/network-CqgsdUF2.d.cts +0 -46
- package/dist/services/config.cjs +0 -2354
- package/dist/services/config.d.cts +0 -42
- package/dist/services/config.d.ts +0 -42
- package/dist/services/config.js +0 -17
- package/dist/services/connection.cjs +0 -17691
- package/dist/services/connection.d.cts +0 -95
- package/dist/services/connection.d.ts +0 -95
- package/dist/services/connection.js +0 -11
- package/dist/services/index.d.cts +0 -21
- package/dist/services/index.d.ts +0 -21
- package/dist/services/index.js +0 -58
- package/dist/services/lrc-connection.cjs +0 -4713
- package/dist/services/lrc-connection.d.cts +0 -34
- package/dist/services/lrc-connection.d.ts +0 -34
- package/dist/services/lrc-connection.js +0 -11
- package/dist/services/token-transactions.cjs +0 -2877
- package/dist/services/token-transactions.d.cts +0 -75
- package/dist/services/token-transactions.d.ts +0 -75
- package/dist/services/token-transactions.js +0 -15
- package/dist/services/wallet-config.cjs +0 -340
- package/dist/services/wallet-config.d.cts +0 -56
- package/dist/services/wallet-config.d.ts +0 -56
- package/dist/services/wallet-config.js +0 -33
- package/dist/signer/signer.cjs +0 -2004
- package/dist/signer/signer.d.cts +0 -10
- package/dist/signer/signer.d.ts +0 -10
- package/dist/signer/signer.js +0 -24
- package/dist/signer-BocS_J6B.d.ts +0 -187
- package/dist/signer-DKS0AJkw.d.cts +0 -187
- package/dist/utils/index.cjs +0 -2947
- package/dist/utils/index.d.cts +0 -18
- package/dist/utils/index.d.ts +0 -18
- package/dist/utils/index.js +0 -157
- package/src/address/index.ts +0 -1
- package/src/services/lrc-connection.ts +0 -215
package/dist/chunk-5FUB65LX.js
DELETED
|
@@ -1,838 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
encodeSparkAddress
|
|
3
|
-
} from "./chunk-KMUMFYFX.js";
|
|
4
|
-
import {
|
|
5
|
-
getNetwork,
|
|
6
|
-
getNetworkFromAddress
|
|
7
|
-
} from "./chunk-Z5HIAYFT.js";
|
|
8
|
-
import {
|
|
9
|
-
ELECTRS_CREDENTIALS,
|
|
10
|
-
getElectrsUrl
|
|
11
|
-
} from "./chunk-R5PXJZQS.js";
|
|
12
|
-
import {
|
|
13
|
-
BitcoinNetwork_default
|
|
14
|
-
} from "./chunk-HMLOC6TE.js";
|
|
15
|
-
import {
|
|
16
|
-
ValidationError
|
|
17
|
-
} from "./chunk-GSI4OLXZ.js";
|
|
18
|
-
import {
|
|
19
|
-
TreeNode
|
|
20
|
-
} from "./chunk-BGGEVUJK.js";
|
|
21
|
-
import {
|
|
22
|
-
Buffer
|
|
23
|
-
} from "./chunk-MVRQ5US7.js";
|
|
24
|
-
|
|
25
|
-
// src/utils/bitcoin.ts
|
|
26
|
-
import {
|
|
27
|
-
bytesToHex,
|
|
28
|
-
bytesToNumberBE,
|
|
29
|
-
hexToBytes
|
|
30
|
-
} from "@noble/curves/abstract/utils";
|
|
31
|
-
import { schnorr, secp256k1 } from "@noble/curves/secp256k1";
|
|
32
|
-
import { sha256 } from "@noble/hashes/sha2";
|
|
33
|
-
import * as btc from "@scure/btc-signer";
|
|
34
|
-
function computeTaprootKeyNoScript(pubkey) {
|
|
35
|
-
if (pubkey.length !== 32) {
|
|
36
|
-
throw new ValidationError("Public key must be 32 bytes", {
|
|
37
|
-
field: "pubkey",
|
|
38
|
-
value: pubkey.length,
|
|
39
|
-
expected: 32
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
const taggedHash = schnorr.utils.taggedHash("TapTweak", pubkey);
|
|
43
|
-
const tweak = bytesToNumberBE(taggedHash);
|
|
44
|
-
const P = schnorr.utils.lift_x(schnorr.utils.bytesToNumberBE(pubkey));
|
|
45
|
-
const Q = P.add(secp256k1.ProjectivePoint.fromPrivateKey(tweak));
|
|
46
|
-
return Q.toRawBytes();
|
|
47
|
-
}
|
|
48
|
-
function getP2TRScriptFromPublicKey(pubKey, network) {
|
|
49
|
-
if (pubKey.length !== 33) {
|
|
50
|
-
throw new ValidationError("Public key must be 33 bytes", {
|
|
51
|
-
field: "pubKey",
|
|
52
|
-
value: pubKey.length,
|
|
53
|
-
expected: 33
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
const internalKey = secp256k1.ProjectivePoint.fromHex(pubKey);
|
|
57
|
-
const script = btc.p2tr(
|
|
58
|
-
internalKey.toRawBytes().slice(1, 33),
|
|
59
|
-
void 0,
|
|
60
|
-
getNetwork(network)
|
|
61
|
-
).script;
|
|
62
|
-
if (!script) {
|
|
63
|
-
throw new ValidationError("Failed to get P2TR script", {
|
|
64
|
-
field: "script",
|
|
65
|
-
value: "null"
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
return script;
|
|
69
|
-
}
|
|
70
|
-
function getP2TRAddressFromPublicKey(pubKey, network) {
|
|
71
|
-
if (pubKey.length !== 33) {
|
|
72
|
-
throw new ValidationError("Public key must be 33 bytes", {
|
|
73
|
-
field: "pubKey",
|
|
74
|
-
value: pubKey.length,
|
|
75
|
-
expected: 33
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
const internalKey = secp256k1.ProjectivePoint.fromHex(pubKey);
|
|
79
|
-
const address = btc.p2tr(
|
|
80
|
-
internalKey.toRawBytes().slice(1, 33),
|
|
81
|
-
void 0,
|
|
82
|
-
getNetwork(network)
|
|
83
|
-
).address;
|
|
84
|
-
if (!address) {
|
|
85
|
-
throw new ValidationError("Failed to get P2TR address", {
|
|
86
|
-
field: "address",
|
|
87
|
-
value: "null"
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
return address;
|
|
91
|
-
}
|
|
92
|
-
function getP2TRAddressFromPkScript(pkScript, network) {
|
|
93
|
-
if (pkScript.length !== 34 || pkScript[0] !== 81 || pkScript[1] !== 32) {
|
|
94
|
-
throw new ValidationError("Invalid pkscript", {
|
|
95
|
-
field: "pkScript",
|
|
96
|
-
value: bytesToHex(pkScript),
|
|
97
|
-
expected: "34 bytes starting with 0x51 0x20"
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
const parsedScript = btc.OutScript.decode(pkScript);
|
|
101
|
-
return btc.Address(getNetwork(network)).encode(parsedScript);
|
|
102
|
-
}
|
|
103
|
-
function getP2WPKHAddressFromPublicKey(pubKey, network) {
|
|
104
|
-
if (pubKey.length !== 33) {
|
|
105
|
-
throw new ValidationError("Public key must be 33 bytes", {
|
|
106
|
-
field: "pubKey",
|
|
107
|
-
value: pubKey.length,
|
|
108
|
-
expected: 33
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
const address = btc.p2wpkh(pubKey, getNetwork(network)).address;
|
|
112
|
-
if (!address) {
|
|
113
|
-
throw new ValidationError("Failed to get P2WPKH address", {
|
|
114
|
-
field: "address",
|
|
115
|
-
value: "null"
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
return address;
|
|
119
|
-
}
|
|
120
|
-
function getTxFromRawTxHex(rawTxHex) {
|
|
121
|
-
const txBytes = hexToBytes(rawTxHex);
|
|
122
|
-
const tx = btc.Transaction.fromRaw(txBytes, {
|
|
123
|
-
allowUnknownOutputs: true
|
|
124
|
-
});
|
|
125
|
-
if (!tx) {
|
|
126
|
-
throw new ValidationError("Failed to parse transaction", {
|
|
127
|
-
field: "tx",
|
|
128
|
-
value: "null"
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
return tx;
|
|
132
|
-
}
|
|
133
|
-
function getTxFromRawTxBytes(rawTxBytes) {
|
|
134
|
-
const tx = btc.Transaction.fromRaw(rawTxBytes, {
|
|
135
|
-
allowUnknownOutputs: true
|
|
136
|
-
});
|
|
137
|
-
if (!tx) {
|
|
138
|
-
throw new ValidationError("Failed to parse transaction", {
|
|
139
|
-
field: "tx",
|
|
140
|
-
value: "null"
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
return tx;
|
|
144
|
-
}
|
|
145
|
-
function getSigHashFromTx(tx, inputIndex, prevOutput) {
|
|
146
|
-
const prevScript = prevOutput.script;
|
|
147
|
-
if (!prevScript) {
|
|
148
|
-
throw new ValidationError("No script found in prevOutput", {
|
|
149
|
-
field: "prevScript",
|
|
150
|
-
value: "null"
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
const amount = prevOutput.amount;
|
|
154
|
-
if (!amount) {
|
|
155
|
-
throw new ValidationError("No amount found in prevOutput", {
|
|
156
|
-
field: "amount",
|
|
157
|
-
value: "null"
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
return tx.preimageWitnessV1(
|
|
161
|
-
inputIndex,
|
|
162
|
-
new Array(tx.inputsLength).fill(prevScript),
|
|
163
|
-
btc.SigHash.DEFAULT,
|
|
164
|
-
new Array(tx.inputsLength).fill(amount)
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
function getTxId(tx) {
|
|
168
|
-
return bytesToHex(sha256(sha256(tx.toBytes(true))).reverse());
|
|
169
|
-
}
|
|
170
|
-
function getTxIdNoReverse(tx) {
|
|
171
|
-
return bytesToHex(sha256(sha256(tx.toBytes(true))));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// src/utils/mempool.ts
|
|
175
|
-
async function getLatestDepositTxId(address) {
|
|
176
|
-
const network = getNetworkFromAddress(address);
|
|
177
|
-
const baseUrl = network === BitcoinNetwork_default.REGTEST ? getElectrsUrl("REGTEST") : getElectrsUrl("MAINNET");
|
|
178
|
-
const headers = {};
|
|
179
|
-
if (network === BitcoinNetwork_default.REGTEST) {
|
|
180
|
-
const auth = btoa(
|
|
181
|
-
`${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
|
|
182
|
-
);
|
|
183
|
-
headers["Authorization"] = `Basic ${auth}`;
|
|
184
|
-
}
|
|
185
|
-
const response = await fetch(`${baseUrl}/address/${address}/txs`, {
|
|
186
|
-
headers
|
|
187
|
-
});
|
|
188
|
-
const addressTxs = await response.json();
|
|
189
|
-
if (addressTxs && addressTxs.length > 0) {
|
|
190
|
-
const latestTx = addressTxs[0];
|
|
191
|
-
const outputIndex = latestTx.vout.findIndex(
|
|
192
|
-
(output) => output.scriptpubkey_address === address
|
|
193
|
-
);
|
|
194
|
-
if (outputIndex === -1) {
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
return latestTx.txid;
|
|
198
|
-
}
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
async function isTxBroadcast(txid, baseUrl, network) {
|
|
202
|
-
const headers = {};
|
|
203
|
-
if (network === 3 /* REGTEST */) {
|
|
204
|
-
const auth = btoa(
|
|
205
|
-
`${ELECTRS_CREDENTIALS.username}:${ELECTRS_CREDENTIALS.password}`
|
|
206
|
-
);
|
|
207
|
-
headers["Authorization"] = `Basic ${auth}`;
|
|
208
|
-
}
|
|
209
|
-
const response = await fetch(`${baseUrl}/tx/${txid}`, {
|
|
210
|
-
headers
|
|
211
|
-
});
|
|
212
|
-
const tx = await response.json();
|
|
213
|
-
if (tx.error) {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// src/utils/proof.ts
|
|
220
|
-
import { sha256 as sha2562 } from "@noble/hashes/sha2";
|
|
221
|
-
function proofOfPossessionMessageHashForDepositAddress(userPubkey, operatorPubkey, depositAddress) {
|
|
222
|
-
const encoder = new TextEncoder();
|
|
223
|
-
const depositAddressBytes = encoder.encode(depositAddress);
|
|
224
|
-
const proofMsg = new Uint8Array([
|
|
225
|
-
...userPubkey,
|
|
226
|
-
...operatorPubkey,
|
|
227
|
-
...depositAddressBytes
|
|
228
|
-
]);
|
|
229
|
-
return sha2562(proofMsg);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// src/utils/transfer_package.ts
|
|
233
|
-
import { hexToBytes as hexToBytes2 } from "@noble/curves/abstract/utils";
|
|
234
|
-
import { sha256 as sha2563 } from "@noble/hashes/sha2";
|
|
235
|
-
function getTransferPackageSigningPayload(transferID, transferPackage) {
|
|
236
|
-
const encryptedPayload = transferPackage.keyTweakPackage;
|
|
237
|
-
const pairs = Object.entries(
|
|
238
|
-
encryptedPayload
|
|
239
|
-
).map(([key, value]) => ({ key, value }));
|
|
240
|
-
pairs.sort((a, b) => a.key.localeCompare(b.key));
|
|
241
|
-
const encoder = new TextEncoder();
|
|
242
|
-
let message = hexToBytes2(transferID.replaceAll("-", ""));
|
|
243
|
-
for (const pair of pairs) {
|
|
244
|
-
const keyPart = encoder.encode(pair.key + ":");
|
|
245
|
-
const separator = encoder.encode(";");
|
|
246
|
-
message = new Uint8Array([
|
|
247
|
-
...message,
|
|
248
|
-
...keyPart,
|
|
249
|
-
...pair.value,
|
|
250
|
-
...separator
|
|
251
|
-
]);
|
|
252
|
-
}
|
|
253
|
-
return sha2563(message);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// src/utils/transaction.ts
|
|
257
|
-
import { Transaction as Transaction2 } from "@scure/btc-signer";
|
|
258
|
-
var TIME_LOCK_INTERVAL = 100;
|
|
259
|
-
var ESTIMATED_TX_SIZE = 191;
|
|
260
|
-
var DEFAULT_SATS_PER_VBYTE = 5;
|
|
261
|
-
var DEFAULT_FEE_SATS = ESTIMATED_TX_SIZE * DEFAULT_SATS_PER_VBYTE;
|
|
262
|
-
function maybeApplyFee(amount) {
|
|
263
|
-
if (amount > BigInt(DEFAULT_FEE_SATS)) {
|
|
264
|
-
return amount - BigInt(DEFAULT_FEE_SATS);
|
|
265
|
-
}
|
|
266
|
-
return amount;
|
|
267
|
-
}
|
|
268
|
-
function createRefundTx(sequence, nodeOutPoint, amountSats, receivingPubkey, network) {
|
|
269
|
-
const newRefundTx = new Transaction2({
|
|
270
|
-
version: 3,
|
|
271
|
-
allowUnknownOutputs: true
|
|
272
|
-
});
|
|
273
|
-
newRefundTx.addInput({
|
|
274
|
-
...nodeOutPoint,
|
|
275
|
-
sequence
|
|
276
|
-
});
|
|
277
|
-
const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
|
|
278
|
-
newRefundTx.addOutput({
|
|
279
|
-
script: refundPkScript,
|
|
280
|
-
amount: amountSats
|
|
281
|
-
});
|
|
282
|
-
newRefundTx.addOutput(getEphemeralAnchorOutput());
|
|
283
|
-
return newRefundTx;
|
|
284
|
-
}
|
|
285
|
-
function getCurrentTimelock(currSequence) {
|
|
286
|
-
return (currSequence || 0) & 65535;
|
|
287
|
-
}
|
|
288
|
-
function getTransactionSequence(currSequence) {
|
|
289
|
-
const timelock = getCurrentTimelock(currSequence);
|
|
290
|
-
return 1 << 30 | timelock;
|
|
291
|
-
}
|
|
292
|
-
function checkIfValidSequence(currSequence) {
|
|
293
|
-
const TIME_LOCK_ACTIVE = (currSequence || 0) & 2147483648;
|
|
294
|
-
if (TIME_LOCK_ACTIVE !== 0) {
|
|
295
|
-
throw new ValidationError("Timelock not active", {
|
|
296
|
-
field: "currSequence",
|
|
297
|
-
value: currSequence
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
const RELATIVE_TIME_LOCK_ACTIVE = (currSequence || 0) & 4194304;
|
|
301
|
-
if (RELATIVE_TIME_LOCK_ACTIVE !== 0) {
|
|
302
|
-
throw new ValidationError("Block based timelock not active", {
|
|
303
|
-
field: "currSequence",
|
|
304
|
-
value: currSequence
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
function getNextTransactionSequence(currSequence, forRefresh) {
|
|
309
|
-
const currentTimelock = getCurrentTimelock(currSequence);
|
|
310
|
-
const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
|
|
311
|
-
if (forRefresh && nextTimelock <= 100 && currentTimelock > 0) {
|
|
312
|
-
return {
|
|
313
|
-
nextSequence: 1 << 30 | nextTimelock,
|
|
314
|
-
needRefresh: true
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
if (nextTimelock < 0) {
|
|
318
|
-
throw new ValidationError("timelock interval is less than 0", {
|
|
319
|
-
field: "nextTimelock",
|
|
320
|
-
value: nextTimelock
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
return {
|
|
324
|
-
nextSequence: 1 << 30 | nextTimelock,
|
|
325
|
-
needRefresh: nextTimelock <= 100
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
function getEphemeralAnchorOutput() {
|
|
329
|
-
return {
|
|
330
|
-
script: new Uint8Array([81, 2, 78, 115]),
|
|
331
|
-
// Pay-to-anchor (P2A) ephemeral anchor output
|
|
332
|
-
amount: 0n
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// src/utils/unilateral-exit.ts
|
|
337
|
-
import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes3 } from "@noble/curves/abstract/utils";
|
|
338
|
-
import { ripemd160 } from "@noble/hashes/legacy";
|
|
339
|
-
import { sha256 as sha2564 } from "@noble/hashes/sha2";
|
|
340
|
-
import * as btc2 from "@scure/btc-signer";
|
|
341
|
-
function isEphemeralAnchorOutput(script, amount) {
|
|
342
|
-
return Boolean(
|
|
343
|
-
amount === 0n && script && // Pattern 1: Bare OP_TRUE (single byte 0x51)
|
|
344
|
-
(script.length === 1 && script[0] === 81 || // Pattern 2: Push OP_TRUE (two bytes 0x01 0x51) - MALFORMED but we detect it
|
|
345
|
-
script.length === 2 && script[0] === 1 && script[1] === 81 || // Pattern 3: Bitcoin v29 ephemeral anchor script (7 bytes: 015152014e0173)
|
|
346
|
-
script.length === 7 && script[0] === 1 && script[1] === 81 && script[2] === 82 && script[3] === 1 && script[4] === 78 && script[5] === 1 && script[6] === 115 || // Pattern 4: Bitcoin ephemeral anchor OP_1 + push 2 bytes (4 bytes: 51024e73)
|
|
347
|
-
script.length === 4 && script[0] === 81 && script[1] === 2 && script[2] === 78 && script[3] === 115)
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
async function constructUnilateralExitTxs(nodeHexStrings, sparkClient, network) {
|
|
351
|
-
const result = [];
|
|
352
|
-
const nodes = nodeHexStrings.map((hex) => TreeNode.decode(hexToBytes3(hex)));
|
|
353
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
354
|
-
for (const node of nodes) {
|
|
355
|
-
nodeMap.set(node.id, node);
|
|
356
|
-
}
|
|
357
|
-
for (const node of nodes) {
|
|
358
|
-
const transactions = [];
|
|
359
|
-
const chain = [];
|
|
360
|
-
let currentNode = node;
|
|
361
|
-
while (currentNode) {
|
|
362
|
-
chain.unshift(currentNode);
|
|
363
|
-
if (currentNode.parentNodeId) {
|
|
364
|
-
let parentNode = nodeMap.get(currentNode.parentNodeId);
|
|
365
|
-
if (!parentNode && sparkClient) {
|
|
366
|
-
try {
|
|
367
|
-
const response = await sparkClient.query_nodes({
|
|
368
|
-
source: {
|
|
369
|
-
$case: "nodeIds",
|
|
370
|
-
nodeIds: {
|
|
371
|
-
nodeIds: [currentNode.parentNodeId]
|
|
372
|
-
}
|
|
373
|
-
},
|
|
374
|
-
includeParents: true,
|
|
375
|
-
network: network || 0
|
|
376
|
-
// Default to mainnet if not provided
|
|
377
|
-
});
|
|
378
|
-
parentNode = response.nodes[currentNode.parentNodeId];
|
|
379
|
-
if (parentNode) {
|
|
380
|
-
nodeMap.set(currentNode.parentNodeId, parentNode);
|
|
381
|
-
}
|
|
382
|
-
} catch (error) {
|
|
383
|
-
console.warn(
|
|
384
|
-
`Failed to query parent node ${currentNode.parentNodeId}: ${error}`
|
|
385
|
-
);
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (parentNode) {
|
|
390
|
-
currentNode = parentNode;
|
|
391
|
-
} else {
|
|
392
|
-
if (!sparkClient) {
|
|
393
|
-
console.warn(
|
|
394
|
-
`Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
|
|
395
|
-
);
|
|
396
|
-
} else {
|
|
397
|
-
console.warn(
|
|
398
|
-
`Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
break;
|
|
402
|
-
}
|
|
403
|
-
} else {
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
for (const chainNode of chain) {
|
|
408
|
-
const nodeTx = bytesToHex2(chainNode.nodeTx);
|
|
409
|
-
transactions.push(nodeTx);
|
|
410
|
-
if (chainNode.id === node.id) {
|
|
411
|
-
const refundTx = bytesToHex2(chainNode.refundTx);
|
|
412
|
-
transactions.push(refundTx);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
result.push({
|
|
416
|
-
leafId: node.id,
|
|
417
|
-
transactions
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
return result;
|
|
421
|
-
}
|
|
422
|
-
async function constructUnilateralExitFeeBumpPackages(nodeHexStrings, utxos, feeRate, electrsUrl, sparkClient, network) {
|
|
423
|
-
const result = [];
|
|
424
|
-
const availableUtxos = [...utxos].sort((a, b) => {
|
|
425
|
-
if (a.value > b.value) return -1;
|
|
426
|
-
if (a.value < b.value) return 1;
|
|
427
|
-
return 0;
|
|
428
|
-
});
|
|
429
|
-
const nodes = [];
|
|
430
|
-
for (let i = 0; i < nodeHexStrings.length; i++) {
|
|
431
|
-
const hex = nodeHexStrings[i];
|
|
432
|
-
try {
|
|
433
|
-
if (!hex || hex.length === 0) {
|
|
434
|
-
throw new Error(`Node hex string at index ${i} is empty`);
|
|
435
|
-
}
|
|
436
|
-
if (hex.startsWith("03000000") || hex.startsWith("02000000") || hex.startsWith("01000000")) {
|
|
437
|
-
throw new Error(
|
|
438
|
-
`Node hex string at index ${i} appears to be a raw transaction hex, not a TreeNode protobuf. Use 'leafidtohex' command to convert node IDs to proper hex strings.`
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
const nodeBytes = hexToBytes3(hex);
|
|
442
|
-
const node = TreeNode.decode(nodeBytes);
|
|
443
|
-
if (!node.id) {
|
|
444
|
-
throw new Error(
|
|
445
|
-
`Decoded TreeNode at index ${i} is missing required 'id' field`
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
if (!node.nodeTx || node.nodeTx.length === 0) {
|
|
449
|
-
throw new Error(
|
|
450
|
-
`Decoded TreeNode at index ${i} is missing required 'nodeTx' field`
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
nodes.push(node);
|
|
454
|
-
} catch (decodeError) {
|
|
455
|
-
throw new Error(
|
|
456
|
-
`Failed to decode TreeNode hex string at index ${i}: ${decodeError}. Make sure you're providing TreeNode protobuf hex strings, not raw transaction hex. Use 'leafidtohex' command to get proper hex strings.`
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
461
|
-
for (const node of nodes) {
|
|
462
|
-
nodeMap.set(node.id, node);
|
|
463
|
-
}
|
|
464
|
-
const broadcastTxs = /* @__PURE__ */ new Map();
|
|
465
|
-
for (const node of nodes) {
|
|
466
|
-
const txPackages = [];
|
|
467
|
-
let previousFeeBumpTx;
|
|
468
|
-
const chain = [];
|
|
469
|
-
let currentNode = node;
|
|
470
|
-
while (currentNode) {
|
|
471
|
-
chain.unshift(currentNode);
|
|
472
|
-
if (currentNode.parentNodeId) {
|
|
473
|
-
let parentNode = nodeMap.get(currentNode.parentNodeId);
|
|
474
|
-
if (!parentNode && sparkClient) {
|
|
475
|
-
try {
|
|
476
|
-
const response = await sparkClient.query_nodes({
|
|
477
|
-
source: {
|
|
478
|
-
$case: "nodeIds",
|
|
479
|
-
nodeIds: {
|
|
480
|
-
nodeIds: [currentNode.parentNodeId]
|
|
481
|
-
}
|
|
482
|
-
},
|
|
483
|
-
includeParents: true,
|
|
484
|
-
network: network || 0
|
|
485
|
-
// Default to mainnet if not provided
|
|
486
|
-
});
|
|
487
|
-
parentNode = response.nodes[currentNode.parentNodeId];
|
|
488
|
-
if (parentNode) {
|
|
489
|
-
nodeMap.set(currentNode.parentNodeId, parentNode);
|
|
490
|
-
}
|
|
491
|
-
} catch (error) {
|
|
492
|
-
console.warn(
|
|
493
|
-
`Failed to query parent node ${currentNode.parentNodeId}: ${error}`
|
|
494
|
-
);
|
|
495
|
-
break;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
if (parentNode) {
|
|
499
|
-
currentNode = parentNode;
|
|
500
|
-
} else {
|
|
501
|
-
if (!sparkClient) {
|
|
502
|
-
console.warn(
|
|
503
|
-
`Parent node ${currentNode.parentNodeId} not found. Provide a sparkClient to fetch missing parents.`
|
|
504
|
-
);
|
|
505
|
-
} else {
|
|
506
|
-
console.warn(
|
|
507
|
-
`Parent node ${currentNode.parentNodeId} not found in database. Chain may be incomplete.`
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
break;
|
|
511
|
-
}
|
|
512
|
-
} else {
|
|
513
|
-
break;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
for (const chainNode of chain) {
|
|
517
|
-
let nodeTxHex = bytesToHex2(chainNode.nodeTx);
|
|
518
|
-
try {
|
|
519
|
-
const txObj = getTxFromRawTxHex(nodeTxHex);
|
|
520
|
-
const txid = getTxId(txObj);
|
|
521
|
-
if (broadcastTxs.get(txid)) {
|
|
522
|
-
continue;
|
|
523
|
-
}
|
|
524
|
-
broadcastTxs.set(txid, true);
|
|
525
|
-
const isBroadcast = await isTxBroadcast(txid, electrsUrl, network);
|
|
526
|
-
if (isBroadcast) {
|
|
527
|
-
continue;
|
|
528
|
-
} else {
|
|
529
|
-
}
|
|
530
|
-
let anchorOutputScriptHex;
|
|
531
|
-
for (let i = txObj.outputsLength - 1; i >= 0; i--) {
|
|
532
|
-
const output = txObj.getOutput(i);
|
|
533
|
-
if (output?.amount === 0n && output.script) {
|
|
534
|
-
anchorOutputScriptHex = bytesToHex2(output.script);
|
|
535
|
-
break;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
} catch (parseError) {
|
|
539
|
-
console.error(
|
|
540
|
-
`\u274C Error parsing nodeTx for anchor check (node ${chainNode.id}): ${parseError}`
|
|
541
|
-
);
|
|
542
|
-
console.log(
|
|
543
|
-
` This may indicate a corrupted transaction in the TreeNode.`
|
|
544
|
-
);
|
|
545
|
-
console.log(` Transaction hex: ${nodeTxHex}`);
|
|
546
|
-
console.log(
|
|
547
|
-
` Attempting to continue with original hex, but fee bump may fail.`
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
const {
|
|
551
|
-
feeBumpPsbt: nodeFeeBumpPsbt,
|
|
552
|
-
usedUtxos,
|
|
553
|
-
correctedParentTx
|
|
554
|
-
} = constructFeeBumpTx(nodeTxHex, availableUtxos, feeRate, void 0);
|
|
555
|
-
const feeBumpTx = btc2.Transaction.fromPSBT(hexToBytes3(nodeFeeBumpPsbt));
|
|
556
|
-
var feeBumpOut = feeBumpTx.outputsLength === 1 ? feeBumpTx.getOutput(0) : null;
|
|
557
|
-
var feeBumpOutPubKey = null;
|
|
558
|
-
for (const usedUtxo of usedUtxos) {
|
|
559
|
-
if (feeBumpOut && bytesToHex2(feeBumpOut.script) == usedUtxo.script) {
|
|
560
|
-
feeBumpOutPubKey = usedUtxo.publicKey;
|
|
561
|
-
}
|
|
562
|
-
const index = availableUtxos.findIndex(
|
|
563
|
-
(u) => u.txid === usedUtxo.txid && u.vout === usedUtxo.vout
|
|
564
|
-
);
|
|
565
|
-
if (index !== -1) {
|
|
566
|
-
availableUtxos.splice(index, 1);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
if (feeBumpOut)
|
|
570
|
-
availableUtxos.unshift({
|
|
571
|
-
txid: getTxId(feeBumpTx),
|
|
572
|
-
vout: 0,
|
|
573
|
-
value: feeBumpOut.amount,
|
|
574
|
-
script: bytesToHex2(feeBumpOut.script),
|
|
575
|
-
publicKey: feeBumpOutPubKey
|
|
576
|
-
});
|
|
577
|
-
const finalNodeTx = correctedParentTx || nodeTxHex;
|
|
578
|
-
txPackages.push({ tx: finalNodeTx, feeBumpPsbt: nodeFeeBumpPsbt });
|
|
579
|
-
if (chainNode.id === node.id) {
|
|
580
|
-
let refundTxHex = bytesToHex2(chainNode.refundTx);
|
|
581
|
-
try {
|
|
582
|
-
const txObj = getTxFromRawTxHex(refundTxHex);
|
|
583
|
-
let anchorOutputScriptHex;
|
|
584
|
-
for (let i = txObj.outputsLength - 1; i >= 0; i--) {
|
|
585
|
-
const output = txObj.getOutput(i);
|
|
586
|
-
if (output?.amount === 0n && output.script) {
|
|
587
|
-
anchorOutputScriptHex = bytesToHex2(output.script);
|
|
588
|
-
break;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
} catch (parseError) {
|
|
592
|
-
console.error(
|
|
593
|
-
`\u274C Error parsing refundTx for anchor check (node ${chainNode.id}): ${parseError}`
|
|
594
|
-
);
|
|
595
|
-
console.log(
|
|
596
|
-
` This may indicate a corrupted refund transaction in the TreeNode.`
|
|
597
|
-
);
|
|
598
|
-
console.log(` Refund transaction hex: ${refundTxHex}`);
|
|
599
|
-
console.log(
|
|
600
|
-
` Attempting to continue with original refund hex, but this transaction may be invalid.`
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
const refundFeeBump = constructFeeBumpTx(
|
|
604
|
-
refundTxHex,
|
|
605
|
-
availableUtxos,
|
|
606
|
-
feeRate,
|
|
607
|
-
void 0
|
|
608
|
-
);
|
|
609
|
-
txPackages.push({
|
|
610
|
-
tx: refundTxHex,
|
|
611
|
-
feeBumpPsbt: refundFeeBump.feeBumpPsbt
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
result.push({
|
|
616
|
-
leafId: node.id,
|
|
617
|
-
txPackages
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
return result;
|
|
621
|
-
}
|
|
622
|
-
function hash160(data) {
|
|
623
|
-
const sha256Hash = sha2564(data);
|
|
624
|
-
return ripemd160(sha256Hash);
|
|
625
|
-
}
|
|
626
|
-
function constructFeeBumpTx(txHex, utxos, feeRate, previousFeeBumpTx) {
|
|
627
|
-
if (!txHex || txHex.length === 0) {
|
|
628
|
-
throw new Error("Transaction hex string is empty or undefined");
|
|
629
|
-
}
|
|
630
|
-
if (utxos.length === 0) {
|
|
631
|
-
throw new Error("No UTXOs available for fee bump");
|
|
632
|
-
}
|
|
633
|
-
let correctedTxHex = txHex;
|
|
634
|
-
let parentTx;
|
|
635
|
-
try {
|
|
636
|
-
parentTx = getTxFromRawTxHex(correctedTxHex);
|
|
637
|
-
if (!parentTx) {
|
|
638
|
-
throw new Error("getTxFromRawTxHex returned null/undefined");
|
|
639
|
-
}
|
|
640
|
-
} catch (parseError) {
|
|
641
|
-
throw new Error(
|
|
642
|
-
`Failed to parse parent transaction hex: ${parseError}. Transaction hex: ${correctedTxHex}`
|
|
643
|
-
);
|
|
644
|
-
}
|
|
645
|
-
try {
|
|
646
|
-
const outputsLength = parentTx.outputsLength;
|
|
647
|
-
const inputsLength = parentTx.inputsLength;
|
|
648
|
-
if (typeof outputsLength !== "number" || outputsLength < 0) {
|
|
649
|
-
throw new Error(
|
|
650
|
-
"Invalid transaction: outputsLength is not a valid number"
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
if (typeof inputsLength !== "number" || inputsLength < 0) {
|
|
654
|
-
throw new Error(
|
|
655
|
-
"Invalid transaction: inputsLength is not a valid number"
|
|
656
|
-
);
|
|
657
|
-
}
|
|
658
|
-
} catch (validationError) {
|
|
659
|
-
throw new Error(
|
|
660
|
-
`Transaction validation failed: ${validationError}. This may indicate a corrupted or malformed transaction.`
|
|
661
|
-
);
|
|
662
|
-
}
|
|
663
|
-
const parentTxIdFromLib = parentTx.id;
|
|
664
|
-
let ephemeralAnchorIndex = -1;
|
|
665
|
-
for (let i = 0; i < parentTx.outputsLength; i++) {
|
|
666
|
-
const output = parentTx.getOutput(i);
|
|
667
|
-
const isEphemeralAnchor = isEphemeralAnchorOutput(
|
|
668
|
-
output?.script,
|
|
669
|
-
output?.amount
|
|
670
|
-
);
|
|
671
|
-
if (isEphemeralAnchor) {
|
|
672
|
-
ephemeralAnchorIndex = i;
|
|
673
|
-
break;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
if (ephemeralAnchorIndex === -1) {
|
|
677
|
-
throw new Error(
|
|
678
|
-
"No ephemeral anchor output found in parent transaction. Expected a 0-value output with OP_TRUE script (0x51), malformed OP_TRUE (0x0151), Bitcoin v29 ephemeral anchor script (015152014e0173), or Bitcoin OP_1 + push 2 bytes script (51024e73)."
|
|
679
|
-
);
|
|
680
|
-
}
|
|
681
|
-
const ephemeralAnchorOutput = parentTx.getOutput(ephemeralAnchorIndex);
|
|
682
|
-
if (!ephemeralAnchorOutput)
|
|
683
|
-
throw new Error("No ephemeral anchor output found");
|
|
684
|
-
if (!ephemeralAnchorOutput.script)
|
|
685
|
-
throw new Error("No script found in ephemeral anchor output");
|
|
686
|
-
if (utxos.length === 0) {
|
|
687
|
-
throw new Error("No UTXOs available for fee bump");
|
|
688
|
-
}
|
|
689
|
-
const builder = new btc2.Transaction({
|
|
690
|
-
version: 3,
|
|
691
|
-
allowUnknown: true,
|
|
692
|
-
allowLegacyWitnessUtxo: true
|
|
693
|
-
});
|
|
694
|
-
let totalValue = 0n;
|
|
695
|
-
const processedUtxos = [];
|
|
696
|
-
for (let i = 0; i < utxos.length; i++) {
|
|
697
|
-
const fundingUtxo = utxos[i];
|
|
698
|
-
if (!fundingUtxo) {
|
|
699
|
-
throw new Error(`UTXO at index ${i} is undefined`);
|
|
700
|
-
}
|
|
701
|
-
const pubKeyHash = hash160(hexToBytes3(fundingUtxo.publicKey));
|
|
702
|
-
const scriptToUse = new Uint8Array([0, 20, ...pubKeyHash]);
|
|
703
|
-
const providedScript = hexToBytes3(fundingUtxo.script);
|
|
704
|
-
if (bytesToHex2(scriptToUse) !== bytesToHex2(providedScript)) {
|
|
705
|
-
throw new Error(
|
|
706
|
-
`\u274C Derived script doesn't match provided script for UTXO ${i + 1}.`
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
builder.addInput({
|
|
710
|
-
txid: fundingUtxo.txid,
|
|
711
|
-
index: fundingUtxo.vout,
|
|
712
|
-
sequence: 4294967295,
|
|
713
|
-
witnessUtxo: {
|
|
714
|
-
script: scriptToUse,
|
|
715
|
-
// Always P2WPKH
|
|
716
|
-
amount: fundingUtxo.value
|
|
717
|
-
}
|
|
718
|
-
});
|
|
719
|
-
totalValue += fundingUtxo.value;
|
|
720
|
-
processedUtxos.push({
|
|
721
|
-
utxo: fundingUtxo,
|
|
722
|
-
p2wpkhScript: scriptToUse
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
builder.addInput({
|
|
726
|
-
txid: parentTxIdFromLib,
|
|
727
|
-
index: ephemeralAnchorIndex,
|
|
728
|
-
sequence: 4294967295,
|
|
729
|
-
witnessUtxo: {
|
|
730
|
-
script: ephemeralAnchorOutput.script,
|
|
731
|
-
// Use the original script directly (not P2WSH wrapped)
|
|
732
|
-
amount: 0n
|
|
733
|
-
}
|
|
734
|
-
});
|
|
735
|
-
const fee = 1500n;
|
|
736
|
-
const remainingValue = totalValue - fee;
|
|
737
|
-
if (remainingValue <= 0n) {
|
|
738
|
-
throw new Error(
|
|
739
|
-
`Insufficient funds for fee bump. Required fee: ${fee} sats, Available: ${totalValue} sats`
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
if (processedUtxos.length === 0) {
|
|
743
|
-
throw new Error("No processed UTXOs available for change output");
|
|
744
|
-
}
|
|
745
|
-
const firstProcessedUtxo = processedUtxos[0];
|
|
746
|
-
if (!firstProcessedUtxo) {
|
|
747
|
-
throw new Error("First processed UTXO is undefined");
|
|
748
|
-
}
|
|
749
|
-
builder.addOutput({
|
|
750
|
-
script: firstProcessedUtxo.p2wpkhScript,
|
|
751
|
-
amount: remainingValue
|
|
752
|
-
});
|
|
753
|
-
for (let i = 0; i < processedUtxos.length; i++) {
|
|
754
|
-
const processed = processedUtxos[i];
|
|
755
|
-
if (!processed) {
|
|
756
|
-
throw new Error(`Processed UTXO at index ${i} is undefined`);
|
|
757
|
-
}
|
|
758
|
-
try {
|
|
759
|
-
builder.updateInput(i, {
|
|
760
|
-
witnessScript: processed.p2wpkhScript
|
|
761
|
-
});
|
|
762
|
-
builder.signIdx;
|
|
763
|
-
} catch (error) {
|
|
764
|
-
throw new Error(`Failed to handle funding UTXO input ${i + 1}: ${error}`);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
let psbtHex;
|
|
768
|
-
try {
|
|
769
|
-
psbtHex = bytesToHex2(builder.toPSBT());
|
|
770
|
-
} catch (error) {
|
|
771
|
-
throw new Error(`Failed to extract transaction: ${error}`);
|
|
772
|
-
}
|
|
773
|
-
return {
|
|
774
|
-
feeBumpPsbt: psbtHex,
|
|
775
|
-
usedUtxos: utxos,
|
|
776
|
-
correctedParentTx: correctedTxHex !== txHex ? correctedTxHex : void 0
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// src/utils/xchain-address.ts
|
|
781
|
-
import * as btc3 from "@scure/btc-signer";
|
|
782
|
-
var networkByType = {
|
|
783
|
-
MAINNET: btc3.NETWORK,
|
|
784
|
-
TESTNET: btc3.TEST_NETWORK,
|
|
785
|
-
REGTEST: {
|
|
786
|
-
...btc3.TEST_NETWORK,
|
|
787
|
-
bech32: "bcrt"
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
function getSparkAddressFromTaproot(taprootAddress) {
|
|
791
|
-
for (const networkType of ["MAINNET", "TESTNET", "REGTEST"]) {
|
|
792
|
-
try {
|
|
793
|
-
const result = btc3.Address(networkByType[networkType]).decode(taprootAddress);
|
|
794
|
-
if (result.type === "tr") {
|
|
795
|
-
const outputPublicKey = result.pubkey;
|
|
796
|
-
return encodeSparkAddress({
|
|
797
|
-
identityPublicKey: Buffer.concat([
|
|
798
|
-
Buffer.from([2]),
|
|
799
|
-
outputPublicKey
|
|
800
|
-
]).toString("hex"),
|
|
801
|
-
network: networkType
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
} catch (_) {
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
throw new ValidationError("Invalid taproot address");
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
export {
|
|
811
|
-
computeTaprootKeyNoScript,
|
|
812
|
-
getP2TRScriptFromPublicKey,
|
|
813
|
-
getP2TRAddressFromPublicKey,
|
|
814
|
-
getP2TRAddressFromPkScript,
|
|
815
|
-
getP2WPKHAddressFromPublicKey,
|
|
816
|
-
getTxFromRawTxHex,
|
|
817
|
-
getTxFromRawTxBytes,
|
|
818
|
-
getSigHashFromTx,
|
|
819
|
-
getTxId,
|
|
820
|
-
getTxIdNoReverse,
|
|
821
|
-
DEFAULT_FEE_SATS,
|
|
822
|
-
maybeApplyFee,
|
|
823
|
-
createRefundTx,
|
|
824
|
-
getCurrentTimelock,
|
|
825
|
-
getTransactionSequence,
|
|
826
|
-
checkIfValidSequence,
|
|
827
|
-
getNextTransactionSequence,
|
|
828
|
-
getEphemeralAnchorOutput,
|
|
829
|
-
getTransferPackageSigningPayload,
|
|
830
|
-
proofOfPossessionMessageHashForDepositAddress,
|
|
831
|
-
getLatestDepositTxId,
|
|
832
|
-
isTxBroadcast,
|
|
833
|
-
isEphemeralAnchorOutput,
|
|
834
|
-
constructUnilateralExitTxs,
|
|
835
|
-
constructUnilateralExitFeeBumpPackages,
|
|
836
|
-
constructFeeBumpTx,
|
|
837
|
-
getSparkAddressFromTaproot
|
|
838
|
-
};
|