@buildonspark/spark-sdk 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/{chunk-TM6CHQXC.js → chunk-3SEOTO43.js} +1 -1
- package/dist/{chunk-2ENZX6LT.js → chunk-AAZWSPUK.js} +84 -8
- package/dist/{chunk-4JD4HIAN.js → chunk-G4MSZ6DE.js} +299 -1
- package/dist/{chunk-S2AL73MZ.js → chunk-TVUMSHWA.js} +1 -1
- package/dist/{chunk-2TUM3R6C.js → chunk-W4ZRBSWM.js} +2351 -797
- package/dist/{chunk-CDLETEDT.js → chunk-WAQKYSDI.js} +13 -1
- package/dist/{client-CGTRS23n.d.ts → client-BF4cn8F4.d.ts} +15 -3
- package/dist/{client-CcYzmpmj.d.cts → client-KhNkrXz4.d.cts} +15 -3
- package/dist/debug.cjs +2948 -1023
- package/dist/debug.d.cts +19 -6
- package/dist/debug.d.ts +19 -6
- package/dist/debug.js +5 -5
- package/dist/graphql/objects/index.cjs +13 -1
- package/dist/graphql/objects/index.d.cts +2 -2
- package/dist/graphql/objects/index.d.ts +2 -2
- package/dist/graphql/objects/index.js +1 -1
- package/dist/index.cjs +2794 -858
- package/dist/index.d.cts +190 -9
- package/dist/index.d.ts +190 -9
- package/dist/index.js +32 -6
- package/dist/index.node.cjs +2931 -892
- package/dist/index.node.d.cts +10 -188
- package/dist/index.node.d.ts +10 -188
- package/dist/index.node.js +134 -6
- package/dist/native/index.cjs +2794 -858
- package/dist/native/index.d.cts +148 -40
- package/dist/native/index.d.ts +148 -40
- package/dist/native/index.js +2799 -877
- package/dist/proto/lrc20.d.cts +1 -1
- package/dist/proto/lrc20.d.ts +1 -1
- package/dist/proto/lrc20.js +1 -1
- package/dist/proto/spark.cjs +84 -8
- package/dist/proto/spark.d.cts +1 -1
- package/dist/proto/spark.d.ts +1 -1
- package/dist/proto/spark.js +1 -1
- package/dist/proto/spark_token.cjs +301 -0
- package/dist/proto/spark_token.d.cts +35 -2
- package/dist/proto/spark_token.d.ts +35 -2
- package/dist/proto/spark_token.js +8 -2
- package/dist/{sdk-types-DJ2ve9YY.d.cts → sdk-types-CB9HrW5O.d.cts} +1 -1
- package/dist/{sdk-types-DCIVdKUT.d.ts → sdk-types-CkRNraXT.d.ts} +1 -1
- package/dist/{spark-BUOx3U7Q.d.cts → spark-B_7nZx6T.d.cts} +112 -10
- package/dist/{spark-BUOx3U7Q.d.ts → spark-B_7nZx6T.d.ts} +112 -10
- package/dist/{spark-wallet-B_96y9BS.d.ts → spark-wallet-C1Tr_VKI.d.ts} +38 -28
- package/dist/{spark-wallet-CHwKQYJu.d.cts → spark-wallet-DG3x2obf.d.cts} +38 -28
- package/dist/spark-wallet.node-CGxoeCpH.d.ts +13 -0
- package/dist/spark-wallet.node-CN9LoB_O.d.cts +13 -0
- package/dist/tests/test-utils.cjs +1086 -218
- package/dist/tests/test-utils.d.cts +13 -13
- package/dist/tests/test-utils.d.ts +13 -13
- package/dist/tests/test-utils.js +56 -19
- package/dist/types/index.cjs +97 -9
- package/dist/types/index.d.cts +3 -3
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js +3 -3
- package/dist/{xchain-address-D5MIHCDL.d.cts → xchain-address-BHu6CpZC.d.ts} +55 -8
- package/dist/{xchain-address-DLbW1iDh.d.ts → xchain-address-HBr6isnc.d.cts} +55 -8
- package/package.json +1 -1
- package/src/graphql/client.ts +8 -0
- package/src/graphql/mutations/CompleteLeavesSwap.ts +9 -1
- package/src/graphql/mutations/RequestSwapLeaves.ts +4 -0
- package/src/graphql/objects/CompleteLeavesSwapInput.ts +34 -34
- package/src/graphql/objects/LeavesSwapRequest.ts +4 -0
- package/src/graphql/objects/RequestLeavesSwapInput.ts +48 -47
- package/src/graphql/objects/SwapLeaf.ts +40 -32
- package/src/graphql/objects/UserLeafInput.ts +24 -0
- package/src/graphql/objects/UserRequest.ts +4 -0
- package/src/index.node.ts +1 -1
- package/src/native/index.ts +4 -5
- package/src/proto/spark.ts +172 -16
- package/src/proto/spark_token.ts +369 -0
- package/src/services/coop-exit.ts +171 -36
- package/src/services/deposit.ts +471 -74
- package/src/services/lightning.ts +18 -5
- package/src/services/signing.ts +162 -50
- package/src/services/token-transactions.ts +6 -2
- package/src/services/transfer.ts +950 -384
- package/src/services/tree-creation.ts +342 -121
- package/src/spark-wallet/spark-wallet.node.ts +71 -66
- package/src/spark-wallet/spark-wallet.ts +459 -166
- package/src/tests/integration/coop-exit.test.ts +3 -8
- package/src/tests/integration/deposit.test.ts +3 -3
- package/src/tests/integration/lightning.test.ts +521 -466
- package/src/tests/integration/swap.test.ts +559 -307
- package/src/tests/integration/transfer.test.ts +625 -623
- package/src/tests/integration/wallet.test.ts +2 -2
- package/src/tests/integration/watchtower.test.ts +211 -0
- package/src/tests/test-utils.ts +63 -14
- package/src/tests/utils/test-faucet.ts +4 -2
- package/src/utils/adaptor-signature.ts +15 -5
- package/src/utils/fetch.ts +75 -0
- package/src/utils/mempool.ts +9 -4
- package/src/utils/transaction.ts +388 -26
package/src/utils/transaction.ts
CHANGED
|
@@ -4,7 +4,18 @@ import { ValidationError } from "../errors/types.js";
|
|
|
4
4
|
import { getP2TRScriptFromPublicKey } from "./bitcoin.js";
|
|
5
5
|
import { Network } from "./network.js";
|
|
6
6
|
|
|
7
|
+
const INITIAL_TIMELOCK = 2000;
|
|
8
|
+
const TEST_UNILATERAL_TIMELOCK = 100;
|
|
9
|
+
|
|
7
10
|
const TIME_LOCK_INTERVAL = 100;
|
|
11
|
+
export const DIRECT_TIMELOCK_OFFSET = 50;
|
|
12
|
+
|
|
13
|
+
export const INITIAL_SEQUENCE = (1 << 30) | INITIAL_TIMELOCK;
|
|
14
|
+
export const INITIAL_DIRECT_SEQUENCE =
|
|
15
|
+
(1 << 30) | (INITIAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET);
|
|
16
|
+
export const TEST_UNILATERAL_SEQUENCE = (1 << 30) | TEST_UNILATERAL_TIMELOCK;
|
|
17
|
+
export const TEST_UNILATERAL_DIRECT_SEQUENCE =
|
|
18
|
+
(1 << 30) | (TEST_UNILATERAL_TIMELOCK + DIRECT_TIMELOCK_OFFSET);
|
|
8
19
|
|
|
9
20
|
// Default fee constants matching Go implementation
|
|
10
21
|
const ESTIMATED_TX_SIZE = 191;
|
|
@@ -22,41 +33,383 @@ export function maybeApplyFee(amount: bigint): bigint {
|
|
|
22
33
|
return amount;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
|
-
export function
|
|
36
|
+
export function createRootTx(
|
|
37
|
+
depositOutPoint: TransactionInput,
|
|
38
|
+
depositTxOut: TransactionOutput,
|
|
39
|
+
): [Transaction, Transaction] {
|
|
40
|
+
// Create CPFP-friendly root tx (with ephemeral anchor, no fee)
|
|
41
|
+
const cpfpRootTx = new Transaction({
|
|
42
|
+
version: 3,
|
|
43
|
+
allowUnknownOutputs: true,
|
|
44
|
+
});
|
|
45
|
+
cpfpRootTx.addInput(depositOutPoint);
|
|
46
|
+
cpfpRootTx.addOutput(depositTxOut);
|
|
47
|
+
cpfpRootTx.addOutput(getEphemeralAnchorOutput());
|
|
48
|
+
|
|
49
|
+
// Create direct root tx (with fee, no anchor)
|
|
50
|
+
const directRootTx = new Transaction({
|
|
51
|
+
version: 3,
|
|
52
|
+
allowUnknownOutputs: true,
|
|
53
|
+
});
|
|
54
|
+
directRootTx.addInput(depositOutPoint);
|
|
55
|
+
directRootTx.addOutput({
|
|
56
|
+
script: depositTxOut.script,
|
|
57
|
+
amount: maybeApplyFee(depositTxOut.amount ?? 0n),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return [cpfpRootTx, directRootTx];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createSplitTx(
|
|
64
|
+
parentOutPoint: TransactionInput,
|
|
65
|
+
childTxOuts: TransactionOutput[],
|
|
66
|
+
): [Transaction, Transaction] {
|
|
67
|
+
// Create CPFP-friendly split tx (with ephemeral anchor, no fee)
|
|
68
|
+
const cpfpSplitTx = new Transaction({
|
|
69
|
+
version: 3,
|
|
70
|
+
allowUnknownOutputs: true,
|
|
71
|
+
});
|
|
72
|
+
cpfpSplitTx.addInput(parentOutPoint);
|
|
73
|
+
for (const txOut of childTxOuts) {
|
|
74
|
+
cpfpSplitTx.addOutput(txOut);
|
|
75
|
+
}
|
|
76
|
+
cpfpSplitTx.addOutput(getEphemeralAnchorOutput());
|
|
77
|
+
|
|
78
|
+
// Create direct split tx (with fee, no anchor)
|
|
79
|
+
const directSplitTx = new Transaction({
|
|
80
|
+
version: 3,
|
|
81
|
+
allowUnknownOutputs: true,
|
|
82
|
+
});
|
|
83
|
+
directSplitTx.addInput(parentOutPoint);
|
|
84
|
+
|
|
85
|
+
// Adjust output amounts to account for fee
|
|
86
|
+
let totalOutputAmount = 0n;
|
|
87
|
+
for (const txOut of childTxOuts) {
|
|
88
|
+
totalOutputAmount += txOut.amount ?? 0n;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (totalOutputAmount > BigInt(DEFAULT_FEE_SATS)) {
|
|
92
|
+
// Distribute fee proportionally across outputs
|
|
93
|
+
const feeRatio = Number(DEFAULT_FEE_SATS) / Number(totalOutputAmount);
|
|
94
|
+
for (const txOut of childTxOuts) {
|
|
95
|
+
const adjustedAmount = BigInt(
|
|
96
|
+
Math.floor(Number(txOut.amount ?? 0n) * (1 - feeRatio)),
|
|
97
|
+
);
|
|
98
|
+
directSplitTx.addOutput({
|
|
99
|
+
script: txOut.script,
|
|
100
|
+
amount: adjustedAmount,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
// If fee is larger than total output, just pass through original amounts
|
|
105
|
+
for (const txOut of childTxOuts) {
|
|
106
|
+
directSplitTx.addOutput(txOut);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return [cpfpSplitTx, directSplitTx];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface CreateNodeTxInput {
|
|
114
|
+
txOut: TransactionOutput;
|
|
115
|
+
parentOutPoint: TransactionInput;
|
|
116
|
+
applyFee?: boolean;
|
|
117
|
+
includeAnchor?: boolean;
|
|
118
|
+
}
|
|
119
|
+
// createNodeTx creates a node transaction.
|
|
120
|
+
// This stands in between a split tx and a leaf node tx,
|
|
121
|
+
// and has no timelock.
|
|
122
|
+
export function createNodeTx({
|
|
123
|
+
txOut,
|
|
124
|
+
parentOutPoint,
|
|
125
|
+
applyFee,
|
|
126
|
+
includeAnchor,
|
|
127
|
+
}: CreateNodeTxInput): Transaction {
|
|
128
|
+
const nodeTx = new Transaction({
|
|
129
|
+
version: 3,
|
|
130
|
+
allowUnknownOutputs: true,
|
|
131
|
+
});
|
|
132
|
+
nodeTx.addInput(parentOutPoint);
|
|
133
|
+
|
|
134
|
+
if (applyFee) {
|
|
135
|
+
nodeTx.addOutput({
|
|
136
|
+
script: txOut.script,
|
|
137
|
+
amount: maybeApplyFee(txOut.amount ?? 0n),
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
nodeTx.addOutput(txOut);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (includeAnchor) {
|
|
144
|
+
nodeTx.addOutput(getEphemeralAnchorOutput());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return nodeTx;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function createNodeTxs(
|
|
151
|
+
txOut: TransactionOutput,
|
|
152
|
+
txIn: TransactionInput,
|
|
153
|
+
directTxIn?: TransactionInput,
|
|
154
|
+
): {
|
|
155
|
+
cpfpNodeTx: Transaction;
|
|
156
|
+
directNodeTx?: Transaction;
|
|
157
|
+
} {
|
|
158
|
+
const cpfpNodeTx = createNodeTx({
|
|
159
|
+
txOut,
|
|
160
|
+
parentOutPoint: txIn,
|
|
161
|
+
includeAnchor: true,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
let directNodeTx: Transaction | undefined;
|
|
165
|
+
if (directTxIn) {
|
|
166
|
+
directNodeTx = createNodeTx({
|
|
167
|
+
txOut,
|
|
168
|
+
parentOutPoint: directTxIn,
|
|
169
|
+
includeAnchor: false,
|
|
170
|
+
applyFee: true,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { cpfpNodeTx, directNodeTx };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// createLeafNodeTx creates a leaf node transaction.
|
|
178
|
+
// This transaction provides an intermediate transaction
|
|
179
|
+
// to allow the timelock of the final refund transaction
|
|
180
|
+
// to be extended. E.g. when the refund tx timelock reaches
|
|
181
|
+
// 0, the leaf node tx can be re-signed with a decremented
|
|
182
|
+
// timelock, and the refund tx can be reset it's timelock.
|
|
183
|
+
export function createLeafNodeTx(
|
|
26
184
|
sequence: number,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
): Transaction {
|
|
32
|
-
|
|
185
|
+
directSequence: number,
|
|
186
|
+
parentOutPoint: TransactionInput,
|
|
187
|
+
txOut: TransactionOutput,
|
|
188
|
+
shouldCalculateFee: boolean,
|
|
189
|
+
): [Transaction, Transaction] {
|
|
190
|
+
// Create CPFP-friendly leaf node tx (with ephemeral anchor, no fee)
|
|
191
|
+
const cpfpLeafTx = new Transaction({
|
|
33
192
|
version: 3,
|
|
34
193
|
allowUnknownOutputs: true,
|
|
35
194
|
});
|
|
36
|
-
|
|
37
|
-
...
|
|
195
|
+
cpfpLeafTx.addInput({
|
|
196
|
+
...parentOutPoint,
|
|
197
|
+
sequence,
|
|
198
|
+
});
|
|
199
|
+
cpfpLeafTx.addOutput(txOut);
|
|
200
|
+
cpfpLeafTx.addOutput(getEphemeralAnchorOutput());
|
|
201
|
+
|
|
202
|
+
// Create direct leaf node tx (with fee, no anchor)
|
|
203
|
+
const directLeafTx = new Transaction({
|
|
204
|
+
version: 3,
|
|
205
|
+
allowUnknownOutputs: true,
|
|
206
|
+
});
|
|
207
|
+
directLeafTx.addInput({
|
|
208
|
+
...parentOutPoint,
|
|
209
|
+
sequence: directSequence,
|
|
210
|
+
});
|
|
211
|
+
const amountSats = txOut.amount ?? 0n;
|
|
212
|
+
let outputAmount = amountSats;
|
|
213
|
+
if (shouldCalculateFee) {
|
|
214
|
+
outputAmount = maybeApplyFee(amountSats);
|
|
215
|
+
}
|
|
216
|
+
directLeafTx.addOutput({
|
|
217
|
+
script: txOut.script,
|
|
218
|
+
amount: outputAmount,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return [cpfpLeafTx, directLeafTx];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
interface CreateRefundTxInput {
|
|
225
|
+
sequence: number;
|
|
226
|
+
input: TransactionInput;
|
|
227
|
+
amountSats: bigint;
|
|
228
|
+
receivingPubkey: Uint8Array;
|
|
229
|
+
network: Network;
|
|
230
|
+
shouldCalculateFee: boolean;
|
|
231
|
+
includeAnchor: boolean;
|
|
232
|
+
}
|
|
233
|
+
export function createRefundTx({
|
|
234
|
+
sequence,
|
|
235
|
+
input,
|
|
236
|
+
amountSats,
|
|
237
|
+
receivingPubkey,
|
|
238
|
+
network,
|
|
239
|
+
shouldCalculateFee,
|
|
240
|
+
includeAnchor,
|
|
241
|
+
}: CreateRefundTxInput): Transaction {
|
|
242
|
+
const refundTx = new Transaction({
|
|
243
|
+
version: 3,
|
|
244
|
+
allowUnknownOutputs: true,
|
|
245
|
+
});
|
|
246
|
+
refundTx.addInput({
|
|
247
|
+
...input,
|
|
38
248
|
sequence,
|
|
39
249
|
});
|
|
40
250
|
|
|
41
251
|
const refundPkScript = getP2TRScriptFromPublicKey(receivingPubkey, network);
|
|
42
252
|
|
|
43
|
-
|
|
253
|
+
let outputAmount = amountSats;
|
|
254
|
+
if (shouldCalculateFee) {
|
|
255
|
+
outputAmount = maybeApplyFee(amountSats);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
refundTx.addOutput({
|
|
44
259
|
script: refundPkScript,
|
|
260
|
+
amount: outputAmount,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
if (includeAnchor) {
|
|
264
|
+
refundTx.addOutput(getEphemeralAnchorOutput());
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return refundTx;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
interface CreateRefundTxsInput {
|
|
271
|
+
sequence: number;
|
|
272
|
+
directSequence?: number;
|
|
273
|
+
input: TransactionInput;
|
|
274
|
+
directInput?: TransactionInput;
|
|
275
|
+
amountSats: bigint;
|
|
276
|
+
receivingPubkey: Uint8Array;
|
|
277
|
+
network: Network;
|
|
278
|
+
}
|
|
279
|
+
export function createRefundTxs({
|
|
280
|
+
sequence,
|
|
281
|
+
directSequence,
|
|
282
|
+
input,
|
|
283
|
+
directInput,
|
|
284
|
+
amountSats,
|
|
285
|
+
receivingPubkey,
|
|
286
|
+
network,
|
|
287
|
+
}: CreateRefundTxsInput): {
|
|
288
|
+
cpfpRefundTx: Transaction;
|
|
289
|
+
directRefundTx?: Transaction;
|
|
290
|
+
directFromCpfpRefundTx?: Transaction;
|
|
291
|
+
} {
|
|
292
|
+
const cpfpRefundTx = createRefundTx({
|
|
293
|
+
sequence,
|
|
294
|
+
input,
|
|
295
|
+
amountSats,
|
|
296
|
+
receivingPubkey,
|
|
297
|
+
network,
|
|
298
|
+
shouldCalculateFee: false,
|
|
299
|
+
includeAnchor: true,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
let directRefundTx: Transaction | undefined;
|
|
303
|
+
let directFromCpfpRefundTx: Transaction | undefined;
|
|
304
|
+
if (directSequence && directInput) {
|
|
305
|
+
directRefundTx = createRefundTx({
|
|
306
|
+
sequence: directSequence,
|
|
307
|
+
input: directInput,
|
|
308
|
+
amountSats,
|
|
309
|
+
receivingPubkey,
|
|
310
|
+
network,
|
|
311
|
+
shouldCalculateFee: true,
|
|
312
|
+
includeAnchor: false,
|
|
313
|
+
});
|
|
314
|
+
directFromCpfpRefundTx = createRefundTx({
|
|
315
|
+
sequence: directSequence,
|
|
316
|
+
input,
|
|
317
|
+
amountSats,
|
|
318
|
+
receivingPubkey,
|
|
319
|
+
network,
|
|
320
|
+
shouldCalculateFee: true,
|
|
321
|
+
includeAnchor: false,
|
|
322
|
+
});
|
|
323
|
+
} else if (directInput && !directSequence) {
|
|
324
|
+
throw new ValidationError(
|
|
325
|
+
"directSequence must be provided if directInput is",
|
|
326
|
+
{
|
|
327
|
+
field: "directSequence",
|
|
328
|
+
value: directSequence,
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return { cpfpRefundTx, directRefundTx, directFromCpfpRefundTx };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function createConnectorRefundTransactions(
|
|
337
|
+
sequence: number,
|
|
338
|
+
cpfpNodeOutPoint: TransactionInput,
|
|
339
|
+
directNodeOutPoint: TransactionInput,
|
|
340
|
+
connectorOutput: TransactionInput,
|
|
341
|
+
amountSats: bigint,
|
|
342
|
+
receiverPubKey: Uint8Array,
|
|
343
|
+
network: Network,
|
|
344
|
+
shouldCalculateFee: boolean,
|
|
345
|
+
): [Transaction, Transaction, Transaction] {
|
|
346
|
+
// Create CPFP-friendly connector refund tx (with ephemeral anchor, no fee)
|
|
347
|
+
const cpfpRefundTx = new Transaction({
|
|
348
|
+
version: 3,
|
|
349
|
+
allowUnknownOutputs: true,
|
|
350
|
+
});
|
|
351
|
+
cpfpRefundTx.addInput({
|
|
352
|
+
...cpfpNodeOutPoint,
|
|
353
|
+
sequence,
|
|
354
|
+
});
|
|
355
|
+
cpfpRefundTx.addInput(connectorOutput);
|
|
356
|
+
const receiverScript = getP2TRScriptFromPublicKey(receiverPubKey, network);
|
|
357
|
+
cpfpRefundTx.addOutput({
|
|
358
|
+
script: receiverScript,
|
|
45
359
|
amount: amountSats,
|
|
46
360
|
});
|
|
47
361
|
|
|
48
|
-
|
|
362
|
+
// Create direct connector refund tx (with fee, no anchor)
|
|
363
|
+
const directRefundTx = new Transaction({
|
|
364
|
+
version: 3,
|
|
365
|
+
allowUnknownOutputs: true,
|
|
366
|
+
});
|
|
367
|
+
directRefundTx.addInput({
|
|
368
|
+
...directNodeOutPoint,
|
|
369
|
+
sequence,
|
|
370
|
+
});
|
|
371
|
+
directRefundTx.addInput(connectorOutput);
|
|
372
|
+
|
|
373
|
+
let outputAmount = amountSats;
|
|
374
|
+
if (shouldCalculateFee) {
|
|
375
|
+
outputAmount = maybeApplyFee(amountSats);
|
|
376
|
+
}
|
|
377
|
+
directRefundTx.addOutput({
|
|
378
|
+
script: receiverScript,
|
|
379
|
+
amount: outputAmount,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Create direct-style refund tx that spends from CPFP outpoint (with fee, no anchor)
|
|
383
|
+
const directFromCpfpTx = new Transaction({
|
|
384
|
+
version: 3,
|
|
385
|
+
allowUnknownOutputs: true,
|
|
386
|
+
});
|
|
387
|
+
directFromCpfpTx.addInput({
|
|
388
|
+
...cpfpNodeOutPoint,
|
|
389
|
+
sequence,
|
|
390
|
+
});
|
|
391
|
+
directFromCpfpTx.addInput(connectorOutput);
|
|
392
|
+
directFromCpfpTx.addOutput({
|
|
393
|
+
script: receiverScript,
|
|
394
|
+
amount: outputAmount,
|
|
395
|
+
});
|
|
49
396
|
|
|
50
|
-
return
|
|
397
|
+
return [cpfpRefundTx, directRefundTx, directFromCpfpTx];
|
|
51
398
|
}
|
|
52
399
|
|
|
53
400
|
export function getCurrentTimelock(currSequence?: number): number {
|
|
54
401
|
return (currSequence || 0) & 0xffff;
|
|
55
402
|
}
|
|
56
403
|
|
|
57
|
-
export function getTransactionSequence(currSequence?: number):
|
|
404
|
+
export function getTransactionSequence(currSequence?: number): {
|
|
405
|
+
nextSequence: number;
|
|
406
|
+
nextDirectSequence: number;
|
|
407
|
+
} {
|
|
58
408
|
const timelock = getCurrentTimelock(currSequence);
|
|
59
|
-
return
|
|
409
|
+
return {
|
|
410
|
+
nextSequence: (1 << 30) | timelock,
|
|
411
|
+
nextDirectSequence: (1 << 30) | (timelock + DIRECT_TIMELOCK_OFFSET),
|
|
412
|
+
};
|
|
60
413
|
}
|
|
61
414
|
|
|
62
415
|
export function checkIfValidSequence(currSequence?: number) {
|
|
@@ -79,34 +432,43 @@ export function checkIfValidSequence(currSequence?: number) {
|
|
|
79
432
|
}
|
|
80
433
|
}
|
|
81
434
|
|
|
435
|
+
export function doesLeafNeedRefresh(currSequence: number, isNodeTx?: boolean) {
|
|
436
|
+
const currentTimelock = getCurrentTimelock(currSequence);
|
|
437
|
+
|
|
438
|
+
if (isNodeTx) {
|
|
439
|
+
return currentTimelock === 0;
|
|
440
|
+
}
|
|
441
|
+
return currentTimelock <= 100;
|
|
442
|
+
}
|
|
443
|
+
|
|
82
444
|
// make sure that the leaves are ok before sending or else next user could lose funds
|
|
83
445
|
export function getNextTransactionSequence(
|
|
84
|
-
currSequence
|
|
85
|
-
|
|
446
|
+
currSequence: number,
|
|
447
|
+
isNodeTx?: boolean,
|
|
86
448
|
): {
|
|
87
449
|
nextSequence: number;
|
|
88
|
-
|
|
450
|
+
nextDirectSequence: number;
|
|
89
451
|
} {
|
|
90
452
|
const currentTimelock = getCurrentTimelock(currSequence);
|
|
91
453
|
const nextTimelock = currentTimelock - TIME_LOCK_INTERVAL;
|
|
92
454
|
|
|
93
|
-
if (
|
|
94
|
-
return {
|
|
95
|
-
nextSequence: (1 << 30) | nextTimelock,
|
|
96
|
-
needRefresh: true,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (nextTimelock < 0) {
|
|
455
|
+
if (isNodeTx && nextTimelock < 0) {
|
|
101
456
|
throw new ValidationError("timelock interval is less than 0", {
|
|
102
457
|
field: "nextTimelock",
|
|
103
458
|
value: nextTimelock,
|
|
459
|
+
expected: "Non-negative timelock interval",
|
|
460
|
+
});
|
|
461
|
+
} else if (!isNodeTx && nextTimelock <= 0) {
|
|
462
|
+
throw new ValidationError("timelock interval is less than or equal to 0", {
|
|
463
|
+
field: "nextTimelock",
|
|
464
|
+
value: nextTimelock,
|
|
465
|
+
expected: "Timelock greater than 0",
|
|
104
466
|
});
|
|
105
467
|
}
|
|
106
468
|
|
|
107
469
|
return {
|
|
108
470
|
nextSequence: (1 << 30) | nextTimelock,
|
|
109
|
-
|
|
471
|
+
nextDirectSequence: (1 << 30) | (nextTimelock + DIRECT_TIMELOCK_OFFSET),
|
|
110
472
|
};
|
|
111
473
|
}
|
|
112
474
|
|