@satora/swap 0.2.0 → 0.3.1
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 +4 -0
- package/dist/{bitcoin-reader-esplora-GRUP7RAG.js → bitcoin-reader-esplora-HV6CANAA.js} +2 -2
- package/dist/{chunk-J35RZBDW.js → chunk-EIWELJJK.js} +25 -9
- package/dist/chunk-EIWELJJK.js.map +1 -0
- package/dist/index.cjs +737 -208
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +301 -87
- package/dist/index.d.ts +301 -87
- package/dist/index.js +720 -199
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-J35RZBDW.js.map +0 -1
- /package/dist/{bitcoin-reader-esplora-GRUP7RAG.js.map → bitcoin-reader-esplora-HV6CANAA.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SwapStatus, Client as Client$1, ClientBuilder as ClientBuilder$1, StoredSwap } from '@lendasat/lendaswap-sdk-pure';
|
|
2
2
|
export * from '@lendasat/lendaswap-sdk-pure';
|
|
3
3
|
import { VirtualCoin } from '@arkade-os/sdk';
|
|
4
|
-
import {
|
|
4
|
+
import { Block } from 'viem';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The derived "what should the client do next?" model.
|
|
@@ -44,8 +44,37 @@ type CctpClaimEvidence = {
|
|
|
44
44
|
burnTxHash: `0x${string}`;
|
|
45
45
|
attestationAvailable: boolean;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* What a `wait` is waiting FOR — machine-readable, so a consumer can pick the
|
|
49
|
+
* right surface (invoice page, "deposit seen…", processing spinner, refund
|
|
50
|
+
* countdown) by switching on this field instead of re-deriving it from
|
|
51
|
+
* status + observations.
|
|
52
|
+
*
|
|
53
|
+
* One caveat: `client_payment` covers the whole Lightning-funded window — the
|
|
54
|
+
* chain cannot see whether the invoice was already paid (the server is the
|
|
55
|
+
* only witness for that split), so a consumer that wants "invoice shown" vs
|
|
56
|
+
* "payment in flight" as separate screens still refines this one value with
|
|
57
|
+
* the server status.
|
|
58
|
+
*/
|
|
59
|
+
type WaitingOn =
|
|
60
|
+
/** A Lightning payment to be made / locked in as a funded HTLC. */
|
|
61
|
+
"client_payment"
|
|
62
|
+
/** The client's funding tx was seen and is awaiting confirmation. */
|
|
63
|
+
| "client_funding_confirmation"
|
|
64
|
+
/** The client funded; the server's HTLC hasn't appeared yet. */
|
|
65
|
+
| "server_funding"
|
|
66
|
+
/** The client's claim is in flight, awaiting confirmation. */
|
|
67
|
+
| "claim_confirmation"
|
|
68
|
+
/** Nothing will progress — waiting for the client refund timelock to pass. */
|
|
69
|
+
| "refund_timelock";
|
|
70
|
+
/**
|
|
71
|
+
* How a finished swap ended — so a consumer can render success vs refund vs
|
|
72
|
+
* expiry from the terminal `none` action without interpreting `SwapStatus`.
|
|
73
|
+
*/
|
|
74
|
+
type SwapOutcome = "completed" | "refunded" | "expired";
|
|
47
75
|
type WaitAction = SwapActionBase & {
|
|
48
76
|
id: "wait";
|
|
77
|
+
waitingOn: WaitingOn;
|
|
49
78
|
};
|
|
50
79
|
type FundAction = SwapActionBase & {
|
|
51
80
|
id: "fund";
|
|
@@ -66,6 +95,7 @@ type RecoverCctpClaimAction = SwapActionBase & {
|
|
|
66
95
|
};
|
|
67
96
|
type NoneAction = SwapActionBase & {
|
|
68
97
|
id: "none";
|
|
98
|
+
outcome: SwapOutcome;
|
|
69
99
|
};
|
|
70
100
|
/** A single next-action option. Discriminated union on `id`. */
|
|
71
101
|
type SwapAction = WaitAction | FundAction | ClaimAction | RefundCollaborativeAction | RefundUnilateralAction | RecoverCctpClaimAction | NoneAction;
|
|
@@ -76,6 +106,14 @@ type SwapActions = {
|
|
|
76
106
|
/** id of the recommended action, if any; convenience pointer into `actions`. */
|
|
77
107
|
recommended?: SwapActionId;
|
|
78
108
|
actions: SwapAction[];
|
|
109
|
+
/**
|
|
110
|
+
* The raw chain observations these actions were derived from (set by the
|
|
111
|
+
* tracker; absent from a bare `deriveSwapActions` call). Chain FACTS — which
|
|
112
|
+
* leg is funded/spent — not protocol states: lets a UI render progress from
|
|
113
|
+
* chain truth (even when the server is stale or unreachable) without ever
|
|
114
|
+
* interpreting the `SwapStatus` enum.
|
|
115
|
+
*/
|
|
116
|
+
observations?: SwapObservations;
|
|
79
117
|
};
|
|
80
118
|
/**
|
|
81
119
|
* The pure input to {@link deriveSwapActions}: a plain, fully-resolved snapshot.
|
|
@@ -228,15 +266,28 @@ type HtlcRef = {
|
|
|
228
266
|
preimageHash: string;
|
|
229
267
|
/** Expected funding amount in sats; a funding below this is `invalid`. */
|
|
230
268
|
expectedSats: number;
|
|
269
|
+
/**
|
|
270
|
+
* Confirmations this leg's funding needs before it observes as
|
|
271
|
+
* `confirmed`, overriding the reader's default.
|
|
272
|
+
*
|
|
273
|
+
* Set per leg because the two directions are not symmetric. A funding the
|
|
274
|
+
* SERVER made can be taken at 0-conf: the server does not double-spend its
|
|
275
|
+
* own HTLC, and waiting a block would stall every claim. A funding the
|
|
276
|
+
* CLIENT made is read back under the rule the server applies to it — the
|
|
277
|
+
* server does not act until the tx has a blocktime, so calling it funded
|
|
278
|
+
* sooner reports progress the other side has not agreed to.
|
|
279
|
+
*/
|
|
280
|
+
minConfirmations?: number;
|
|
231
281
|
} | {
|
|
232
282
|
ledger: "evm";
|
|
233
283
|
chainId: number;
|
|
234
284
|
/** The HTLCErc20 contract holding this leg — where its events are emitted. */
|
|
235
285
|
htlc: `0x${string}`;
|
|
236
286
|
/**
|
|
237
|
-
* SHA256 preimage hash (`0x`-prefixed) — the indexed topic
|
|
238
|
-
*
|
|
239
|
-
*
|
|
287
|
+
* SHA256 preimage hash (`0x`-prefixed) — the indexed topic the contract's
|
|
288
|
+
* `SwapCreated`/`SwapRedeemed`/`SwapRefunded` events are filtered by. It
|
|
289
|
+
* narrows a log scan but identifies no single HTLC: any number can share
|
|
290
|
+
* one hash, so a swap is told apart by the rest of the tuple below.
|
|
240
291
|
*/
|
|
241
292
|
preimageHash: `0x${string}`;
|
|
242
293
|
/**
|
|
@@ -254,6 +305,22 @@ type HtlcRef = {
|
|
|
254
305
|
expectedAmount: bigint;
|
|
255
306
|
/** Expected token address; the funding is `invalid` if it locks another token. */
|
|
256
307
|
expectedToken?: `0x${string}`;
|
|
308
|
+
/**
|
|
309
|
+
* The funder (the HTLC's refund address). Together with the other fields it
|
|
310
|
+
* completes the contract's swap-key tuple, enabling the cheap `isActive`
|
|
311
|
+
* eth_call fast path (which verifies the terms implicitly — the key hashes
|
|
312
|
+
* amount/token/addresses/timelock). Legs whose response omits a tuple field
|
|
313
|
+
* fall back to log scans.
|
|
314
|
+
*/
|
|
315
|
+
sender?: `0x${string}`;
|
|
316
|
+
/** The HTLC refund timelock in unix SECONDS, exactly as funded on-chain. */
|
|
317
|
+
timelockSec?: number;
|
|
318
|
+
/**
|
|
319
|
+
* Swap creation time (ms). Lower-bounds log scans: the HTLC can have no
|
|
320
|
+
* events before the swap existed, so the scan starts near the corresponding
|
|
321
|
+
* block instead of genesis.
|
|
322
|
+
*/
|
|
323
|
+
createdAtMs?: number;
|
|
257
324
|
} | {
|
|
258
325
|
ledger: "lightning";
|
|
259
326
|
/** Payment hash — the contract's unique id. */
|
|
@@ -328,11 +395,20 @@ type TrackedSwap = {
|
|
|
328
395
|
type ActionSubscriber = (swapId: string, actions: SwapActions) => void;
|
|
329
396
|
type SwapTrackerOptions = {
|
|
330
397
|
/**
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
398
|
+
* The local tick interval (ms). Each tick recomputes every tracked swap from
|
|
399
|
+
* extrapolated chain clocks — pure local work, NO chain reads — so timelock
|
|
400
|
+
* flips (a refund unlocking, a stale pending swap turning reapable) surface
|
|
401
|
+
* between hints. `0` disables the timer (tests drive `refresh` manually).
|
|
334
402
|
*/
|
|
335
403
|
refreshIntervalMs?: number;
|
|
404
|
+
/**
|
|
405
|
+
* Minimum ms between chain reconciles of AT-RISK swaps — those whose client
|
|
406
|
+
* leg holds funds on-chain (≥ clientfunded, unresolved), where refund safety
|
|
407
|
+
* must not depend on the server. Everything else advances on server hints
|
|
408
|
+
* only ({@link applyHint}); a swap with nothing at stake costs zero chain
|
|
409
|
+
* reads. Default 60s.
|
|
410
|
+
*/
|
|
411
|
+
atRiskReconcileIntervalMs?: number;
|
|
336
412
|
};
|
|
337
413
|
declare class SwapTracker {
|
|
338
414
|
#private;
|
|
@@ -411,6 +487,13 @@ type AutoClaimConfig = {
|
|
|
411
487
|
type TrackingConfig = {
|
|
412
488
|
/** Whether tracking is enabled at all (default on; `withoutTracking()` clears). */
|
|
413
489
|
enabled: boolean;
|
|
490
|
+
/**
|
|
491
|
+
* The swap server URL (from `withBaseUrl`). When set, tracking opens the
|
|
492
|
+
* server's status WebSocket as a HINT feed: a pushed transition triggers a
|
|
493
|
+
* targeted chain verify, so the chain pollers can stay slow. Chain reads
|
|
494
|
+
* remain the source of truth.
|
|
495
|
+
*/
|
|
496
|
+
serverUrl?: string;
|
|
414
497
|
/** Explicit per-ledger managers, bypassing auto-construction (advanced/testing). */
|
|
415
498
|
managers?: Map<Ledger, ContractManager>;
|
|
416
499
|
/** Ark server URL override; defaults to the mainnet server when unset. */
|
|
@@ -419,7 +502,17 @@ type TrackingConfig = {
|
|
|
419
502
|
evmRpcUrls?: Record<number, string>;
|
|
420
503
|
/** Esplora REST URL override for the Bitcoin manager; defaults to mainnet mempool.space. */
|
|
421
504
|
esploraUrl?: string;
|
|
422
|
-
/**
|
|
505
|
+
/**
|
|
506
|
+
* Confirmations a Bitcoin funding tx needs before it observes as
|
|
507
|
+
* `confirmed` (gating e.g. the evm→bitcoin claim). Default `0`: accept
|
|
508
|
+
* 0-conf, trusting the funder not to double-spend. `1` = wait for a block.
|
|
509
|
+
*/
|
|
510
|
+
bitcoinMinConfirmations?: number;
|
|
511
|
+
/**
|
|
512
|
+
* Local tick interval (ms) — recomputes actions off extrapolated clocks, no
|
|
513
|
+
* chain reads. Chain reads happen on server hints and, for swaps whose
|
|
514
|
+
* client leg holds funds, on the tracker's at-risk cadence. Defaults to 5s.
|
|
515
|
+
*/
|
|
423
516
|
refreshIntervalMs?: number;
|
|
424
517
|
/** Opt-in hint-driven auto-claim; unset leaves tracking observe-only. */
|
|
425
518
|
autoClaim?: AutoClaimConfig;
|
|
@@ -639,6 +732,15 @@ declare class ClientBuilder {
|
|
|
639
732
|
* otherwise defaults to mainnet mempool.space; set this for a dev/regtest node.
|
|
640
733
|
*/
|
|
641
734
|
withEsploraUrl(url: string): this;
|
|
735
|
+
/**
|
|
736
|
+
* Confirmations a Bitcoin funding tx needs before it observes as `confirmed`
|
|
737
|
+
* (gating e.g. the evm→bitcoin claim). Default `0`: claim as soon as the
|
|
738
|
+
* funding hits the mempool, which TRUSTS the funder not to double-spend it
|
|
739
|
+
* (claiming publishes the preimage, so a funder that then replaced its
|
|
740
|
+
* funding could take both legs). Set `1` (or more) for a block-depth policy
|
|
741
|
+
* that doesn't rely on the funder's good behaviour.
|
|
742
|
+
*/
|
|
743
|
+
withBitcoinMinConfirmations(minConfirmations: number): this;
|
|
642
744
|
/**
|
|
643
745
|
* Override the Ark server URL. Optional for tracking — it otherwise defaults to
|
|
644
746
|
* the mainnet server; set this to track a non-mainnet (dev/signet) deployment.
|
|
@@ -663,7 +765,7 @@ type VirtualTxSource = {
|
|
|
663
765
|
|
|
664
766
|
/**
|
|
665
767
|
* The Arkade {@link ContractManager} — the stateful I/O adapter that observes a
|
|
666
|
-
* swap's VHTLC
|
|
768
|
+
* swap's VHTLC via the Ark indexer.
|
|
667
769
|
*
|
|
668
770
|
* We deliberately do NOT use `@arkade-os/sdk`'s `ContractManager`: its vtxo
|
|
669
771
|
* annotation calls `script.forfeit()` on every contract, but a VHTLC script has
|
|
@@ -672,11 +774,14 @@ type VirtualTxSource = {
|
|
|
672
774
|
* the legacy SDK's claim/refund paths do — and map them with the pure helpers in
|
|
673
775
|
* `./arkade.js`.
|
|
674
776
|
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
777
|
+
* Observation is on-demand: {@link refresh} (tracker start / newly tracked
|
|
778
|
+
* swap) and targeted {@link reconcile}s (server hints, the tracker's at-risk
|
|
779
|
+
* safety net). There is no standing subscription or periodic poll.
|
|
677
780
|
*/
|
|
678
781
|
|
|
679
|
-
/**
|
|
782
|
+
/**
|
|
783
|
+
* The Ark indexer surface the observer needs (RestIndexerProvider satisfies it).
|
|
784
|
+
*/
|
|
680
785
|
type ArkadeIndexer = VirtualTxSource & {
|
|
681
786
|
getVtxos(opts: {
|
|
682
787
|
scripts: string[];
|
|
@@ -716,7 +821,13 @@ declare class ArkadeContractManager implements ContractManager {
|
|
|
716
821
|
getState(ref: HtlcRef): HtlcObservation | undefined;
|
|
717
822
|
chainNow(_ref: HtlcRef): number | undefined;
|
|
718
823
|
onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void;
|
|
824
|
+
/**
|
|
825
|
+
* Full reconcile of every tracked ref (clock + vtxos). Called on demand
|
|
826
|
+
* (tracker start / newly tracked swap), NOT periodically: onward, hints and
|
|
827
|
+
* the tracker's at-risk safety net drive targeted `reconcile`s.
|
|
828
|
+
*/
|
|
719
829
|
refresh(): Promise<void>;
|
|
830
|
+
/** Targeted verify (hint / pre-action path) — never gated. */
|
|
720
831
|
reconcile(ref: HtlcRef): Promise<void>;
|
|
721
832
|
dispose(): void;
|
|
722
833
|
/** The verified preimage recovered from a claim spend, if one was seen. */
|
|
@@ -730,6 +841,15 @@ declare class ArkadeContractManager implements ContractManager {
|
|
|
730
841
|
*/
|
|
731
842
|
declare function defaultArkadeServerUrl(network: string): string | undefined;
|
|
732
843
|
|
|
844
|
+
/**
|
|
845
|
+
* Build the Arkade {@link HtlcRef} for a swap's VHTLC leg from the plain swap
|
|
846
|
+
* fields, deriving the pkScript and serialized contract params the
|
|
847
|
+
* `ArkadeContractManager` needs to register it for watching.
|
|
848
|
+
*
|
|
849
|
+
* This is the one place that reconstructs the VHTLC from a swap, mirroring the
|
|
850
|
+
* SDK VHTLC construction, so the mapper and manager stay free of VHTLC internals.
|
|
851
|
+
*/
|
|
852
|
+
|
|
733
853
|
type ArkadeRef = Extract<HtlcRef, {
|
|
734
854
|
ledger: "arkade";
|
|
735
855
|
}>;
|
|
@@ -754,6 +874,8 @@ type ArkadeVhtlcInput = {
|
|
|
754
874
|
unilateralRefundWithoutReceiverDelay: number;
|
|
755
875
|
/** Expected funding amount in sats (a short funding is `invalid`, not confirmed). */
|
|
756
876
|
expectedSats: number;
|
|
877
|
+
/** Arkade HTLC script version. Missing means legacy v0 for old stored swaps. */
|
|
878
|
+
arkadeHtlcScriptVersion?: number;
|
|
757
879
|
};
|
|
758
880
|
/**
|
|
759
881
|
* The VHTLC always commits to `ripemd160(sha256(preimage))` (HASH160). When
|
|
@@ -782,13 +904,25 @@ type BitcoinHtlcFacts = {
|
|
|
782
904
|
/** The esplora surface the observer needs (a `BitcoinReaderEsplora` satisfies it). */
|
|
783
905
|
type BitcoinChainReader = {
|
|
784
906
|
/** Funding + spend facts for an HTLC witness-script address. */
|
|
785
|
-
|
|
907
|
+
/**
|
|
908
|
+
* Funding + spend facts for an HTLC witness-script address. `minConfirmations`
|
|
909
|
+
* overrides the reader's default for this address alone — the two legs of a
|
|
910
|
+
* swap do not share one rule.
|
|
911
|
+
*/
|
|
912
|
+
getHtlcFacts(address: string, minConfirmations?: number): Promise<BitcoinHtlcFacts>;
|
|
786
913
|
};
|
|
787
914
|
type BitcoinCreateConfig = {
|
|
788
915
|
/** One or more esplora REST base URLs; several are tried in rotation with failover. */
|
|
789
916
|
esploraUrl: string | string[];
|
|
790
917
|
/** The current Bitcoin MTP (ms); typically `async () => (await client.getMtp()).mtp * 1000`. */
|
|
791
918
|
chainTime?: () => Promise<number>;
|
|
919
|
+
/**
|
|
920
|
+
* Confirmations a funding tx needs before it observes as `confirmed`.
|
|
921
|
+
* Default `0`: accept an unconfirmed funding, trusting the funder not to
|
|
922
|
+
* double-spend it (the claim reveals the preimage against it). `1` = wait
|
|
923
|
+
* for a block.
|
|
924
|
+
*/
|
|
925
|
+
minConfirmations?: number;
|
|
792
926
|
};
|
|
793
927
|
type BitcoinContractManagerDeps = {
|
|
794
928
|
reader: BitcoinChainReader;
|
|
@@ -837,14 +971,33 @@ type EsploraTx = {
|
|
|
837
971
|
}>;
|
|
838
972
|
status?: {
|
|
839
973
|
confirmed?: boolean;
|
|
974
|
+
block_height?: number;
|
|
840
975
|
};
|
|
841
976
|
};
|
|
977
|
+
/**
|
|
978
|
+
* When a funding tx counts as `confirmed` for observation purposes.
|
|
979
|
+
*
|
|
980
|
+
* `minConfirmations: 0` (the default) accepts an UNCONFIRMED funding, so a swap
|
|
981
|
+
* claims as soon as the funding hits the mempool instead of waiting ~10min for
|
|
982
|
+
* a block. This TRUSTS the funder not to double-spend: claiming publishes the
|
|
983
|
+
* preimage, so a funder who then replaces its funding tx could take both legs.
|
|
984
|
+
* Our own server therefore broadcasts HTLC fundings without signalling RBF.
|
|
985
|
+
* Note that a non-signalling tx is still replaceable by full-RBF miners — set
|
|
986
|
+
* `1` (or more) for a policy that doesn't rely on the funder's good behaviour;
|
|
987
|
+
* values above 1 make the reader fetch the tip height to compute depth.
|
|
988
|
+
*/
|
|
989
|
+
type BitcoinConfirmationPolicy = {
|
|
990
|
+
minConfirmations?: number;
|
|
991
|
+
};
|
|
842
992
|
/**
|
|
843
993
|
* Reduce an address's esplora tx history to HTLC facts. If a tx spends an output
|
|
844
994
|
* at the address, that's the resolving spend (its witness classifies claim vs
|
|
845
|
-
* refund); otherwise a tx paying the address is the funding
|
|
995
|
+
* refund); otherwise a tx paying the address is the funding, gated by the
|
|
996
|
+
* confirmation policy (see {@link BitcoinConfirmationPolicy}).
|
|
846
997
|
*/
|
|
847
|
-
declare function htlcFactsFromEsploraTxs(txs: EsploraTx[], address: string
|
|
998
|
+
declare function htlcFactsFromEsploraTxs(txs: EsploraTx[], address: string, opts?: BitcoinConfirmationPolicy & {
|
|
999
|
+
tipHeight?: number;
|
|
1000
|
+
}): BitcoinHtlcFacts;
|
|
848
1001
|
/**
|
|
849
1002
|
* Public esplora endpoints tried in rotation (mainnet). blockstream.info runs the
|
|
850
1003
|
* reference esplora, so it's API-compatible with mempool.space; spreading calls
|
|
@@ -857,7 +1010,7 @@ declare const DEFAULT_ESPLORA_URLS: string[];
|
|
|
857
1010
|
* providers) and fails over to the rest on error — so a throttled or failing
|
|
858
1011
|
* provider doesn't stall tracking.
|
|
859
1012
|
*/
|
|
860
|
-
declare function esploraReader(esploraUrls: string | string[], fetchImpl?: typeof fetch): BitcoinChainReader;
|
|
1013
|
+
declare function esploraReader(esploraUrls: string | string[], fetchImpl?: typeof fetch, policy?: BitcoinConfirmationPolicy): BitcoinChainReader;
|
|
861
1014
|
|
|
862
1015
|
/**
|
|
863
1016
|
* The `EvmContractManager`'s pure core.
|
|
@@ -894,17 +1047,80 @@ type EvmHtlcEvent = {
|
|
|
894
1047
|
* {@link chainNow} is ref-scoped.
|
|
895
1048
|
*/
|
|
896
1049
|
|
|
1050
|
+
/** Identifies one HTLC to read: contract + preimageHash (+ the claim guard). */
|
|
1051
|
+
type EvmHtlcQuery = {
|
|
1052
|
+
htlc: `0x${string}`;
|
|
1053
|
+
preimageHash: `0x${string}`;
|
|
1054
|
+
/** A `SwapCreated` counts only when it pays this address (term check). */
|
|
1055
|
+
claimAddress: `0x${string}`;
|
|
1056
|
+
/**
|
|
1057
|
+
* The rest of the swap-key tuple, when the swap response exposed it. It makes
|
|
1058
|
+
* {@link htlcQueryKey} unique to this HTLC, so two swaps sharing a preimage
|
|
1059
|
+
* hash hold separate entries rather than one overwriting the other. It does
|
|
1060
|
+
* not change how settlements are attributed — those go by hash either way.
|
|
1061
|
+
*/
|
|
1062
|
+
terms?: EvmHtlcTerms;
|
|
1063
|
+
};
|
|
1064
|
+
/**
|
|
1065
|
+
* The result key for one query in a batch.
|
|
1066
|
+
*
|
|
1067
|
+
* `preimageHash` identifies no single HTLC — any number can share one — so the
|
|
1068
|
+
* rest of the swap-key tuple is what separates two HTLCs on the same contract.
|
|
1069
|
+
* A caller that knows the tuple gets a key unique to its HTLC; one that does not
|
|
1070
|
+
* falls back to contract + hash, which is ambiguous by construction — two such
|
|
1071
|
+
* callers on one hash share an entry.
|
|
1072
|
+
*/
|
|
1073
|
+
declare function htlcQueryKey(q: {
|
|
1074
|
+
htlc: `0x${string}`;
|
|
1075
|
+
preimageHash: `0x${string}`;
|
|
1076
|
+
claimAddress?: `0x${string}`;
|
|
1077
|
+
terms?: EvmHtlcTerms;
|
|
1078
|
+
}): string;
|
|
1079
|
+
/** The swap-key tuple fields beyond contract, hash and claim address. */
|
|
1080
|
+
type EvmHtlcTerms = {
|
|
1081
|
+
amount: bigint;
|
|
1082
|
+
token: `0x${string}`;
|
|
1083
|
+
sender: `0x${string}`;
|
|
1084
|
+
timelockSec: number;
|
|
1085
|
+
};
|
|
1086
|
+
/**
|
|
1087
|
+
* The contract's full swap-key tuple for one HTLC. `isActive` hashes all of it
|
|
1088
|
+
* into the swap key, so an `isActive == true` with these EXPECTED values also
|
|
1089
|
+
* proves the HTLC was funded on exactly the expected terms.
|
|
1090
|
+
*/
|
|
1091
|
+
type EvmActiveQuery = {
|
|
1092
|
+
htlc: `0x${string}`;
|
|
1093
|
+
preimageHash: `0x${string}`;
|
|
1094
|
+
amount: bigint;
|
|
1095
|
+
token: `0x${string}`;
|
|
1096
|
+
sender: `0x${string}`;
|
|
1097
|
+
claimAddress: `0x${string}`;
|
|
1098
|
+
timelockSec: number;
|
|
1099
|
+
};
|
|
897
1100
|
/** Reads `HTLCErc20` state for one EVM chain. Implemented over viem/ethers/etc. */
|
|
898
1101
|
type EvmChainReader = {
|
|
899
1102
|
/**
|
|
900
|
-
* The decoded lifecycle events
|
|
901
|
-
*
|
|
1103
|
+
* The decoded lifecycle events for every queried HTLC, keyed by
|
|
1104
|
+
* {@link htlcQueryKey}. Batched so a whole chain scan costs one RPC request
|
|
1105
|
+
* regardless of how many swaps are tracked; a queried HTLC with no events maps
|
|
1106
|
+
* to an empty array. `fromBlock` lower-bounds the scan (default genesis) —
|
|
1107
|
+
* pass the swaps' creation-block estimate so providers never see an
|
|
1108
|
+
* unbounded log query.
|
|
902
1109
|
*/
|
|
903
|
-
|
|
904
|
-
/**
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1110
|
+
getHtlcEventsBatch(queries: EvmHtlcQuery[], fromBlock?: bigint): Promise<Map<string, EvmHtlcEvent[]>>;
|
|
1111
|
+
/**
|
|
1112
|
+
* Whether each queried HTLC is currently open, keyed by {@link htlcQueryKey}.
|
|
1113
|
+
* The cheap routine check (`eth_call`, batched via Multicall3): `true` means
|
|
1114
|
+
* funded-and-unspent on exactly the queried terms; `false` is ambiguous
|
|
1115
|
+
* (never funded / claimed / refunded) and needs {@link getHtlcEventsBatch}
|
|
1116
|
+
* to classify.
|
|
1117
|
+
*/
|
|
1118
|
+
isActiveBatch(queries: EvmActiveQuery[]): Promise<Map<string, boolean>>;
|
|
1119
|
+
/** The latest block's timestamp (ms) and number. */
|
|
1120
|
+
getLatestBlock(): Promise<{
|
|
1121
|
+
timeMs: number;
|
|
1122
|
+
number: bigint;
|
|
1123
|
+
}>;
|
|
908
1124
|
};
|
|
909
1125
|
type EvmContractManagerDeps = {
|
|
910
1126
|
/** A chain reader per EVM `chainId` this manager serves. */
|
|
@@ -921,7 +1137,14 @@ declare class EvmContractManager implements ContractManager {
|
|
|
921
1137
|
getState(ref: HtlcRef): HtlcObservation | undefined;
|
|
922
1138
|
chainNow(ref: HtlcRef): number | undefined;
|
|
923
1139
|
onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void;
|
|
1140
|
+
/**
|
|
1141
|
+
* Full reconcile of every tracked chain — seeds clocks + observations. Called
|
|
1142
|
+
* on demand (tracker start / newly tracked swap), NOT periodically: onward,
|
|
1143
|
+
* hints drive targeted `reconcile`s and the tracker's at-risk safety net
|
|
1144
|
+
* re-reads only swaps with client funds on the line.
|
|
1145
|
+
*/
|
|
924
1146
|
refresh(): Promise<void>;
|
|
1147
|
+
/** Targeted verify (hint / pre-action path) — never gated. */
|
|
925
1148
|
reconcile(ref: HtlcRef): Promise<void>;
|
|
926
1149
|
dispose(): void;
|
|
927
1150
|
/** The preimage a claim revealed on this HTLC, if one was seen. */
|
|
@@ -935,83 +1158,58 @@ declare class EvmContractManager implements ContractManager {
|
|
|
935
1158
|
* Kept separate from the manager so the manager itself stays free of any
|
|
936
1159
|
* chain-library dependency (and unit-testable against a fake reader). This module
|
|
937
1160
|
* is the only place that touches viem and the `HTLCErc20` ABI.
|
|
1161
|
+
*
|
|
1162
|
+
* All HTLCs on a chain are read with a SINGLE `eth_getLogs` call: the three
|
|
1163
|
+
* `HTLCErc20` lifecycle events all index `preimageHash` as topic1, so one filter
|
|
1164
|
+
* ORs the three event signatures on topic0 and the tracked preimage hashes on
|
|
1165
|
+
* topic1 across all HTLC contract addresses. Request count per scan is constant
|
|
1166
|
+
* in the number of tracked swaps — this matters, since public RPCs rate-limit by
|
|
1167
|
+
* request count.
|
|
938
1168
|
*/
|
|
939
1169
|
|
|
940
1170
|
/**
|
|
941
1171
|
* Tested public RPC endpoints per supported chainId, tried in order via viem's
|
|
942
|
-
*
|
|
1172
|
+
* `fallback`. Mirrors the frontend's `evmTransport.ts` list — the client
|
|
943
1173
|
* uses these by default so Arkade↔EVM tracking works out of the box; a caller can
|
|
944
1174
|
* override per chain with `ClientBuilder.withEvmRpcUrls`.
|
|
945
1175
|
*/
|
|
946
1176
|
declare const DEFAULT_EVM_RPCS: Record<number, string[]>;
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
readonly name: "preimageHash";
|
|
953
|
-
readonly indexed: true;
|
|
954
|
-
}, {
|
|
955
|
-
readonly type: "address";
|
|
956
|
-
readonly name: "refundAddress";
|
|
957
|
-
readonly indexed: true;
|
|
958
|
-
}, {
|
|
959
|
-
readonly type: "address";
|
|
960
|
-
readonly name: "claimAddress";
|
|
961
|
-
readonly indexed: true;
|
|
962
|
-
}, {
|
|
963
|
-
readonly type: "address";
|
|
964
|
-
readonly name: "token";
|
|
965
|
-
}, {
|
|
966
|
-
readonly type: "uint256";
|
|
967
|
-
readonly name: "amount";
|
|
968
|
-
}, {
|
|
969
|
-
readonly type: "uint256";
|
|
970
|
-
readonly name: "timelock";
|
|
971
|
-
}];
|
|
972
|
-
};
|
|
973
|
-
declare const SWAP_REDEEMED: {
|
|
974
|
-
readonly name: "SwapRedeemed";
|
|
975
|
-
readonly type: "event";
|
|
976
|
-
readonly inputs: readonly [{
|
|
977
|
-
readonly type: "bytes32";
|
|
978
|
-
readonly name: "preimageHash";
|
|
979
|
-
readonly indexed: true;
|
|
980
|
-
}, {
|
|
981
|
-
readonly type: "bytes32";
|
|
982
|
-
readonly name: "preimage";
|
|
983
|
-
}];
|
|
984
|
-
};
|
|
985
|
-
declare const SWAP_REFUNDED: {
|
|
986
|
-
readonly name: "SwapRefunded";
|
|
987
|
-
readonly type: "event";
|
|
988
|
-
readonly inputs: readonly [{
|
|
989
|
-
readonly type: "bytes32";
|
|
990
|
-
readonly name: "preimageHash";
|
|
991
|
-
readonly indexed: true;
|
|
992
|
-
}];
|
|
1177
|
+
/** The undecoded log shape `eth_getLogs` returns. */
|
|
1178
|
+
type RawLog = {
|
|
1179
|
+
address: `0x${string}`;
|
|
1180
|
+
topics: [`0x${string}`, ...`0x${string}`[]];
|
|
1181
|
+
data: `0x${string}`;
|
|
993
1182
|
};
|
|
994
1183
|
/** The viem surface the reader needs — a seam so it can be faked in tests. */
|
|
995
1184
|
type EvmLogClient = {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1185
|
+
request(args: {
|
|
1186
|
+
method: "eth_getLogs";
|
|
1187
|
+
params: [
|
|
1188
|
+
{
|
|
1189
|
+
address: `0x${string}`[];
|
|
1190
|
+
topics: (`0x${string}`[] | null)[];
|
|
1191
|
+
fromBlock: `0x${string}`;
|
|
1192
|
+
}
|
|
1193
|
+
];
|
|
1194
|
+
}): Promise<RawLog[]>;
|
|
1195
|
+
/** A read-only `eth_call`. */
|
|
1196
|
+
call(args: {
|
|
1197
|
+
to: `0x${string}`;
|
|
1198
|
+
data: `0x${string}`;
|
|
1199
|
+
}): Promise<{
|
|
1200
|
+
data?: `0x${string}`;
|
|
1201
|
+
}>;
|
|
1202
|
+
getBlock(): Promise<Pick<Block, "timestamp" | "number">>;
|
|
1009
1203
|
};
|
|
1010
1204
|
/** Build an {@link EvmChainReader} over an existing viem-like client. */
|
|
1011
1205
|
declare function evmReaderFromClient(client: EvmLogClient): EvmChainReader;
|
|
1012
1206
|
/**
|
|
1013
1207
|
* Build an {@link EvmChainReader} over one or more EVM JSON-RPC endpoints. With
|
|
1014
|
-
* several, viem's
|
|
1208
|
+
* several, viem's `fallback` tries them in order and fails over on error.
|
|
1209
|
+
* Deliberately NOT ranked: ranking health-pings every listed endpoint on an
|
|
1210
|
+
* interval from every open tab, which is exactly the background burst that got
|
|
1211
|
+
* public RPCs rate-limiting us (publicnode 403s) — and it contradicts this
|
|
1212
|
+
* package's near-zero-RPC design.
|
|
1015
1213
|
*/
|
|
1016
1214
|
declare function createEvmRpcReader(rpcUrls: string | string[]): EvmChainReader;
|
|
1017
1215
|
/**
|
|
@@ -1056,6 +1254,12 @@ interface WorkerHintSource {
|
|
|
1056
1254
|
onStatus(cb: (update: {
|
|
1057
1255
|
swapId: string;
|
|
1058
1256
|
}) => void): () => void;
|
|
1257
|
+
/**
|
|
1258
|
+
* Optional: fires when the feed comes back up after an outage, during which
|
|
1259
|
+
* pushed transitions were lost for good. The worker answers by re-verifying
|
|
1260
|
+
* every tracked swap against the chain.
|
|
1261
|
+
*/
|
|
1262
|
+
onReconnect?(cb: () => void): () => void;
|
|
1059
1263
|
}
|
|
1060
1264
|
/** Backoff policy for retrying a failed auto-claim. */
|
|
1061
1265
|
type SwapWorkerRetryOptions = {
|
|
@@ -1072,9 +1276,10 @@ type SwapWorkerOptions = {
|
|
|
1072
1276
|
/**
|
|
1073
1277
|
* Run the recommended auto action for a swap (today: claim). Reject to signal
|
|
1074
1278
|
* failure — the worker re-verifies against chain and retries with backoff.
|
|
1075
|
-
* Resolve once submitted.
|
|
1279
|
+
* Resolve once submitted. OMIT for observe-only mode: hints still trigger
|
|
1280
|
+
* chain re-verifies (fast, cheap derivations), but nothing is ever run.
|
|
1076
1281
|
*/
|
|
1077
|
-
execute
|
|
1282
|
+
execute?: (swapId: string, actionId: SwapActionId) => Promise<void>;
|
|
1078
1283
|
/**
|
|
1079
1284
|
* Surface an action that needs the user rather than being auto-run — a manual
|
|
1080
1285
|
* `fund`, a refund the user must confirm, or a claim whose retries are spent.
|
|
@@ -1146,6 +1351,15 @@ declare class WsStatusSource {
|
|
|
1146
1351
|
stop(): void;
|
|
1147
1352
|
/** Emit for each pushed status update. Returns an unsubscribe fn. */
|
|
1148
1353
|
onStatus(cb: (update: SwapStatusUpdate) => void): () => void;
|
|
1354
|
+
/**
|
|
1355
|
+
* Emit whenever the socket comes (back) up after a disruption — a drop, or a
|
|
1356
|
+
* first connect that had to be retried. NOT emitted on a pristine first open:
|
|
1357
|
+
* hints were never live before it, so there is no gap to recover from. While
|
|
1358
|
+
* the socket was down, pushed transitions were lost for good (the server only
|
|
1359
|
+
* snapshots on subscribe), so a consumer should re-verify its world against
|
|
1360
|
+
* the chain on this signal.
|
|
1361
|
+
*/
|
|
1362
|
+
onReconnect(cb: () => void): () => void;
|
|
1149
1363
|
/**
|
|
1150
1364
|
* Track these swap ids — subscribe on the socket now if open, else on next open.
|
|
1151
1365
|
*
|
|
@@ -1174,4 +1388,4 @@ declare class WsStatusSource {
|
|
|
1174
1388
|
|
|
1175
1389
|
declare function swapToTracked(stored: StoredSwap): TrackedSwap | undefined;
|
|
1176
1390
|
|
|
1177
|
-
export { type ActionSubscriber, ArkadeContractManager, type ArkadeContractManagerDeps, type ArkadeCreateConfig, type ArkadeVhtlcInput, type BitcoinChainReader, BitcoinContractManager, type BitcoinContractManagerDeps, type BitcoinCreateConfig, type BlockedBy, type BlockedReason, type CctpClaimEvidence, type ClaimAction, Client, ClientBuilder, type ContractManager, DEFAULT_ESPLORA_URLS, DEFAULT_EVM_RPCS, type EvmChainReader, EvmContractManager, type EvmContractManagerDeps, type EvmLogClient, type FundAction, type HtlcObservation, type HtlcRef, type Ledger, type NoneAction, type RecoverCctpClaimAction, type RefundCollaborativeAction, type RefundUnilateralAction, type SwapAction, type SwapActionAutomation, type SwapActionId, type SwapActionInput, type SwapActions, type SwapObservations, type SwapStatusUpdate, SwapTracker, SwapWorker, type SwapWorkerOptions, type TrackedSwap, type WaitAction, type WebSocketLike, type WorkerHintSource, type WorkerTracker, WsStatusSource, type WsStatusSourceOptions, buildArkadeVhtlcRef, createEvmRpcReader, defaultArkadeServerUrl, defaultEvmReaders, deriveSwapActions, deriveSwapStatus, esploraReader, evmReaderFromClient, htlcFactsFromEsploraTxs, htlcKey, swapToTracked };
|
|
1391
|
+
export { type ActionSubscriber, ArkadeContractManager, type ArkadeContractManagerDeps, type ArkadeCreateConfig, type ArkadeVhtlcInput, type BitcoinChainReader, type BitcoinConfirmationPolicy, BitcoinContractManager, type BitcoinContractManagerDeps, type BitcoinCreateConfig, type BlockedBy, type BlockedReason, type CctpClaimEvidence, type ClaimAction, Client, ClientBuilder, type ContractManager, DEFAULT_ESPLORA_URLS, DEFAULT_EVM_RPCS, type EvmChainReader, EvmContractManager, type EvmContractManagerDeps, type EvmHtlcQuery, type EvmLogClient, type FundAction, type HtlcObservation, type HtlcRef, type Ledger, type NoneAction, type RawLog, type RecoverCctpClaimAction, type RefundCollaborativeAction, type RefundUnilateralAction, type SwapAction, type SwapActionAutomation, type SwapActionId, type SwapActionInput, type SwapActions, type SwapObservations, type SwapOutcome, type SwapStatusUpdate, SwapTracker, SwapWorker, type SwapWorkerOptions, type TrackedSwap, type WaitAction, type WaitingOn, type WebSocketLike, type WorkerHintSource, type WorkerTracker, WsStatusSource, type WsStatusSourceOptions, buildArkadeVhtlcRef, createEvmRpcReader, defaultArkadeServerUrl, defaultEvmReaders, deriveSwapActions, deriveSwapStatus, esploraReader, evmReaderFromClient, htlcFactsFromEsploraTxs, htlcKey, htlcQueryKey, swapToTracked };
|