@satora/swap 0.3.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bitcoin-reader-electrum-4DOJ7NGH.js +7 -0
- package/dist/bitcoin-reader-electrum-4DOJ7NGH.js.map +1 -0
- package/dist/chunk-VHXEU534.js +145 -0
- package/dist/chunk-VHXEU534.js.map +1 -0
- package/dist/index.cjs +266 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -14
- package/dist/index.d.ts +86 -14
- package/dist/index.js +102 -113
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@ import {
|
|
|
3
3
|
esploraReader,
|
|
4
4
|
htlcFactsFromEsploraTxs
|
|
5
5
|
} from "./chunk-EIWELJJK.js";
|
|
6
|
+
import {
|
|
7
|
+
electrumReader
|
|
8
|
+
} from "./chunk-VHXEU534.js";
|
|
6
9
|
|
|
7
10
|
// src/index.ts
|
|
8
11
|
export * from "@lendasat/lendaswap-sdk-pure";
|
|
@@ -311,6 +314,7 @@ function payOnLightningStatus(serverHtlc) {
|
|
|
311
314
|
|
|
312
315
|
// src/client.ts
|
|
313
316
|
import {
|
|
317
|
+
DEFAULT_ELECTRUM_WS_URLS,
|
|
314
318
|
Client as LegacyClient
|
|
315
319
|
} from "@lendasat/lendaswap-sdk-pure";
|
|
316
320
|
|
|
@@ -344,10 +348,12 @@ function arkadeObservation(facts) {
|
|
|
344
348
|
}
|
|
345
349
|
function classifyArkadeSpend(encodedSpendTx, paymentHash) {
|
|
346
350
|
const tx = Transaction.fromPSBT(base64.decode(encodedSpendTx));
|
|
347
|
-
for (
|
|
348
|
-
for (const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
+
for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {
|
|
352
|
+
for (const witness of getArkPsbtFields(tx, inputIndex, ConditionWitness)) {
|
|
353
|
+
for (const element of witness) {
|
|
354
|
+
if (element.length === 32 && preimageMatches(element, paymentHash)) {
|
|
355
|
+
return { spend: "claim", preimage: element };
|
|
356
|
+
}
|
|
351
357
|
}
|
|
352
358
|
}
|
|
353
359
|
}
|
|
@@ -551,6 +557,8 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
551
557
|
#obs = /* @__PURE__ */ new Map();
|
|
552
558
|
/** address → the preimage a claim revealed. */
|
|
553
559
|
#preimages = /* @__PURE__ */ new Map();
|
|
560
|
+
/** address → push-subscription teardown (only when the reader supports push). */
|
|
561
|
+
#subscriptions = /* @__PURE__ */ new Map();
|
|
554
562
|
#listeners = /* @__PURE__ */ new Set();
|
|
555
563
|
/** Last MTP reading (ms) + when it was fetched, for extrapolation. */
|
|
556
564
|
#now;
|
|
@@ -563,10 +571,24 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
563
571
|
}
|
|
564
572
|
static async create(config) {
|
|
565
573
|
const { esploraReader: esploraReader2 } = await import("./bitcoin-reader-esplora-HV6CANAA.js");
|
|
574
|
+
let reader = esploraReader2(
|
|
575
|
+
config.esploraUrl,
|
|
576
|
+
void 0,
|
|
577
|
+
{ minConfirmations: config.minConfirmations }
|
|
578
|
+
);
|
|
579
|
+
if (config.electrumWsUrl) {
|
|
580
|
+
const [{ electrumReader: electrumReader2 }, { ElectrumWsClient }] = await Promise.all([
|
|
581
|
+
import("./bitcoin-reader-electrum-4DOJ7NGH.js"),
|
|
582
|
+
import("@lendasat/lendaswap-sdk-pure")
|
|
583
|
+
]);
|
|
584
|
+
reader = electrumReader2(new ElectrumWsClient(config.electrumWsUrl), {
|
|
585
|
+
network: config.network,
|
|
586
|
+
minConfirmations: config.minConfirmations,
|
|
587
|
+
fallback: reader
|
|
588
|
+
});
|
|
589
|
+
}
|
|
566
590
|
return _BitcoinContractManager.fromDeps({
|
|
567
|
-
reader
|
|
568
|
-
minConfirmations: config.minConfirmations
|
|
569
|
-
}),
|
|
591
|
+
reader,
|
|
570
592
|
chainTime: config.chainTime
|
|
571
593
|
});
|
|
572
594
|
}
|
|
@@ -579,10 +601,25 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
579
601
|
`BitcoinContractManager can't track a '${ref.ledger}' HTLC`
|
|
580
602
|
);
|
|
581
603
|
this.#refs.set(ref.address, ref);
|
|
604
|
+
if (this.#reader.subscribe && !this.#subscriptions.has(ref.address)) {
|
|
605
|
+
this.#subscriptions.set(
|
|
606
|
+
ref.address,
|
|
607
|
+
this.#reader.subscribe(ref.address, () => {
|
|
608
|
+
this.#reconcileRef(ref).catch((error) => {
|
|
609
|
+
console.warn(
|
|
610
|
+
"BitcoinContractManager: push reconcile failed:",
|
|
611
|
+
error instanceof Error ? error.message : error
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
})
|
|
615
|
+
);
|
|
616
|
+
}
|
|
582
617
|
await this.#reconcileRef(ref);
|
|
583
618
|
}
|
|
584
619
|
async unregister(ref) {
|
|
585
620
|
if (ref.ledger !== "bitcoin") return;
|
|
621
|
+
this.#subscriptions.get(ref.address)?.();
|
|
622
|
+
this.#subscriptions.delete(ref.address);
|
|
586
623
|
this.#refs.delete(ref.address);
|
|
587
624
|
this.#obs.delete(ref.address);
|
|
588
625
|
this.#preimages.delete(ref.address);
|
|
@@ -617,6 +654,8 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
617
654
|
this.#now = { mtpMs: await this.#chainTime(), fetchedAtMs: Date.now() };
|
|
618
655
|
}
|
|
619
656
|
dispose() {
|
|
657
|
+
for (const unsubscribe of this.#subscriptions.values()) unsubscribe();
|
|
658
|
+
this.#subscriptions.clear();
|
|
620
659
|
this.#listeners.clear();
|
|
621
660
|
}
|
|
622
661
|
/** The preimage a claim revealed on this HTLC, if one was seen. */
|
|
@@ -1519,9 +1558,8 @@ function defaultWebSocketFactory(url) {
|
|
|
1519
1558
|
}
|
|
1520
1559
|
|
|
1521
1560
|
// src/contracts/arkade-vhtlc.ts
|
|
1522
|
-
import {
|
|
1561
|
+
import { VHTLCContractHandler } from "@arkade-os/sdk";
|
|
1523
1562
|
import {
|
|
1524
|
-
ARKADE_HTLC_SCRIPT_VERSION_LEGACY,
|
|
1525
1563
|
ARKADE_HTLC_SCRIPT_VERSION_STRICT,
|
|
1526
1564
|
StrictVhtlcScript
|
|
1527
1565
|
} from "@lendasat/lendaswap-sdk-pure";
|
|
@@ -1551,18 +1589,12 @@ function buildArkadeVhtlcRef(input) {
|
|
|
1551
1589
|
input.unilateralRefundWithoutReceiverDelay
|
|
1552
1590
|
)
|
|
1553
1591
|
};
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
default:
|
|
1561
|
-
throw new Error(
|
|
1562
|
-
`Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
})();
|
|
1592
|
+
if (input.arkadeHtlcScriptVersion !== void 0 && input.arkadeHtlcScriptVersion !== ARKADE_HTLC_SCRIPT_VERSION_STRICT) {
|
|
1593
|
+
throw new Error(
|
|
1594
|
+
`Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
const vhtlc = new StrictVhtlcScript(params);
|
|
1566
1598
|
return {
|
|
1567
1599
|
ledger: "arkade",
|
|
1568
1600
|
script: hex5.encode(vhtlc.pkScript),
|
|
@@ -1759,47 +1791,6 @@ function swapToTracked(stored) {
|
|
|
1759
1791
|
serverRefundLocktime: ms(r.vhtlc_refund_locktime)
|
|
1760
1792
|
};
|
|
1761
1793
|
// ─── Lightning: one on-chain leg, one off-chain LN payment ───────────────
|
|
1762
|
-
// Receive-on-Lightning: the client funds an on-chain HTLC and is paid over
|
|
1763
|
-
// Lightning. No server leg to watch — the server sweeping the client's leg
|
|
1764
|
-
// (with the preimage it got by paying the invoice) is the done signal.
|
|
1765
|
-
// Client funds the Arkade VHTLC; Boltz pays the invoice. The deposit is
|
|
1766
|
-
// `boltz_amount_sats` (source minus fees) — the amount actually locked, not
|
|
1767
|
-
// `source_amount` — else the client's own funding reads as under-funded.
|
|
1768
|
-
case "arkade_to_lightning":
|
|
1769
|
-
return {
|
|
1770
|
-
swapId: r.id,
|
|
1771
|
-
clientHtlc: arkadeLeg(
|
|
1772
|
-
r,
|
|
1773
|
-
r.arkade_vhtlc_address,
|
|
1774
|
-
Number(r.boltz_amount_sats)
|
|
1775
|
-
),
|
|
1776
|
-
clientRefundLocktime: ms(r.vhtlc_refund_locktime),
|
|
1777
|
-
serverRefundLocktime: 0
|
|
1778
|
-
// no on-chain server leg
|
|
1779
|
-
};
|
|
1780
|
-
// Client funds the EVM HTLC (server claims it after paying the invoice).
|
|
1781
|
-
case "evm_to_lightning":
|
|
1782
|
-
return {
|
|
1783
|
-
swapId: r.id,
|
|
1784
|
-
clientHtlc: evmLeg({
|
|
1785
|
-
chainId: r.evm_chain_id,
|
|
1786
|
-
htlc: r.evm_htlc_address,
|
|
1787
|
-
hashLock: r.hash_lock,
|
|
1788
|
-
claimAddress: r.server_evm_address,
|
|
1789
|
-
// the server claims the client's leg
|
|
1790
|
-
expectedSats: r.evm_expected_sats,
|
|
1791
|
-
// evm_to_lightning doesn't expose the locked token — fall back to the
|
|
1792
|
-
// per-chain mainnet constant so the isActive tuple is complete.
|
|
1793
|
-
token: lockedTokenFallback(r.evm_chain_id, r.network),
|
|
1794
|
-
sender: r.client_evm_address,
|
|
1795
|
-
// the client funded it
|
|
1796
|
-
timelockSec: r.evm_refund_locktime,
|
|
1797
|
-
createdAt: r.created_at
|
|
1798
|
-
}),
|
|
1799
|
-
clientRefundLocktime: ms(r.evm_refund_locktime),
|
|
1800
|
-
serverRefundLocktime: 0
|
|
1801
|
-
// no on-chain server leg
|
|
1802
|
-
};
|
|
1803
1794
|
// Pay-on-Lightning: the client pays a Lightning invoice (off-chain, nothing to
|
|
1804
1795
|
// watch) and claims an on-chain HTLC. No client-funded leg — the client's
|
|
1805
1796
|
// claim completes the swap; a hold invoice that never settles auto-unwinds.
|
|
@@ -1816,27 +1807,6 @@ function swapToTracked(stored) {
|
|
|
1816
1807
|
// no on-chain client leg
|
|
1817
1808
|
serverRefundLocktime: ms(r.vhtlc_refund_locktime)
|
|
1818
1809
|
};
|
|
1819
|
-
// Client pays the invoice, then claims the EVM HTLC.
|
|
1820
|
-
case "lightning_to_evm":
|
|
1821
|
-
return {
|
|
1822
|
-
swapId: r.id,
|
|
1823
|
-
serverHtlc: evmLeg({
|
|
1824
|
-
chainId: r.evm_chain_id,
|
|
1825
|
-
htlc: r.evm_htlc_address,
|
|
1826
|
-
hashLock: r.hash_lock,
|
|
1827
|
-
claimAddress: r.client_evm_address,
|
|
1828
|
-
// the client claims the server's leg
|
|
1829
|
-
expectedSats: r.evm_expected_sats,
|
|
1830
|
-
token: r.wbtc_address,
|
|
1831
|
-
sender: r.server_evm_address,
|
|
1832
|
-
// the server funded it
|
|
1833
|
-
timelockSec: r.evm_refund_locktime,
|
|
1834
|
-
createdAt: r.created_at
|
|
1835
|
-
}),
|
|
1836
|
-
clientRefundLocktime: 0,
|
|
1837
|
-
// no on-chain client leg
|
|
1838
|
-
serverRefundLocktime: ms(r.evm_refund_locktime)
|
|
1839
|
-
};
|
|
1840
1810
|
default:
|
|
1841
1811
|
return void 0;
|
|
1842
1812
|
}
|
|
@@ -2347,12 +2317,6 @@ var Client = class {
|
|
|
2347
2317
|
);
|
|
2348
2318
|
}
|
|
2349
2319
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2350
|
-
createLightningToEvmSwapGeneric(...args) {
|
|
2351
|
-
return this.#trackAfterCreate(
|
|
2352
|
-
this.#legacy.createLightningToEvmSwapGeneric(...args)
|
|
2353
|
-
);
|
|
2354
|
-
}
|
|
2355
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2356
2320
|
createBitcoinToEvmSwap(...args) {
|
|
2357
2321
|
return this.#trackAfterCreate(this.#legacy.createBitcoinToEvmSwap(...args));
|
|
2358
2322
|
}
|
|
@@ -2369,22 +2333,6 @@ var Client = class {
|
|
|
2369
2333
|
);
|
|
2370
2334
|
}
|
|
2371
2335
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2372
|
-
createArkadeToLightningSwap(...args) {
|
|
2373
|
-
return this.#trackAfterCreate(
|
|
2374
|
-
this.#legacy.createArkadeToLightningSwap(...args)
|
|
2375
|
-
);
|
|
2376
|
-
}
|
|
2377
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2378
|
-
getArkadeToLightningQuote(...args) {
|
|
2379
|
-
return this.#legacy.getArkadeToLightningQuote(...args);
|
|
2380
|
-
}
|
|
2381
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2382
|
-
retryArkadeToLightningSwap(...args) {
|
|
2383
|
-
return this.#trackAfterRetry(
|
|
2384
|
-
this.#legacy.retryArkadeToLightningSwap(...args)
|
|
2385
|
-
);
|
|
2386
|
-
}
|
|
2387
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2388
2336
|
createEvmToArkadeSwapGeneric(...args) {
|
|
2389
2337
|
return this.#trackAfterCreate(
|
|
2390
2338
|
this.#legacy.createEvmToArkadeSwapGeneric(...args)
|
|
@@ -2395,12 +2343,6 @@ var Client = class {
|
|
|
2395
2343
|
return this.#trackAfterCreate(this.#legacy.createEvmToBitcoinSwap(...args));
|
|
2396
2344
|
}
|
|
2397
2345
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2398
|
-
createEvmToLightningSwapGeneric(...args) {
|
|
2399
|
-
return this.#trackAfterCreate(
|
|
2400
|
-
this.#legacy.createEvmToLightningSwapGeneric(...args)
|
|
2401
|
-
);
|
|
2402
|
-
}
|
|
2403
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2404
2346
|
getCoordinatorFundingCallDataPermit2(...args) {
|
|
2405
2347
|
return this.#legacy.getCoordinatorFundingCallDataPermit2(...args);
|
|
2406
2348
|
}
|
|
@@ -2520,7 +2462,17 @@ var Client = class {
|
|
|
2520
2462
|
/** Every stored swap whose ledgers are observable, mapped to a {@link TrackedSwap}. */
|
|
2521
2463
|
async #loadTrackedSwaps() {
|
|
2522
2464
|
const swaps = await this.listAllSwaps();
|
|
2523
|
-
return swaps.filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status)).map(
|
|
2465
|
+
return swaps.filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status)).map((s) => {
|
|
2466
|
+
try {
|
|
2467
|
+
return swapToTracked(s);
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
console.warn(
|
|
2470
|
+
`Client: skipping untrackable stored swap ${s.response.id}:`,
|
|
2471
|
+
error
|
|
2472
|
+
);
|
|
2473
|
+
return void 0;
|
|
2474
|
+
}
|
|
2475
|
+
}).filter((s) => s !== void 0);
|
|
2524
2476
|
}
|
|
2525
2477
|
/**
|
|
2526
2478
|
* Run a create, then fold the new swap into tracking.
|
|
@@ -2640,7 +2592,14 @@ var Client = class {
|
|
|
2640
2592
|
return this.#managers;
|
|
2641
2593
|
}
|
|
2642
2594
|
const managers = /* @__PURE__ */ new Map();
|
|
2643
|
-
const {
|
|
2595
|
+
const {
|
|
2596
|
+
arkadeServerUrl,
|
|
2597
|
+
evmRpcUrls,
|
|
2598
|
+
esploraUrl,
|
|
2599
|
+
electrumWsUrl,
|
|
2600
|
+
bitcoinNetwork,
|
|
2601
|
+
bitcoinMinConfirmations
|
|
2602
|
+
} = this.#tracking;
|
|
2644
2603
|
const chainTime = async () => (await this.getMtp()).mtp * 1e3;
|
|
2645
2604
|
const arkadeUrl = arkadeServerUrl ?? defaultArkadeServerUrl("bitcoin");
|
|
2646
2605
|
if (arkadeUrl) {
|
|
@@ -2652,10 +2611,13 @@ var Client = class {
|
|
|
2652
2611
|
const readers = defaultEvmReaders(evmRpcUrls);
|
|
2653
2612
|
if (readers.size > 0)
|
|
2654
2613
|
managers.set("evm", EvmContractManager.fromDeps({ readers }));
|
|
2614
|
+
const network = bitcoinNetwork ?? "mainnet";
|
|
2655
2615
|
managers.set(
|
|
2656
2616
|
"bitcoin",
|
|
2657
2617
|
await BitcoinContractManager.create({
|
|
2658
2618
|
esploraUrl: esploraUrl ? [esploraUrl] : DEFAULT_ESPLORA_URLS,
|
|
2619
|
+
electrumWsUrl: electrumWsUrl ?? DEFAULT_ELECTRUM_WS_URLS[network],
|
|
2620
|
+
network,
|
|
2659
2621
|
chainTime,
|
|
2660
2622
|
minConfirmations: bitcoinMinConfirmations
|
|
2661
2623
|
})
|
|
@@ -2672,6 +2634,8 @@ var ClientBuilder = class {
|
|
|
2672
2634
|
#serverUrl;
|
|
2673
2635
|
#arkadeServerUrl;
|
|
2674
2636
|
#esploraUrl;
|
|
2637
|
+
#electrumWsUrl;
|
|
2638
|
+
#bitcoinNetwork;
|
|
2675
2639
|
#bitcoinMinConfirmations;
|
|
2676
2640
|
#evmRpcUrls;
|
|
2677
2641
|
#managers;
|
|
@@ -2737,6 +2701,28 @@ var ClientBuilder = class {
|
|
|
2737
2701
|
this.#esploraUrl = url;
|
|
2738
2702
|
return this;
|
|
2739
2703
|
}
|
|
2704
|
+
/**
|
|
2705
|
+
* Set an Electrum server WebSocket URL (a Fulcrum `ws`/`wss` port) for
|
|
2706
|
+
* Bitcoin claim/refund chain access. When set, the underlying client
|
|
2707
|
+
* prefers it over Esplora for lookups and broadcasts, and waits for HTLC
|
|
2708
|
+
* fundings via `blockchain.scripthash.subscribe` push instead of polling.
|
|
2709
|
+
* Esplora remains the fallback whenever the Electrum server errors.
|
|
2710
|
+
*/
|
|
2711
|
+
withElectrumWsUrl(url) {
|
|
2712
|
+
this.#inner.withElectrumWsUrl(url);
|
|
2713
|
+
this.#electrumWsUrl = url;
|
|
2714
|
+
return this;
|
|
2715
|
+
}
|
|
2716
|
+
/**
|
|
2717
|
+
* Declare the Bitcoin network of this deployment's HTLC addresses. Only
|
|
2718
|
+
* needed together with {@link withElectrumWsUrl} on a non-mainnet
|
|
2719
|
+
* deployment — the Electrum reader decodes addresses to scripthashes and
|
|
2720
|
+
* defaults to mainnet parameters.
|
|
2721
|
+
*/
|
|
2722
|
+
withBitcoinNetwork(network) {
|
|
2723
|
+
this.#bitcoinNetwork = network;
|
|
2724
|
+
return this;
|
|
2725
|
+
}
|
|
2740
2726
|
/**
|
|
2741
2727
|
* Confirmations a Bitcoin funding tx needs before it observes as `confirmed`
|
|
2742
2728
|
* (gating e.g. the evm→bitcoin claim). Default `0`: claim as soon as the
|
|
@@ -2793,6 +2779,8 @@ var ClientBuilder = class {
|
|
|
2793
2779
|
managers: this.#managers,
|
|
2794
2780
|
arkadeServerUrl: this.#arkadeServerUrl,
|
|
2795
2781
|
esploraUrl: this.#esploraUrl,
|
|
2782
|
+
electrumWsUrl: this.#electrumWsUrl,
|
|
2783
|
+
bitcoinNetwork: this.#bitcoinNetwork,
|
|
2796
2784
|
bitcoinMinConfirmations: this.#bitcoinMinConfirmations,
|
|
2797
2785
|
evmRpcUrls: this.#evmRpcUrls,
|
|
2798
2786
|
autoClaim: this.#autoClaim
|
|
@@ -2828,6 +2816,7 @@ export {
|
|
|
2828
2816
|
defaultEvmReaders,
|
|
2829
2817
|
deriveSwapActions,
|
|
2830
2818
|
deriveSwapStatus,
|
|
2819
|
+
electrumReader,
|
|
2831
2820
|
esploraReader,
|
|
2832
2821
|
evmReaderFromClient,
|
|
2833
2822
|
htlcFactsFromEsploraTxs,
|