@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.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __esm = (fn, res) => function __init() {
|
|
7
9
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
@@ -19,6 +21,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
21
|
return to;
|
|
20
22
|
};
|
|
21
23
|
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
22
32
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
23
33
|
|
|
24
34
|
// src/contracts/bitcoin-reader-esplora.ts
|
|
@@ -108,6 +118,157 @@ var init_bitcoin_reader_esplora = __esm({
|
|
|
108
118
|
}
|
|
109
119
|
});
|
|
110
120
|
|
|
121
|
+
// src/contracts/bitcoin-reader-electrum.ts
|
|
122
|
+
var bitcoin_reader_electrum_exports = {};
|
|
123
|
+
__export(bitcoin_reader_electrum_exports, {
|
|
124
|
+
electrumReader: () => electrumReader
|
|
125
|
+
});
|
|
126
|
+
function toBtcSignerNetwork(network) {
|
|
127
|
+
switch (network) {
|
|
128
|
+
case "mainnet":
|
|
129
|
+
return btc.NETWORK;
|
|
130
|
+
case "testnet":
|
|
131
|
+
case "signet":
|
|
132
|
+
return btc.TEST_NETWORK;
|
|
133
|
+
case "regtest":
|
|
134
|
+
return REGTEST_NETWORK;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function scriptHashOf(script) {
|
|
138
|
+
return import_base5.hex.encode((0, import_sha22.sha256)(script).reverse());
|
|
139
|
+
}
|
|
140
|
+
function electrumReader(client, opts) {
|
|
141
|
+
const network = opts?.network ?? "mainnet";
|
|
142
|
+
const defaultMinConf = opts?.minConfirmations ?? 0;
|
|
143
|
+
const fallback2 = opts?.fallback;
|
|
144
|
+
const scriptFor = (address) => btc.OutScript.encode(
|
|
145
|
+
btc.Address(toBtcSignerNetwork(network)).decode(address)
|
|
146
|
+
);
|
|
147
|
+
async function getHtlcFactsElectrum(address, required) {
|
|
148
|
+
const script = scriptFor(address);
|
|
149
|
+
const scripthash = scriptHashOf(script);
|
|
150
|
+
const history = await client.request(
|
|
151
|
+
"blockchain.scripthash.get_history",
|
|
152
|
+
[scripthash]
|
|
153
|
+
);
|
|
154
|
+
if (!history || history.length === 0)
|
|
155
|
+
return { funding: "absent", fundedSats: 0 };
|
|
156
|
+
const txs = await Promise.all(
|
|
157
|
+
history.map(async (entry) => {
|
|
158
|
+
const raw = await client.request("blockchain.transaction.get", [
|
|
159
|
+
entry.tx_hash
|
|
160
|
+
]);
|
|
161
|
+
return {
|
|
162
|
+
entry,
|
|
163
|
+
tx: btc.Transaction.fromRaw(import_base5.hex.decode(raw), {
|
|
164
|
+
allowUnknownOutputs: true,
|
|
165
|
+
allowUnknownInputs: true,
|
|
166
|
+
disableScriptCheck: true
|
|
167
|
+
})
|
|
168
|
+
};
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
const scriptHex = import_base5.hex.encode(script);
|
|
172
|
+
const outputIndicesPaying = (tx) => {
|
|
173
|
+
const indices = [];
|
|
174
|
+
for (let i = 0; i < tx.outputsLength; i++) {
|
|
175
|
+
const out = tx.getOutput(i);
|
|
176
|
+
if (out.script && import_base5.hex.encode(out.script) === scriptHex) indices.push(i);
|
|
177
|
+
}
|
|
178
|
+
return indices;
|
|
179
|
+
};
|
|
180
|
+
const funding = txs.find(({ tx }) => outputIndicesPaying(tx).length > 0);
|
|
181
|
+
if (!funding) return { funding: "absent", fundedSats: 0 };
|
|
182
|
+
const fundedVouts = outputIndicesPaying(funding.tx);
|
|
183
|
+
for (const { tx } of txs) {
|
|
184
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
185
|
+
const input = tx.getInput(i);
|
|
186
|
+
if (input.txid && import_base5.hex.encode(input.txid) === funding.entry.tx_hash && input.index !== void 0 && fundedVouts.includes(input.index)) {
|
|
187
|
+
return {
|
|
188
|
+
funding: "confirmed",
|
|
189
|
+
fundedSats: 0,
|
|
190
|
+
// already spent — the amount no longer matters
|
|
191
|
+
spendWitness: (input.finalScriptWitness ?? []).map(
|
|
192
|
+
(item) => import_base5.hex.encode(item)
|
|
193
|
+
)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
let fundedSats = 0;
|
|
199
|
+
for (const i of fundedVouts)
|
|
200
|
+
fundedSats += Number(funding.tx.getOutput(i).amount ?? 0n);
|
|
201
|
+
const height = funding.entry.height;
|
|
202
|
+
const isConfirmed = height > 0;
|
|
203
|
+
let deepEnough;
|
|
204
|
+
if (isConfirmed) {
|
|
205
|
+
if (required <= 1) {
|
|
206
|
+
deepEnough = true;
|
|
207
|
+
} else {
|
|
208
|
+
const tip = await client.request(
|
|
209
|
+
"blockchain.headers.subscribe",
|
|
210
|
+
[]
|
|
211
|
+
);
|
|
212
|
+
deepEnough = tip.height - height + 1 >= required;
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
deepEnough = required === 0;
|
|
216
|
+
}
|
|
217
|
+
return { funding: deepEnough ? "confirmed" : "mempool", fundedSats };
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
async getHtlcFacts(address, minConfirmations) {
|
|
221
|
+
const required = minConfirmations ?? defaultMinConf;
|
|
222
|
+
try {
|
|
223
|
+
return await getHtlcFactsElectrum(address, required);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (!fallback2) throw error;
|
|
226
|
+
console.warn(
|
|
227
|
+
"electrumReader: falling back to secondary reader:",
|
|
228
|
+
error instanceof Error ? error.message : error
|
|
229
|
+
);
|
|
230
|
+
return fallback2.getHtlcFacts(address, minConfirmations);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
subscribe(address, onChange) {
|
|
234
|
+
let scripthash;
|
|
235
|
+
try {
|
|
236
|
+
scripthash = scriptHashOf(scriptFor(address));
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.warn(
|
|
239
|
+
"electrumReader: subscribe skipped (address decode failed):",
|
|
240
|
+
error instanceof Error ? error.message : error
|
|
241
|
+
);
|
|
242
|
+
return () => {
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const handler = () => onChange();
|
|
246
|
+
client.subscribeScriptHash(scripthash, handler).catch((error) => {
|
|
247
|
+
console.warn(
|
|
248
|
+
"electrumReader: scripthash subscribe failed:",
|
|
249
|
+
error instanceof Error ? error.message : error
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
return () => client.unsubscribeScriptHash(scripthash, handler);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
var import_sha22, import_base5, btc, REGTEST_NETWORK;
|
|
257
|
+
var init_bitcoin_reader_electrum = __esm({
|
|
258
|
+
"src/contracts/bitcoin-reader-electrum.ts"() {
|
|
259
|
+
"use strict";
|
|
260
|
+
import_sha22 = require("@noble/hashes/sha2.js");
|
|
261
|
+
import_base5 = require("@scure/base");
|
|
262
|
+
btc = __toESM(require("@scure/btc-signer"), 1);
|
|
263
|
+
REGTEST_NETWORK = {
|
|
264
|
+
bech32: "bcrt",
|
|
265
|
+
pubKeyHash: 111,
|
|
266
|
+
scriptHash: 196,
|
|
267
|
+
wif: 239
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
111
272
|
// src/index.ts
|
|
112
273
|
var src_exports = {};
|
|
113
274
|
__export(src_exports, {
|
|
@@ -127,6 +288,7 @@ __export(src_exports, {
|
|
|
127
288
|
defaultEvmReaders: () => defaultEvmReaders,
|
|
128
289
|
deriveSwapActions: () => deriveSwapActions,
|
|
129
290
|
deriveSwapStatus: () => deriveSwapStatus,
|
|
291
|
+
electrumReader: () => electrumReader,
|
|
130
292
|
esploraReader: () => esploraReader,
|
|
131
293
|
evmReaderFromClient: () => evmReaderFromClient,
|
|
132
294
|
htlcFactsFromEsploraTxs: () => htlcFactsFromEsploraTxs,
|
|
@@ -468,10 +630,12 @@ function arkadeObservation(facts) {
|
|
|
468
630
|
}
|
|
469
631
|
function classifyArkadeSpend(encodedSpendTx, paymentHash) {
|
|
470
632
|
const tx = import_sdk.Transaction.fromPSBT(import_base2.base64.decode(encodedSpendTx));
|
|
471
|
-
for (
|
|
472
|
-
for (const
|
|
473
|
-
|
|
474
|
-
|
|
633
|
+
for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {
|
|
634
|
+
for (const witness of (0, import_sdk.getArkPsbtFields)(tx, inputIndex, import_sdk.ConditionWitness)) {
|
|
635
|
+
for (const element of witness) {
|
|
636
|
+
if (element.length === 32 && preimageMatches(element, paymentHash)) {
|
|
637
|
+
return { spend: "claim", preimage: element };
|
|
638
|
+
}
|
|
475
639
|
}
|
|
476
640
|
}
|
|
477
641
|
}
|
|
@@ -640,7 +804,7 @@ function defaultArkadeServerUrl(network) {
|
|
|
640
804
|
}
|
|
641
805
|
|
|
642
806
|
// src/contracts/bitcoin-manager.ts
|
|
643
|
-
var
|
|
807
|
+
var import_base6 = require("@scure/base");
|
|
644
808
|
|
|
645
809
|
// src/contracts/bitcoin.ts
|
|
646
810
|
var import_base4 = require("@scure/base");
|
|
@@ -675,6 +839,8 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
675
839
|
#obs = /* @__PURE__ */ new Map();
|
|
676
840
|
/** address → the preimage a claim revealed. */
|
|
677
841
|
#preimages = /* @__PURE__ */ new Map();
|
|
842
|
+
/** address → push-subscription teardown (only when the reader supports push). */
|
|
843
|
+
#subscriptions = /* @__PURE__ */ new Map();
|
|
678
844
|
#listeners = /* @__PURE__ */ new Set();
|
|
679
845
|
/** Last MTP reading (ms) + when it was fetched, for extrapolation. */
|
|
680
846
|
#now;
|
|
@@ -687,10 +853,24 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
687
853
|
}
|
|
688
854
|
static async create(config) {
|
|
689
855
|
const { esploraReader: esploraReader2 } = await Promise.resolve().then(() => (init_bitcoin_reader_esplora(), bitcoin_reader_esplora_exports));
|
|
856
|
+
let reader = esploraReader2(
|
|
857
|
+
config.esploraUrl,
|
|
858
|
+
void 0,
|
|
859
|
+
{ minConfirmations: config.minConfirmations }
|
|
860
|
+
);
|
|
861
|
+
if (config.electrumWsUrl) {
|
|
862
|
+
const [{ electrumReader: electrumReader2 }, { ElectrumWsClient }] = await Promise.all([
|
|
863
|
+
Promise.resolve().then(() => (init_bitcoin_reader_electrum(), bitcoin_reader_electrum_exports)),
|
|
864
|
+
import("@lendasat/lendaswap-sdk-pure")
|
|
865
|
+
]);
|
|
866
|
+
reader = electrumReader2(new ElectrumWsClient(config.electrumWsUrl), {
|
|
867
|
+
network: config.network,
|
|
868
|
+
minConfirmations: config.minConfirmations,
|
|
869
|
+
fallback: reader
|
|
870
|
+
});
|
|
871
|
+
}
|
|
690
872
|
return _BitcoinContractManager.fromDeps({
|
|
691
|
-
reader
|
|
692
|
-
minConfirmations: config.minConfirmations
|
|
693
|
-
}),
|
|
873
|
+
reader,
|
|
694
874
|
chainTime: config.chainTime
|
|
695
875
|
});
|
|
696
876
|
}
|
|
@@ -703,10 +883,25 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
703
883
|
`BitcoinContractManager can't track a '${ref.ledger}' HTLC`
|
|
704
884
|
);
|
|
705
885
|
this.#refs.set(ref.address, ref);
|
|
886
|
+
if (this.#reader.subscribe && !this.#subscriptions.has(ref.address)) {
|
|
887
|
+
this.#subscriptions.set(
|
|
888
|
+
ref.address,
|
|
889
|
+
this.#reader.subscribe(ref.address, () => {
|
|
890
|
+
this.#reconcileRef(ref).catch((error) => {
|
|
891
|
+
console.warn(
|
|
892
|
+
"BitcoinContractManager: push reconcile failed:",
|
|
893
|
+
error instanceof Error ? error.message : error
|
|
894
|
+
);
|
|
895
|
+
});
|
|
896
|
+
})
|
|
897
|
+
);
|
|
898
|
+
}
|
|
706
899
|
await this.#reconcileRef(ref);
|
|
707
900
|
}
|
|
708
901
|
async unregister(ref) {
|
|
709
902
|
if (ref.ledger !== "bitcoin") return;
|
|
903
|
+
this.#subscriptions.get(ref.address)?.();
|
|
904
|
+
this.#subscriptions.delete(ref.address);
|
|
710
905
|
this.#refs.delete(ref.address);
|
|
711
906
|
this.#obs.delete(ref.address);
|
|
712
907
|
this.#preimages.delete(ref.address);
|
|
@@ -741,6 +936,8 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
741
936
|
this.#now = { mtpMs: await this.#chainTime(), fetchedAtMs: Date.now() };
|
|
742
937
|
}
|
|
743
938
|
dispose() {
|
|
939
|
+
for (const unsubscribe of this.#subscriptions.values()) unsubscribe();
|
|
940
|
+
this.#subscriptions.clear();
|
|
744
941
|
this.#listeners.clear();
|
|
745
942
|
}
|
|
746
943
|
/** The preimage a claim revealed on this HTLC, if one was seen. */
|
|
@@ -754,7 +951,7 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
754
951
|
);
|
|
755
952
|
const { observation, preimage } = bitcoinObservation(
|
|
756
953
|
facts,
|
|
757
|
-
|
|
954
|
+
import_base6.hex.decode(ref.preimageHash),
|
|
758
955
|
ref.expectedSats
|
|
759
956
|
);
|
|
760
957
|
if (preimage) this.#preimages.set(ref.address, preimage);
|
|
@@ -1639,19 +1836,19 @@ function defaultWebSocketFactory(url) {
|
|
|
1639
1836
|
var import_sdk3 = require("@arkade-os/sdk");
|
|
1640
1837
|
var import_lendaswap_sdk_pure = require("@lendasat/lendaswap-sdk-pure");
|
|
1641
1838
|
var import_legacy2 = require("@noble/hashes/legacy.js");
|
|
1642
|
-
var
|
|
1839
|
+
var import_base7 = require("@scure/base");
|
|
1643
1840
|
function strip0x(value) {
|
|
1644
1841
|
return value.startsWith("0x") ? value.slice(2) : value;
|
|
1645
1842
|
}
|
|
1646
1843
|
function xOnly(pubKeyHex) {
|
|
1647
|
-
const bytes =
|
|
1844
|
+
const bytes = import_base7.hex.decode(strip0x(pubKeyHex));
|
|
1648
1845
|
if (bytes.length === 33) return bytes.slice(1);
|
|
1649
1846
|
if (bytes.length === 32) return bytes;
|
|
1650
1847
|
throw new Error(`invalid public key length: ${bytes.length}`);
|
|
1651
1848
|
}
|
|
1652
1849
|
var seconds = (value) => ({ type: "seconds", value: BigInt(value) });
|
|
1653
1850
|
function buildArkadeVhtlcRef(input) {
|
|
1654
|
-
const hashLockBytes =
|
|
1851
|
+
const hashLockBytes = import_base7.hex.decode(strip0x(input.hashLock));
|
|
1655
1852
|
const params = {
|
|
1656
1853
|
sender: xOnly(input.senderPk),
|
|
1657
1854
|
receiver: xOnly(input.receiverPk),
|
|
@@ -1664,21 +1861,15 @@ function buildArkadeVhtlcRef(input) {
|
|
|
1664
1861
|
input.unilateralRefundWithoutReceiverDelay
|
|
1665
1862
|
)
|
|
1666
1863
|
};
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
default:
|
|
1674
|
-
throw new Error(
|
|
1675
|
-
`Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`
|
|
1676
|
-
);
|
|
1677
|
-
}
|
|
1678
|
-
})();
|
|
1864
|
+
if (input.arkadeHtlcScriptVersion !== void 0 && input.arkadeHtlcScriptVersion !== import_lendaswap_sdk_pure.ARKADE_HTLC_SCRIPT_VERSION_STRICT) {
|
|
1865
|
+
throw new Error(
|
|
1866
|
+
`Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
const vhtlc = new import_lendaswap_sdk_pure.StrictVhtlcScript(params);
|
|
1679
1870
|
return {
|
|
1680
1871
|
ledger: "arkade",
|
|
1681
|
-
script:
|
|
1872
|
+
script: import_base7.hex.encode(vhtlc.pkScript),
|
|
1682
1873
|
address: input.address,
|
|
1683
1874
|
preimageHash: strip0x(input.hashLock),
|
|
1684
1875
|
expectedSats: input.expectedSats,
|
|
@@ -1872,47 +2063,6 @@ function swapToTracked(stored) {
|
|
|
1872
2063
|
serverRefundLocktime: ms(r.vhtlc_refund_locktime)
|
|
1873
2064
|
};
|
|
1874
2065
|
// ─── Lightning: one on-chain leg, one off-chain LN payment ───────────────
|
|
1875
|
-
// Receive-on-Lightning: the client funds an on-chain HTLC and is paid over
|
|
1876
|
-
// Lightning. No server leg to watch — the server sweeping the client's leg
|
|
1877
|
-
// (with the preimage it got by paying the invoice) is the done signal.
|
|
1878
|
-
// Client funds the Arkade VHTLC; Boltz pays the invoice. The deposit is
|
|
1879
|
-
// `boltz_amount_sats` (source minus fees) — the amount actually locked, not
|
|
1880
|
-
// `source_amount` — else the client's own funding reads as under-funded.
|
|
1881
|
-
case "arkade_to_lightning":
|
|
1882
|
-
return {
|
|
1883
|
-
swapId: r.id,
|
|
1884
|
-
clientHtlc: arkadeLeg(
|
|
1885
|
-
r,
|
|
1886
|
-
r.arkade_vhtlc_address,
|
|
1887
|
-
Number(r.boltz_amount_sats)
|
|
1888
|
-
),
|
|
1889
|
-
clientRefundLocktime: ms(r.vhtlc_refund_locktime),
|
|
1890
|
-
serverRefundLocktime: 0
|
|
1891
|
-
// no on-chain server leg
|
|
1892
|
-
};
|
|
1893
|
-
// Client funds the EVM HTLC (server claims it after paying the invoice).
|
|
1894
|
-
case "evm_to_lightning":
|
|
1895
|
-
return {
|
|
1896
|
-
swapId: r.id,
|
|
1897
|
-
clientHtlc: evmLeg({
|
|
1898
|
-
chainId: r.evm_chain_id,
|
|
1899
|
-
htlc: r.evm_htlc_address,
|
|
1900
|
-
hashLock: r.hash_lock,
|
|
1901
|
-
claimAddress: r.server_evm_address,
|
|
1902
|
-
// the server claims the client's leg
|
|
1903
|
-
expectedSats: r.evm_expected_sats,
|
|
1904
|
-
// evm_to_lightning doesn't expose the locked token — fall back to the
|
|
1905
|
-
// per-chain mainnet constant so the isActive tuple is complete.
|
|
1906
|
-
token: lockedTokenFallback(r.evm_chain_id, r.network),
|
|
1907
|
-
sender: r.client_evm_address,
|
|
1908
|
-
// the client funded it
|
|
1909
|
-
timelockSec: r.evm_refund_locktime,
|
|
1910
|
-
createdAt: r.created_at
|
|
1911
|
-
}),
|
|
1912
|
-
clientRefundLocktime: ms(r.evm_refund_locktime),
|
|
1913
|
-
serverRefundLocktime: 0
|
|
1914
|
-
// no on-chain server leg
|
|
1915
|
-
};
|
|
1916
2066
|
// Pay-on-Lightning: the client pays a Lightning invoice (off-chain, nothing to
|
|
1917
2067
|
// watch) and claims an on-chain HTLC. No client-funded leg — the client's
|
|
1918
2068
|
// claim completes the swap; a hold invoice that never settles auto-unwinds.
|
|
@@ -1929,27 +2079,6 @@ function swapToTracked(stored) {
|
|
|
1929
2079
|
// no on-chain client leg
|
|
1930
2080
|
serverRefundLocktime: ms(r.vhtlc_refund_locktime)
|
|
1931
2081
|
};
|
|
1932
|
-
// Client pays the invoice, then claims the EVM HTLC.
|
|
1933
|
-
case "lightning_to_evm":
|
|
1934
|
-
return {
|
|
1935
|
-
swapId: r.id,
|
|
1936
|
-
serverHtlc: evmLeg({
|
|
1937
|
-
chainId: r.evm_chain_id,
|
|
1938
|
-
htlc: r.evm_htlc_address,
|
|
1939
|
-
hashLock: r.hash_lock,
|
|
1940
|
-
claimAddress: r.client_evm_address,
|
|
1941
|
-
// the client claims the server's leg
|
|
1942
|
-
expectedSats: r.evm_expected_sats,
|
|
1943
|
-
token: r.wbtc_address,
|
|
1944
|
-
sender: r.server_evm_address,
|
|
1945
|
-
// the server funded it
|
|
1946
|
-
timelockSec: r.evm_refund_locktime,
|
|
1947
|
-
createdAt: r.created_at
|
|
1948
|
-
}),
|
|
1949
|
-
clientRefundLocktime: 0,
|
|
1950
|
-
// no on-chain client leg
|
|
1951
|
-
serverRefundLocktime: ms(r.evm_refund_locktime)
|
|
1952
|
-
};
|
|
1953
2082
|
default:
|
|
1954
2083
|
return void 0;
|
|
1955
2084
|
}
|
|
@@ -2460,12 +2589,6 @@ var Client = class {
|
|
|
2460
2589
|
);
|
|
2461
2590
|
}
|
|
2462
2591
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2463
|
-
createLightningToEvmSwapGeneric(...args) {
|
|
2464
|
-
return this.#trackAfterCreate(
|
|
2465
|
-
this.#legacy.createLightningToEvmSwapGeneric(...args)
|
|
2466
|
-
);
|
|
2467
|
-
}
|
|
2468
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2469
2592
|
createBitcoinToEvmSwap(...args) {
|
|
2470
2593
|
return this.#trackAfterCreate(this.#legacy.createBitcoinToEvmSwap(...args));
|
|
2471
2594
|
}
|
|
@@ -2482,22 +2605,6 @@ var Client = class {
|
|
|
2482
2605
|
);
|
|
2483
2606
|
}
|
|
2484
2607
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2485
|
-
createArkadeToLightningSwap(...args) {
|
|
2486
|
-
return this.#trackAfterCreate(
|
|
2487
|
-
this.#legacy.createArkadeToLightningSwap(...args)
|
|
2488
|
-
);
|
|
2489
|
-
}
|
|
2490
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2491
|
-
getArkadeToLightningQuote(...args) {
|
|
2492
|
-
return this.#legacy.getArkadeToLightningQuote(...args);
|
|
2493
|
-
}
|
|
2494
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2495
|
-
retryArkadeToLightningSwap(...args) {
|
|
2496
|
-
return this.#trackAfterRetry(
|
|
2497
|
-
this.#legacy.retryArkadeToLightningSwap(...args)
|
|
2498
|
-
);
|
|
2499
|
-
}
|
|
2500
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2501
2608
|
createEvmToArkadeSwapGeneric(...args) {
|
|
2502
2609
|
return this.#trackAfterCreate(
|
|
2503
2610
|
this.#legacy.createEvmToArkadeSwapGeneric(...args)
|
|
@@ -2508,12 +2615,6 @@ var Client = class {
|
|
|
2508
2615
|
return this.#trackAfterCreate(this.#legacy.createEvmToBitcoinSwap(...args));
|
|
2509
2616
|
}
|
|
2510
2617
|
/** Delegated to the legacy client (migration checkpoint). */
|
|
2511
|
-
createEvmToLightningSwapGeneric(...args) {
|
|
2512
|
-
return this.#trackAfterCreate(
|
|
2513
|
-
this.#legacy.createEvmToLightningSwapGeneric(...args)
|
|
2514
|
-
);
|
|
2515
|
-
}
|
|
2516
|
-
/** Delegated to the legacy client (migration checkpoint). */
|
|
2517
2618
|
getCoordinatorFundingCallDataPermit2(...args) {
|
|
2518
2619
|
return this.#legacy.getCoordinatorFundingCallDataPermit2(...args);
|
|
2519
2620
|
}
|
|
@@ -2633,7 +2734,17 @@ var Client = class {
|
|
|
2633
2734
|
/** Every stored swap whose ledgers are observable, mapped to a {@link TrackedSwap}. */
|
|
2634
2735
|
async #loadTrackedSwaps() {
|
|
2635
2736
|
const swaps = await this.listAllSwaps();
|
|
2636
|
-
return swaps.filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status)).map(
|
|
2737
|
+
return swaps.filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status)).map((s) => {
|
|
2738
|
+
try {
|
|
2739
|
+
return swapToTracked(s);
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
console.warn(
|
|
2742
|
+
`Client: skipping untrackable stored swap ${s.response.id}:`,
|
|
2743
|
+
error
|
|
2744
|
+
);
|
|
2745
|
+
return void 0;
|
|
2746
|
+
}
|
|
2747
|
+
}).filter((s) => s !== void 0);
|
|
2637
2748
|
}
|
|
2638
2749
|
/**
|
|
2639
2750
|
* Run a create, then fold the new swap into tracking.
|
|
@@ -2753,7 +2864,14 @@ var Client = class {
|
|
|
2753
2864
|
return this.#managers;
|
|
2754
2865
|
}
|
|
2755
2866
|
const managers = /* @__PURE__ */ new Map();
|
|
2756
|
-
const {
|
|
2867
|
+
const {
|
|
2868
|
+
arkadeServerUrl,
|
|
2869
|
+
evmRpcUrls,
|
|
2870
|
+
esploraUrl,
|
|
2871
|
+
electrumWsUrl,
|
|
2872
|
+
bitcoinNetwork,
|
|
2873
|
+
bitcoinMinConfirmations
|
|
2874
|
+
} = this.#tracking;
|
|
2757
2875
|
const chainTime = async () => (await this.getMtp()).mtp * 1e3;
|
|
2758
2876
|
const arkadeUrl = arkadeServerUrl ?? defaultArkadeServerUrl("bitcoin");
|
|
2759
2877
|
if (arkadeUrl) {
|
|
@@ -2765,10 +2883,13 @@ var Client = class {
|
|
|
2765
2883
|
const readers = defaultEvmReaders(evmRpcUrls);
|
|
2766
2884
|
if (readers.size > 0)
|
|
2767
2885
|
managers.set("evm", EvmContractManager.fromDeps({ readers }));
|
|
2886
|
+
const network = bitcoinNetwork ?? "mainnet";
|
|
2768
2887
|
managers.set(
|
|
2769
2888
|
"bitcoin",
|
|
2770
2889
|
await BitcoinContractManager.create({
|
|
2771
2890
|
esploraUrl: esploraUrl ? [esploraUrl] : DEFAULT_ESPLORA_URLS,
|
|
2891
|
+
electrumWsUrl: electrumWsUrl ?? import_lendaswap_sdk_pure2.DEFAULT_ELECTRUM_WS_URLS[network],
|
|
2892
|
+
network,
|
|
2772
2893
|
chainTime,
|
|
2773
2894
|
minConfirmations: bitcoinMinConfirmations
|
|
2774
2895
|
})
|
|
@@ -2785,6 +2906,8 @@ var ClientBuilder = class {
|
|
|
2785
2906
|
#serverUrl;
|
|
2786
2907
|
#arkadeServerUrl;
|
|
2787
2908
|
#esploraUrl;
|
|
2909
|
+
#electrumWsUrl;
|
|
2910
|
+
#bitcoinNetwork;
|
|
2788
2911
|
#bitcoinMinConfirmations;
|
|
2789
2912
|
#evmRpcUrls;
|
|
2790
2913
|
#managers;
|
|
@@ -2850,6 +2973,28 @@ var ClientBuilder = class {
|
|
|
2850
2973
|
this.#esploraUrl = url;
|
|
2851
2974
|
return this;
|
|
2852
2975
|
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Set an Electrum server WebSocket URL (a Fulcrum `ws`/`wss` port) for
|
|
2978
|
+
* Bitcoin claim/refund chain access. When set, the underlying client
|
|
2979
|
+
* prefers it over Esplora for lookups and broadcasts, and waits for HTLC
|
|
2980
|
+
* fundings via `blockchain.scripthash.subscribe` push instead of polling.
|
|
2981
|
+
* Esplora remains the fallback whenever the Electrum server errors.
|
|
2982
|
+
*/
|
|
2983
|
+
withElectrumWsUrl(url) {
|
|
2984
|
+
this.#inner.withElectrumWsUrl(url);
|
|
2985
|
+
this.#electrumWsUrl = url;
|
|
2986
|
+
return this;
|
|
2987
|
+
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Declare the Bitcoin network of this deployment's HTLC addresses. Only
|
|
2990
|
+
* needed together with {@link withElectrumWsUrl} on a non-mainnet
|
|
2991
|
+
* deployment — the Electrum reader decodes addresses to scripthashes and
|
|
2992
|
+
* defaults to mainnet parameters.
|
|
2993
|
+
*/
|
|
2994
|
+
withBitcoinNetwork(network) {
|
|
2995
|
+
this.#bitcoinNetwork = network;
|
|
2996
|
+
return this;
|
|
2997
|
+
}
|
|
2853
2998
|
/**
|
|
2854
2999
|
* Confirmations a Bitcoin funding tx needs before it observes as `confirmed`
|
|
2855
3000
|
* (gating e.g. the evm→bitcoin claim). Default `0`: claim as soon as the
|
|
@@ -2906,6 +3051,8 @@ var ClientBuilder = class {
|
|
|
2906
3051
|
managers: this.#managers,
|
|
2907
3052
|
arkadeServerUrl: this.#arkadeServerUrl,
|
|
2908
3053
|
esploraUrl: this.#esploraUrl,
|
|
3054
|
+
electrumWsUrl: this.#electrumWsUrl,
|
|
3055
|
+
bitcoinNetwork: this.#bitcoinNetwork,
|
|
2909
3056
|
bitcoinMinConfirmations: this.#bitcoinMinConfirmations,
|
|
2910
3057
|
evmRpcUrls: this.#evmRpcUrls,
|
|
2911
3058
|
autoClaim: this.#autoClaim
|
|
@@ -2926,6 +3073,7 @@ function createdSwapId(result) {
|
|
|
2926
3073
|
}
|
|
2927
3074
|
|
|
2928
3075
|
// src/index.ts
|
|
3076
|
+
init_bitcoin_reader_electrum();
|
|
2929
3077
|
init_bitcoin_reader_esplora();
|
|
2930
3078
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2931
3079
|
0 && (module.exports = {
|
|
@@ -2945,6 +3093,7 @@ init_bitcoin_reader_esplora();
|
|
|
2945
3093
|
defaultEvmReaders,
|
|
2946
3094
|
deriveSwapActions,
|
|
2947
3095
|
deriveSwapStatus,
|
|
3096
|
+
electrumReader,
|
|
2948
3097
|
esploraReader,
|
|
2949
3098
|
evmReaderFromClient,
|
|
2950
3099
|
htlcFactsFromEsploraTxs,
|