@arkade-os/boltz-swap 0.1.4 → 0.1.8
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/README.md +51 -6
- package/dist/index.cjs +63 -18
- package/dist/index.d.cts +24 -5
- package/dist/index.d.ts +24 -5
- package/dist/index.js +59 -18
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ npm install @arkade-os/sdk @arkade-os/boltz-swap
|
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
27
|
import { Wallet } from '@arkade-os/sdk';
|
|
28
|
-
import { ArkadeLightning, BoltzSwapProvider } from '@arkade-os/boltz-swap';
|
|
28
|
+
import { ArkadeLightning, BoltzSwapProvider, StorageProvider } from '@arkade-os/boltz-swap';
|
|
29
29
|
|
|
30
30
|
// Initialize your Arkade wallet
|
|
31
31
|
const wallet = await Wallet.create({
|
|
@@ -39,8 +39,8 @@ const swapProvider = new BoltzSwapProvider({
|
|
|
39
39
|
network: 'mutinynet',
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
const storageProvider = StorageProvider.create();
|
|
42
|
+
// Optionally: initialize a storage provider
|
|
43
|
+
const storageProvider = await StorageProvider.create();
|
|
44
44
|
|
|
45
45
|
// Create the ArkadeLightning instance
|
|
46
46
|
const arkadeLightning = new ArkadeLightning({
|
|
@@ -50,6 +50,49 @@ const arkadeLightning = new ArkadeLightning({
|
|
|
50
50
|
});
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
## Wallet class Compatibility
|
|
54
|
+
|
|
55
|
+
This library supports both wallet interface patterns:
|
|
56
|
+
|
|
57
|
+
### Wallet (with optional nested identity and providers)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { Wallet } from '@arkade-os/sdk';
|
|
61
|
+
|
|
62
|
+
const wallet = await Wallet.create({
|
|
63
|
+
identity,
|
|
64
|
+
arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Wallet may have built-in providers
|
|
68
|
+
const arkadeLightning = new ArkadeLightning({
|
|
69
|
+
wallet,
|
|
70
|
+
swapProvider,
|
|
71
|
+
// arkProvider and indexerProvider can be provided here if wallet doesn't have them
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### ServiceWorkerWallet (legacy interface)
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { RestArkProvider, RestIndexerProvider } from '@arkade-os/sdk';
|
|
79
|
+
|
|
80
|
+
// ServiceWorkerWallet has identity methods spread directly (no nested identity)
|
|
81
|
+
const serviceWorkerWallet = new ServiceWorkerWallet(serviceWorker);
|
|
82
|
+
await serviceWorkerWallet.init({
|
|
83
|
+
privateKey: 'your_private_key_hex',
|
|
84
|
+
arkServerUrl: 'https://ark.example.com'
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Must provide external providers for ServiceWorkerWallet (it doesn't have them)
|
|
88
|
+
const arkadeLightning = new ArkadeLightning({
|
|
89
|
+
wallet: serviceWorkerWallet,
|
|
90
|
+
arkProvider: new RestArkProvider('https://ark.example.com'),
|
|
91
|
+
indexerProvider: new RestIndexerProvider('https://indexer.example.com'),
|
|
92
|
+
swapProvider,
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
53
96
|
## Storage
|
|
54
97
|
|
|
55
98
|
By default this library doesn't store pending swaps.
|
|
@@ -57,7 +100,7 @@ By default this library doesn't store pending swaps.
|
|
|
57
100
|
If you need it you must initialize a storageProvider:
|
|
58
101
|
|
|
59
102
|
```typescript
|
|
60
|
-
const storageProvider = StorageProvider.create({ storagePath: './storage.json' });
|
|
103
|
+
const storageProvider = await StorageProvider.create({ storagePath: './storage.json' });
|
|
61
104
|
|
|
62
105
|
const arkadeLightning = new ArkadeLightning({
|
|
63
106
|
wallet,
|
|
@@ -109,14 +152,16 @@ console.log('Transaction ID:', receivalResult.txid);
|
|
|
109
152
|
To send a payment from your Arkade wallet to a Lightning invoice:
|
|
110
153
|
|
|
111
154
|
```typescript
|
|
155
|
+
import { decodeInvoice } from '@arkade-os/boltz-swap';
|
|
156
|
+
|
|
112
157
|
// Parse a Lightning invoice
|
|
113
|
-
const invoiceDetails =
|
|
158
|
+
const invoiceDetails = decodeInvoice(
|
|
114
159
|
'lnbc500u1pj...' // Lightning invoice string
|
|
115
160
|
);
|
|
116
161
|
|
|
117
162
|
console.log('Invoice amount:', invoiceDetails.amountSats, 'sats');
|
|
118
163
|
console.log('Description:', invoiceDetails.description);
|
|
119
|
-
console.log('
|
|
164
|
+
console.log('Payment Hash:', invoiceDetails.paymentHash);
|
|
120
165
|
|
|
121
166
|
// Pay the Lightning invoice from your Arkade wallet
|
|
122
167
|
const paymentResult = await arkadeLightning.sendLightningPayment({
|
package/dist/index.cjs
CHANGED
|
@@ -34,9 +34,13 @@ __export(index_exports, {
|
|
|
34
34
|
BoltzSwapProvider: () => BoltzSwapProvider,
|
|
35
35
|
InsufficientFundsError: () => InsufficientFundsError,
|
|
36
36
|
InvoiceExpiredError: () => InvoiceExpiredError,
|
|
37
|
+
InvoiceFailedToPayError: () => InvoiceFailedToPayError,
|
|
37
38
|
NetworkError: () => NetworkError,
|
|
39
|
+
SchemaError: () => SchemaError,
|
|
38
40
|
StorageProvider: () => StorageProvider,
|
|
39
41
|
SwapError: () => SwapError,
|
|
42
|
+
SwapExpiredError: () => SwapExpiredError,
|
|
43
|
+
TransactionFailedError: () => TransactionFailedError,
|
|
40
44
|
decodeInvoice: () => decodeInvoice,
|
|
41
45
|
getInvoicePaymentHash: () => getInvoicePaymentHash,
|
|
42
46
|
getInvoiceSatoshis: () => getInvoiceSatoshis
|
|
@@ -115,6 +119,11 @@ var TransactionRefundedError = class extends SwapError {
|
|
|
115
119
|
var import_sdk = require("@arkade-os/sdk");
|
|
116
120
|
var import_sha2 = require("@noble/hashes/sha2");
|
|
117
121
|
var import_base = require("@scure/base");
|
|
122
|
+
|
|
123
|
+
// src/types.ts
|
|
124
|
+
var isWalletWithNestedIdentity = (w) => !!w.identity && typeof w.identity?.xOnlyPublicKey === "function";
|
|
125
|
+
|
|
126
|
+
// src/arkade-lightning.ts
|
|
118
127
|
var import_utils = require("@noble/hashes/utils");
|
|
119
128
|
var import_btc_signer = require("@scure/btc-signer");
|
|
120
129
|
var import_legacy = require("@noble/hashes/legacy");
|
|
@@ -139,6 +148,26 @@ var getInvoicePaymentHash = (invoice) => {
|
|
|
139
148
|
};
|
|
140
149
|
|
|
141
150
|
// src/arkade-lightning.ts
|
|
151
|
+
function getIdentity(wallet) {
|
|
152
|
+
if (isWalletWithNestedIdentity(wallet)) {
|
|
153
|
+
return wallet.identity;
|
|
154
|
+
}
|
|
155
|
+
return wallet;
|
|
156
|
+
}
|
|
157
|
+
function getXOnlyPublicKey(wallet) {
|
|
158
|
+
return getIdentity(wallet).xOnlyPublicKey();
|
|
159
|
+
}
|
|
160
|
+
function getSignerSession(wallet) {
|
|
161
|
+
const identity = getIdentity(wallet);
|
|
162
|
+
const signerSession = identity?.signerSession;
|
|
163
|
+
if (typeof signerSession === "function") {
|
|
164
|
+
return signerSession();
|
|
165
|
+
}
|
|
166
|
+
return signerSession;
|
|
167
|
+
}
|
|
168
|
+
async function signTransaction(wallet, tx, inputIndexes) {
|
|
169
|
+
return getIdentity(wallet).sign(tx, inputIndexes);
|
|
170
|
+
}
|
|
142
171
|
var ArkadeLightning = class {
|
|
143
172
|
wallet;
|
|
144
173
|
arkProvider;
|
|
@@ -147,14 +176,16 @@ var ArkadeLightning = class {
|
|
|
147
176
|
indexerProvider;
|
|
148
177
|
constructor(config) {
|
|
149
178
|
if (!config.wallet) throw new Error("Wallet is required.");
|
|
150
|
-
if (!config.arkProvider) throw new Error("Ark provider is required.");
|
|
151
179
|
if (!config.swapProvider) throw new Error("Swap provider is required.");
|
|
152
|
-
if (!config.indexerProvider) throw new Error("Indexer provider is required.");
|
|
153
180
|
this.wallet = config.wallet;
|
|
154
|
-
|
|
181
|
+
const arkProvider = config.wallet.arkProvider ?? config.arkProvider;
|
|
182
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
183
|
+
this.arkProvider = arkProvider;
|
|
184
|
+
const indexerProvider = config.wallet.indexerProvider ?? config.indexerProvider;
|
|
185
|
+
if (!indexerProvider) throw new Error("Indexer provider is required either in wallet or config.");
|
|
186
|
+
this.indexerProvider = indexerProvider;
|
|
155
187
|
this.swapProvider = config.swapProvider;
|
|
156
188
|
this.storageProvider = config.storageProvider ?? null;
|
|
157
|
-
this.indexerProvider = config.indexerProvider;
|
|
158
189
|
}
|
|
159
190
|
// receive from lightning = reverse submarine swap
|
|
160
191
|
//
|
|
@@ -189,10 +220,10 @@ var ArkadeLightning = class {
|
|
|
189
220
|
async sendLightningPayment(args) {
|
|
190
221
|
return new Promise((resolve, reject) => {
|
|
191
222
|
this.createSubmarineSwap(args).then((pendingSwap) => {
|
|
192
|
-
if (args.maxFeeSats) {
|
|
193
|
-
const invoiceAmount = decodeInvoice(args.invoice).amountSats;
|
|
223
|
+
if (args.maxFeeSats != null) {
|
|
224
|
+
const invoiceAmount = decodeInvoice(args.invoice).amountSats ?? 0;
|
|
194
225
|
const fees = pendingSwap.response.expectedAmount - invoiceAmount;
|
|
195
|
-
if (fees > args.maxFeeSats) {
|
|
226
|
+
if (invoiceAmount > 0 && fees > args.maxFeeSats) {
|
|
196
227
|
reject(new SwapError({ message: `Swap fees ${fees} exceed max allowed ${args.maxFeeSats}` }));
|
|
197
228
|
}
|
|
198
229
|
}
|
|
@@ -218,7 +249,7 @@ var ArkadeLightning = class {
|
|
|
218
249
|
}
|
|
219
250
|
// create submarine swap
|
|
220
251
|
async createSubmarineSwap(args) {
|
|
221
|
-
const refundPublicKey = import_base.hex.encode(this.wallet
|
|
252
|
+
const refundPublicKey = import_base.hex.encode(getXOnlyPublicKey(this.wallet));
|
|
222
253
|
if (!refundPublicKey) throw new SwapError({ message: "Failed to get refund public key from wallet" });
|
|
223
254
|
const invoice = args.invoice;
|
|
224
255
|
if (!invoice) throw new SwapError({ message: "Invoice is required" });
|
|
@@ -240,7 +271,7 @@ var ArkadeLightning = class {
|
|
|
240
271
|
// create reverse submarine swap
|
|
241
272
|
async createReverseSwap(args) {
|
|
242
273
|
if (args.amount <= 0) throw new SwapError({ message: "Amount must be greater than 0" });
|
|
243
|
-
const claimPublicKey = import_base.hex.encode(this.wallet
|
|
274
|
+
const claimPublicKey = import_base.hex.encode(getXOnlyPublicKey(this.wallet));
|
|
244
275
|
if (!claimPublicKey) throw new SwapError({ message: "Failed to get claim public key from wallet" });
|
|
245
276
|
const preimage = (0, import_utils.randomBytes)(32);
|
|
246
277
|
const preimageHash = import_base.hex.encode((0, import_sha2.sha256)(preimage));
|
|
@@ -267,7 +298,7 @@ var ArkadeLightning = class {
|
|
|
267
298
|
const preimage = import_base.hex.decode(pendingSwap.preimage);
|
|
268
299
|
const aspInfo = await this.arkProvider.getInfo();
|
|
269
300
|
const address = await this.wallet.getAddress();
|
|
270
|
-
let receiverXOnlyPublicKey = this.wallet
|
|
301
|
+
let receiverXOnlyPublicKey = getXOnlyPublicKey(this.wallet);
|
|
271
302
|
if (receiverXOnlyPublicKey.length == 33) {
|
|
272
303
|
receiverXOnlyPublicKey = receiverXOnlyPublicKey.slice(1);
|
|
273
304
|
} else if (receiverXOnlyPublicKey.length !== 32) {
|
|
@@ -298,13 +329,13 @@ var ArkadeLightning = class {
|
|
|
298
329
|
const vhtlcIdentity = {
|
|
299
330
|
sign: async (tx, inputIndexes) => {
|
|
300
331
|
const cpy = tx.clone();
|
|
301
|
-
let signedTx = await this.wallet
|
|
332
|
+
let signedTx = await signTransaction(this.wallet, cpy, inputIndexes);
|
|
302
333
|
signedTx = import_btc_signer.Transaction.fromPSBT(signedTx.toPSBT(), { allowUnknown: true });
|
|
303
334
|
(0, import_sdk.setArkPsbtField)(signedTx, 0, import_sdk.ConditionWitness, [preimage]);
|
|
304
335
|
return signedTx;
|
|
305
336
|
},
|
|
306
337
|
xOnlyPublicKey: receiverXOnlyPublicKey,
|
|
307
|
-
signerSession: this.wallet
|
|
338
|
+
signerSession: getSignerSession(this.wallet)
|
|
308
339
|
};
|
|
309
340
|
const serverUnrollScript = import_sdk.CSVMultisigTapscript.encode({
|
|
310
341
|
pubkeys: [serverXOnlyPublicKey],
|
|
@@ -356,7 +387,7 @@ var ArkadeLightning = class {
|
|
|
356
387
|
const aspInfo = await this.arkProvider.getInfo();
|
|
357
388
|
const address = await this.wallet.getAddress();
|
|
358
389
|
if (!address) throw new Error("Failed to get ark address from service worker wallet");
|
|
359
|
-
let receiverXOnlyPublicKey = this.wallet
|
|
390
|
+
let receiverXOnlyPublicKey = getXOnlyPublicKey(this.wallet);
|
|
360
391
|
if (receiverXOnlyPublicKey.length == 33) {
|
|
361
392
|
receiverXOnlyPublicKey = receiverXOnlyPublicKey.slice(1);
|
|
362
393
|
} else if (receiverXOnlyPublicKey.length !== 32) {
|
|
@@ -372,7 +403,7 @@ var ArkadeLightning = class {
|
|
|
372
403
|
network: aspInfo.network,
|
|
373
404
|
preimageHash: import_base.hex.decode(getInvoicePaymentHash(pendingSwap.request.invoice)),
|
|
374
405
|
receiverPubkey: pendingSwap.response.claimPublicKey,
|
|
375
|
-
senderPubkey: import_base.hex.encode(this.wallet
|
|
406
|
+
senderPubkey: import_base.hex.encode(getXOnlyPublicKey(this.wallet)),
|
|
376
407
|
serverPubkey: aspInfo.signerPubkey,
|
|
377
408
|
timeoutBlockHeights: pendingSwap.response.timeoutBlockHeights
|
|
378
409
|
});
|
|
@@ -388,11 +419,11 @@ var ArkadeLightning = class {
|
|
|
388
419
|
const vhtlcIdentity = {
|
|
389
420
|
sign: async (tx, inputIndexes) => {
|
|
390
421
|
const cpy = tx.clone();
|
|
391
|
-
let signedTx = await this.wallet
|
|
422
|
+
let signedTx = await signTransaction(this.wallet, cpy, inputIndexes);
|
|
392
423
|
return import_btc_signer.Transaction.fromPSBT(signedTx.toPSBT(), { allowUnknown: true });
|
|
393
424
|
},
|
|
394
425
|
xOnlyPublicKey: receiverXOnlyPublicKey,
|
|
395
|
-
signerSession: this.wallet
|
|
426
|
+
signerSession: getSignerSession(this.wallet)
|
|
396
427
|
};
|
|
397
428
|
const serverUnrollScript = import_sdk.CSVMultisigTapscript.encode({
|
|
398
429
|
pubkeys: [serverXOnlyPublicKey],
|
|
@@ -486,23 +517,28 @@ var ArkadeLightning = class {
|
|
|
486
517
|
*/
|
|
487
518
|
async waitForSwapSettlement(pendingSwap) {
|
|
488
519
|
return new Promise((resolve, reject) => {
|
|
520
|
+
let isResolved = false;
|
|
489
521
|
const onStatusUpdate = async (status) => {
|
|
522
|
+
if (isResolved) return;
|
|
490
523
|
switch (status) {
|
|
491
524
|
case "swap.expired":
|
|
525
|
+
isResolved = true;
|
|
492
526
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, status });
|
|
493
527
|
reject(new SwapExpiredError({ isRefundable: true, pendingSwap }));
|
|
494
528
|
break;
|
|
495
529
|
case "invoice.failedToPay":
|
|
530
|
+
isResolved = true;
|
|
496
531
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, status });
|
|
497
532
|
reject(new InvoiceFailedToPayError({ isRefundable: true, pendingSwap }));
|
|
498
533
|
break;
|
|
499
534
|
case "transaction.lockupFailed":
|
|
535
|
+
isResolved = true;
|
|
500
536
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, status });
|
|
501
537
|
reject(new TransactionLockupFailedError({ isRefundable: true, pendingSwap }));
|
|
502
538
|
break;
|
|
503
539
|
case "transaction.claimed": {
|
|
540
|
+
isResolved = true;
|
|
504
541
|
const { preimage } = await this.swapProvider.getSwapPreimage(pendingSwap.response.id);
|
|
505
|
-
console.log("preimage returned", { preimage });
|
|
506
542
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, preimage, status });
|
|
507
543
|
resolve({ preimage });
|
|
508
544
|
break;
|
|
@@ -512,7 +548,12 @@ var ArkadeLightning = class {
|
|
|
512
548
|
break;
|
|
513
549
|
}
|
|
514
550
|
};
|
|
515
|
-
this.swapProvider.monitorSwap(pendingSwap.response.id, onStatusUpdate)
|
|
551
|
+
this.swapProvider.monitorSwap(pendingSwap.response.id, onStatusUpdate).catch((error) => {
|
|
552
|
+
if (!isResolved) {
|
|
553
|
+
isResolved = true;
|
|
554
|
+
reject(error);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
516
557
|
});
|
|
517
558
|
}
|
|
518
559
|
// validators
|
|
@@ -1002,9 +1043,13 @@ var BoltzSwapProvider = class {
|
|
|
1002
1043
|
BoltzSwapProvider,
|
|
1003
1044
|
InsufficientFundsError,
|
|
1004
1045
|
InvoiceExpiredError,
|
|
1046
|
+
InvoiceFailedToPayError,
|
|
1005
1047
|
NetworkError,
|
|
1048
|
+
SchemaError,
|
|
1006
1049
|
StorageProvider,
|
|
1007
1050
|
SwapError,
|
|
1051
|
+
SwapExpiredError,
|
|
1052
|
+
TransactionFailedError,
|
|
1008
1053
|
decodeInvoice,
|
|
1009
1054
|
getInvoicePaymentHash,
|
|
1010
1055
|
getInvoiceSatoshis
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IWallet,
|
|
1
|
+
import { IWallet, ArkProvider, IndexerProvider, Identity, VHTLC } from '@arkade-os/sdk';
|
|
2
2
|
|
|
3
3
|
interface StorageOptions {
|
|
4
4
|
storagePath?: string;
|
|
@@ -119,13 +119,20 @@ interface Vtxo {
|
|
|
119
119
|
locktime: number;
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
type
|
|
122
|
+
type WalletWithNestedIdentity = IWallet & {
|
|
123
|
+
arkProvider?: ArkProvider;
|
|
124
|
+
indexerProvider?: IndexerProvider;
|
|
125
|
+
identity: Identity;
|
|
126
|
+
};
|
|
127
|
+
type ServiceWorkerWallet = IWallet & Identity;
|
|
128
|
+
type Wallet = WalletWithNestedIdentity | ServiceWorkerWallet;
|
|
123
129
|
type Network = 'bitcoin' | 'mutinynet' | 'regtest' | 'testnet';
|
|
124
130
|
interface CreateLightningInvoiceRequest {
|
|
125
131
|
amount: number;
|
|
126
132
|
description?: string;
|
|
127
133
|
}
|
|
128
134
|
interface CreateLightningInvoiceResponse {
|
|
135
|
+
amount: number;
|
|
129
136
|
expiry: number;
|
|
130
137
|
invoice: string;
|
|
131
138
|
paymentHash: string;
|
|
@@ -162,9 +169,9 @@ interface RefundHandler {
|
|
|
162
169
|
}
|
|
163
170
|
interface ArkadeLightningConfig {
|
|
164
171
|
wallet: Wallet;
|
|
165
|
-
arkProvider
|
|
172
|
+
arkProvider?: ArkProvider;
|
|
166
173
|
swapProvider: BoltzSwapProvider;
|
|
167
|
-
indexerProvider
|
|
174
|
+
indexerProvider?: IndexerProvider;
|
|
168
175
|
feeConfig?: Partial<FeeConfig>;
|
|
169
176
|
refundHandler?: RefundHandler;
|
|
170
177
|
storageProvider?: StorageProvider | null;
|
|
@@ -316,12 +323,24 @@ declare class SwapError extends Error {
|
|
|
316
323
|
declare class InvoiceExpiredError extends SwapError {
|
|
317
324
|
constructor(options: ErrorOptions);
|
|
318
325
|
}
|
|
326
|
+
declare class InvoiceFailedToPayError extends SwapError {
|
|
327
|
+
constructor(options: ErrorOptions);
|
|
328
|
+
}
|
|
319
329
|
declare class InsufficientFundsError extends SwapError {
|
|
320
330
|
constructor(options?: ErrorOptions);
|
|
321
331
|
}
|
|
322
332
|
declare class NetworkError extends Error {
|
|
323
333
|
constructor(message: string);
|
|
324
334
|
}
|
|
335
|
+
declare class SchemaError extends SwapError {
|
|
336
|
+
constructor(options?: ErrorOptions);
|
|
337
|
+
}
|
|
338
|
+
declare class SwapExpiredError extends SwapError {
|
|
339
|
+
constructor(options: ErrorOptions);
|
|
340
|
+
}
|
|
341
|
+
declare class TransactionFailedError extends SwapError {
|
|
342
|
+
constructor(options?: ErrorOptions);
|
|
343
|
+
}
|
|
325
344
|
|
|
326
345
|
/**
|
|
327
346
|
* Decodes a Lightning invoice.
|
|
@@ -332,4 +351,4 @@ declare const decodeInvoice: (invoice: string) => DecodedInvoice;
|
|
|
332
351
|
declare const getInvoiceSatoshis: (invoice: string) => number;
|
|
333
352
|
declare const getInvoicePaymentHash: (invoice: string) => string;
|
|
334
353
|
|
|
335
|
-
export { ArkadeLightning, type ArkadeLightningConfig, BoltzSwapProvider, type BoltzSwapStatus, type CreateLightningInvoiceResponse, type DecodedInvoice, type FeeConfig, type IncomingPaymentSubscription, InsufficientFundsError, InvoiceExpiredError, type LimitsResponse, type Network, NetworkError, type PendingReverseSwap, type PendingSubmarineSwap, type RefundHandler, type RetryConfig, type SendLightningPaymentRequest, type SendLightningPaymentResponse, StorageProvider, SwapError, type TimeoutConfig, type Vtxo, type Wallet, decodeInvoice, getInvoicePaymentHash, getInvoiceSatoshis };
|
|
354
|
+
export { ArkadeLightning, type ArkadeLightningConfig, BoltzSwapProvider, type BoltzSwapStatus, type CreateLightningInvoiceResponse, type DecodedInvoice, type FeeConfig, type IncomingPaymentSubscription, InsufficientFundsError, InvoiceExpiredError, InvoiceFailedToPayError, type LimitsResponse, type Network, NetworkError, type PendingReverseSwap, type PendingSubmarineSwap, type RefundHandler, type RetryConfig, SchemaError, type SendLightningPaymentRequest, type SendLightningPaymentResponse, StorageProvider, SwapError, SwapExpiredError, type TimeoutConfig, TransactionFailedError, type Vtxo, type Wallet, decodeInvoice, getInvoicePaymentHash, getInvoiceSatoshis };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IWallet,
|
|
1
|
+
import { IWallet, ArkProvider, IndexerProvider, Identity, VHTLC } from '@arkade-os/sdk';
|
|
2
2
|
|
|
3
3
|
interface StorageOptions {
|
|
4
4
|
storagePath?: string;
|
|
@@ -119,13 +119,20 @@ interface Vtxo {
|
|
|
119
119
|
locktime: number;
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
type
|
|
122
|
+
type WalletWithNestedIdentity = IWallet & {
|
|
123
|
+
arkProvider?: ArkProvider;
|
|
124
|
+
indexerProvider?: IndexerProvider;
|
|
125
|
+
identity: Identity;
|
|
126
|
+
};
|
|
127
|
+
type ServiceWorkerWallet = IWallet & Identity;
|
|
128
|
+
type Wallet = WalletWithNestedIdentity | ServiceWorkerWallet;
|
|
123
129
|
type Network = 'bitcoin' | 'mutinynet' | 'regtest' | 'testnet';
|
|
124
130
|
interface CreateLightningInvoiceRequest {
|
|
125
131
|
amount: number;
|
|
126
132
|
description?: string;
|
|
127
133
|
}
|
|
128
134
|
interface CreateLightningInvoiceResponse {
|
|
135
|
+
amount: number;
|
|
129
136
|
expiry: number;
|
|
130
137
|
invoice: string;
|
|
131
138
|
paymentHash: string;
|
|
@@ -162,9 +169,9 @@ interface RefundHandler {
|
|
|
162
169
|
}
|
|
163
170
|
interface ArkadeLightningConfig {
|
|
164
171
|
wallet: Wallet;
|
|
165
|
-
arkProvider
|
|
172
|
+
arkProvider?: ArkProvider;
|
|
166
173
|
swapProvider: BoltzSwapProvider;
|
|
167
|
-
indexerProvider
|
|
174
|
+
indexerProvider?: IndexerProvider;
|
|
168
175
|
feeConfig?: Partial<FeeConfig>;
|
|
169
176
|
refundHandler?: RefundHandler;
|
|
170
177
|
storageProvider?: StorageProvider | null;
|
|
@@ -316,12 +323,24 @@ declare class SwapError extends Error {
|
|
|
316
323
|
declare class InvoiceExpiredError extends SwapError {
|
|
317
324
|
constructor(options: ErrorOptions);
|
|
318
325
|
}
|
|
326
|
+
declare class InvoiceFailedToPayError extends SwapError {
|
|
327
|
+
constructor(options: ErrorOptions);
|
|
328
|
+
}
|
|
319
329
|
declare class InsufficientFundsError extends SwapError {
|
|
320
330
|
constructor(options?: ErrorOptions);
|
|
321
331
|
}
|
|
322
332
|
declare class NetworkError extends Error {
|
|
323
333
|
constructor(message: string);
|
|
324
334
|
}
|
|
335
|
+
declare class SchemaError extends SwapError {
|
|
336
|
+
constructor(options?: ErrorOptions);
|
|
337
|
+
}
|
|
338
|
+
declare class SwapExpiredError extends SwapError {
|
|
339
|
+
constructor(options: ErrorOptions);
|
|
340
|
+
}
|
|
341
|
+
declare class TransactionFailedError extends SwapError {
|
|
342
|
+
constructor(options?: ErrorOptions);
|
|
343
|
+
}
|
|
325
344
|
|
|
326
345
|
/**
|
|
327
346
|
* Decodes a Lightning invoice.
|
|
@@ -332,4 +351,4 @@ declare const decodeInvoice: (invoice: string) => DecodedInvoice;
|
|
|
332
351
|
declare const getInvoiceSatoshis: (invoice: string) => number;
|
|
333
352
|
declare const getInvoicePaymentHash: (invoice: string) => string;
|
|
334
353
|
|
|
335
|
-
export { ArkadeLightning, type ArkadeLightningConfig, BoltzSwapProvider, type BoltzSwapStatus, type CreateLightningInvoiceResponse, type DecodedInvoice, type FeeConfig, type IncomingPaymentSubscription, InsufficientFundsError, InvoiceExpiredError, type LimitsResponse, type Network, NetworkError, type PendingReverseSwap, type PendingSubmarineSwap, type RefundHandler, type RetryConfig, type SendLightningPaymentRequest, type SendLightningPaymentResponse, StorageProvider, SwapError, type TimeoutConfig, type Vtxo, type Wallet, decodeInvoice, getInvoicePaymentHash, getInvoiceSatoshis };
|
|
354
|
+
export { ArkadeLightning, type ArkadeLightningConfig, BoltzSwapProvider, type BoltzSwapStatus, type CreateLightningInvoiceResponse, type DecodedInvoice, type FeeConfig, type IncomingPaymentSubscription, InsufficientFundsError, InvoiceExpiredError, InvoiceFailedToPayError, type LimitsResponse, type Network, NetworkError, type PendingReverseSwap, type PendingSubmarineSwap, type RefundHandler, type RetryConfig, SchemaError, type SendLightningPaymentRequest, type SendLightningPaymentResponse, StorageProvider, SwapError, SwapExpiredError, type TimeoutConfig, TransactionFailedError, type Vtxo, type Wallet, decodeInvoice, getInvoicePaymentHash, getInvoiceSatoshis };
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,11 @@ import {
|
|
|
77
77
|
} from "@arkade-os/sdk";
|
|
78
78
|
import { sha256 } from "@noble/hashes/sha2";
|
|
79
79
|
import { base64, hex } from "@scure/base";
|
|
80
|
+
|
|
81
|
+
// src/types.ts
|
|
82
|
+
var isWalletWithNestedIdentity = (w) => !!w.identity && typeof w.identity?.xOnlyPublicKey === "function";
|
|
83
|
+
|
|
84
|
+
// src/arkade-lightning.ts
|
|
80
85
|
import { randomBytes } from "@noble/hashes/utils";
|
|
81
86
|
import { Transaction } from "@scure/btc-signer";
|
|
82
87
|
import { ripemd160 } from "@noble/hashes/legacy";
|
|
@@ -101,6 +106,26 @@ var getInvoicePaymentHash = (invoice) => {
|
|
|
101
106
|
};
|
|
102
107
|
|
|
103
108
|
// src/arkade-lightning.ts
|
|
109
|
+
function getIdentity(wallet) {
|
|
110
|
+
if (isWalletWithNestedIdentity(wallet)) {
|
|
111
|
+
return wallet.identity;
|
|
112
|
+
}
|
|
113
|
+
return wallet;
|
|
114
|
+
}
|
|
115
|
+
function getXOnlyPublicKey(wallet) {
|
|
116
|
+
return getIdentity(wallet).xOnlyPublicKey();
|
|
117
|
+
}
|
|
118
|
+
function getSignerSession(wallet) {
|
|
119
|
+
const identity = getIdentity(wallet);
|
|
120
|
+
const signerSession = identity?.signerSession;
|
|
121
|
+
if (typeof signerSession === "function") {
|
|
122
|
+
return signerSession();
|
|
123
|
+
}
|
|
124
|
+
return signerSession;
|
|
125
|
+
}
|
|
126
|
+
async function signTransaction(wallet, tx, inputIndexes) {
|
|
127
|
+
return getIdentity(wallet).sign(tx, inputIndexes);
|
|
128
|
+
}
|
|
104
129
|
var ArkadeLightning = class {
|
|
105
130
|
wallet;
|
|
106
131
|
arkProvider;
|
|
@@ -109,14 +134,16 @@ var ArkadeLightning = class {
|
|
|
109
134
|
indexerProvider;
|
|
110
135
|
constructor(config) {
|
|
111
136
|
if (!config.wallet) throw new Error("Wallet is required.");
|
|
112
|
-
if (!config.arkProvider) throw new Error("Ark provider is required.");
|
|
113
137
|
if (!config.swapProvider) throw new Error("Swap provider is required.");
|
|
114
|
-
if (!config.indexerProvider) throw new Error("Indexer provider is required.");
|
|
115
138
|
this.wallet = config.wallet;
|
|
116
|
-
|
|
139
|
+
const arkProvider = config.wallet.arkProvider ?? config.arkProvider;
|
|
140
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
141
|
+
this.arkProvider = arkProvider;
|
|
142
|
+
const indexerProvider = config.wallet.indexerProvider ?? config.indexerProvider;
|
|
143
|
+
if (!indexerProvider) throw new Error("Indexer provider is required either in wallet or config.");
|
|
144
|
+
this.indexerProvider = indexerProvider;
|
|
117
145
|
this.swapProvider = config.swapProvider;
|
|
118
146
|
this.storageProvider = config.storageProvider ?? null;
|
|
119
|
-
this.indexerProvider = config.indexerProvider;
|
|
120
147
|
}
|
|
121
148
|
// receive from lightning = reverse submarine swap
|
|
122
149
|
//
|
|
@@ -151,10 +178,10 @@ var ArkadeLightning = class {
|
|
|
151
178
|
async sendLightningPayment(args) {
|
|
152
179
|
return new Promise((resolve, reject) => {
|
|
153
180
|
this.createSubmarineSwap(args).then((pendingSwap) => {
|
|
154
|
-
if (args.maxFeeSats) {
|
|
155
|
-
const invoiceAmount = decodeInvoice(args.invoice).amountSats;
|
|
181
|
+
if (args.maxFeeSats != null) {
|
|
182
|
+
const invoiceAmount = decodeInvoice(args.invoice).amountSats ?? 0;
|
|
156
183
|
const fees = pendingSwap.response.expectedAmount - invoiceAmount;
|
|
157
|
-
if (fees > args.maxFeeSats) {
|
|
184
|
+
if (invoiceAmount > 0 && fees > args.maxFeeSats) {
|
|
158
185
|
reject(new SwapError({ message: `Swap fees ${fees} exceed max allowed ${args.maxFeeSats}` }));
|
|
159
186
|
}
|
|
160
187
|
}
|
|
@@ -180,7 +207,7 @@ var ArkadeLightning = class {
|
|
|
180
207
|
}
|
|
181
208
|
// create submarine swap
|
|
182
209
|
async createSubmarineSwap(args) {
|
|
183
|
-
const refundPublicKey = hex.encode(this.wallet
|
|
210
|
+
const refundPublicKey = hex.encode(getXOnlyPublicKey(this.wallet));
|
|
184
211
|
if (!refundPublicKey) throw new SwapError({ message: "Failed to get refund public key from wallet" });
|
|
185
212
|
const invoice = args.invoice;
|
|
186
213
|
if (!invoice) throw new SwapError({ message: "Invoice is required" });
|
|
@@ -202,7 +229,7 @@ var ArkadeLightning = class {
|
|
|
202
229
|
// create reverse submarine swap
|
|
203
230
|
async createReverseSwap(args) {
|
|
204
231
|
if (args.amount <= 0) throw new SwapError({ message: "Amount must be greater than 0" });
|
|
205
|
-
const claimPublicKey = hex.encode(this.wallet
|
|
232
|
+
const claimPublicKey = hex.encode(getXOnlyPublicKey(this.wallet));
|
|
206
233
|
if (!claimPublicKey) throw new SwapError({ message: "Failed to get claim public key from wallet" });
|
|
207
234
|
const preimage = randomBytes(32);
|
|
208
235
|
const preimageHash = hex.encode(sha256(preimage));
|
|
@@ -229,7 +256,7 @@ var ArkadeLightning = class {
|
|
|
229
256
|
const preimage = hex.decode(pendingSwap.preimage);
|
|
230
257
|
const aspInfo = await this.arkProvider.getInfo();
|
|
231
258
|
const address = await this.wallet.getAddress();
|
|
232
|
-
let receiverXOnlyPublicKey = this.wallet
|
|
259
|
+
let receiverXOnlyPublicKey = getXOnlyPublicKey(this.wallet);
|
|
233
260
|
if (receiverXOnlyPublicKey.length == 33) {
|
|
234
261
|
receiverXOnlyPublicKey = receiverXOnlyPublicKey.slice(1);
|
|
235
262
|
} else if (receiverXOnlyPublicKey.length !== 32) {
|
|
@@ -260,13 +287,13 @@ var ArkadeLightning = class {
|
|
|
260
287
|
const vhtlcIdentity = {
|
|
261
288
|
sign: async (tx, inputIndexes) => {
|
|
262
289
|
const cpy = tx.clone();
|
|
263
|
-
let signedTx = await this.wallet
|
|
290
|
+
let signedTx = await signTransaction(this.wallet, cpy, inputIndexes);
|
|
264
291
|
signedTx = Transaction.fromPSBT(signedTx.toPSBT(), { allowUnknown: true });
|
|
265
292
|
setArkPsbtField(signedTx, 0, ConditionWitness, [preimage]);
|
|
266
293
|
return signedTx;
|
|
267
294
|
},
|
|
268
295
|
xOnlyPublicKey: receiverXOnlyPublicKey,
|
|
269
|
-
signerSession: this.wallet
|
|
296
|
+
signerSession: getSignerSession(this.wallet)
|
|
270
297
|
};
|
|
271
298
|
const serverUnrollScript = CSVMultisigTapscript.encode({
|
|
272
299
|
pubkeys: [serverXOnlyPublicKey],
|
|
@@ -318,7 +345,7 @@ var ArkadeLightning = class {
|
|
|
318
345
|
const aspInfo = await this.arkProvider.getInfo();
|
|
319
346
|
const address = await this.wallet.getAddress();
|
|
320
347
|
if (!address) throw new Error("Failed to get ark address from service worker wallet");
|
|
321
|
-
let receiverXOnlyPublicKey = this.wallet
|
|
348
|
+
let receiverXOnlyPublicKey = getXOnlyPublicKey(this.wallet);
|
|
322
349
|
if (receiverXOnlyPublicKey.length == 33) {
|
|
323
350
|
receiverXOnlyPublicKey = receiverXOnlyPublicKey.slice(1);
|
|
324
351
|
} else if (receiverXOnlyPublicKey.length !== 32) {
|
|
@@ -334,7 +361,7 @@ var ArkadeLightning = class {
|
|
|
334
361
|
network: aspInfo.network,
|
|
335
362
|
preimageHash: hex.decode(getInvoicePaymentHash(pendingSwap.request.invoice)),
|
|
336
363
|
receiverPubkey: pendingSwap.response.claimPublicKey,
|
|
337
|
-
senderPubkey: hex.encode(this.wallet
|
|
364
|
+
senderPubkey: hex.encode(getXOnlyPublicKey(this.wallet)),
|
|
338
365
|
serverPubkey: aspInfo.signerPubkey,
|
|
339
366
|
timeoutBlockHeights: pendingSwap.response.timeoutBlockHeights
|
|
340
367
|
});
|
|
@@ -350,11 +377,11 @@ var ArkadeLightning = class {
|
|
|
350
377
|
const vhtlcIdentity = {
|
|
351
378
|
sign: async (tx, inputIndexes) => {
|
|
352
379
|
const cpy = tx.clone();
|
|
353
|
-
let signedTx = await this.wallet
|
|
380
|
+
let signedTx = await signTransaction(this.wallet, cpy, inputIndexes);
|
|
354
381
|
return Transaction.fromPSBT(signedTx.toPSBT(), { allowUnknown: true });
|
|
355
382
|
},
|
|
356
383
|
xOnlyPublicKey: receiverXOnlyPublicKey,
|
|
357
|
-
signerSession: this.wallet
|
|
384
|
+
signerSession: getSignerSession(this.wallet)
|
|
358
385
|
};
|
|
359
386
|
const serverUnrollScript = CSVMultisigTapscript.encode({
|
|
360
387
|
pubkeys: [serverXOnlyPublicKey],
|
|
@@ -448,23 +475,28 @@ var ArkadeLightning = class {
|
|
|
448
475
|
*/
|
|
449
476
|
async waitForSwapSettlement(pendingSwap) {
|
|
450
477
|
return new Promise((resolve, reject) => {
|
|
478
|
+
let isResolved = false;
|
|
451
479
|
const onStatusUpdate = async (status) => {
|
|
480
|
+
if (isResolved) return;
|
|
452
481
|
switch (status) {
|
|
453
482
|
case "swap.expired":
|
|
483
|
+
isResolved = true;
|
|
454
484
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, status });
|
|
455
485
|
reject(new SwapExpiredError({ isRefundable: true, pendingSwap }));
|
|
456
486
|
break;
|
|
457
487
|
case "invoice.failedToPay":
|
|
488
|
+
isResolved = true;
|
|
458
489
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, status });
|
|
459
490
|
reject(new InvoiceFailedToPayError({ isRefundable: true, pendingSwap }));
|
|
460
491
|
break;
|
|
461
492
|
case "transaction.lockupFailed":
|
|
493
|
+
isResolved = true;
|
|
462
494
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, status });
|
|
463
495
|
reject(new TransactionLockupFailedError({ isRefundable: true, pendingSwap }));
|
|
464
496
|
break;
|
|
465
497
|
case "transaction.claimed": {
|
|
498
|
+
isResolved = true;
|
|
466
499
|
const { preimage } = await this.swapProvider.getSwapPreimage(pendingSwap.response.id);
|
|
467
|
-
console.log("preimage returned", { preimage });
|
|
468
500
|
this.storageProvider?.savePendingSubmarineSwap({ ...pendingSwap, preimage, status });
|
|
469
501
|
resolve({ preimage });
|
|
470
502
|
break;
|
|
@@ -474,7 +506,12 @@ var ArkadeLightning = class {
|
|
|
474
506
|
break;
|
|
475
507
|
}
|
|
476
508
|
};
|
|
477
|
-
this.swapProvider.monitorSwap(pendingSwap.response.id, onStatusUpdate)
|
|
509
|
+
this.swapProvider.monitorSwap(pendingSwap.response.id, onStatusUpdate).catch((error) => {
|
|
510
|
+
if (!isResolved) {
|
|
511
|
+
isResolved = true;
|
|
512
|
+
reject(error);
|
|
513
|
+
}
|
|
514
|
+
});
|
|
478
515
|
});
|
|
479
516
|
}
|
|
480
517
|
// validators
|
|
@@ -963,9 +1000,13 @@ export {
|
|
|
963
1000
|
BoltzSwapProvider,
|
|
964
1001
|
InsufficientFundsError,
|
|
965
1002
|
InvoiceExpiredError,
|
|
1003
|
+
InvoiceFailedToPayError,
|
|
966
1004
|
NetworkError,
|
|
1005
|
+
SchemaError,
|
|
967
1006
|
StorageProvider,
|
|
968
1007
|
SwapError,
|
|
1008
|
+
SwapExpiredError,
|
|
1009
|
+
TransactionFailedError,
|
|
969
1010
|
decodeInvoice,
|
|
970
1011
|
getInvoicePaymentHash,
|
|
971
1012
|
getInvoiceSatoshis
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkade-os/boltz-swap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A production-ready TypeScript package that brings Boltz submarine-swaps to Arkade.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@arkade-os/sdk": "^0.2.1",
|
|
34
|
-
"@noble/hashes": "
|
|
34
|
+
"@noble/hashes": "1.8.0",
|
|
35
35
|
"@scure/base": "^1.2.6",
|
|
36
36
|
"@scure/btc-signer": "^1.2.1",
|
|
37
37
|
"light-bolt11-decoder": "^3.2.0"
|