@satora/swap 0.3.2 → 1.1.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/README.md +1 -1
- package/dist/bitcoin-reader-electrum-PY7DHD6T.js +7 -0
- package/dist/{bitcoin-reader-esplora-HV6CANAA.js → bitcoin-reader-esplora-ZLPMM5FF.js} +2 -2
- package/dist/bitcoin-reader-esplora-ZLPMM5FF.js.map +1 -0
- package/dist/chunk-FTPIQSRW.js +152 -0
- package/dist/chunk-FTPIQSRW.js.map +1 -0
- package/dist/{chunk-EIWELJJK.js → chunk-THI4UO6R.js} +11 -5
- package/dist/chunk-THI4UO6R.js.map +1 -0
- package/dist/index.cjs +295 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -12
- package/dist/index.d.ts +86 -12
- package/dist/index.js +116 -106
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-EIWELJJK.js.map +0 -1
- /package/dist/{bitcoin-reader-esplora-HV6CANAA.js.map → bitcoin-reader-electrum-PY7DHD6T.js.map} +0 -0
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
|
|
@@ -41,11 +51,17 @@ function htlcFactsFromEsploraTxs(txs, address, opts) {
|
|
|
41
51
|
spendWitness: spend.witness ?? []
|
|
42
52
|
};
|
|
43
53
|
}
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
const paidSats = (tx) => tx.vout.filter((vout) => vout.scriptpubkey_address === address).reduce((sum, vout) => sum + (vout.value ?? 0), 0);
|
|
55
|
+
let funding;
|
|
56
|
+
let fundedSats = 0;
|
|
57
|
+
for (const tx of txs) {
|
|
58
|
+
const paid = paidSats(tx);
|
|
59
|
+
if (paid > fundedSats) {
|
|
60
|
+
funding = tx;
|
|
61
|
+
fundedSats = paid;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
47
64
|
if (funding) {
|
|
48
|
-
const fundedSats = funding.vout.filter((vout) => vout.scriptpubkey_address === address).reduce((sum, vout) => sum + (vout.value ?? 0), 0);
|
|
49
65
|
const minConf = opts?.minConfirmations ?? 0;
|
|
50
66
|
let deepEnough;
|
|
51
67
|
if (funding.status?.confirmed) {
|
|
@@ -108,6 +124,164 @@ var init_bitcoin_reader_esplora = __esm({
|
|
|
108
124
|
}
|
|
109
125
|
});
|
|
110
126
|
|
|
127
|
+
// src/contracts/bitcoin-reader-electrum.ts
|
|
128
|
+
var bitcoin_reader_electrum_exports = {};
|
|
129
|
+
__export(bitcoin_reader_electrum_exports, {
|
|
130
|
+
electrumReader: () => electrumReader
|
|
131
|
+
});
|
|
132
|
+
function toBtcSignerNetwork(network) {
|
|
133
|
+
switch (network) {
|
|
134
|
+
case "mainnet":
|
|
135
|
+
return btc.NETWORK;
|
|
136
|
+
case "testnet":
|
|
137
|
+
case "signet":
|
|
138
|
+
return btc.TEST_NETWORK;
|
|
139
|
+
case "regtest":
|
|
140
|
+
return REGTEST_NETWORK;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function scriptHashOf(script) {
|
|
144
|
+
return import_base5.hex.encode((0, import_sha22.sha256)(script).reverse());
|
|
145
|
+
}
|
|
146
|
+
function electrumReader(client, opts) {
|
|
147
|
+
const network = opts?.network ?? "mainnet";
|
|
148
|
+
const defaultMinConf = opts?.minConfirmations ?? 0;
|
|
149
|
+
const fallback2 = opts?.fallback;
|
|
150
|
+
const scriptFor = (address) => btc.OutScript.encode(
|
|
151
|
+
btc.Address(toBtcSignerNetwork(network)).decode(address)
|
|
152
|
+
);
|
|
153
|
+
async function getHtlcFactsElectrum(address, required) {
|
|
154
|
+
const script = scriptFor(address);
|
|
155
|
+
const scripthash = scriptHashOf(script);
|
|
156
|
+
const history = await client.request(
|
|
157
|
+
"blockchain.scripthash.get_history",
|
|
158
|
+
[scripthash]
|
|
159
|
+
);
|
|
160
|
+
if (!history || history.length === 0)
|
|
161
|
+
return { funding: "absent", fundedSats: 0 };
|
|
162
|
+
const txs = await Promise.all(
|
|
163
|
+
history.map(async (entry) => {
|
|
164
|
+
const raw = await client.request("blockchain.transaction.get", [
|
|
165
|
+
entry.tx_hash
|
|
166
|
+
]);
|
|
167
|
+
return {
|
|
168
|
+
entry,
|
|
169
|
+
tx: btc.Transaction.fromRaw(import_base5.hex.decode(raw), {
|
|
170
|
+
allowUnknownOutputs: true,
|
|
171
|
+
allowUnknownInputs: true,
|
|
172
|
+
disableScriptCheck: true
|
|
173
|
+
})
|
|
174
|
+
};
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
const scriptHex = import_base5.hex.encode(script);
|
|
178
|
+
const outputIndicesPaying = (tx) => {
|
|
179
|
+
const indices = [];
|
|
180
|
+
for (let i = 0; i < tx.outputsLength; i++) {
|
|
181
|
+
const out = tx.getOutput(i);
|
|
182
|
+
if (out.script && import_base5.hex.encode(out.script) === scriptHex) indices.push(i);
|
|
183
|
+
}
|
|
184
|
+
return indices;
|
|
185
|
+
};
|
|
186
|
+
let funding;
|
|
187
|
+
let fundedSats = 0;
|
|
188
|
+
for (const candidate of txs) {
|
|
189
|
+
let paid = 0;
|
|
190
|
+
for (const i of outputIndicesPaying(candidate.tx))
|
|
191
|
+
paid += Number(candidate.tx.getOutput(i).amount ?? 0n);
|
|
192
|
+
if (paid > fundedSats) {
|
|
193
|
+
funding = candidate;
|
|
194
|
+
fundedSats = paid;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!funding) return { funding: "absent", fundedSats: 0 };
|
|
198
|
+
const fundedVouts = outputIndicesPaying(funding.tx);
|
|
199
|
+
for (const { tx } of txs) {
|
|
200
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
201
|
+
const input = tx.getInput(i);
|
|
202
|
+
if (input.txid && import_base5.hex.encode(input.txid) === funding.entry.tx_hash && input.index !== void 0 && fundedVouts.includes(input.index)) {
|
|
203
|
+
return {
|
|
204
|
+
funding: "confirmed",
|
|
205
|
+
fundedSats: 0,
|
|
206
|
+
// already spent — the amount no longer matters
|
|
207
|
+
spendWitness: (input.finalScriptWitness ?? []).map(
|
|
208
|
+
(item) => import_base5.hex.encode(item)
|
|
209
|
+
)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const height = funding.entry.height;
|
|
215
|
+
const isConfirmed = height > 0;
|
|
216
|
+
let deepEnough;
|
|
217
|
+
if (isConfirmed) {
|
|
218
|
+
if (required <= 1) {
|
|
219
|
+
deepEnough = true;
|
|
220
|
+
} else {
|
|
221
|
+
const tip = await client.request(
|
|
222
|
+
"blockchain.headers.subscribe",
|
|
223
|
+
[]
|
|
224
|
+
);
|
|
225
|
+
deepEnough = tip.height - height + 1 >= required;
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
deepEnough = required === 0;
|
|
229
|
+
}
|
|
230
|
+
return { funding: deepEnough ? "confirmed" : "mempool", fundedSats };
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
async getHtlcFacts(address, minConfirmations) {
|
|
234
|
+
const required = minConfirmations ?? defaultMinConf;
|
|
235
|
+
try {
|
|
236
|
+
return await getHtlcFactsElectrum(address, required);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (!fallback2) throw error;
|
|
239
|
+
console.warn(
|
|
240
|
+
"electrumReader: falling back to secondary reader:",
|
|
241
|
+
error instanceof Error ? error.message : error
|
|
242
|
+
);
|
|
243
|
+
return fallback2.getHtlcFacts(address, minConfirmations);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
subscribe(address, onChange) {
|
|
247
|
+
let scripthash;
|
|
248
|
+
try {
|
|
249
|
+
scripthash = scriptHashOf(scriptFor(address));
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.warn(
|
|
252
|
+
"electrumReader: subscribe skipped (address decode failed):",
|
|
253
|
+
error instanceof Error ? error.message : error
|
|
254
|
+
);
|
|
255
|
+
return () => {
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const handler = () => onChange();
|
|
259
|
+
client.subscribeScriptHash(scripthash, handler).catch((error) => {
|
|
260
|
+
console.warn(
|
|
261
|
+
"electrumReader: scripthash subscribe failed:",
|
|
262
|
+
error instanceof Error ? error.message : error
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
return () => client.unsubscribeScriptHash(scripthash, handler);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
var import_sha22, import_base5, btc, REGTEST_NETWORK;
|
|
270
|
+
var init_bitcoin_reader_electrum = __esm({
|
|
271
|
+
"src/contracts/bitcoin-reader-electrum.ts"() {
|
|
272
|
+
"use strict";
|
|
273
|
+
import_sha22 = require("@noble/hashes/sha2.js");
|
|
274
|
+
import_base5 = require("@scure/base");
|
|
275
|
+
btc = __toESM(require("@scure/btc-signer"), 1);
|
|
276
|
+
REGTEST_NETWORK = {
|
|
277
|
+
bech32: "bcrt",
|
|
278
|
+
pubKeyHash: 111,
|
|
279
|
+
scriptHash: 196,
|
|
280
|
+
wif: 239
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
111
285
|
// src/index.ts
|
|
112
286
|
var src_exports = {};
|
|
113
287
|
__export(src_exports, {
|
|
@@ -127,6 +301,7 @@ __export(src_exports, {
|
|
|
127
301
|
defaultEvmReaders: () => defaultEvmReaders,
|
|
128
302
|
deriveSwapActions: () => deriveSwapActions,
|
|
129
303
|
deriveSwapStatus: () => deriveSwapStatus,
|
|
304
|
+
electrumReader: () => electrumReader,
|
|
130
305
|
esploraReader: () => esploraReader,
|
|
131
306
|
evmReaderFromClient: () => evmReaderFromClient,
|
|
132
307
|
htlcFactsFromEsploraTxs: () => htlcFactsFromEsploraTxs,
|
|
@@ -468,10 +643,12 @@ function arkadeObservation(facts) {
|
|
|
468
643
|
}
|
|
469
644
|
function classifyArkadeSpend(encodedSpendTx, paymentHash) {
|
|
470
645
|
const tx = import_sdk.Transaction.fromPSBT(import_base2.base64.decode(encodedSpendTx));
|
|
471
|
-
for (
|
|
472
|
-
for (const
|
|
473
|
-
|
|
474
|
-
|
|
646
|
+
for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {
|
|
647
|
+
for (const witness of (0, import_sdk.getArkPsbtFields)(tx, inputIndex, import_sdk.ConditionWitness)) {
|
|
648
|
+
for (const element of witness) {
|
|
649
|
+
if (element.length === 32 && preimageMatches(element, paymentHash)) {
|
|
650
|
+
return { spend: "claim", preimage: element };
|
|
651
|
+
}
|
|
475
652
|
}
|
|
476
653
|
}
|
|
477
654
|
}
|
|
@@ -640,7 +817,7 @@ function defaultArkadeServerUrl(network) {
|
|
|
640
817
|
}
|
|
641
818
|
|
|
642
819
|
// src/contracts/bitcoin-manager.ts
|
|
643
|
-
var
|
|
820
|
+
var import_base6 = require("@scure/base");
|
|
644
821
|
|
|
645
822
|
// src/contracts/bitcoin.ts
|
|
646
823
|
var import_base4 = require("@scure/base");
|
|
@@ -675,6 +852,8 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
675
852
|
#obs = /* @__PURE__ */ new Map();
|
|
676
853
|
/** address → the preimage a claim revealed. */
|
|
677
854
|
#preimages = /* @__PURE__ */ new Map();
|
|
855
|
+
/** address → push-subscription teardown (only when the reader supports push). */
|
|
856
|
+
#subscriptions = /* @__PURE__ */ new Map();
|
|
678
857
|
#listeners = /* @__PURE__ */ new Set();
|
|
679
858
|
/** Last MTP reading (ms) + when it was fetched, for extrapolation. */
|
|
680
859
|
#now;
|
|
@@ -687,10 +866,24 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
687
866
|
}
|
|
688
867
|
static async create(config) {
|
|
689
868
|
const { esploraReader: esploraReader2 } = await Promise.resolve().then(() => (init_bitcoin_reader_esplora(), bitcoin_reader_esplora_exports));
|
|
869
|
+
let reader = esploraReader2(
|
|
870
|
+
config.esploraUrl,
|
|
871
|
+
void 0,
|
|
872
|
+
{ minConfirmations: config.minConfirmations }
|
|
873
|
+
);
|
|
874
|
+
if (config.electrumWsUrl) {
|
|
875
|
+
const [{ electrumReader: electrumReader2 }, { ElectrumWsClient }] = await Promise.all([
|
|
876
|
+
Promise.resolve().then(() => (init_bitcoin_reader_electrum(), bitcoin_reader_electrum_exports)),
|
|
877
|
+
import("@lendasat/lendaswap-sdk-pure")
|
|
878
|
+
]);
|
|
879
|
+
reader = electrumReader2(new ElectrumWsClient(config.electrumWsUrl), {
|
|
880
|
+
network: config.network,
|
|
881
|
+
minConfirmations: config.minConfirmations,
|
|
882
|
+
fallback: reader
|
|
883
|
+
});
|
|
884
|
+
}
|
|
690
885
|
return _BitcoinContractManager.fromDeps({
|
|
691
|
-
reader
|
|
692
|
-
minConfirmations: config.minConfirmations
|
|
693
|
-
}),
|
|
886
|
+
reader,
|
|
694
887
|
chainTime: config.chainTime
|
|
695
888
|
});
|
|
696
889
|
}
|
|
@@ -703,10 +896,25 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
703
896
|
`BitcoinContractManager can't track a '${ref.ledger}' HTLC`
|
|
704
897
|
);
|
|
705
898
|
this.#refs.set(ref.address, ref);
|
|
899
|
+
if (this.#reader.subscribe && !this.#subscriptions.has(ref.address)) {
|
|
900
|
+
this.#subscriptions.set(
|
|
901
|
+
ref.address,
|
|
902
|
+
this.#reader.subscribe(ref.address, () => {
|
|
903
|
+
this.#reconcileRef(ref).catch((error) => {
|
|
904
|
+
console.warn(
|
|
905
|
+
"BitcoinContractManager: push reconcile failed:",
|
|
906
|
+
error instanceof Error ? error.message : error
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
})
|
|
910
|
+
);
|
|
911
|
+
}
|
|
706
912
|
await this.#reconcileRef(ref);
|
|
707
913
|
}
|
|
708
914
|
async unregister(ref) {
|
|
709
915
|
if (ref.ledger !== "bitcoin") return;
|
|
916
|
+
this.#subscriptions.get(ref.address)?.();
|
|
917
|
+
this.#subscriptions.delete(ref.address);
|
|
710
918
|
this.#refs.delete(ref.address);
|
|
711
919
|
this.#obs.delete(ref.address);
|
|
712
920
|
this.#preimages.delete(ref.address);
|
|
@@ -741,6 +949,8 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
741
949
|
this.#now = { mtpMs: await this.#chainTime(), fetchedAtMs: Date.now() };
|
|
742
950
|
}
|
|
743
951
|
dispose() {
|
|
952
|
+
for (const unsubscribe of this.#subscriptions.values()) unsubscribe();
|
|
953
|
+
this.#subscriptions.clear();
|
|
744
954
|
this.#listeners.clear();
|
|
745
955
|
}
|
|
746
956
|
/** The preimage a claim revealed on this HTLC, if one was seen. */
|
|
@@ -754,7 +964,7 @@ var BitcoinContractManager = class _BitcoinContractManager {
|
|
|
754
964
|
);
|
|
755
965
|
const { observation, preimage } = bitcoinObservation(
|
|
756
966
|
facts,
|
|
757
|
-
|
|
967
|
+
import_base6.hex.decode(ref.preimageHash),
|
|
758
968
|
ref.expectedSats
|
|
759
969
|
);
|
|
760
970
|
if (preimage) this.#preimages.set(ref.address, preimage);
|
|
@@ -1639,19 +1849,19 @@ function defaultWebSocketFactory(url) {
|
|
|
1639
1849
|
var import_sdk3 = require("@arkade-os/sdk");
|
|
1640
1850
|
var import_lendaswap_sdk_pure = require("@lendasat/lendaswap-sdk-pure");
|
|
1641
1851
|
var import_legacy2 = require("@noble/hashes/legacy.js");
|
|
1642
|
-
var
|
|
1852
|
+
var import_base7 = require("@scure/base");
|
|
1643
1853
|
function strip0x(value) {
|
|
1644
1854
|
return value.startsWith("0x") ? value.slice(2) : value;
|
|
1645
1855
|
}
|
|
1646
1856
|
function xOnly(pubKeyHex) {
|
|
1647
|
-
const bytes =
|
|
1857
|
+
const bytes = import_base7.hex.decode(strip0x(pubKeyHex));
|
|
1648
1858
|
if (bytes.length === 33) return bytes.slice(1);
|
|
1649
1859
|
if (bytes.length === 32) return bytes;
|
|
1650
1860
|
throw new Error(`invalid public key length: ${bytes.length}`);
|
|
1651
1861
|
}
|
|
1652
1862
|
var seconds = (value) => ({ type: "seconds", value: BigInt(value) });
|
|
1653
1863
|
function buildArkadeVhtlcRef(input) {
|
|
1654
|
-
const hashLockBytes =
|
|
1864
|
+
const hashLockBytes = import_base7.hex.decode(strip0x(input.hashLock));
|
|
1655
1865
|
const params = {
|
|
1656
1866
|
sender: xOnly(input.senderPk),
|
|
1657
1867
|
receiver: xOnly(input.receiverPk),
|
|
@@ -1664,21 +1874,15 @@ function buildArkadeVhtlcRef(input) {
|
|
|
1664
1874
|
input.unilateralRefundWithoutReceiverDelay
|
|
1665
1875
|
)
|
|
1666
1876
|
};
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
default:
|
|
1674
|
-
throw new Error(
|
|
1675
|
-
`Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`
|
|
1676
|
-
);
|
|
1677
|
-
}
|
|
1678
|
-
})();
|
|
1877
|
+
if (input.arkadeHtlcScriptVersion !== void 0 && input.arkadeHtlcScriptVersion !== import_lendaswap_sdk_pure.ARKADE_HTLC_SCRIPT_VERSION_STRICT) {
|
|
1878
|
+
throw new Error(
|
|
1879
|
+
`Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1882
|
+
const vhtlc = new import_lendaswap_sdk_pure.StrictVhtlcScript(params);
|
|
1679
1883
|
return {
|
|
1680
1884
|
ledger: "arkade",
|
|
1681
|
-
script:
|
|
1885
|
+
script: import_base7.hex.encode(vhtlc.pkScript),
|
|
1682
1886
|
address: input.address,
|
|
1683
1887
|
preimageHash: strip0x(input.hashLock),
|
|
1684
1888
|
expectedSats: input.expectedSats,
|
|
@@ -1872,47 +2076,6 @@ function swapToTracked(stored) {
|
|
|
1872
2076
|
serverRefundLocktime: ms(r.vhtlc_refund_locktime)
|
|
1873
2077
|
};
|
|
1874
2078
|
// ─── 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
2079
|
// Pay-on-Lightning: the client pays a Lightning invoice (off-chain, nothing to
|
|
1917
2080
|
// watch) and claims an on-chain HTLC. No client-funded leg — the client's
|
|
1918
2081
|
// claim completes the swap; a hold invoice that never settles auto-unwinds.
|
|
@@ -1929,26 +2092,20 @@ function swapToTracked(stored) {
|
|
|
1929
2092
|
// no on-chain client leg
|
|
1930
2093
|
serverRefundLocktime: ms(r.vhtlc_refund_locktime)
|
|
1931
2094
|
};
|
|
1932
|
-
// Client
|
|
1933
|
-
|
|
2095
|
+
// Client funds the Arkade VHTLC (source_amount); the server pays the
|
|
2096
|
+
// Lightning invoice off-chain and claims the VHTLC — no server leg to
|
|
2097
|
+
// watch.
|
|
2098
|
+
case "arkade_to_lightning":
|
|
1934
2099
|
return {
|
|
1935
2100
|
swapId: r.id,
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
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)
|
|
2101
|
+
clientHtlc: arkadeLeg(
|
|
2102
|
+
r,
|
|
2103
|
+
r.arkade_vhtlc_address,
|
|
2104
|
+
Number(r.source_amount)
|
|
2105
|
+
),
|
|
2106
|
+
clientRefundLocktime: ms(r.vhtlc_refund_locktime),
|
|
2107
|
+
serverRefundLocktime: 0
|
|
2108
|
+
// no on-chain server leg
|
|
1952
2109
|
};
|
|
1953
2110
|
default:
|
|
1954
2111
|
return void 0;
|
|
@@ -2460,12 +2617,6 @@ var Client = class {
|
|
|
2460
2617
|
);
|
|
2461
2618
|
}
|
|
2462
2619
|
/** 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
2620
|
createBitcoinToEvmSwap(...args) {
|
|
2470
2621
|
return this.#trackAfterCreate(this.#legacy.createBitcoinToEvmSwap(...args));
|
|
2471
2622
|
}
|
|
@@ -2488,16 +2639,6 @@ var Client = class {
|
|
|
2488
2639
|
);
|
|
2489
2640
|
}
|
|
2490
2641
|
/** 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
2642
|
createEvmToArkadeSwapGeneric(...args) {
|
|
2502
2643
|
return this.#trackAfterCreate(
|
|
2503
2644
|
this.#legacy.createEvmToArkadeSwapGeneric(...args)
|
|
@@ -2508,12 +2649,6 @@ var Client = class {
|
|
|
2508
2649
|
return this.#trackAfterCreate(this.#legacy.createEvmToBitcoinSwap(...args));
|
|
2509
2650
|
}
|
|
2510
2651
|
/** 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
2652
|
getCoordinatorFundingCallDataPermit2(...args) {
|
|
2518
2653
|
return this.#legacy.getCoordinatorFundingCallDataPermit2(...args);
|
|
2519
2654
|
}
|
|
@@ -2633,7 +2768,17 @@ var Client = class {
|
|
|
2633
2768
|
/** Every stored swap whose ledgers are observable, mapped to a {@link TrackedSwap}. */
|
|
2634
2769
|
async #loadTrackedSwaps() {
|
|
2635
2770
|
const swaps = await this.listAllSwaps();
|
|
2636
|
-
return swaps.filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status)).map(
|
|
2771
|
+
return swaps.filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status)).map((s) => {
|
|
2772
|
+
try {
|
|
2773
|
+
return swapToTracked(s);
|
|
2774
|
+
} catch (error) {
|
|
2775
|
+
console.warn(
|
|
2776
|
+
`Client: skipping untrackable stored swap ${s.response.id}:`,
|
|
2777
|
+
error
|
|
2778
|
+
);
|
|
2779
|
+
return void 0;
|
|
2780
|
+
}
|
|
2781
|
+
}).filter((s) => s !== void 0);
|
|
2637
2782
|
}
|
|
2638
2783
|
/**
|
|
2639
2784
|
* Run a create, then fold the new swap into tracking.
|
|
@@ -2753,7 +2898,14 @@ var Client = class {
|
|
|
2753
2898
|
return this.#managers;
|
|
2754
2899
|
}
|
|
2755
2900
|
const managers = /* @__PURE__ */ new Map();
|
|
2756
|
-
const {
|
|
2901
|
+
const {
|
|
2902
|
+
arkadeServerUrl,
|
|
2903
|
+
evmRpcUrls,
|
|
2904
|
+
esploraUrl,
|
|
2905
|
+
electrumWsUrl,
|
|
2906
|
+
bitcoinNetwork,
|
|
2907
|
+
bitcoinMinConfirmations
|
|
2908
|
+
} = this.#tracking;
|
|
2757
2909
|
const chainTime = async () => (await this.getMtp()).mtp * 1e3;
|
|
2758
2910
|
const arkadeUrl = arkadeServerUrl ?? defaultArkadeServerUrl("bitcoin");
|
|
2759
2911
|
if (arkadeUrl) {
|
|
@@ -2765,10 +2917,13 @@ var Client = class {
|
|
|
2765
2917
|
const readers = defaultEvmReaders(evmRpcUrls);
|
|
2766
2918
|
if (readers.size > 0)
|
|
2767
2919
|
managers.set("evm", EvmContractManager.fromDeps({ readers }));
|
|
2920
|
+
const network = bitcoinNetwork ?? "mainnet";
|
|
2768
2921
|
managers.set(
|
|
2769
2922
|
"bitcoin",
|
|
2770
2923
|
await BitcoinContractManager.create({
|
|
2771
2924
|
esploraUrl: esploraUrl ? [esploraUrl] : DEFAULT_ESPLORA_URLS,
|
|
2925
|
+
electrumWsUrl: electrumWsUrl ?? import_lendaswap_sdk_pure2.DEFAULT_ELECTRUM_WS_URLS[network],
|
|
2926
|
+
network,
|
|
2772
2927
|
chainTime,
|
|
2773
2928
|
minConfirmations: bitcoinMinConfirmations
|
|
2774
2929
|
})
|
|
@@ -2785,6 +2940,8 @@ var ClientBuilder = class {
|
|
|
2785
2940
|
#serverUrl;
|
|
2786
2941
|
#arkadeServerUrl;
|
|
2787
2942
|
#esploraUrl;
|
|
2943
|
+
#electrumWsUrl;
|
|
2944
|
+
#bitcoinNetwork;
|
|
2788
2945
|
#bitcoinMinConfirmations;
|
|
2789
2946
|
#evmRpcUrls;
|
|
2790
2947
|
#managers;
|
|
@@ -2850,6 +3007,28 @@ var ClientBuilder = class {
|
|
|
2850
3007
|
this.#esploraUrl = url;
|
|
2851
3008
|
return this;
|
|
2852
3009
|
}
|
|
3010
|
+
/**
|
|
3011
|
+
* Set an Electrum server WebSocket URL (a Fulcrum `ws`/`wss` port) for
|
|
3012
|
+
* Bitcoin claim/refund chain access. When set, the underlying client
|
|
3013
|
+
* prefers it over Esplora for lookups and broadcasts, and waits for HTLC
|
|
3014
|
+
* fundings via `blockchain.scripthash.subscribe` push instead of polling.
|
|
3015
|
+
* Esplora remains the fallback whenever the Electrum server errors.
|
|
3016
|
+
*/
|
|
3017
|
+
withElectrumWsUrl(url) {
|
|
3018
|
+
this.#inner.withElectrumWsUrl(url);
|
|
3019
|
+
this.#electrumWsUrl = url;
|
|
3020
|
+
return this;
|
|
3021
|
+
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Declare the Bitcoin network of this deployment's HTLC addresses. Only
|
|
3024
|
+
* needed together with {@link withElectrumWsUrl} on a non-mainnet
|
|
3025
|
+
* deployment — the Electrum reader decodes addresses to scripthashes and
|
|
3026
|
+
* defaults to mainnet parameters.
|
|
3027
|
+
*/
|
|
3028
|
+
withBitcoinNetwork(network) {
|
|
3029
|
+
this.#bitcoinNetwork = network;
|
|
3030
|
+
return this;
|
|
3031
|
+
}
|
|
2853
3032
|
/**
|
|
2854
3033
|
* Confirmations a Bitcoin funding tx needs before it observes as `confirmed`
|
|
2855
3034
|
* (gating e.g. the evm→bitcoin claim). Default `0`: claim as soon as the
|
|
@@ -2906,6 +3085,8 @@ var ClientBuilder = class {
|
|
|
2906
3085
|
managers: this.#managers,
|
|
2907
3086
|
arkadeServerUrl: this.#arkadeServerUrl,
|
|
2908
3087
|
esploraUrl: this.#esploraUrl,
|
|
3088
|
+
electrumWsUrl: this.#electrumWsUrl,
|
|
3089
|
+
bitcoinNetwork: this.#bitcoinNetwork,
|
|
2909
3090
|
bitcoinMinConfirmations: this.#bitcoinMinConfirmations,
|
|
2910
3091
|
evmRpcUrls: this.#evmRpcUrls,
|
|
2911
3092
|
autoClaim: this.#autoClaim
|
|
@@ -2926,6 +3107,7 @@ function createdSwapId(result) {
|
|
|
2926
3107
|
}
|
|
2927
3108
|
|
|
2928
3109
|
// src/index.ts
|
|
3110
|
+
init_bitcoin_reader_electrum();
|
|
2929
3111
|
init_bitcoin_reader_esplora();
|
|
2930
3112
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2931
3113
|
0 && (module.exports = {
|
|
@@ -2945,6 +3127,7 @@ init_bitcoin_reader_esplora();
|
|
|
2945
3127
|
defaultEvmReaders,
|
|
2946
3128
|
deriveSwapActions,
|
|
2947
3129
|
deriveSwapStatus,
|
|
3130
|
+
electrumReader,
|
|
2948
3131
|
esploraReader,
|
|
2949
3132
|
evmReaderFromClient,
|
|
2950
3133
|
htlcFactsFromEsploraTxs,
|