@satora/swap 1.1.0 → 1.2.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/index.cjs +145 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -4
- package/dist/index.d.ts +117 -4
- package/dist/index.js +144 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/actions/derive.ts","../src/actions/status.ts","../src/client.ts","../src/contracts/arkade-manager.ts","../src/contracts/arkade.ts","../src/contracts/preimage.ts","../src/contracts/arkade-network.ts","../src/contracts/bitcoin-manager.ts","../src/contracts/bitcoin.ts","../src/contracts/evm.ts","../src/contracts/types.ts","../src/contracts/evm-manager.ts","../src/contracts/evm-reader-viem.ts","../src/hints/swap-worker.ts","../src/hints/ws-status-source.ts","../src/contracts/arkade-vhtlc.ts","../src/tracker/from-swap.ts","../src/tracker/swap-tracker.ts"],"sourcesContent":["/**\n * @satora/swap — the Satora swap client.\n *\n * Re-exports the legacy `@lendasat/lendaswap-sdk-pure` bundle (types, signer\n * helpers, storage, etc.), but shadows `Client` and `ClientBuilder` with the\n * Satora-native versions from `./client`. Those are drop-in replacements — same\n * public surface, forwarding to an internal legacy client for now — and are the\n * seam for migrating implementation into `@satora/*` and adding new features.\n *\n * Explicit named exports below take precedence over the `export *` star for the\n * same names, so consumers importing `{ Client, ClientBuilder }` get the new\n * ones while everything else stays legacy.\n */\nexport * from \"@lendasat/lendaswap-sdk-pure\";\n// Satora-native features.\nexport { deriveSwapActions } from \"./actions/derive.js\";\nexport { deriveSwapStatus } from \"./actions/status.js\";\nexport type * from \"./actions/types.js\";\nexport { Client, ClientBuilder } from \"./client.js\";\n// Observe-mode tracking: per-ledger monitors + the recovery-bundle mapper.\nexport {\n ArkadeContractManager,\n type ArkadeContractManagerDeps,\n type ArkadeCreateConfig,\n} from \"./contracts/arkade-manager.js\";\nexport { defaultArkadeServerUrl } from \"./contracts/arkade-network.js\";\nexport {\n type ArkadeVhtlcInput,\n buildArkadeVhtlcRef,\n} from \"./contracts/arkade-vhtlc.js\";\nexport {\n type BitcoinChainReader,\n BitcoinContractManager,\n type BitcoinContractManagerDeps,\n type BitcoinCreateConfig,\n} from \"./contracts/bitcoin-manager.js\";\nexport {\n type ElectrumReaderNetwork,\n type ElectrumRpc,\n electrumReader,\n} from \"./contracts/bitcoin-reader-electrum.js\";\nexport {\n type BitcoinConfirmationPolicy,\n DEFAULT_ESPLORA_URLS,\n esploraReader,\n htlcFactsFromEsploraTxs,\n} from \"./contracts/bitcoin-reader-esplora.js\";\nexport {\n type EvmChainReader,\n EvmContractManager,\n type EvmContractManagerDeps,\n type EvmHtlcQuery,\n htlcQueryKey,\n} from \"./contracts/evm-manager.js\";\nexport {\n createEvmRpcReader,\n DEFAULT_EVM_RPCS,\n defaultEvmReaders,\n type EvmLogClient,\n evmReaderFromClient,\n type RawLog,\n} from \"./contracts/evm-reader-viem.js\";\nexport type * from \"./contracts/types.js\";\nexport { htlcKey } from \"./contracts/types.js\";\nexport {\n SwapWorker,\n type SwapWorkerOptions,\n type WorkerHintSource,\n type WorkerTracker,\n} from \"./hints/swap-worker.js\";\nexport {\n type SwapStatusUpdate,\n type WebSocketLike,\n WsStatusSource,\n type WsStatusSourceOptions,\n} from \"./hints/ws-status-source.js\";\nexport { swapToTracked } from \"./tracker/from-swap.js\";\nexport {\n type ActionSubscriber,\n SwapTracker,\n type TrackedSwap,\n} from \"./tracker/swap-tracker.js\";\n","import type {\n ClaimAction,\n FundAction,\n NoneAction,\n RefundUnilateralAction,\n SwapActionInput,\n SwapActions,\n WaitAction,\n} from \"./types.js\";\n\n/**\n * How long past the client refund locktime a `pending` swap keeps being watched\n * before it is declared dead. Covers observation lag on a deadline-straddling\n * funding tx (EVM has no mempool-level observation, and clocks are partly\n * extrapolated); one that stays invisible longer than this is treated as never\n * sent. A later `startTracking` still picks the swap up from storage if a\n * funding somehow surfaces afterwards.\n */\nexport const FUNDING_REAP_GRACE_MS = 60 * 60 * 1000;\n\nexport function deriveSwapActions(input: SwapActionInput): SwapActions {\n const {\n status,\n clientChainNow,\n serverChainNow,\n clientRefundLocktime,\n serverRefundLocktime,\n clientFunds = true,\n } = input;\n\n switch (status) {\n case \"pending\": {\n // Pay-on-Lightning: the client's \"funding\" is paying the invoice off-chain\n // (handled by their Lightning wallet), not an on-chain deposit — there is\n // nothing for us to recommend funding, so just wait for the invoice to land\n // as a funded HTLC we can claim.\n if (!clientFunds) {\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"client_payment\",\n recommended: true,\n automation: \"auto\",\n reason: \"Waiting for the Lightning payment to be locked in.\",\n };\n return { recommended: \"wait\", actions: [wait] };\n }\n\n // Funding is \"too late\" once past the client's refund locktime (the funded\n // deposit would be immediately refundable). But `pending` only means the\n // last chain read saw NO deposit — a funding broadcast near the deadline\n // can still surface after it (and would need the refund path). So keep\n // watching through a grace window; only once it has clearly stayed unfunded\n // derive `none`, letting the tracker reap the dead swap instead of\n // watching it forever.\n if (clientChainNow >= clientRefundLocktime + FUNDING_REAP_GRACE_MS) {\n const done: NoneAction = {\n id: \"none\",\n outcome: \"expired\",\n recommended: true,\n automation: \"auto\",\n reason: \"The funding window closed.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n if (clientChainNow >= clientRefundLocktime) return { actions: [] };\n\n const fund: FundAction = {\n id: \"fund\",\n recommended: true,\n // Funding spends new user money, so it is never auto-run.\n automation: \"manual\",\n reason: \"Send the deposit to start the swap.\",\n };\n return { recommended: \"fund\", actions: [fund] };\n }\n\n case \"clientfundingseen\": {\n // Funding tx seen but not yet confirmed — wait for confirmation (then the\n // server funds its side). Nothing to refund until the HTLC confirms.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"client_funding_confirmation\",\n recommended: true,\n automation: \"auto\",\n reason: \"Waiting for your funding transaction to confirm.\",\n };\n return { recommended: \"wait\", actions: [wait] };\n }\n\n case \"clientfunded\": {\n const refundUnlocked = clientChainNow >= clientRefundLocktime;\n\n if (refundUnlocked) {\n // Timelock passed: reclaiming the deposit is the recommended way out.\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: true,\n automation: \"confirm\",\n reason: \"Reclaim your deposit.\",\n };\n return { recommended: \"refund_unilateral\", actions: [refund] };\n }\n\n // Still within the timelock: wait for the server; refund is a valid but\n // not-yet-runnable option, surfaced so the UI can show when it unlocks.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"server_funding\",\n recommended: true,\n automation: \"auto\",\n reason: \"Waiting for the server to fund the swap.\",\n };\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: false,\n automation: \"confirm\",\n reason: \"Refund becomes available once the timelock passes.\",\n blockedBy: {\n kind: \"timelock_not_expired\",\n message: \"The refund timelock has not passed yet.\",\n },\n };\n return { recommended: \"wait\", actions: [wait, refund] };\n }\n\n case \"serverfunded\": {\n // The client created the swap, so it always holds the preimage.\n const claimWindowClosed = serverChainNow >= serverRefundLocktime;\n\n if (!claimWindowClosed) {\n // Server can't refund yet, so revealing the preimage by claiming is safe.\n // Claiming secures the user's funds, so it is safe to auto-run.\n const claim: ClaimAction = {\n id: \"claim\",\n recommended: true,\n automation: \"auto\",\n reason: \"Redeem the swap with your preimage.\",\n };\n return { recommended: \"claim\", actions: [claim] };\n }\n\n // Pay-on-Lightning: the client has no on-chain deposit at risk, so there is\n // nothing to protect by keeping the secret and nothing to refund. Once the\n // claim window closed the leg is (being) reclaimed by the server; the\n // client's Lightning payment unwinds on its own. Terminal.\n if (!clientFunds) {\n const done: NoneAction = {\n id: \"none\",\n outcome: \"refunded\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"The claim window passed — your Lightning payment will be refunded automatically.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n // Claim window closed: the server can now refund its HTLC, and claiming\n // would reveal the preimage — letting the server also take the client's\n // deposit. Never claim now; refund instead (keeping the secret). Refund\n // unlocks at the client's own, later timelock.\n const refundUnlocked = clientChainNow >= clientRefundLocktime;\n\n if (refundUnlocked) {\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: true,\n automation: \"confirm\",\n reason:\n \"Reclaim your deposit — do not claim, as revealing the preimage would let the server take it.\",\n };\n return { recommended: \"refund_unilateral\", actions: [refund] };\n }\n\n // Refund not unlocked yet: wait for the client timelock, then refund. Still\n // must not claim.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"refund_timelock\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"Wait for your refund timelock — do not claim, as revealing the preimage would let the server take your deposit.\",\n };\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: false,\n automation: \"confirm\",\n reason: \"Refund becomes available once the timelock passes.\",\n blockedBy: {\n kind: \"timelock_not_expired\",\n message: \"The refund timelock has not passed yet.\",\n },\n };\n return { recommended: \"wait\", actions: [wait, refund] };\n }\n\n case \"clientredeeming\": {\n // The client's claim (redeem) is in flight — wait for it to confirm, then\n // the funds are received.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"claim_confirmation\",\n recommended: true,\n automation: \"auto\",\n reason: \"Redeeming your funds — waiting for the claim to confirm.\",\n };\n return { recommended: \"wait\", actions: [wait] };\n }\n\n case \"clientredeemed\":\n case \"serverredeemed\": {\n // The client claimed its funds — done from the client's side. The server\n // will sweep the client's deposit with the now-public preimage (and\n // `serverredeemed` means it already has); the client must not try to refund\n // it. Both are terminal successes — nothing to do.\n const done: NoneAction = {\n id: \"none\",\n outcome: \"completed\",\n recommended: true,\n automation: \"auto\",\n reason: \"Swap complete — you've received your funds.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"expired\": {\n // \"Pending → Expired\": the funding window passed without the client\n // funding, so nothing is locked and there is nothing to refund. Terminal.\n const done: NoneAction = {\n id: \"none\",\n outcome: \"expired\",\n recommended: true,\n automation: \"auto\",\n reason: \"The swap expired without funding — nothing was locked.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"clientrefunded\":\n case \"clientrefundedserverrefunded\":\n case \"clientrefundedserverfunded\": {\n // Terminal: the client's deposit was refunded. (`*serverrefunded` also\n // returned the server's; `clientrefundedserverfunded` is the backend's\n // \"should never occur\" error where the server funded after the client\n // refunded — the server's problem, not the client's.) Nothing to do.\n const done: NoneAction = {\n id: \"none\",\n outcome: \"refunded\",\n recommended: true,\n automation: \"auto\",\n reason: \"Refunded — your deposit was returned.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"clientredeemedandclientrefunded\": {\n // Error state: the client both redeemed and refunded (took both sides).\n // Anomalous but terminal — nothing for the client to do.\n const done: NoneAction = {\n id: \"none\",\n // The client holds both sides' funds — \"completed\" from its viewpoint.\n outcome: \"completed\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"This swap ended in an anomalous state (redeemed and refunded); no action needed.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"clientfundedserverrefunded\":\n case \"serverwontfund\":\n case \"clientfundedtoolate\":\n case \"clientinvalidfunded\": {\n // The swap failed with the client's deposit funded — the only path is to\n // reclaim it via the client's refund timelock. (No happy-path wait: unlike\n // `clientfunded`, the swap will not complete.)\n const refundUnlocked = clientChainNow >= clientRefundLocktime;\n\n if (refundUnlocked) {\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: true,\n automation: \"confirm\",\n reason: \"Reclaim your deposit — the swap did not complete.\",\n };\n return { recommended: \"refund_unilateral\", actions: [refund] };\n }\n\n // Refund not unlocked yet: wait for the client timelock, then refund.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"refund_timelock\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"The swap did not complete; waiting for the refund timelock to reclaim your deposit.\",\n };\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: false,\n automation: \"confirm\",\n reason: \"Refund becomes available once the timelock passes.\",\n blockedBy: {\n kind: \"timelock_not_expired\",\n message: \"The refund timelock has not passed yet.\",\n },\n };\n return { recommended: \"wait\", actions: [wait, refund] };\n }\n\n default:\n return assertNever(status);\n }\n}\n\n/** Exhaustiveness guard: a compile error here means a `SwapStatus` case is missing. */\nfunction assertNever(status: never): never {\n throw new Error(`unhandled swap status: ${String(status)}`);\n}\n","/**\n * Derive a swap's lifecycle status from on-chain observations alone — the\n * trusted, server-independent source of truth.\n *\n * Pure: `deriveSwapStatus(observations) -> SwapStatus | undefined`. The wrapper\n * gathers the {@link SwapObservations} from chain (the server's status is only a\n * hint used to fetch efficiently), then cross-checks: if this disagrees with the\n * server, act on this. If the server is unavailable, derive from the local\n * recovery bundle's HTLC addresses — recovery still works.\n *\n * Returns `undefined` for contradictory/indeterminate observations (e.g. the\n * server's HTLC swept-by-claim while the client's HTLC was never involved — the\n * server can't hold the preimage before the client reveals it).\n *\n * Scope: this maps *settled* HTLC state to a status. Time- or param-dependent\n * refinements aren't derivable from HTLC state alone and are layered on\n * afterwards (or collapse into the state here):\n * - `expired` / `clientfundedtoolate`: `pending`/`clientfunded` + a clock check.\n * - `clientinvalidfunded`: needs the expected HTLC params, not just \"confirmed\".\n * - `serverwontfund`: a server intention, invisible on-chain — acts like\n * `clientfunded` (refund after the timelock), so it never needs deriving.\n * - `clientredeeming`: an in-flight (mempool) claim — a transient, not modelled\n * from settled state here.\n */\nimport type { SwapStatus } from \"@lendasat/lendaswap-sdk-pure\";\nimport type { HtlcObservation, SwapObservations } from \"./types.js\";\n\nexport function deriveSwapStatus(\n obs: SwapObservations,\n): SwapStatus | undefined {\n const { clientHtlc, serverHtlc } = obs;\n\n // Lightning swaps watch a single on-chain leg; the other side is an off-chain\n // Lightning payment with no HTLC (see SwapObservations). Derive from the leg\n // that exists.\n if (serverHtlc === undefined && clientHtlc !== undefined) {\n return receiveOnLightningStatus(clientHtlc); // *_to_lightning\n }\n if (clientHtlc === undefined && serverHtlc !== undefined) {\n return payOnLightningStatus(serverHtlc); // lightning_to_*\n }\n if (clientHtlc === undefined || serverHtlc === undefined) return undefined;\n\n // A spend on either HTLC has resolved (part of) the swap — check these first.\n if (clientHtlc === \"spent_claim\" && serverHtlc === \"spent_claim\") {\n return \"serverredeemed\"; // client redeemed, server swept — fully complete\n }\n if (clientHtlc === \"spent_refund\" && serverHtlc === \"spent_claim\") {\n return \"clientredeemedandclientrefunded\"; // client took both sides (anomaly)\n }\n if (clientHtlc === \"spent_refund\" && serverHtlc === \"spent_refund\") {\n return \"clientrefundedserverrefunded\";\n }\n if (clientHtlc === \"spent_refund\" && serverHtlc === \"confirmed\") {\n return \"clientrefundedserverfunded\"; // error state per the backend\n }\n if (\n clientHtlc === \"spent_refund\" &&\n (serverHtlc === \"absent\" || serverHtlc === \"mempool\")\n ) {\n return \"clientrefunded\";\n }\n if (clientHtlc === \"confirmed\" && serverHtlc === \"spent_claim\") {\n return \"clientredeemed\"; // client claimed the server's HTLC\n }\n if (clientHtlc === \"confirmed\" && serverHtlc === \"spent_refund\") {\n return \"clientfundedserverrefunded\"; // server let its HTLC time out\n }\n\n // Client funded, but not on the swap's terms (e.g. under the expected amount) —\n // the swap won't complete honestly, so reclaim the deposit.\n if (clientHtlc === \"invalid\") return \"clientinvalidfunded\";\n\n // No relevant spend — funding progression. `serverfunded` requires the server's\n // HTLC to be `confirmed` (correct amount/terms); an `invalid` server funding is\n // deliberately NOT serverfunded, so the client never claims a bad server leg —\n // it stays `clientfunded` and the client refunds after its timelock.\n if (clientHtlc === \"absent\") return \"pending\";\n if (clientHtlc === \"mempool\") return \"clientfundingseen\";\n if (clientHtlc === \"confirmed\") {\n return serverHtlc === \"confirmed\" ? \"serverfunded\" : \"clientfunded\";\n }\n\n // Contradictory (e.g. the client's HTLC swept-by-claim while the server's HTLC\n // isn't also claimed — impossible without the preimage having been revealed).\n return undefined;\n}\n\n/**\n * Receive-on-Lightning (`arkade_to_lightning`, `evm_to_lightning`): the client\n * funds a single on-chain HTLC and receives via Lightning. There is no\n * client-claimed server leg — the server sweeping the client's HTLC with the\n * preimage (it obtained by paying the client's Lightning invoice) is the success\n * signal. So the whole lifecycle derives from the client's leg alone.\n */\nfunction receiveOnLightningStatus(clientHtlc: HtlcObservation): SwapStatus {\n switch (clientHtlc) {\n case \"spent_claim\":\n return \"serverredeemed\"; // server swept the deposit ⟹ it paid the invoice\n case \"spent_refund\":\n return \"clientrefunded\";\n case \"invalid\":\n return \"clientinvalidfunded\";\n case \"confirmed\":\n return \"clientfunded\"; // funded; waiting for the server to pay + sweep\n case \"mempool\":\n return \"clientfundingseen\";\n case \"absent\":\n return \"pending\";\n }\n}\n\n/**\n * Pay-on-Lightning (`lightning_to_arkade`, `lightning_to_evm`): the client pays a\n * Lightning invoice (off-chain, nothing for us to watch) and claims a single\n * on-chain HTLC. That claim completes the swap. The client has no on-chain\n * deposit at risk, so there is never a refund — a hold invoice that never settles\n * is auto-cancelled by the Lightning wallet.\n */\nfunction payOnLightningStatus(serverHtlc: HtlcObservation): SwapStatus {\n switch (serverHtlc) {\n case \"spent_claim\":\n return \"clientredeemed\"; // client claimed the on-chain leg ⟹ complete\n case \"confirmed\":\n return \"serverfunded\"; // ready to claim\n case \"spent_refund\":\n // The server reclaimed its leg — the swap failed; the client's Lightning\n // payment unwinds on its own. Terminal, nothing on-chain for the client.\n return \"clientrefunded\";\n case \"invalid\":\n case \"mempool\":\n case \"absent\":\n // Nothing (correct) to claim yet — keep waiting for the invoice to land as a\n // funded HTLC. `invalid` (wrong amount/terms) also stays a wait: the client\n // must not claim a bad leg, and the Lightning payment will unwind.\n return \"pending\";\n }\n}\n","/**\n * @satora/swap — the Satora swap client.\n *\n * A standalone `Client` with the same public surface as the legacy\n * `@lendasat/lendaswap-sdk-pure` client, so it is a drop-in replacement. For\n * now every legacy method is forwarded to an internally-owned legacy client\n * instance (see the `@deprecated` delegators below). New Satora-native features\n * are added directly on this class, and\n * individual delegators get replaced with native implementations over time.\n */\nimport {\n DEFAULT_ELECTRUM_WS_URLS,\n Client as LegacyClient,\n type ClientBuilder as LegacyClientBuilder,\n type SwapStatus,\n} from \"@lendasat/lendaswap-sdk-pure\";\nimport type { SwapActions } from \"./actions/types.js\";\nimport { ArkadeContractManager } from \"./contracts/arkade-manager.js\";\nimport { defaultArkadeServerUrl } from \"./contracts/arkade-network.js\";\nimport { BitcoinContractManager } from \"./contracts/bitcoin-manager.js\";\nimport { DEFAULT_ESPLORA_URLS } from \"./contracts/bitcoin-reader-esplora.js\";\nimport { EvmContractManager } from \"./contracts/evm-manager.js\";\nimport { defaultEvmReaders } from \"./contracts/evm-reader-viem.js\";\nimport type { ContractManager, Ledger } from \"./contracts/types.js\";\nimport { SwapWorker } from \"./hints/swap-worker.js\";\nimport { WsStatusSource } from \"./hints/ws-status-source.js\";\nimport { swapToTracked } from \"./tracker/from-swap.js\";\nimport {\n type ActionSubscriber,\n SwapTracker,\n type TrackedSwap,\n} from \"./tracker/swap-tracker.js\";\n\n/**\n * Hint-driven auto-execution, opt-in via {@link ClientBuilder.withAutoClaim}.\n *\n * When set, tracking also opens the server status WebSocket (a faster trigger\n * than the chain poll) and runs a {@link SwapWorker}: it auto-claims a swap the\n * moment the chain confirms it is claimable, and surfaces the rest — a manual\n * `fund`, or a refund the user must confirm — via {@link onActionRequired}.\n */\ntype AutoClaimConfig = {\n /**\n * Surface an action that needs the user rather than being auto-run — a manual\n * `fund`, or a refund to confirm. The seam a frontend notification center\n * plugs into. Omit to only auto-claim and ignore the rest.\n */\n onActionRequired?: (swapId: string, actions: SwapActions) => void;\n};\n\n/** How the client should set up observe-mode tracking. */\ntype TrackingConfig = {\n /** Whether tracking is enabled at all (default on; `withoutTracking()` clears). */\n enabled: boolean;\n /**\n * The swap server URL (from `withBaseUrl`). When set, tracking opens the\n * server's status WebSocket as a HINT feed: a pushed transition triggers a\n * targeted chain verify, so the chain pollers can stay slow. Chain reads\n * remain the source of truth.\n */\n serverUrl?: string;\n /** Explicit per-ledger managers, bypassing auto-construction (advanced/testing). */\n managers?: Map<Ledger, ContractManager>;\n /** Ark server URL override; defaults to the mainnet server when unset. */\n arkadeServerUrl?: string;\n /** Per-chain EVM RPC overrides; the EVM manager is auto-built either way. */\n evmRpcUrls?: Record<number, string>;\n /** Esplora REST URL override for the Bitcoin manager; defaults to mainnet mempool.space. */\n esploraUrl?: string;\n /**\n * Electrum WebSocket URL (Fulcrum). When set, the Bitcoin manager reads via\n * Electrum with scripthash-subscription push reconciles; esplora stays the\n * fallback on Electrum errors.\n */\n electrumWsUrl?: string;\n /**\n * Network the Bitcoin HTLC addresses live on — needed by the Electrum\n * reader to compute scripthashes. Defaults to mainnet; a signet/regtest\n * deployment must set it or Electrum address decoding fails.\n */\n bitcoinNetwork?: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\";\n /**\n * Confirmations a Bitcoin funding tx needs before it observes as\n * `confirmed` (gating e.g. the evm→bitcoin claim). Default `0`: accept\n * 0-conf, trusting the funder not to double-spend. `1` = wait for a block.\n */\n bitcoinMinConfirmations?: number;\n /**\n * Local tick interval (ms) — recomputes actions off extrapolated clocks, no\n * chain reads. Chain reads happen on server hints and, for swaps whose\n * client leg holds funds, on the tracker's at-risk cadence. Defaults to 5s.\n */\n refreshIntervalMs?: number;\n /** Opt-in hint-driven auto-claim; unset leaves tracking observe-only. */\n autoClaim?: AutoClaimConfig;\n};\n\nexport class Client {\n /** The wrapped legacy client. Calls are forwarded here until migrated. */\n readonly #legacy: LegacyClient;\n /** Tracking configuration; managers are built lazily from it on first use. */\n readonly #tracking: TrackingConfig;\n /** Per-ledger monitors, resolved once from {@link #tracking}. */\n #managers: Map<Ledger, ContractManager> | undefined;\n #tracker: SwapTracker | undefined;\n /** Hint-driven auto-claim worker; only built when `withAutoClaim` opted in. */\n #worker: SwapWorker | undefined;\n #startPromise?: Promise<void>;\n /** Swap ids whose settled status was already re-fetched this session. */\n readonly #settledSyncDone = new Set<string>();\n\n /**\n * Creates a new Client instance.\n *\n * Prefer {@link Client.builder} for new code.\n */\n constructor(...args: ConstructorParameters<typeof LegacyClient>);\n /** @internal Wrap an already-built legacy client, optionally with tracking. */\n constructor(legacy: LegacyClient, tracking?: TrackingConfig);\n constructor(\n ...args:\n | ConstructorParameters<typeof LegacyClient>\n | [legacy: LegacyClient, tracking?: TrackingConfig]\n ) {\n if (args[0] instanceof LegacyClient) {\n this.#legacy = args[0];\n this.#tracking = (args[1] as TrackingConfig | undefined) ?? {\n enabled: false,\n };\n } else {\n this.#legacy = new LegacyClient(\n ...(args as ConstructorParameters<typeof LegacyClient>),\n );\n // A raw (non-builder) client has no tracking config to build from.\n this.#tracking = { enabled: false };\n }\n }\n\n /** Start building a {@link Client}. */\n static builder(): ClientBuilder {\n return new ClientBuilder();\n }\n\n /** Escape hatch: the underlying legacy client, during migration. */\n get legacy(): LegacyClient {\n return this.#legacy;\n }\n\n // --- delegated legacy surface (getters) ---\n\n /** Delegated to the legacy client (migration checkpoint). */\n get api(): LegacyClient[\"api\"] {\n return this.#legacy.api;\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n get baseUrl(): LegacyClient[\"baseUrl\"] {\n return this.#legacy.baseUrl;\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n get cctpInbound(): LegacyClient[\"cctpInbound\"] {\n return this.#legacy.cctpInbound;\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n get swapStorage(): LegacyClient[\"swapStorage\"] {\n return this.#legacy.swapStorage;\n }\n\n // --- delegated legacy surface (methods) ---\n\n /** Delegated to the legacy client (migration checkpoint). */\n subscribeToSwaps(\n ...args: Parameters<LegacyClient[\"subscribeToSwaps\"]>\n ): ReturnType<LegacyClient[\"subscribeToSwaps\"]> {\n return this.#legacy.subscribeToSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n unsubscribeFromSwaps(\n ...args: Parameters<LegacyClient[\"unsubscribeFromSwaps\"]>\n ): ReturnType<LegacyClient[\"unsubscribeFromSwaps\"]> {\n return this.#legacy.unsubscribeFromSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n closeSwapStatusSocket(\n ...args: Parameters<LegacyClient[\"closeSwapStatusSocket\"]>\n ): ReturnType<LegacyClient[\"closeSwapStatusSocket\"]> {\n return this.#legacy.closeSwapStatusSocket(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getMnemonic(\n ...args: Parameters<LegacyClient[\"getMnemonic\"]>\n ): ReturnType<LegacyClient[\"getMnemonic\"]> {\n return this.#legacy.getMnemonic(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n loadMnemonic(\n ...args: Parameters<LegacyClient[\"loadMnemonic\"]>\n ): ReturnType<LegacyClient[\"loadMnemonic\"]> {\n return this.#legacy.loadMnemonic(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getNostrKeyHex(\n ...args: Parameters<LegacyClient[\"getNostrKeyHex\"]>\n ): ReturnType<LegacyClient[\"getNostrKeyHex\"]> {\n return this.#legacy.getNostrKeyHex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getUserIdXpub(\n ...args: Parameters<LegacyClient[\"getUserIdXpub\"]>\n ): ReturnType<LegacyClient[\"getUserIdXpub\"]> {\n return this.#legacy.getUserIdXpub(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n deriveSwapParams(\n ...args: Parameters<LegacyClient[\"deriveSwapParams\"]>\n ): ReturnType<LegacyClient[\"deriveSwapParams\"]> {\n return this.#legacy.deriveSwapParams(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n deriveSwapParamsAtIndex(\n ...args: Parameters<LegacyClient[\"deriveSwapParamsAtIndex\"]>\n ): ReturnType<LegacyClient[\"deriveSwapParamsAtIndex\"]> {\n return this.#legacy.deriveSwapParamsAtIndex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getEvmAddress(\n ...args: Parameters<LegacyClient[\"getEvmAddress\"]>\n ): ReturnType<LegacyClient[\"getEvmAddress\"]> {\n return this.#legacy.getEvmAddress(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getKeyIndex(\n ...args: Parameters<LegacyClient[\"getKeyIndex\"]>\n ): ReturnType<LegacyClient[\"getKeyIndex\"]> {\n return this.#legacy.getKeyIndex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n setKeyIndex(\n ...args: Parameters<LegacyClient[\"setKeyIndex\"]>\n ): ReturnType<LegacyClient[\"setKeyIndex\"]> {\n return this.#legacy.setKeyIndex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getStatus(\n ...args: Parameters<LegacyClient[\"getStatus\"]>\n ): ReturnType<LegacyClient[\"getStatus\"]> {\n return this.#legacy.getStatus(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n healthCheck(\n ...args: Parameters<LegacyClient[\"healthCheck\"]>\n ): ReturnType<LegacyClient[\"healthCheck\"]> {\n return this.#legacy.healthCheck(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getVersion(\n ...args: Parameters<LegacyClient[\"getVersion\"]>\n ): ReturnType<LegacyClient[\"getVersion\"]> {\n return this.#legacy.getVersion(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSupportAgents(\n ...args: Parameters<LegacyClient[\"getSupportAgents\"]>\n ): ReturnType<LegacyClient[\"getSupportAgents\"]> {\n return this.#legacy.getSupportAgents(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getMtp(\n ...args: Parameters<LegacyClient[\"getMtp\"]>\n ): ReturnType<LegacyClient[\"getMtp\"]> {\n return this.#legacy.getMtp(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getTokens(\n ...args: Parameters<LegacyClient[\"getTokens\"]>\n ): ReturnType<LegacyClient[\"getTokens\"]> {\n return this.#legacy.getTokens(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSwapPairs(\n ...args: Parameters<LegacyClient[\"getSwapPairs\"]>\n ): ReturnType<LegacyClient[\"getSwapPairs\"]> {\n return this.#legacy.getSwapPairs(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getChainConfig(\n ...args: Parameters<LegacyClient[\"getChainConfig\"]>\n ): ReturnType<LegacyClient[\"getChainConfig\"]> {\n return this.#legacy.getChainConfig(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getReferralFee(\n ...args: Parameters<LegacyClient[\"getReferralFee\"]>\n ): ReturnType<LegacyClient[\"getReferralFee\"]> {\n return this.#legacy.getReferralFee(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getNetworkFees(\n ...args: Parameters<LegacyClient[\"getNetworkFees\"]>\n ): ReturnType<LegacyClient[\"getNetworkFees\"]> {\n return this.#legacy.getNetworkFees(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getDexQuote(\n ...args: Parameters<LegacyClient[\"getDexQuote\"]>\n ): ReturnType<LegacyClient[\"getDexQuote\"]> {\n return this.#legacy.getDexQuote(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n composeQuote(\n ...args: Parameters<LegacyClient[\"composeQuote\"]>\n ): ReturnType<LegacyClient[\"composeQuote\"]> {\n return this.#legacy.composeQuote(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getQuote(\n ...args: Parameters<LegacyClient[\"getQuote\"]>\n ): ReturnType<LegacyClient[\"getQuote\"]> {\n return this.#legacy.getQuote(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSwap(\n ...args: Parameters<LegacyClient[\"getSwap\"]>\n ): ReturnType<LegacyClient[\"getSwap\"]> {\n return this.#legacy.getSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getBulkStatus(\n ...args: Parameters<LegacyClient[\"getBulkStatus\"]>\n ): ReturnType<LegacyClient[\"getBulkStatus\"]> {\n return this.#legacy.getBulkStatus(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getStoredSwap(\n ...args: Parameters<LegacyClient[\"getStoredSwap\"]>\n ): ReturnType<LegacyClient[\"getStoredSwap\"]> {\n return this.#legacy.getStoredSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n listAllSwaps(\n ...args: Parameters<LegacyClient[\"listAllSwaps\"]>\n ): ReturnType<LegacyClient[\"listAllSwaps\"]> {\n return this.#legacy.listAllSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n deleteSwap(\n ...args: Parameters<LegacyClient[\"deleteSwap\"]>\n ): ReturnType<LegacyClient[\"deleteSwap\"]> {\n return this.#legacy.deleteSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n clearSwapStorage(\n ...args: Parameters<LegacyClient[\"clearSwapStorage\"]>\n ): ReturnType<LegacyClient[\"clearSwapStorage\"]> {\n return this.#legacy.clearSwapStorage(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n recoverSwaps(\n ...args: Parameters<LegacyClient[\"recoverSwaps\"]>\n ): ReturnType<LegacyClient[\"recoverSwaps\"]> {\n return this.#legacy.recoverSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n recoverAllSwaps(\n ...args: Parameters<LegacyClient[\"recoverAllSwaps\"]>\n ): ReturnType<LegacyClient[\"recoverAllSwaps\"]> {\n return this.#legacy.recoverAllSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n amountsForSwap(\n ...args: Parameters<LegacyClient[\"amountsForSwap\"]>\n ): ReturnType<LegacyClient[\"amountsForSwap\"]> {\n return this.#legacy.amountsForSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n claim(\n ...args: Parameters<LegacyClient[\"claim\"]>\n ): ReturnType<LegacyClient[\"claim\"]> {\n return this.#legacy.claim(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n claimViaGasless(\n ...args: Parameters<LegacyClient[\"claimViaGasless\"]>\n ): ReturnType<LegacyClient[\"claimViaGasless\"]> {\n return this.#legacy.claimViaGasless(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n claimArkade(\n ...args: Parameters<LegacyClient[\"claimArkade\"]>\n ): ReturnType<LegacyClient[\"claimArkade\"]> {\n return this.#legacy.claimArkade(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n continueArkadeClaimSwap(\n ...args: Parameters<LegacyClient[\"continueArkadeClaimSwap\"]>\n ): ReturnType<LegacyClient[\"continueArkadeClaimSwap\"]> {\n return this.#legacy.continueArkadeClaimSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n refundSwap(\n ...args: Parameters<LegacyClient[\"refundSwap\"]>\n ): ReturnType<LegacyClient[\"refundSwap\"]> {\n return this.#legacy.refundSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getCollabRefundEvmParams(\n ...args: Parameters<LegacyClient[\"getCollabRefundEvmParams\"]>\n ): ReturnType<LegacyClient[\"getCollabRefundEvmParams\"]> {\n return this.#legacy.getCollabRefundEvmParams(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n buildCollabRefundEvmTypedData(\n ...args: Parameters<LegacyClient[\"buildCollabRefundEvmTypedData\"]>\n ): ReturnType<LegacyClient[\"buildCollabRefundEvmTypedData\"]> {\n return this.#legacy.buildCollabRefundEvmTypedData(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n collabRefundEvmSwap(\n ...args: Parameters<LegacyClient[\"collabRefundEvmSwap\"]>\n ): ReturnType<LegacyClient[\"collabRefundEvmSwap\"]> {\n return this.#legacy.collabRefundEvmSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n submitCollabRefundEvm(\n ...args: Parameters<LegacyClient[\"submitCollabRefundEvm\"]>\n ): ReturnType<LegacyClient[\"submitCollabRefundEvm\"]> {\n return this.#legacy.submitCollabRefundEvm(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createSwap(\n ...args: Parameters<LegacyClient[\"createSwap\"]>\n ): ReturnType<LegacyClient[\"createSwap\"]> {\n return this.#trackAfterCreate(this.#legacy.createSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createArkadeToEvmSwapGeneric(\n ...args: Parameters<LegacyClient[\"createArkadeToEvmSwapGeneric\"]>\n ): ReturnType<LegacyClient[\"createArkadeToEvmSwapGeneric\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createArkadeToEvmSwapGeneric(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createBitcoinToEvmSwap(\n ...args: Parameters<LegacyClient[\"createBitcoinToEvmSwap\"]>\n ): ReturnType<LegacyClient[\"createBitcoinToEvmSwap\"]> {\n return this.#trackAfterCreate(this.#legacy.createBitcoinToEvmSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createBitcoinToArkadeSwap(\n ...args: Parameters<LegacyClient[\"createBitcoinToArkadeSwap\"]>\n ): ReturnType<LegacyClient[\"createBitcoinToArkadeSwap\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createBitcoinToArkadeSwap(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createLightningToArkadeSwap(\n ...args: Parameters<LegacyClient[\"createLightningToArkadeSwap\"]>\n ): ReturnType<LegacyClient[\"createLightningToArkadeSwap\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createLightningToArkadeSwap(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createArkadeToLightningSwap(\n ...args: Parameters<LegacyClient[\"createArkadeToLightningSwap\"]>\n ): ReturnType<LegacyClient[\"createArkadeToLightningSwap\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createArkadeToLightningSwap(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createEvmToArkadeSwapGeneric(\n ...args: Parameters<LegacyClient[\"createEvmToArkadeSwapGeneric\"]>\n ): ReturnType<LegacyClient[\"createEvmToArkadeSwapGeneric\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createEvmToArkadeSwapGeneric(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createEvmToBitcoinSwap(\n ...args: Parameters<LegacyClient[\"createEvmToBitcoinSwap\"]>\n ): ReturnType<LegacyClient[\"createEvmToBitcoinSwap\"]> {\n return this.#trackAfterCreate(this.#legacy.createEvmToBitcoinSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getCoordinatorFundingCallDataPermit2(\n ...args: Parameters<LegacyClient[\"getCoordinatorFundingCallDataPermit2\"]>\n ): ReturnType<LegacyClient[\"getCoordinatorFundingCallDataPermit2\"]> {\n return this.#legacy.getCoordinatorFundingCallDataPermit2(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getPermit2FundingParamsUnsigned(\n ...args: Parameters<LegacyClient[\"getPermit2FundingParamsUnsigned\"]>\n ): ReturnType<LegacyClient[\"getPermit2FundingParamsUnsigned\"]> {\n return this.#legacy.getPermit2FundingParamsUnsigned(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n fundSwap(\n ...args: Parameters<LegacyClient[\"fundSwap\"]>\n ): ReturnType<LegacyClient[\"fundSwap\"]> {\n return this.#legacy.fundSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n refundEvmWithSigner(\n ...args: Parameters<LegacyClient[\"refundEvmWithSigner\"]>\n ): ReturnType<LegacyClient[\"refundEvmWithSigner\"]> {\n return this.#legacy.refundEvmWithSigner(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n collabRefundEvmWithSigner(\n ...args: Parameters<LegacyClient[\"collabRefundEvmWithSigner\"]>\n ): ReturnType<LegacyClient[\"collabRefundEvmWithSigner\"]> {\n return this.#legacy.collabRefundEvmWithSigner(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n fundSwapGasless(\n ...args: Parameters<LegacyClient[\"fundSwapGasless\"]>\n ): ReturnType<LegacyClient[\"fundSwapGasless\"]> {\n return this.#legacy.fundSwapGasless(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSwapDepositorKey(\n ...args: Parameters<LegacyClient[\"getSwapDepositorKey\"]>\n ): ReturnType<LegacyClient[\"getSwapDepositorKey\"]> {\n return this.#legacy.getSwapDepositorKey(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getEvmDepositorKey(\n ...args: Parameters<LegacyClient[\"getEvmDepositorKey\"]>\n ): ReturnType<LegacyClient[\"getEvmDepositorKey\"]> {\n return this.#legacy.getEvmDepositorKey(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getRefundedEvmSwapContinuation(\n ...args: Parameters<LegacyClient[\"getRefundedEvmSwapContinuation\"]>\n ): ReturnType<LegacyClient[\"getRefundedEvmSwapContinuation\"]> {\n return this.#legacy.getRefundedEvmSwapContinuation(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n continueRefundedEvmSwap(\n ...args: Parameters<LegacyClient[\"continueRefundedEvmSwap\"]>\n ): ReturnType<LegacyClient[\"continueRefundedEvmSwap\"]> {\n return this.#trackAfterRetry(this.#legacy.continueRefundedEvmSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n recoverGaslessFunds(\n ...args: Parameters<LegacyClient[\"recoverGaslessFunds\"]>\n ): ReturnType<LegacyClient[\"recoverGaslessFunds\"]> {\n return this.#legacy.recoverGaslessFunds(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n hasReceivedVtxo(\n ...args: Parameters<LegacyClient[\"hasReceivedVtxo\"]>\n ): ReturnType<LegacyClient[\"hasReceivedVtxo\"]> {\n return this.#legacy.hasReceivedVtxo(...args);\n }\n\n // --- Satora-native features go below ---\n\n /**\n * Start observing the user's active swaps and deriving each one's next action.\n *\n * Loads stored swaps, maps the ones whose ledgers are observable to\n * {@link TrackedSwap}s, and runs the per-ledger monitors. Idempotent. Requires\n * the client to have been built with {@link ClientBuilder.withTracking}.\n * Subscribe with {@link subscribeToActions}; release with\n * {@link stopTracking}.\n */\n startTracking(): Promise<void> {\n if (!this.#tracking.enabled)\n return Promise.reject(\n new Error(\n \"tracking is disabled — remove .withoutTracking() to enable it\",\n ),\n );\n // Cache the in-flight promise, not a boolean: a concurrent caller must\n // await the first call's completion, or it would be told \"started\" while\n // `#tracker` is still unset and crash in `subscribeToActions`.\n this.#startPromise ??= this.#doStartTracking().catch((error) => {\n this.#startPromise = undefined; // let a later call retry cleanly\n throw error;\n });\n return this.#startPromise;\n }\n\n async #doStartTracking(): Promise<void> {\n try {\n const tracker = new SwapTracker(await this.#ensureManagers(), {\n refreshIntervalMs: this.#tracking.refreshIntervalMs ?? 5_000,\n });\n this.#tracker = tracker;\n await tracker.startTracking(await this.#loadTrackedSwaps());\n // Keep STORED statuses converging on chain truth: without this, a swap\n // that settled while the app was closed keeps a stale active status and\n // #loadTrackedSwaps re-tracks it on every future start. Dies with the\n // tracker (stop() drops its subscribers), so no unsub bookkeeping.\n tracker.subscribeToActions((swapId, actions) =>\n this.#syncSettledStatus(swapId, actions),\n );\n this.#startWorker(tracker);\n } catch (error) {\n // A partway failure (e.g. a ledger register/refresh erroring on an RPC or\n // indexer hiccup) left `#tracker`/`#worker` set and some legs registered.\n // Tear them down so `subscribeToActions` still reports \"not started\" and a\n // retry begins clean, instead of leaking registrations/listeners/sockets.\n this.#worker?.stop();\n this.#worker = undefined;\n this.#tracker?.stop();\n this.#tracker = undefined;\n throw error;\n }\n }\n\n /**\n * The chain says this swap is finished (`none`) — if the STORED status still\n * reads active, re-fetch and persist it so the next session's\n * {@link #loadTrackedSwaps} settled-filter catches it (instead of tracking\n * and scanning the swap once per session forever). Fire-and-forget with a\n * per-session dedupe: a failed fetch just means one more one-scan round next\n * session, which is exactly the current behavior. A swap whose stored status\n * is already settled costs nothing (no server call).\n */\n #syncSettledStatus(swapId: string, actions: SwapActions): void {\n if (actions.recommended !== \"none\") return;\n if (this.#settledSyncDone.has(swapId)) return;\n this.#settledSyncDone.add(swapId);\n void (async () => {\n const stored = (await this.listAllSwaps()).find(\n (s) => s.response.id === swapId,\n );\n if (!stored || SETTLED_STORED_STATUSES.has(stored.response.status))\n return;\n await this.getSwap(swapId, { updateStorage: true });\n })().catch((error) => {\n console.warn(\n `Client: refreshing settled status of swap ${swapId} failed:`,\n error,\n );\n });\n }\n\n /** Every stored swap whose ledgers are observable, mapped to a {@link TrackedSwap}. */\n async #loadTrackedSwaps(): Promise<TrackedSwap[]> {\n const swaps = await this.listAllSwaps();\n return swaps\n .filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status))\n .map((s) => {\n // Isolate per swap: one unmappable stored swap (e.g. a legacy record\n // with an unsupported script version) must not prevent every other\n // swap from being tracked and auto-claimed.\n try {\n return swapToTracked(s);\n } catch (error) {\n console.warn(\n `Client: skipping untrackable stored swap ${s.response.id}:`,\n error,\n );\n return undefined;\n }\n })\n .filter((s): s is TrackedSwap => s !== undefined);\n }\n\n /**\n * Run a create, then fold the new swap into tracking.\n *\n * When tracking is active, first REJECT a swap this client can't reach: a leg on\n * an unconfigured chain can be neither observed nor claimed, so creating it would\n * strand funds. Throwing here (before the caller funds it) is deliberately\n * stricter than {@link startTracking}, which only *skips* such a pre-existing\n * swap. Otherwise fold the new swap(s) in — fire-and-forget, so a tracking hiccup\n * never fails or delays the create; a no-op when tracking isn't running (a later\n * {@link startTracking} picks it up from storage).\n */\n async #trackAfterCreate<T>(op: Promise<T>): Promise<T> {\n const result = await op;\n const tracker = this.#tracker;\n if (!tracker) return result;\n\n let swaps: TrackedSwap[];\n try {\n swaps = await this.#loadTrackedSwaps();\n } catch (error) {\n // A storage-read hiccup must not fail a create that already succeeded; the\n // next startTracking reconciles from storage anyway.\n console.warn(\"Client: loading swaps after create failed:\", error);\n return result;\n }\n\n const created = swaps.find((s) => s.swapId === createdSwapId(result));\n if (created && !tracker.canObserve(created))\n throw new Error(\n `Refusing swap ${created.swapId}: a leg is on a chain this client can't reach, ` +\n `so it can be neither tracked nor claimed. Configure the chain ` +\n `(e.g. ClientBuilder.withEvmRpcUrls) before creating it.`,\n );\n void this.#trackAll(tracker, swaps);\n return result;\n }\n\n /**\n * Run a retry, then fold its replacement swap into tracking.\n *\n * Unlike a create there is no unreachable-leg\n * rejection: by the time the retry returns, the old VHTLC has already been\n * collab-refunded into the new one, so refusing here can't protect any funds\n * — and {@link SwapTracker.track} skips a swap it can't observe anyway.\n */\n async #trackAfterRetry<T>(op: Promise<T>): Promise<T> {\n const result = await op;\n const tracker = this.#tracker;\n if (!tracker) return result;\n\n try {\n void this.#trackAll(tracker, await this.#loadTrackedSwaps());\n } catch (error) {\n // Same rationale as #trackAfterCreate: the retry already succeeded.\n console.warn(\"Client: loading swaps after retry failed:\", error);\n }\n return result;\n }\n\n /** Track each given swap the tracker isn't already watching (isolated per swap). */\n async #trackAll(tracker: SwapTracker, swaps: TrackedSwap[]): Promise<void> {\n // Isolate per swap: one that fails to register (and rolls itself back) must\n // not stop the rest of the batch from being tracked.\n for (const swap of swaps) {\n try {\n await tracker.track(swap);\n } catch (error) {\n console.warn(`Client: tracking swap ${swap.swapId} failed:`, error);\n }\n }\n }\n\n /**\n * Wire the server status WebSocket into a {@link SwapWorker} whenever a server\n * URL is configured: pushed status transitions trigger a targeted chain\n * re-verify (`applyHint`), which is what lets the chain pollers stay slow.\n * Auto-EXECUTION on top of that is opt-in: only with `withAutoClaim` does the\n * worker get an `execute` (claims) and an `onActionRequired` surface —\n * otherwise it runs observe-only, hints in, no spends.\n */\n #startWorker(tracker: SwapTracker): void {\n const serverUrl = this.#tracking.serverUrl;\n if (!serverUrl) return;\n const autoClaim = this.#tracking.autoClaim;\n\n const hintSource = new WsStatusSource({ serverUrl });\n const worker = new SwapWorker({\n tracker,\n hintSource,\n execute: autoClaim\n ? async (swapId, actionId) => {\n // The worker only ever auto-runs claims (its AUTO_EXECUTABLE set).\n if (actionId !== \"claim\")\n throw new Error(`refusing to auto-run action '${actionId}'`);\n const result = await this.claim(swapId);\n if (!result.success)\n throw new Error(result.message ?? \"claim failed\");\n }\n : undefined,\n onActionRequired: autoClaim?.onActionRequired,\n });\n this.#worker = worker;\n worker.start();\n }\n\n /**\n * Subscribe to next-action updates: `cb` fires with the current action for each\n * tracked swap immediately, then on every change. Call after\n * {@link startTracking}. Returns an unsubscribe fn.\n */\n subscribeToActions(cb: ActionSubscriber): () => void {\n if (!this.#tracker)\n throw new Error(\"call startTracking() before subscribeToActions()\");\n return this.#tracker.subscribeToActions(cb);\n }\n\n /** Stop tracking and drop subscribers. The managers themselves are not disposed. */\n stopTracking(): void {\n this.#worker?.stop();\n this.#worker = undefined;\n this.#tracker?.stop();\n this.#tracker = undefined;\n this.#startPromise = undefined;\n }\n\n /**\n * Resolve the per-ledger managers once — the explicit override if given, else\n * auto-built from config: an {@link ArkadeContractManager} from the configured\n * Ark server URL (defaulting to the mainnet server), clocked by `getMtp`, and an\n * {@link EvmContractManager} from the tested default RPCs.\n */\n async #ensureManagers(): Promise<Map<Ledger, ContractManager>> {\n if (this.#managers) return this.#managers;\n if (this.#tracking.managers) {\n this.#managers = this.#tracking.managers;\n return this.#managers;\n }\n const managers = new Map<Ledger, ContractManager>();\n const {\n arkadeServerUrl,\n evmRpcUrls,\n esploraUrl,\n electrumWsUrl,\n bitcoinNetwork,\n bitcoinMinConfirmations,\n } = this.#tracking;\n // Arkade + Bitcoin share the Bitcoin MTP clock.\n const chainTime = async () => (await this.getMtp()).mtp * 1000;\n\n // Default to mainnet; dev/other networks override via withArkadeServerUrl.\n const arkadeUrl = arkadeServerUrl ?? defaultArkadeServerUrl(\"bitcoin\");\n if (arkadeUrl) {\n managers.set(\n \"arkade\",\n await ArkadeContractManager.create({ serverUrl: arkadeUrl, chainTime }),\n );\n }\n // EVM tracks out of the box via tested default RPCs; overrides take priority.\n const readers = defaultEvmReaders(evmRpcUrls);\n if (readers.size > 0)\n managers.set(\"evm\", EvmContractManager.fromDeps({ readers }));\n // Bitcoin observes on-chain HTLCs via esplora. Default to the public pair\n // (mempool.space + blockstream.info) with rotation/failover; an explicit URL\n // replaces them (a dev/regtest node must not fail over to mainnet).\n // Electrum defaults per network (Satora's Fulcrum on mainnet, nothing\n // elsewhere); an explicit URL always wins. Esplora stays the fallback,\n // so the default endpoint being down degrades to plain Esplora reads.\n const network = bitcoinNetwork ?? \"mainnet\";\n managers.set(\n \"bitcoin\",\n await BitcoinContractManager.create({\n esploraUrl: esploraUrl ? [esploraUrl] : DEFAULT_ESPLORA_URLS,\n electrumWsUrl: electrumWsUrl ?? DEFAULT_ELECTRUM_WS_URLS[network],\n network,\n chainTime,\n minConfirmations: bitcoinMinConfirmations,\n }),\n );\n this.#managers = managers;\n return managers;\n }\n}\n\n/**\n * Builds a {@link Client}. Mirrors the legacy `ClientBuilder` fluent surface and\n * constructs the legacy client internally, then wraps it.\n */\nexport class ClientBuilder {\n readonly #inner: LegacyClientBuilder = LegacyClient.builder();\n // Observe-mode tracking is on by default; the built client auto-builds its\n // managers from config unless disabled or given an explicit override.\n #trackingEnabled = true;\n #serverUrl: string | undefined;\n #arkadeServerUrl: string | undefined;\n #esploraUrl: string | undefined;\n #electrumWsUrl: string | undefined;\n #bitcoinNetwork: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\" | undefined;\n #bitcoinMinConfirmations: number | undefined;\n #evmRpcUrls: Record<number, string> | undefined;\n #managers: Map<Ledger, ContractManager> | undefined;\n #autoClaim: AutoClaimConfig | undefined;\n\n /**\n * Override the EVM JSON-RPC endpoint per chainId. Optional — tracking uses\n * tested public defaults otherwise; an override is tried first, with the\n * defaults kept as fallbacks.\n */\n withEvmRpcUrls(urls: Record<number, string>): this {\n this.#evmRpcUrls = urls;\n return this;\n }\n\n /**\n * Opt in to hint-driven auto-claim. Tracking then also subscribes to the\n * server status WebSocket (a faster trigger than the chain poll) and, when the\n * chain confirms a swap is claimable, claims it automatically. Actions that\n * need the user — a manual `fund`, or a refund to confirm — are surfaced via\n * `onActionRequired` instead of being run. Off by default: this auto-spends on\n * the user's behalf, so it must be explicit.\n */\n withAutoClaim(options?: {\n onActionRequired?: (swapId: string, actions: SwapActions) => void;\n }): this {\n this.#autoClaim = { onActionRequired: options?.onActionRequired };\n return this;\n }\n\n /** Turn observe-mode tracking off. */\n withoutTracking(): this {\n this.#trackingEnabled = false;\n return this;\n }\n\n /**\n * Advanced: supply per-ledger {@link ContractManager}s explicitly instead of\n * letting the client auto-build them from config (useful for tests or custom\n * chain sources).\n */\n withContractManagers(managers: Map<Ledger, ContractManager>): this {\n this.#managers = managers;\n return this;\n }\n\n withBaseUrl(...args: Parameters<LegacyClientBuilder[\"withBaseUrl\"]>): this {\n this.#inner.withBaseUrl(...args);\n this.#serverUrl = args[0];\n return this;\n }\n\n withReferralCode(\n ...args: Parameters<LegacyClientBuilder[\"withReferralCode\"]>\n ): this {\n this.#inner.withReferralCode(...args);\n return this;\n }\n\n withOrgCode(...args: Parameters<LegacyClientBuilder[\"withOrgCode\"]>): this {\n this.#inner.withOrgCode(...args);\n return this;\n }\n\n withDefaultHeaders(\n ...args: Parameters<LegacyClientBuilder[\"withDefaultHeaders\"]>\n ): this {\n this.#inner.withDefaultHeaders(...args);\n return this;\n }\n\n /**\n * Override the Esplora REST URL. Optional for tracking — the Bitcoin manager\n * otherwise defaults to mainnet mempool.space; set this for a dev/regtest node.\n */\n withEsploraUrl(url: string): this {\n this.#inner.withEsploraUrl(url);\n this.#esploraUrl = url;\n return this;\n }\n\n /**\n * Set an Electrum server WebSocket URL (a Fulcrum `ws`/`wss` port) for\n * Bitcoin claim/refund chain access. When set, the underlying client\n * prefers it over Esplora for lookups and broadcasts, and waits for HTLC\n * fundings via `blockchain.scripthash.subscribe` push instead of polling.\n * Esplora remains the fallback whenever the Electrum server errors.\n */\n withElectrumWsUrl(url: string): this {\n this.#inner.withElectrumWsUrl(url);\n this.#electrumWsUrl = url;\n return this;\n }\n\n /**\n * Declare the Bitcoin network of this deployment's HTLC addresses. Only\n * needed together with {@link withElectrumWsUrl} on a non-mainnet\n * deployment — the Electrum reader decodes addresses to scripthashes and\n * defaults to mainnet parameters.\n */\n withBitcoinNetwork(\n network: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\",\n ): this {\n this.#bitcoinNetwork = network;\n return this;\n }\n\n /**\n * Confirmations a Bitcoin funding tx needs before it observes as `confirmed`\n * (gating e.g. the evm→bitcoin claim). Default `0`: claim as soon as the\n * funding hits the mempool, which TRUSTS the funder not to double-spend it\n * (claiming publishes the preimage, so a funder that then replaced its\n * funding could take both legs). Set `1` (or more) for a block-depth policy\n * that doesn't rely on the funder's good behaviour.\n */\n withBitcoinMinConfirmations(minConfirmations: number): this {\n this.#bitcoinMinConfirmations = minConfirmations;\n return this;\n }\n\n /**\n * Override the Ark server URL. Optional for tracking — it otherwise defaults to\n * the mainnet server; set this to track a non-mainnet (dev/signet) deployment.\n */\n withArkadeServerUrl(url: string): this {\n this.#inner.withArkadeServerUrl(url);\n this.#arkadeServerUrl = url;\n return this;\n }\n\n withSignerStorage(\n ...args: Parameters<LegacyClientBuilder[\"withSignerStorage\"]>\n ): this {\n this.#inner.withSignerStorage(...args);\n return this;\n }\n\n withSwapStorage(\n ...args: Parameters<LegacyClientBuilder[\"withSwapStorage\"]>\n ): this {\n this.#inner.withSwapStorage(...args);\n return this;\n }\n\n withMnemonic(...args: Parameters<LegacyClientBuilder[\"withMnemonic\"]>): this {\n this.#inner.withMnemonic(...args);\n return this;\n }\n\n withXprv(...args: Parameters<LegacyClientBuilder[\"withXprv\"]>): this {\n this.#inner.withXprv(...args);\n return this;\n }\n\n withAa(...args: Parameters<LegacyClientBuilder[\"withAa\"]>): this {\n this.#inner.withAa(...args);\n return this;\n }\n\n withLogger(...args: Parameters<LegacyClientBuilder[\"withLogger\"]>): this {\n this.#inner.withLogger(...args);\n return this;\n }\n\n withLogLevel(...args: Parameters<LegacyClientBuilder[\"withLogLevel\"]>): this {\n this.#inner.withLogLevel(...args);\n return this;\n }\n\n async build(): Promise<Client> {\n return new Client(await this.#inner.build(), {\n enabled: this.#trackingEnabled,\n serverUrl: this.#serverUrl,\n managers: this.#managers,\n arkadeServerUrl: this.#arkadeServerUrl,\n esploraUrl: this.#esploraUrl,\n electrumWsUrl: this.#electrumWsUrl,\n bitcoinNetwork: this.#bitcoinNetwork,\n bitcoinMinConfirmations: this.#bitcoinMinConfirmations,\n evmRpcUrls: this.#evmRpcUrls,\n autoClaim: this.#autoClaim,\n });\n }\n}\n\n/**\n * Stored statuses where the client's money is fully settled — already received,\n * already refunded, or (for `expired`) never deposited: the server only marks a\n * swap expired while it is unfunded (a late-funded one becomes\n * `clientfundedtoolate`, which is NOT in this set). Safe to skip registering\n * entirely, so tracking doesn't re-scan every historical swap on each start;\n * anything ambiguous stays chain-verified.\n */\nconst SETTLED_STORED_STATUSES = new Set<SwapStatus>([\n \"serverredeemed\",\n \"clientrefunded\",\n \"clientrefundedserverfunded\",\n \"clientrefundedserverrefunded\",\n \"clientredeemedandclientrefunded\",\n \"expired\",\n]);\n\n/**\n * The swap id from a create result. Every `create*` result carries the created\n * swap under `response.id`; used to find the just-created swap in storage.\n */\nfunction createdSwapId(result: unknown): string | undefined {\n const id = (result as { response?: { id?: unknown } } | null)?.response?.id;\n return typeof id === \"string\" ? id : undefined;\n}\n","/**\n * The Arkade {@link ContractManager} — the stateful I/O adapter that observes a\n * swap's VHTLC via the Ark indexer.\n *\n * We deliberately do NOT use `@arkade-os/sdk`'s `ContractManager`: its vtxo\n * annotation calls `script.forfeit()` on every contract, but a VHTLC script has\n * no `forfeit()` leaf (it exposes `claim()`/`refund()`), so watching a VHTLC\n * through it throws. Instead we read vtxos directly by pkScript — exactly what\n * the legacy SDK's claim/refund paths do — and map them with the pure helpers in\n * `./arkade.js`.\n *\n * Observation is on-demand: {@link refresh} (tracker start / newly tracked\n * swap) and targeted {@link reconcile}s (server hints, the tracker's at-risk\n * safety net). There is no standing subscription or periodic poll.\n */\nimport { RestIndexerProvider, type VirtualCoin } from \"@arkade-os/sdk\";\nimport { hex } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport type { VirtualTxSource } from \"./arkade.js\";\nimport { arkadeObservation, fetchArkadeSpend } from \"./arkade.js\";\nimport type { ContractManager, HtlcRef, Ledger } from \"./types.js\";\n\n/**\n * The Ark indexer surface the observer needs (RestIndexerProvider satisfies it).\n */\nexport type ArkadeIndexer = VirtualTxSource & {\n getVtxos(opts: {\n scripts: string[];\n spendableOnly?: boolean;\n }): Promise<{ vtxos: VirtualCoin[] }>;\n};\n\nexport type ArkadeCreateConfig = {\n /** Ark server base URL — used for the indexer. */\n serverUrl: string;\n /**\n * The current Bitcoin MTP (ms) for evaluating VHTLC timelocks. Typically\n * `async () => (await client.getMtp()).mtp * 1000`.\n */\n chainTime?: () => Promise<number>;\n};\n\nexport type ArkadeContractManagerDeps = {\n indexer: ArkadeIndexer;\n /**\n * The current Bitcoin MTP (ms) — arkade CLTV timelocks are evaluated against\n * Bitcoin's clock, not the Ark server's. Optional: until wired, `chainNow()`\n * reports `undefined` and the tracker holds swaps as provisional.\n */\n chainTime?: () => Promise<number>;\n};\n\n/** True when a vtxo is a live funding of the contract (not yet spent). */\nfunction isFunded(vtxo: VirtualCoin): boolean {\n const { state } = vtxo.virtualStatus;\n return state === \"preconfirmed\" || state === \"settled\";\n}\n\n/**\n * The offchain txid that spent this contract's VHTLC, if any. Mirrors the backend\n * watcher (`unified_watcher.rs`), which keys spend detection on `arkTxId` — the\n * offchain Arkade tx whose input[0] condition witness carries the revealed\n * preimage. Falls back to `spentBy` (the checkpoint tx, which also reveals it) so\n * a spend is caught under either field: reading only `spentBy` silently missed\n * spent vtxos that expose the spend solely via `arkTxId`, leaving them classified\n * as `confirmed` forever (never emitting spent_claim/spent_refund).\n */\nfunction spendTxid(vtxos: VirtualCoin[]): string | undefined {\n const spent = vtxos.find(\n (v) =>\n v.arkTxId || v.spentBy || v.isSpent || v.virtualStatus.state === \"spent\",\n );\n return spent?.arkTxId ?? spent?.spentBy;\n}\n\nexport class ArkadeContractManager implements ContractManager {\n readonly ledger: Ledger = \"arkade\";\n\n readonly #indexer: ArkadeIndexer;\n readonly #chainTime?: () => Promise<number>;\n\n /** pkScript → the ref we're tracking. */\n readonly #refs = new Map<string, Extract<HtlcRef, { ledger: \"arkade\" }>>();\n /** pkScript → last known observation. */\n readonly #obs = new Map<string, HtlcObservation>();\n /** pkScript → the verified preimage recovered from a claim spend. */\n readonly #preimages = new Map<string, Uint8Array>();\n readonly #listeners = new Set<\n (ref: HtlcRef, state: HtlcObservation) => void\n >();\n\n /** Last MTP reading (ms) + when it was fetched, for extrapolation. */\n #now: { mtpMs: number; fetchedAtMs: number } | undefined;\n\n private constructor(deps: ArkadeContractManagerDeps) {\n this.#indexer = deps.indexer;\n this.#chainTime = deps.chainTime;\n }\n\n static fromDeps(deps: ArkadeContractManagerDeps): ArkadeContractManager {\n return new ArkadeContractManager(deps);\n }\n\n /** Construct a manager backed by a `RestIndexerProvider` over the Ark server. */\n static async create(\n config: ArkadeCreateConfig,\n ): Promise<ArkadeContractManager> {\n const indexer = new RestIndexerProvider(config.serverUrl);\n return ArkadeContractManager.fromDeps({\n indexer,\n chainTime: config.chainTime,\n });\n }\n\n canObserve(ref: HtlcRef): boolean {\n // The Arkade server is always configured (default or override), so any Arkade\n // ref is observable.\n return ref.ledger === \"arkade\";\n }\n\n async register(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"arkade\")\n throw new Error(\n `ArkadeContractManager can't track a '${ref.ledger}' HTLC`,\n );\n this.#refs.set(ref.script, ref);\n }\n\n async unregister(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"arkade\") return;\n this.#refs.delete(ref.script);\n this.#obs.delete(ref.script);\n this.#preimages.delete(ref.script);\n }\n\n getState(ref: HtlcRef): HtlcObservation | undefined {\n return ref.ledger === \"arkade\" ? this.#obs.get(ref.script) : undefined;\n }\n\n chainNow(_ref: HtlcRef): number | undefined {\n // Arkade CLTV timelocks share one clock (Bitcoin MTP), so the ref is unused.\n if (!this.#now) return undefined;\n // Extrapolate between reads: MTP advances with wall-clock (lagging it by a\n // roughly constant margin), so the clock stays current without polling.\n // The worker re-verifies on-chain before acting on any flip this causes.\n return this.#now.mtpMs + Math.max(0, Date.now() - this.#now.fetchedAtMs);\n }\n\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n /**\n * Full reconcile of every tracked ref (clock + vtxos). Called on demand\n * (tracker start / newly tracked swap), NOT periodically: onward, hints and\n * the tracker's at-risk safety net drive targeted `reconcile`s.\n */\n async refresh(): Promise<void> {\n await this.#scanAll();\n }\n\n /** Targeted verify (hint / pre-action path) — never gated. */\n async reconcile(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"arkade\") return;\n const tracked = this.#refs.get(ref.script);\n if (!tracked) return;\n await this.#readClock();\n await this.#reconcileRef(tracked);\n }\n\n dispose(): void {\n this.#listeners.clear();\n }\n\n /** The verified preimage recovered from a claim spend, if one was seen. */\n getPreimage(ref: HtlcRef): Uint8Array | undefined {\n return ref.ledger === \"arkade\"\n ? this.#preimages.get(ref.script)\n : undefined;\n }\n\n /** Read and store the MTP clock (with fetch time, for extrapolation). */\n async #readClock(): Promise<void> {\n if (!this.#chainTime) return;\n this.#now = { mtpMs: await this.#chainTime(), fetchedAtMs: Date.now() };\n }\n\n /** Full scan of every tracked ref (clock + vtxos). */\n async #scanAll(): Promise<void> {\n await this.#readClock();\n await Promise.all(\n [...this.#refs.values()].map((ref) => this.#reconcileRef(ref)),\n );\n }\n\n /** Read the VHTLC's vtxos and map them to an observation. */\n async #reconcileRef(\n ref: Extract<HtlcRef, { ledger: \"arkade\" }>,\n ): Promise<void> {\n const { vtxos } = await this.#indexer.getVtxos({ scripts: [ref.script] });\n const spend = spendTxid(vtxos);\n if (spend) {\n const resolved = await fetchArkadeSpend(\n this.#indexer,\n spend,\n hex.decode(ref.preimageHash),\n );\n if (resolved) {\n if (resolved.spend === \"claim\")\n this.#preimages.set(ref.script, resolved.preimage);\n this.#set(\n ref.script,\n arkadeObservation({ funded: true, spend: resolved.spend }),\n );\n return;\n }\n }\n const funded = vtxos.filter(isFunded);\n const total = funded.reduce((sum, vtxo) => sum + vtxo.value, 0);\n this.#set(\n ref.script,\n arkadeObservation({\n funded: funded.length > 0,\n sufficient: total >= ref.expectedSats,\n }),\n );\n }\n\n /** Update an observation and notify on change; never downgrade a resolved spend. */\n #set(script: string, observation: HtlcObservation): void {\n const ref = this.#refs.get(script);\n if (!ref) return;\n const current = this.#obs.get(script);\n if (current === observation) return;\n // A resolved spend is terminal for the HTLC — a later funded/absent poll\n // (e.g. spent vtxos dropping out of the listing) must not undo it.\n const spendStates: HtlcObservation[] = [\"spent_claim\", \"spent_refund\"];\n if (\n current &&\n spendStates.includes(current) &&\n !spendStates.includes(observation)\n )\n return;\n this.#obs.set(script, observation);\n for (const listener of this.#listeners) listener(ref, observation);\n }\n}\n","/**\n * The `ArkadeContractManager`'s pure core.\n *\n * The manager (built on `@arkade-os/sdk`'s ContractManager) gathers facts about a\n * VHTLC — is it funded, and if spent, how — and this maps them to an\n * {@link HtlcObservation} the swap pipeline consumes.\n */\nimport {\n ConditionWitness,\n getArkPsbtFields,\n Transaction,\n} from \"@arkade-os/sdk\";\nimport { base64 } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { preimageMatches } from \"./preimage.js\";\n\n/** Facts about a VHTLC gathered from the Ark indexer/manager. */\nexport type ArkadeVtxoFacts = {\n /** A vtxo exists at the VHTLC address (it was funded). */\n funded: boolean;\n /**\n * Whether the funding meets the swap's expected amount. Only consulted when\n * `funded`; `false` → the VHTLC is funded but short → `invalid`, not confirmed.\n */\n sufficient?: boolean;\n /**\n * If the VHTLC output was spent, which path the spending tx took — resolved\n * from the spend tx's `ConditionWitness` (preimage present → `claim`).\n */\n spend?: \"claim\" | \"refund\";\n};\n\n/**\n * Map gathered VHTLC facts to an on-chain observation.\n *\n * Note: there is no `mempool` here. An Arkade `preconfirmed` vtxo is server-\n * signed (a far stronger trust assumption than a droppable Bitcoin mempool tx),\n * so a funded VHTLC — preconfirmed or settled — is treated as `confirmed`.\n * `mempool` is reserved for Bitcoin on-chain observers.\n */\nexport function arkadeObservation(facts: ArkadeVtxoFacts): HtlcObservation {\n if (facts.spend === \"claim\") return \"spent_claim\";\n if (facts.spend === \"refund\") return \"spent_refund\";\n if (!facts.funded) return \"absent\";\n return facts.sufficient === false ? \"invalid\" : \"confirmed\";\n}\n\n/** How a VHTLC output was spent. A claim also yields the verified preimage. */\nexport type ArkadeSpend =\n | { spend: \"claim\"; preimage: Uint8Array }\n | { spend: \"refund\" };\n\n/**\n * Classify how a VHTLC output was spent from the spending tx's PSBT.\n *\n * The claim path finalizes the VHTLC's condition-multisig closure, so the spend\n * tx witnesses the preimage in the `ConditionWitness` proprietary field; the\n * unilateral refund path (CSV timelock) never carries it. But we don't trust the\n * field's mere presence — we verify a revealed element actually hashes to *this*\n * swap's `paymentHash` (the SHA-256 preimage hash, i.e. the Lightning\n * payment_hash). That turns \"looks like a claim\" into \"this is the claim of our\n * swap, and here is the verified preimage\" — which the caller needs to settle the\n * counterparty leg (the same reason the backend extracts it in\n * `unified_watcher.rs`). Anything else — no field, or a non-matching field — is a\n * refund.\n *\n * Operates on the already-fetched, base64-encoded PSBT so it stays pure and\n * unit-testable; {@link fetchArkadeSpend} does the indexer I/O.\n */\nexport function classifyArkadeSpend(\n encodedSpendTx: string,\n paymentHash: Uint8Array,\n): ArkadeSpend {\n const tx = Transaction.fromPSBT(base64.decode(encodedSpendTx));\n // Multi-input Arkade spends may reveal the VHTLC condition witness on any\n // input. The condition witness is a stack of elements, one of which (on a\n // claim) is the 32-byte preimage.\n for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {\n for (const witness of getArkPsbtFields(tx, inputIndex, ConditionWitness)) {\n for (const element of witness) {\n if (element.length === 32 && preimageMatches(element, paymentHash)) {\n return { spend: \"claim\", preimage: element };\n }\n }\n }\n }\n return { spend: \"refund\" };\n}\n\n/** The slice of `@arkade-os/sdk`'s indexer the spend classifier needs. */\nexport type VirtualTxSource = {\n getVirtualTxs(txids: string[]): Promise<{ txs: string[] }>;\n};\n\n/**\n * Fetch the tx that spent a VHTLC and classify its path. Returns `undefined`\n * when the indexer can't yet return the spending tx (e.g. not indexed), so the\n * caller keeps the output as `confirmed` rather than guessing.\n */\nexport async function fetchArkadeSpend(\n source: VirtualTxSource,\n spentByTxid: string,\n paymentHash: Uint8Array,\n): Promise<ArkadeSpend | undefined> {\n const { txs } = await source.getVirtualTxs([spentByTxid]);\n const encoded = txs[0];\n return encoded ? classifyArkadeSpend(encoded, paymentHash) : undefined;\n}\n","/**\n * Verify a revealed witness element is the preimage for a swap's hash lock.\n *\n * The hash length selects the algorithm, so callers don't need to know the swap\n * direction: a 20-byte lock is HASH160 = `ripemd160(sha256(preimage))`\n * (`btc_to_arkade`, matching Bitcoin's `OP_HASH160` and the Arkade VHTLC); any\n * other length is `sha256(preimage)` (32 bytes, the EVM/Lightning directions,\n * i.e. the Lightning payment_hash).\n */\nimport { ripemd160 } from \"@noble/hashes/legacy.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { hex } from \"@scure/base\";\n\nexport function preimageMatches(\n element: Uint8Array,\n hashLock: Uint8Array,\n): boolean {\n const digest =\n hashLock.length === 20 ? ripemd160(sha256(element)) : sha256(element);\n return hex.encode(digest) === hex.encode(hashLock);\n}\n","/**\n * Default Arkade server URLs by Bitcoin network, mirroring the legacy SDK's\n * `arkade-network.ts`. Used so tracking can auto-build the Arkade manager without\n * an explicit server URL; a caller can still override via\n * `ClientBuilder.withArkadeServerUrl`.\n */\nconst DEFAULT_ARKADE_URLS: Record<string, string> = {\n bitcoin: \"https://arkade.computer\",\n mainnet: \"https://arkade.computer\",\n signet: \"https://mutinynet.arkade.sh\",\n mutinynet: \"https://mutinynet.arkade.sh\",\n};\n\n/**\n * The default Ark server URL for a Bitcoin network name (case-insensitive), or\n * `undefined` for networks without a public default (e.g. regtest/testnet),\n * where a URL must be supplied explicitly.\n */\nexport function defaultArkadeServerUrl(network: string): string | undefined {\n return DEFAULT_ARKADE_URLS[network.toLowerCase()];\n}\n","/**\n * The Bitcoin {@link ContractManager} — the stateful I/O adapter that observes a\n * swap's on-chain HTLC by polling esplora.\n *\n * Modeled on the Arkade manager: reading is delegated to an injected\n * {@link BitcoinChainReader} (so the package stays free of a bundled esplora\n * client and the adapter is testable against a fake). Observation is on-demand —\n * {@link refresh} at seed time, targeted {@link reconcile}s from hints and the\n * tracker's at-risk safety net. The clock is Bitcoin MTP (the same source the\n * Arkade manager uses), so {@link chainNow} is ref-less.\n */\nimport { hex } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { type BitcoinHtlcFacts, bitcoinObservation } from \"./bitcoin.js\";\nimport type { ContractManager, HtlcRef, Ledger } from \"./types.js\";\n\n/** The chain surface the observer needs (the esplora and electrum readers satisfy it). */\nexport type BitcoinChainReader = {\n /**\n * Funding + spend facts for an HTLC witness-script address. `minConfirmations`\n * overrides the reader's default for this address alone — the two legs of a\n * swap do not share one rule.\n */\n getHtlcFacts(\n address: string,\n minConfirmations?: number,\n ): Promise<BitcoinHtlcFacts>;\n /**\n * Optional push capability: invoke `onChange` whenever the address's tx\n * history changes (e.g. an Electrum scripthash subscription). Returns an\n * unsubscribe function. When present, the manager reconciles on push\n * instead of relying solely on the tracker's poll cadence.\n */\n subscribe?(address: string, onChange: () => void): () => void;\n};\n\nexport type BitcoinCreateConfig = {\n /** One or more esplora REST base URLs; several are tried in rotation with failover. */\n esploraUrl: string | string[];\n /**\n * Optional Electrum WebSocket URL (Fulcrum `ws`/`wss` port). When set, the\n * manager reads via Electrum (fresher than public explorers) and gets push\n * reconciles via scripthash subscriptions; the esplora reader remains the\n * fallback on Electrum errors.\n */\n electrumWsUrl?: string;\n /** Network the HTLC addresses live on (for Electrum scripthashes); default mainnet. */\n network?: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\";\n /** The current Bitcoin MTP (ms); typically `async () => (await client.getMtp()).mtp * 1000`. */\n chainTime?: () => Promise<number>;\n /**\n * Confirmations a funding tx needs before it observes as `confirmed`.\n * Default `0`: accept an unconfirmed funding, trusting the funder not to\n * double-spend it (the claim reveals the preimage against it). `1` = wait\n * for a block.\n */\n minConfirmations?: number;\n};\n\nexport type BitcoinContractManagerDeps = {\n reader: BitcoinChainReader;\n chainTime?: () => Promise<number>;\n};\n\nexport class BitcoinContractManager implements ContractManager {\n readonly ledger: Ledger = \"bitcoin\";\n\n readonly #reader: BitcoinChainReader;\n readonly #chainTime?: () => Promise<number>;\n\n /** address → the ref we're tracking. */\n readonly #refs = new Map<string, Extract<HtlcRef, { ledger: \"bitcoin\" }>>();\n /** address → last known observation. */\n readonly #obs = new Map<string, HtlcObservation>();\n /** address → the preimage a claim revealed. */\n readonly #preimages = new Map<string, Uint8Array>();\n /** address → push-subscription teardown (only when the reader supports push). */\n readonly #subscriptions = new Map<string, () => void>();\n readonly #listeners = new Set<\n (ref: HtlcRef, state: HtlcObservation) => void\n >();\n\n /** Last MTP reading (ms) + when it was fetched, for extrapolation. */\n #now: { mtpMs: number; fetchedAtMs: number } | undefined;\n\n private constructor(deps: BitcoinContractManagerDeps) {\n this.#reader = deps.reader;\n this.#chainTime = deps.chainTime;\n }\n\n static fromDeps(deps: BitcoinContractManagerDeps): BitcoinContractManager {\n return new BitcoinContractManager(deps);\n }\n\n static async create(\n config: BitcoinCreateConfig,\n ): Promise<BitcoinContractManager> {\n const { esploraReader } = await import(\"./bitcoin-reader-esplora.js\");\n let reader: BitcoinChainReader = esploraReader(\n config.esploraUrl,\n undefined,\n { minConfirmations: config.minConfirmations },\n );\n if (config.electrumWsUrl) {\n const [{ electrumReader }, { ElectrumWsClient }] = await Promise.all([\n import(\"./bitcoin-reader-electrum.js\"),\n import(\"@lendasat/lendaswap-sdk-pure\"),\n ]);\n reader = electrumReader(new ElectrumWsClient(config.electrumWsUrl), {\n network: config.network,\n minConfirmations: config.minConfirmations,\n fallback: reader,\n });\n }\n return BitcoinContractManager.fromDeps({\n reader,\n chainTime: config.chainTime,\n });\n }\n\n canObserve(ref: HtlcRef): boolean {\n // Esplora is always configured (default or override), so any Bitcoin ref is\n // observable.\n return ref.ledger === \"bitcoin\";\n }\n\n async register(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"bitcoin\")\n throw new Error(\n `BitcoinContractManager can't track a '${ref.ledger}' HTLC`,\n );\n this.#refs.set(ref.address, ref);\n // Push: reconcile the instant the address's history changes (a reader\n // without the capability leaves the tracker's poll cadence in charge).\n if (this.#reader.subscribe && !this.#subscriptions.has(ref.address)) {\n this.#subscriptions.set(\n ref.address,\n this.#reader.subscribe(ref.address, () => {\n this.#reconcileRef(ref).catch((error) => {\n console.warn(\n \"BitcoinContractManager: push reconcile failed:\",\n error instanceof Error ? error.message : error,\n );\n });\n }),\n );\n }\n await this.#reconcileRef(ref);\n }\n\n async unregister(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"bitcoin\") return;\n this.#subscriptions.get(ref.address)?.();\n this.#subscriptions.delete(ref.address);\n this.#refs.delete(ref.address);\n this.#obs.delete(ref.address);\n this.#preimages.delete(ref.address);\n }\n\n getState(ref: HtlcRef): HtlcObservation | undefined {\n return ref.ledger === \"bitcoin\" ? this.#obs.get(ref.address) : undefined;\n }\n\n chainNow(_ref: HtlcRef): number | undefined {\n // Bitcoin timelocks share one clock (MTP), so the ref is unused.\n if (!this.#now) return undefined;\n // Extrapolate between reads: MTP advances with wall-clock (lagging it by a\n // roughly constant margin), so the clock stays current without polling.\n // The worker re-verifies on-chain before acting on any flip this causes.\n return this.#now.mtpMs + Math.max(0, Date.now() - this.#now.fetchedAtMs);\n }\n\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n async refresh(): Promise<void> {\n await this.#readClock();\n await Promise.all(\n [...this.#refs.values()].map((ref) => this.#reconcileRef(ref)),\n );\n }\n\n async reconcile(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"bitcoin\") return;\n const tracked = this.#refs.get(ref.address);\n if (!tracked) return;\n await this.#readClock();\n await this.#reconcileRef(tracked);\n }\n\n /** Read and store the MTP clock (with fetch time, for extrapolation). */\n async #readClock(): Promise<void> {\n if (!this.#chainTime) return;\n this.#now = { mtpMs: await this.#chainTime(), fetchedAtMs: Date.now() };\n }\n\n dispose(): void {\n for (const unsubscribe of this.#subscriptions.values()) unsubscribe();\n this.#subscriptions.clear();\n this.#listeners.clear();\n }\n\n /** The preimage a claim revealed on this HTLC, if one was seen. */\n getPreimage(ref: HtlcRef): Uint8Array | undefined {\n return ref.ledger === \"bitcoin\"\n ? this.#preimages.get(ref.address)\n : undefined;\n }\n\n async #reconcileRef(\n ref: Extract<HtlcRef, { ledger: \"bitcoin\" }>,\n ): Promise<void> {\n const facts = await this.#reader.getHtlcFacts(\n ref.address,\n ref.minConfirmations,\n );\n const { observation, preimage } = bitcoinObservation(\n facts,\n hex.decode(ref.preimageHash),\n ref.expectedSats,\n );\n if (preimage) this.#preimages.set(ref.address, preimage);\n this.#set(ref.address, observation);\n }\n\n /** Update an observation and notify on change; never downgrade a resolved spend. */\n #set(address: string, observation: HtlcObservation): void {\n const ref = this.#refs.get(address);\n if (!ref) return;\n const current = this.#obs.get(address);\n if (current === observation) return;\n const spendStates: HtlcObservation[] = [\"spent_claim\", \"spent_refund\"];\n if (\n current &&\n spendStates.includes(current) &&\n !spendStates.includes(observation)\n )\n return;\n this.#obs.set(address, observation);\n for (const listener of this.#listeners) listener(ref, observation);\n }\n}\n","/**\n * The `BitcoinContractManager`'s pure core.\n *\n * A Bitcoin HTLC is an on-chain output at a witness-script address. It's observed\n * from esplora: an output paying the address means it's funded; a spend of that\n * output resolves it. The spend's witness reveals the preimage on a claim (a\n * timelock refund does not), so — script-agnostically, as on Arkade/EVM — a spend\n * is a claim iff a witness element hashes to the swap's `paymentHash`.\n */\nimport { hex } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { preimageMatches } from \"./preimage.js\";\n\n/** On-chain funding state of the HTLC address. */\nexport type BitcoinFunding = \"absent\" | \"mempool\" | \"confirmed\";\n\n/** Facts gathered for an HTLC address from esplora. */\nexport type BitcoinHtlcFacts = {\n funding: BitcoinFunding;\n /** Total sats paid to the HTLC address by the funding tx (0 when absent). */\n fundedSats: number;\n /**\n * The witness stack (hex items) of the input that spent the HTLC output, if it\n * was spent. Absent while the output is still unspent.\n */\n spendWitness?: string[];\n};\n\n/** An HTLC's observation, plus the preimage if a claim revealed it. */\nexport type BitcoinHtlcState = {\n observation: HtlcObservation;\n preimage?: Uint8Array;\n};\n\n/**\n * Classify a spend from its witness: a claim path reveals the preimage in the\n * witness (verified against `paymentHash` — SHA-256, or HASH160 for the\n * `btc_to_arkade` script; {@link preimageMatches} picks by length); a timelock\n * refund path does not.\n */\nexport function classifyBitcoinSpend(\n witness: string[],\n paymentHash: Uint8Array,\n): { spend: \"claim\"; preimage: Uint8Array } | { spend: \"refund\" } {\n for (const item of witness) {\n // The preimage is 32 bytes (64 hex chars); skip signatures, pubkeys, scripts.\n if (item.length !== 64) continue;\n const bytes = hex.decode(item);\n if (preimageMatches(bytes, paymentHash))\n return { spend: \"claim\", preimage: bytes };\n }\n return { spend: \"refund\" };\n}\n\n/**\n * Reduce gathered HTLC facts to an observation (+ the preimage on a claim). A\n * confirmed funding below `expectedSats` is `invalid` (funded, but not on the\n * swap's terms) rather than `confirmed`, so the client never claims a\n * short-funded server leg. An unconfirmed (`mempool`) funding isn't actionable\n * yet, so its amount is re-checked once it confirms.\n */\nexport function bitcoinObservation(\n facts: BitcoinHtlcFacts,\n paymentHash: Uint8Array,\n expectedSats: number,\n): BitcoinHtlcState {\n if (facts.spendWitness) {\n const spend = classifyBitcoinSpend(facts.spendWitness, paymentHash);\n return spend.spend === \"claim\"\n ? { observation: \"spent_claim\", preimage: spend.preimage }\n : { observation: \"spent_refund\" };\n }\n if (facts.funding === \"confirmed\" && facts.fundedSats < expectedSats) {\n return { observation: \"invalid\" };\n }\n return { observation: facts.funding };\n}\n","/**\n * The `EvmContractManager`'s pure core.\n *\n * An EVM HTLC (an `HTLCErc20` swap) exposes its lifecycle as three events, all\n * indexed by the swap's `preimageHash`: `SwapCreated` (funded), `SwapRedeemed`\n * (claimed — reveals the preimage), and `SwapRefunded` (reclaimed after the\n * timelock). This maps a decoded event history to an {@link HtlcObservation}.\n */\nimport type { HtlcObservation } from \"../actions/types.js\";\n\n/** One decoded `HTLCErc20` lifecycle event for a single swap's `preimageHash`. */\nexport type EvmHtlcEvent =\n | { kind: \"created\"; amount: bigint; token: `0x${string}` }\n | { kind: \"redeemed\"; preimage: `0x${string}` }\n | { kind: \"refunded\" };\n\n/** An EVM HTLC's observation, plus the preimage if a claim revealed it. */\nexport type EvmHtlcState = {\n observation: HtlcObservation;\n preimage?: `0x${string}`;\n};\n\n/** What a `SwapCreated` must satisfy to be `confirmed` (else `invalid`). */\nexport type EvmExpectation = {\n /** Minimum locked amount (token's smallest unit). */\n amount: bigint;\n /** Expected token address; verified when provided. */\n token?: `0x${string}`;\n};\n\n/**\n * Reduce an HTLC's on-chain event history to an observation.\n *\n * A terminal spend wins over funding regardless of ordering: `SwapRedeemed`\n * reveals the preimage → `spent_claim` (plus the recovered preimage, which the\n * caller needs to settle the counterparty leg — as on Arkade); `SwapRefunded` →\n * `spent_refund`. A `SwapCreated` is `confirmed` only if it locks at least\n * `expected.amount` of the expected token — otherwise `invalid`, so the client\n * never claims a leg funded on the wrong terms. `pending` marks a broadcast-but-\n * unmined funding tx (`mempool`); otherwise `absent`.\n */\nexport function evmObservation(\n events: EvmHtlcEvent[],\n expected: EvmExpectation,\n pending = false,\n): EvmHtlcState {\n const redeemed = events.find((e) => e.kind === \"redeemed\");\n if (redeemed?.kind === \"redeemed\")\n return { observation: \"spent_claim\", preimage: redeemed.preimage };\n if (events.some((e) => e.kind === \"refunded\"))\n return { observation: \"spent_refund\" };\n const created = events.find((e) => e.kind === \"created\");\n if (created?.kind === \"created\") {\n const amountOk = created.amount >= expected.amount;\n const tokenOk =\n expected.token === undefined ||\n created.token.toLowerCase() === expected.token.toLowerCase();\n return { observation: amountOk && tokenOk ? \"confirmed\" : \"invalid\" };\n }\n return { observation: pending ? \"mempool\" : \"absent\" };\n}\n","/**\n * The contract-monitoring layer — modeled on @arkade-os/sdk's\n * `ContractManager`.\n *\n * A {@link ContractManager} is the per-ledger, stateful I/O layer that watches a\n * set of HTLCs and maps their on-chain state to {@link HtlcObservation}. The pure\n * pipeline (`deriveSwapStatus` → `deriveSwapActions`) consumes the observations\n * it produces; a `SwapTracker` (built later) combines the two ledgers of each\n * swap and notifies subscribers of the next action.\n */\nimport type { HtlcObservation } from \"../actions/types.js\";\n\n/** Ledgers a swap HTLC can live on. */\nexport type Ledger = \"arkade\" | \"bitcoin\" | \"evm\" | \"lightning\";\n\n/**\n * Identifies and parameterises one HTLC on its ledger — enough for a\n * {@link ContractManager} to observe it. Sourced from the swap's recovery\n * bundle. Discriminated by `ledger`.\n */\nexport type HtlcRef =\n | {\n ledger: \"arkade\";\n /** VHTLC pkScript (hex) — the contract's unique id. */\n script: string;\n address: string;\n /** SHA256 of the preimage, to tell a claim spend from a refund spend. */\n preimageHash: string;\n /**\n * Expected funding amount in sats. A funding below this is `invalid` (not on\n * the swap's terms) rather than `confirmed` — so the client never claims a\n * server leg the server short-funded.\n */\n expectedSats: number;\n /**\n * Serialized VHTLC parameters (`@arkade-os/sdk`'s `Contract.params`) — the\n * sender/receiver/server keys, preimage hash, and timelocks needed to\n * register the contract for watching. Opaque here: the Client serializes\n * it from the swap's recovery bundle so this layer stays VHTLC-agnostic.\n */\n params: Record<string, string>;\n }\n | {\n ledger: \"bitcoin\";\n /** HTLC witness-script address — the contract's unique id. */\n address: string;\n preimageHash: string;\n /** Expected funding amount in sats; a funding below this is `invalid`. */\n expectedSats: number;\n /**\n * Confirmations this leg's funding needs before it observes as\n * `confirmed`, overriding the reader's default.\n *\n * Set per leg because the two directions are not symmetric. A funding the\n * SERVER made can be taken at 0-conf: the server does not double-spend its\n * own HTLC, and waiting a block would stall every claim. A funding the\n * CLIENT made is read back under the rule the server applies to it — the\n * server does not act until the tx has a blocktime, so calling it funded\n * sooner reports progress the other side has not agreed to.\n */\n minConfirmations?: number;\n }\n | {\n ledger: \"evm\";\n chainId: number;\n /** The HTLCErc20 contract holding this leg — where its events are emitted. */\n htlc: `0x${string}`;\n /**\n * SHA256 preimage hash (`0x`-prefixed) — the indexed topic the contract's\n * `SwapCreated`/`SwapRedeemed`/`SwapRefunded` events are filtered by. It\n * narrows a log scan but identifies no single HTLC: any number can share\n * one hash, so a swap is told apart by the rest of the tuple below.\n */\n preimageHash: `0x${string}`;\n /**\n * The address that can claim this HTLC — the second indexed topic used to\n * filter `SwapCreated`, so only the HTLC actually claimable on the swap's\n * terms is observed (an HTLC with our hash but a different recipient is\n * never seen). Client's EVM address for a server-funded leg; the server's\n * for the client's own leg.\n */\n claimAddress: `0x${string}`;\n /**\n * Expected locked amount (token's smallest unit). A `SwapCreated` below this\n * → `invalid`, so the client never claims a short-funded leg.\n */\n expectedAmount: bigint;\n /** Expected token address; the funding is `invalid` if it locks another token. */\n expectedToken?: `0x${string}`;\n /**\n * The funder (the HTLC's refund address). Together with the other fields it\n * completes the contract's swap-key tuple, enabling the cheap `isActive`\n * eth_call fast path (which verifies the terms implicitly — the key hashes\n * amount/token/addresses/timelock). Legs whose response omits a tuple field\n * fall back to log scans.\n */\n sender?: `0x${string}`;\n /** The HTLC refund timelock in unix SECONDS, exactly as funded on-chain. */\n timelockSec?: number;\n /**\n * Swap creation time (ms). Lower-bounds log scans: the HTLC can have no\n * events before the swap existed, so the scan starts near the corresponding\n * block instead of genesis.\n */\n createdAtMs?: number;\n }\n | {\n ledger: \"lightning\";\n /** Payment hash — the contract's unique id. */\n paymentHash: string;\n };\n\n/** Stable, cross-ledger unique key for an {@link HtlcRef}. */\nexport function htlcKey(ref: HtlcRef): string {\n switch (ref.ledger) {\n case \"arkade\":\n return `arkade:${ref.script}`;\n case \"bitcoin\":\n return `bitcoin:${ref.address}`;\n case \"evm\": {\n // Not the hash alone: any number of HTLCs can carry one preimage hash, so\n // the contract and the rest of the swap-key tuple are what separate them.\n // A leg whose response omitted a tuple field falls back to contract + hash\n // and stays ambiguous — which is why its settlements are not attributed.\n const base = `evm:${ref.chainId}:${ref.htlc.toLowerCase()}:${ref.preimageHash.toLowerCase()}`;\n if (!ref.expectedToken || !ref.sender || ref.timelockSec === undefined) {\n return base;\n }\n return [\n base,\n ref.expectedAmount,\n ref.expectedToken.toLowerCase(),\n ref.sender.toLowerCase(),\n ref.claimAddress.toLowerCase(),\n ref.timelockSec,\n ].join(\":\");\n }\n case \"lightning\":\n return `lightning:${ref.paymentHash}`;\n }\n}\n\n/**\n * Per-ledger contract monitor. Implementations keep an internal, event-driven\n * cache: `register` seeds it, `onEvent` pushes changes, `getState` reads it\n * synchronously, `dispose` tears down subscriptions/loops.\n *\n * One manager exists per ledger actually in use and watches every tracked HTLC\n * on that ledger at once (one connection, not one per HTLC).\n */\nexport interface ContractManager {\n /** The ledger this manager observes. */\n readonly ledger: Ledger;\n\n /**\n * Whether this manager can observe `ref` at all — a permanent capability check,\n * not a transient health probe. `false` means the ledger/chain is not configured\n * (e.g. an EVM chain with no RPC), so the ref can never be observed and the\n * tracker should skip the swap rather than {@link register} it (which throws).\n * Distinct from a register failure, which is a recoverable error on an\n * observable ref.\n */\n canObserve(ref: HtlcRef): boolean;\n\n /** Start tracking an HTLC (idempotent); seeds its state from the ledger. */\n register(ref: HtlcRef): Promise<void>;\n\n /** Stop tracking an HTLC. */\n unregister(ref: HtlcRef): Promise<void>;\n\n /**\n * Current known observation for a tracked HTLC, or `undefined` if it isn't\n * tracked or its state isn't known yet.\n */\n getState(ref: HtlcRef): HtlcObservation | undefined;\n\n /**\n * The current clock (ms) for evaluating this HTLC's timelock — MTP for\n * Bitcoin/Arkade, `block.timestamp` for EVM. Ref-scoped because one ledger can\n * span several chains with independent clocks (EVM chains); managers whose\n * ledger has a single clock (Arkade) ignore the ref. `undefined` until known.\n */\n chainNow(ref: HtlcRef): number | undefined;\n\n /** Subscribe to observation changes for any tracked HTLC. Returns unsubscribe. */\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void;\n\n /** Force a reconcile from the ledger. */\n refresh(): Promise<void>;\n\n /**\n * Re-observe a single tracked HTLC from the ledger now — a targeted refresh, so\n * a faster server hint can trigger an immediate on-chain verification of just\n * that swap's legs without re-scanning every tracked HTLC. A no-op if the ref\n * isn't tracked.\n */\n reconcile(ref: HtlcRef): Promise<void>;\n\n /** Tear down subscriptions and background work. */\n dispose(): void;\n}\n","/**\n * The EVM {@link ContractManager} — the stateful I/O adapter that observes\n * `HTLCErc20` swaps across one or more EVM chains.\n *\n * Reading chain state is delegated to an injected {@link EvmChainReader} (one per\n * chainId) rather than a bundled viem/ethers client, so the swap package stays\n * chain-library-agnostic and this adapter is testable against a fake. The pure\n * event→observation mapping lives in `./evm.js`.\n *\n * Unlike Arkade, EVM spans multiple chains with independent `block.timestamp`\n * clocks, so this manager is chain-aware: it routes each ref by `chainId` and\n * {@link chainNow} is ref-scoped.\n */\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { type EvmHtlcEvent, evmObservation } from \"./evm.js\";\nimport {\n type ContractManager,\n type HtlcRef,\n htlcKey,\n type Ledger,\n} from \"./types.js\";\n\ntype EvmRef = Extract<HtlcRef, { ledger: \"evm\" }>;\n\n/** Identifies one HTLC to read: contract + preimageHash (+ the claim guard). */\nexport type EvmHtlcQuery = {\n htlc: `0x${string}`;\n preimageHash: `0x${string}`;\n /** A `SwapCreated` counts only when it pays this address (term check). */\n claimAddress: `0x${string}`;\n /**\n * The rest of the swap-key tuple, when the swap response exposed it. It makes\n * {@link htlcQueryKey} unique to this HTLC, so two swaps sharing a preimage\n * hash hold separate entries rather than one overwriting the other. It does\n * not change how settlements are attributed — those go by hash either way.\n */\n terms?: EvmHtlcTerms;\n};\n\n/**\n * The result key for one query in a batch.\n *\n * `preimageHash` identifies no single HTLC — any number can share one — so the\n * rest of the swap-key tuple is what separates two HTLCs on the same contract.\n * A caller that knows the tuple gets a key unique to its HTLC; one that does not\n * falls back to contract + hash, which is ambiguous by construction — two such\n * callers on one hash share an entry.\n */\nexport function htlcQueryKey(q: {\n htlc: `0x${string}`;\n preimageHash: `0x${string}`;\n claimAddress?: `0x${string}`;\n terms?: EvmHtlcTerms;\n}): string {\n const base = `${q.htlc.toLowerCase()}:${q.preimageHash.toLowerCase()}`;\n if (!q.terms || !q.claimAddress) return base;\n const { amount, token, sender, timelockSec } = q.terms;\n return [\n base,\n amount,\n token.toLowerCase(),\n sender.toLowerCase(),\n q.claimAddress.toLowerCase(),\n timelockSec,\n ].join(\":\");\n}\n\n/** The swap-key tuple fields beyond contract, hash and claim address. */\nexport type EvmHtlcTerms = {\n amount: bigint;\n token: `0x${string}`;\n sender: `0x${string}`;\n timelockSec: number;\n};\n\n/** The batch key for a tracked ref, carrying its terms when the swap exposed them. */\nexport function refQueryKey(ref: EvmRef): string {\n const full = activeQuery(ref);\n return htlcQueryKey({\n htlc: ref.htlc,\n preimageHash: ref.preimageHash,\n claimAddress: ref.claimAddress,\n terms: full && {\n amount: full.amount,\n token: full.token,\n sender: full.sender,\n timelockSec: full.timelockSec,\n },\n });\n}\n\n/**\n * The contract's full swap-key tuple for one HTLC. `isActive` hashes all of it\n * into the swap key, so an `isActive == true` with these EXPECTED values also\n * proves the HTLC was funded on exactly the expected terms.\n */\nexport type EvmActiveQuery = {\n htlc: `0x${string}`;\n preimageHash: `0x${string}`;\n amount: bigint;\n token: `0x${string}`;\n sender: `0x${string}`;\n claimAddress: `0x${string}`;\n timelockSec: number;\n};\n\n/** Reads `HTLCErc20` state for one EVM chain. Implemented over viem/ethers/etc. */\nexport type EvmChainReader = {\n /**\n * The decoded lifecycle events for every queried HTLC, keyed by\n * {@link htlcQueryKey}. Batched so a whole chain scan costs one RPC request\n * regardless of how many swaps are tracked; a queried HTLC with no events maps\n * to an empty array. `fromBlock` lower-bounds the scan (default genesis) —\n * pass the swaps' creation-block estimate so providers never see an\n * unbounded log query.\n */\n getHtlcEventsBatch(\n queries: EvmHtlcQuery[],\n fromBlock?: bigint,\n ): Promise<Map<string, EvmHtlcEvent[]>>;\n /**\n * Whether each queried HTLC is currently open, keyed by {@link htlcQueryKey}.\n * The cheap routine check (`eth_call`, batched via Multicall3): `true` means\n * funded-and-unspent on exactly the queried terms; `false` is ambiguous\n * (never funded / claimed / refunded) and needs {@link getHtlcEventsBatch}\n * to classify.\n */\n isActiveBatch(queries: EvmActiveQuery[]): Promise<Map<string, boolean>>;\n /** The latest block's timestamp (ms) and number. */\n getLatestBlock(): Promise<{ timeMs: number; number: bigint }>;\n};\n\nexport type EvmContractManagerDeps = {\n /** A chain reader per EVM `chainId` this manager serves. */\n readers: Map<number, EvmChainReader>;\n};\n\n/** A chain clock reading: block.timestamp/number plus when we fetched it. */\ntype ChainClock = {\n blockTimeMs: number;\n blockNumber: bigint;\n fetchedAtMs: number;\n};\n\n/**\n * Rough average block time per supported chain, for estimating \"the block\n * around a swap's creation time\". Only a LOWER bound for log scans, so being\n * generous (scanning further back) is safe; the ×1.5 factor in the estimate\n * absorbs historical variance. An unlisted chain falls back to a genesis scan.\n */\nconst AVG_BLOCK_MS: Record<number, number> = {\n 1: 12_000,\n 137: 2_100,\n 42161: 250,\n};\n\n/** Extra blocks to over-scan past the estimate (reorgs + estimate slack). */\nconst SCAN_MARGIN_BLOCKS = 1_000n;\n\nexport class EvmContractManager implements ContractManager {\n readonly ledger: Ledger = \"evm\";\n\n readonly #readers: Map<number, EvmChainReader>;\n /** htlcKey → the ref we're tracking. */\n readonly #refs = new Map<string, EvmRef>();\n /** htlcKey → last known observation. */\n readonly #obs = new Map<string, HtlcObservation>();\n /** htlcKey → the preimage a claim revealed. */\n readonly #preimages = new Map<string, `0x${string}`>();\n /** chainId → its last clock reading (extrapolated in {@link chainNow}). */\n readonly #now = new Map<number, ChainClock>();\n readonly #listeners = new Set<\n (ref: HtlcRef, state: HtlcObservation) => void\n >();\n\n private constructor(deps: EvmContractManagerDeps) {\n this.#readers = deps.readers;\n }\n\n static fromDeps(deps: EvmContractManagerDeps): EvmContractManager {\n return new EvmContractManager(deps);\n }\n\n canObserve(ref: HtlcRef): boolean {\n return ref.ledger === \"evm\" && this.#readers.has(ref.chainId);\n }\n\n async register(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"evm\")\n throw new Error(`EvmContractManager can't track a '${ref.ledger}' HTLC`);\n // Fail loudly on an unconfigured chain rather than storing a ref we can never\n // observe (its state/clock would stay undefined and the tracker would wait on\n // it forever). The caller must add the chain via ClientBuilder.withEvmRpcUrls().\n if (!this.#readers.has(ref.chainId))\n throw new Error(\n `no EVM reader for chain ${ref.chainId} — configure it via ClientBuilder.withEvmRpcUrls()`,\n );\n this.#refs.set(htlcKey(ref), ref);\n }\n\n async unregister(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"evm\") return;\n const key = htlcKey(ref);\n this.#refs.delete(key);\n this.#obs.delete(key);\n this.#preimages.delete(key);\n // Drop the chain's state once nothing on it is tracked anymore.\n if (![...this.#refs.values()].some((r) => r.chainId === ref.chainId))\n this.#now.delete(ref.chainId);\n }\n\n getState(ref: HtlcRef): HtlcObservation | undefined {\n return ref.ledger === \"evm\" ? this.#obs.get(htlcKey(ref)) : undefined;\n }\n\n chainNow(ref: HtlcRef): number | undefined {\n if (ref.ledger !== \"evm\") return undefined;\n const clock = this.#now.get(ref.chainId);\n if (!clock) return undefined;\n // Extrapolate between reads: EVM block.timestamp tracks wall-clock closely,\n // so the clock stays current without polling `getBlock`. This is what lets a\n // timelock flip (e.g. refund unlocking) surface between slow scans; the\n // worker re-verifies against the real chain before ever acting on it.\n return clock.blockTimeMs + Math.max(0, Date.now() - clock.fetchedAtMs);\n }\n\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n /**\n * Full reconcile of every tracked chain — seeds clocks + observations. Called\n * on demand (tracker start / newly tracked swap), NOT periodically: onward,\n * hints drive targeted `reconcile`s and the tracker's at-risk safety net\n * re-reads only swaps with client funds on the line.\n */\n async refresh(): Promise<void> {\n const chainIds = [\n ...new Set([...this.#refs.values()].map((r) => r.chainId)),\n ];\n // Settled, not all: every EVM chain shares this one manager, so a single\n // endpoint failing would otherwise leave the HTLCs on every OTHER chain\n // unobserved — a swap would stop deriving because an unrelated swap's chain\n // is unreachable. A chain that throws keeps its previous observations and is\n // retried on the next reconcile.\n const results = await Promise.allSettled(\n chainIds.map((c) => this.#reconcileChain(c)),\n );\n results.forEach((result, index) => {\n if (result.status === \"rejected\")\n console.warn(\n `EVM reconcile failed for chain ${chainIds[index]}:`,\n result.reason,\n );\n });\n }\n\n /** Targeted verify (hint / pre-action path) — never gated. */\n async reconcile(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"evm\") return;\n const tracked = this.#refs.get(htlcKey(ref));\n if (!tracked) return;\n const reader = this.#readers.get(tracked.chainId);\n if (!reader) return;\n await this.#readClock(reader, tracked.chainId);\n await this.#reconcileRefs(reader, tracked.chainId, [tracked]);\n }\n\n dispose(): void {\n this.#listeners.clear();\n }\n\n /** The preimage a claim revealed on this HTLC, if one was seen. */\n getPreimage(ref: HtlcRef): `0x${string}` | undefined {\n return ref.ledger === \"evm\" ? this.#preimages.get(htlcKey(ref)) : undefined;\n }\n\n /** Read and store a chain's clock (with its fetch time, for extrapolation). */\n async #readClock(reader: EvmChainReader, chainId: number): Promise<void> {\n const block = await reader.getLatestBlock();\n this.#now.set(chainId, {\n blockTimeMs: block.timeMs,\n blockNumber: block.number,\n fetchedAtMs: Date.now(),\n });\n }\n\n /** Refresh one chain's clock and re-observe every HTLC tracked on it. */\n async #reconcileChain(chainId: number): Promise<void> {\n const reader = this.#readers.get(chainId);\n if (!reader) return;\n await this.#readClock(reader, chainId);\n const refs = [...this.#refs.values()].filter((r) => r.chainId === chainId);\n await this.#reconcileRefs(reader, chainId, refs);\n }\n\n /**\n * Re-observe the given refs. Two tiers:\n *\n * 1. Refs with the full contract tuple get the cheap `isActive` check —\n * `true` proves open-and-on-expected-terms (the swap key hashes them) →\n * `confirmed`, no log scan at all. This is the common case.\n * 2. Everything else — an incomplete tuple, an inactive-and-unclassified\n * HTLC (never funded vs claimed vs refunded), or an `isActive` transport\n * failure (e.g. no Multicall3 on a dev chain) — is classified from one\n * batched log read, lower-bounded by the swaps' creation-block estimate.\n *\n * A ref already latched on a spend is skipped entirely: terminal per leg,\n * nothing left to learn.\n */\n async #reconcileRefs(\n reader: EvmChainReader,\n chainId: number,\n refs: EvmRef[],\n ): Promise<void> {\n const open = refs.filter((r) => !this.#isSpent(r));\n if (open.length === 0) return;\n\n const fast = open.filter((r) => activeQuery(r) !== undefined);\n let needLogs = open.filter((r) => activeQuery(r) === undefined);\n\n if (fast.length > 0) {\n try {\n const active = await reader.isActiveBatch(\n fast.map((r) => activeQuery(r) as EvmActiveQuery),\n );\n for (const ref of fast) {\n if (active.get(refQueryKey(ref)) === true)\n this.#set(htlcKey(ref), \"confirmed\");\n else needLogs.push(ref); // inactive: classify from logs\n }\n } catch (error) {\n console.warn(\n `EVM isActive check failed for chain ${chainId} — falling back to logs:`,\n error,\n );\n needLogs = open;\n }\n }\n if (needLogs.length === 0) return;\n\n // Split by whether the creation time is known: known-age refs share a scan\n // lower-bounded near the oldest creation block, unknown-age refs (legacy\n // stored swaps without created_at) get their own full-range scan. One\n // batch for both would force the WORSE bound on everyone — genesis for the\n // whole batch, or (if unknowns were just filtered from the estimate)\n // possibly missing an old ref's events and misreading it as never funded.\n const known = needLogs.filter((r) => r.createdAtMs !== undefined);\n const unknown = needLogs.filter((r) => r.createdAtMs === undefined);\n await Promise.all(\n (\n [\n [known, this.#estimateFromBlock(chainId, known)],\n [unknown, 0n],\n ] as const\n )\n .filter(([refs]) => refs.length > 0)\n .map(([refs, fromBlock]) =>\n this.#classifyFromLogs(reader, refs, fromBlock),\n ),\n );\n }\n\n /** Read the refs' lifecycle logs (from `fromBlock`) and set observations. */\n async #classifyFromLogs(\n reader: EvmChainReader,\n refs: EvmRef[],\n fromBlock: bigint,\n ): Promise<void> {\n const events = await reader.getHtlcEventsBatch(\n refs.map((r) => {\n const full = activeQuery(r);\n return {\n htlc: r.htlc,\n preimageHash: r.preimageHash,\n claimAddress: r.claimAddress,\n terms: full && {\n amount: full.amount,\n token: full.token,\n sender: full.sender,\n timelockSec: full.timelockSec,\n },\n };\n }),\n fromBlock,\n );\n for (const ref of refs) {\n const { observation, preimage } = evmObservation(\n events.get(refQueryKey(ref)) ?? [],\n { amount: ref.expectedAmount, token: ref.expectedToken },\n );\n const key = htlcKey(ref);\n if (preimage) this.#preimages.set(key, preimage);\n this.#set(key, observation);\n }\n }\n\n #isSpent(ref: EvmRef): boolean {\n const obs = this.#obs.get(htlcKey(ref));\n return obs === \"spent_claim\" || obs === \"spent_refund\";\n }\n\n /**\n * The block to scan logs from: roughly where the OLDEST of the given swaps\n * was created (an HTLC has no events before its swap existed). Only a lower\n * bound, so estimation errs early — ×1.5 on elapsed time plus a fixed block\n * margin. Falls back to genesis when the creation time, clock, or the\n * chain's block time is unknown.\n */\n #estimateFromBlock(chainId: number, refs: EvmRef[]): bigint {\n const clock = this.#now.get(chainId);\n const avgBlockMs = AVG_BLOCK_MS[chainId];\n const createdAts = refs.map((r) => r.createdAtMs ?? 0);\n const oldestMs = Math.min(...createdAts);\n if (!clock || !avgBlockMs || oldestMs <= 0) return 0n;\n const elapsedMs = Math.max(0, clock.blockTimeMs - oldestMs);\n const blocksBehind =\n BigInt(Math.ceil((elapsedMs / avgBlockMs) * 1.5)) + SCAN_MARGIN_BLOCKS;\n const from = clock.blockNumber - blocksBehind;\n return from > 0n ? from : 0n;\n }\n\n /** Update an observation and notify on change; never downgrade a resolved spend. */\n #set(key: string, observation: HtlcObservation): void {\n const ref = this.#refs.get(key);\n if (!ref) return;\n const current = this.#obs.get(key);\n if (current === observation) return;\n const spendStates: HtlcObservation[] = [\"spent_claim\", \"spent_refund\"];\n if (\n current &&\n spendStates.includes(current) &&\n !spendStates.includes(observation)\n )\n return;\n this.#obs.set(key, observation);\n for (const listener of this.#listeners) listener(ref, observation);\n }\n}\n\n/**\n * The complete `isActive` tuple for a ref, or `undefined` when the swap\n * response didn't expose a field (then only the log path can observe the leg).\n */\nfunction activeQuery(ref: EvmRef): EvmActiveQuery | undefined {\n if (\n ref.sender === undefined ||\n ref.expectedToken === undefined ||\n ref.timelockSec === undefined\n )\n return undefined;\n return {\n htlc: ref.htlc,\n preimageHash: ref.preimageHash,\n amount: ref.expectedAmount,\n token: ref.expectedToken,\n sender: ref.sender,\n claimAddress: ref.claimAddress,\n timelockSec: ref.timelockSec,\n };\n}\n","/**\n * A viem-backed {@link EvmChainReader} — the concrete chain source the\n * {@link EvmContractManager} uses in production.\n *\n * Kept separate from the manager so the manager itself stays free of any\n * chain-library dependency (and unit-testable against a fake reader). This module\n * is the only place that touches viem and the `HTLCErc20` ABI.\n *\n * All HTLCs on a chain are read with a SINGLE `eth_getLogs` call: the three\n * `HTLCErc20` lifecycle events all index `preimageHash` as topic1, so one filter\n * ORs the three event signatures on topic0 and the tracked preimage hashes on\n * topic1 across all HTLC contract addresses. Request count per scan is constant\n * in the number of tracked swaps — this matters, since public RPCs rate-limit by\n * request count.\n */\nimport {\n type Block,\n createPublicClient,\n decodeEventLog,\n decodeFunctionResult,\n encodeFunctionData,\n fallback,\n http,\n numberToHex,\n parseAbiItem,\n toEventSelector,\n} from \"viem\";\nimport type { EvmHtlcEvent } from \"./evm.js\";\nimport {\n type EvmActiveQuery,\n type EvmChainReader,\n type EvmHtlcQuery,\n htlcQueryKey,\n} from \"./evm-manager.js\";\n\n/**\n * Tested public RPC endpoints per supported chainId, tried in order via viem's\n * `fallback`. Mirrors the frontend's `evmTransport.ts` list — the client\n * uses these by default so Arkade↔EVM tracking works out of the box; a caller can\n * override per chain with `ClientBuilder.withEvmRpcUrls`.\n */\nexport const DEFAULT_EVM_RPCS: Record<number, string[]> = {\n // Polygon — viem's default (polygon.drpc.org) misbehaves for some calls, so\n // list working public RPCs explicitly.\n 137: [\n \"https://polygon.drpc.org\",\n \"https://tenderly.rpc.polygon.community\",\n \"https://polygon-bor-rpc.publicnode.com\",\n ],\n // No publicnode: it rate-limits by IP across its whole fleet (403s), and as\n // a PRIMARY that costs a full retry cycle per read before failing over.\n // Both entries verified for eth_getLogs + eth_call from a browser origin.\n 1: [\"https://eth.drpc.org\", \"https://rpc.mevblocker.io\"],\n // The official gateway first; publicnode last (see above).\n 42161: [\n \"https://arb1.arbitrum.io/rpc\",\n \"https://arbitrum.drpc.org\",\n \"https://arbitrum-one-rpc.publicnode.com\",\n ],\n};\n\n// The three `HTLCErc20` lifecycle events, each indexed by `preimageHash` and each\n// carrying `key` — the commitment to the swap's full parameter set, and the only\n// field that says which swap an event belongs to.\nconst SWAP_CREATED = parseAbiItem(\n \"event SwapCreated(bytes32 indexed preimageHash, address indexed refundAddress, address indexed claimAddress, address token, uint256 amount, uint256 timelock, bytes32 key)\",\n);\nconst SWAP_REDEEMED = parseAbiItem(\n \"event SwapRedeemed(bytes32 indexed preimageHash, bytes32 indexed key, bytes32 preimage)\",\n);\nconst SWAP_REFUNDED = parseAbiItem(\n \"event SwapRefunded(bytes32 indexed preimageHash, bytes32 indexed key)\",\n);\n\nconst HTLC_EVENTS_ABI = [SWAP_CREATED, SWAP_REDEEMED, SWAP_REFUNDED] as const;\n/** topic0 of the three lifecycle events — the batched getLogs OR-filter. */\nconst HTLC_EVENT_TOPICS = HTLC_EVENTS_ABI.map(toEventSelector);\n\n// The contract's open-check: the args hash into the swap key, so `true` with\n// the EXPECTED values also verifies the funded terms.\nconst IS_ACTIVE = parseAbiItem(\n \"function isActive(bytes32 preimageHash, uint256 amount, address token, address sender, address claimAddress, uint256 timelock) view returns (bool)\",\n);\n\n// Multicall3 — same address on every major EVM chain; aggregates the per-HTLC\n// isActive calls into one eth_call.\nexport const MULTICALL3_ADDRESS =\n \"0xcA11bde05977b3631167028862bE2a173976CA11\" as const;\nconst AGGREGATE3 = parseAbiItem(\n \"function aggregate3((address target, bool allowFailure, bytes callData)[] calls) payable returns ((bool success, bytes returnData)[] returnData)\",\n);\n\n/** An `EvmActiveQuery` carries the whole tuple, so its key always includes terms. */\nfunction activeKey(q: EvmActiveQuery): string {\n return htlcQueryKey({\n htlc: q.htlc,\n preimageHash: q.preimageHash,\n claimAddress: q.claimAddress,\n terms: {\n amount: q.amount,\n token: q.token,\n sender: q.sender,\n timelockSec: q.timelockSec,\n },\n });\n}\n\n/** The undecoded log shape `eth_getLogs` returns. */\nexport type RawLog = {\n address: `0x${string}`;\n topics: [`0x${string}`, ...`0x${string}`[]];\n data: `0x${string}`;\n};\n\n/** The viem surface the reader needs — a seam so it can be faked in tests. */\nexport type EvmLogClient = {\n request(args: {\n method: \"eth_getLogs\";\n params: [\n {\n address: `0x${string}`[];\n topics: (`0x${string}`[] | null)[];\n // Hex quantity, not \"earliest\": strict RPCs (e.g. arb1.arbitrum.io)\n // reject the tag on eth_getLogs.\n fromBlock: `0x${string}`;\n },\n ];\n }): Promise<RawLog[]>;\n /** A read-only `eth_call`. */\n call(args: {\n to: `0x${string}`;\n data: `0x${string}`;\n }): Promise<{ data?: `0x${string}` }>;\n getBlock(): Promise<Pick<Block, \"timestamp\" | \"number\">>;\n};\n\n/** Build an {@link EvmChainReader} over an existing viem-like client. */\nexport function evmReaderFromClient(client: EvmLogClient): EvmChainReader {\n return {\n async getHtlcEventsBatch(queries, fromBlock = 0n) {\n const results = new Map<string, EvmHtlcEvent[]>();\n if (queries.length === 0) return results;\n for (const q of queries) results.set(htlcQueryKey(q), []);\n\n const logs = await client.request({\n method: \"eth_getLogs\",\n params: [\n {\n address: unique(queries.map((q) => q.htlc)),\n topics: [\n HTLC_EVENT_TOPICS,\n unique(queries.map((q) => q.preimageHash)),\n ],\n fromBlock: numberToHex(fromBlock),\n },\n ],\n });\n\n // A log can only be grouped by what it carries on its own — contract and\n // hash — and several HTLCs can share that. So group, then let every query\n // filter the whole group through its own terms. Matching each log to a\n // single query up front would let two queries sharing a hash collapse into\n // one, dropping the other's settlements or attributing them wrongly.\n const grouped = new Map<string, DecodedHtlcLog[]>();\n for (const log of logs) {\n const decoded = tryDecode(log);\n if (!decoded) continue;\n const group = `${log.address.toLowerCase()}:${decoded.preimageHash.toLowerCase()}`;\n const existing = grouped.get(group);\n if (existing) existing.push(decoded);\n else grouped.set(group, [decoded]);\n }\n\n for (const query of queries) {\n const group = `${query.htlc.toLowerCase()}:${query.preimageHash.toLowerCase()}`;\n const events = results.get(htlcQueryKey(query));\n if (!events) continue;\n for (const decoded of grouped.get(group) ?? []) {\n const event = toHtlcEvent(decoded, query);\n if (event) events.push(event);\n }\n }\n return results;\n },\n async isActiveBatch(queries) {\n const results = new Map<string, boolean>();\n if (queries.length === 0) return results;\n const calldatas = queries.map((q) =>\n encodeFunctionData({\n abi: [IS_ACTIVE],\n args: [\n q.preimageHash,\n q.amount,\n q.token,\n q.sender,\n q.claimAddress,\n BigInt(q.timelockSec),\n ],\n }),\n );\n\n const callOne = async (i: number): Promise<void> => {\n const { data } = await client.call({\n to: queries[i].htlc,\n data: calldatas[i],\n });\n results.set(activeKey(queries[i]), decodeIsActive(data));\n };\n\n if (queries.length === 1) {\n await callOne(0);\n return results;\n }\n try {\n const { data } = await client.call({\n to: MULTICALL3_ADDRESS,\n data: encodeFunctionData({\n abi: [AGGREGATE3],\n args: [\n queries.map((q, i) => ({\n target: q.htlc,\n allowFailure: true,\n callData: calldatas[i],\n })),\n ],\n }),\n });\n if (data === undefined || data === \"0x\")\n throw new Error(\"empty multicall result\");\n const decoded = decodeFunctionResult({\n abi: [AGGREGATE3],\n data,\n }) as readonly { success: boolean; returnData: `0x${string}` }[];\n queries.forEach((q, i) => {\n const r = decoded[i];\n // A failed inner call reads as \"not active\" — the caller's log\n // fallback then classifies it, so nothing is silently trusted.\n results.set(\n activeKey(q),\n r?.success === true && decodeIsActive(r.returnData),\n );\n });\n } catch {\n // No Multicall3 on this chain (e.g. a bare dev node) — per-HTLC calls.\n await Promise.all(queries.map((_, i) => callOne(i)));\n }\n return results;\n },\n async getLatestBlock() {\n const block = await client.getBlock();\n return {\n timeMs: Number(block.timestamp) * 1000,\n number: block.number ?? 0n,\n };\n },\n };\n}\n\n/** Decode an `isActive` eth_call result; empty data (no contract) → false. */\nfunction decodeIsActive(data: `0x${string}` | undefined): boolean {\n if (data === undefined || data === \"0x\") return false;\n return decodeFunctionResult({ abi: [IS_ACTIVE], data }) === true;\n}\n\n/**\n * The decoded fields this reader acts on.\n *\n * All three events also carry `key`. It is deliberately not surfaced: nothing\n * here can check it against a query yet, and a field decoded but never read\n * reads as though attribution were key-based. See {@link toHtlcEvent}.\n */\ntype DecodedHtlcLog =\n | {\n eventName: \"SwapCreated\";\n preimageHash: `0x${string}`;\n claimAddress: `0x${string}`;\n token: `0x${string}`;\n amount: bigint;\n }\n | {\n eventName: \"SwapRedeemed\";\n preimageHash: `0x${string}`;\n preimage: `0x${string}`;\n }\n | {\n eventName: \"SwapRefunded\";\n preimageHash: `0x${string}`;\n };\n\n/** Decode one raw log against the three-event ABI; undefined for foreign logs. */\nfunction tryDecode(log: RawLog): DecodedHtlcLog | undefined {\n try {\n const { eventName, args } = decodeEventLog({\n abi: HTLC_EVENTS_ABI,\n data: log.data,\n topics: log.topics,\n });\n return { eventName, ...args } as DecodedHtlcLog;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Map a decoded log to the manager's event, applying the per-query guards the\n * batched filter can't express per-hash.\n *\n * Settlements are attributed by `preimageHash` alone, which identifies no single\n * HTLC — any number may share one hash, each with its own terms and lifecycle.\n * Two queries on the same hash and contract therefore both see the same terminal\n * event. Attribution here is ambiguous by construction; `evmObservation`'s\n * `isActive` check, not this mapping, is what keeps an open HTLC from reading as\n * settled, because the tuple it calls with does hold for the leg being tracked.\n *\n * The events carry `key`, the commitment to the full parameter set, which would\n * separate them — but a query cannot arrive at the funded HTLC's key on its own:\n * a coordinator locks its post-swap balance rather than the quoted amount, and is\n * recorded as `sender` in place of the funder named on the swap. Attributing\n * settlements exactly needs the key recorded at funding time to reach the client\n * rather than being re-derived here.\n *\n * `SwapCreated` is guarded on `claimAddress` for the same reason a key match will\n * not do: it is the event that first reports the funded amount, which may differ\n * from the expected one, so its key is not predictable either. `evmObservation`\n * term-checks it afterwards.\n */\nfunction toHtlcEvent(\n decoded: DecodedHtlcLog,\n query: EvmHtlcQuery,\n): EvmHtlcEvent | undefined {\n if (decoded.eventName === \"SwapCreated\") {\n if (decoded.claimAddress.toLowerCase() !== query.claimAddress.toLowerCase())\n return undefined;\n return { kind: \"created\", amount: decoded.amount, token: decoded.token };\n }\n\n return decoded.eventName === \"SwapRedeemed\"\n ? { kind: \"redeemed\", preimage: decoded.preimage }\n : { kind: \"refunded\" };\n}\n\nfunction unique<T>(values: T[]): T[] {\n return [...new Set(values)];\n}\n\n/**\n * Build an {@link EvmChainReader} over one or more EVM JSON-RPC endpoints. With\n * several, viem's `fallback` tries them in order and fails over on error.\n * Deliberately NOT ranked: ranking health-pings every listed endpoint on an\n * interval from every open tab, which is exactly the background burst that got\n * public RPCs rate-limiting us (publicnode 403s) — and it contradicts this\n * package's near-zero-RPC design.\n */\nexport function createEvmRpcReader(rpcUrls: string | string[]): EvmChainReader {\n const urls = Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls];\n const transport =\n urls.length > 1 ? fallback(urls.map((url) => http(url))) : http(urls[0]);\n const client = createPublicClient({ transport });\n return evmReaderFromClient(client as unknown as EvmLogClient);\n}\n\n/**\n * Resolve the per-chain readers used for tracking: the tested {@link\n * DEFAULT_EVM_RPCS} by default, with any `overrides` taking priority for their\n * chain (kept ahead of the defaults, which remain as fallbacks).\n */\nexport function defaultEvmReaders(\n overrides?: Record<number, string>,\n): Map<number, EvmChainReader> {\n const chainIds = new Set<number>([\n ...Object.keys(DEFAULT_EVM_RPCS).map(Number),\n ...Object.keys(overrides ?? {}).map(Number),\n ]);\n const readers = new Map<number, EvmChainReader>();\n for (const chainId of chainIds) {\n const override = overrides?.[chainId];\n const defaults = DEFAULT_EVM_RPCS[chainId] ?? [];\n const urls = override ? [override, ...defaults] : defaults;\n if (urls.length > 0) readers.set(chainId, createEvmRpcReader(urls));\n }\n return readers;\n}\n","/**\n * Ties the hint feed and the action model together into auto-execution.\n *\n * On a server status hint it verifies the swap against the chain\n * (`tracker.applyHint`) — the hint only makes the derivation faster, the chain\n * stays the source of truth. Then, on every derived action, it AUTO-RUNS the\n * recommended action when it is auto-safe (a claim) and SURFACES the rest — a\n * manual `fund`, or a refund the user must confirm — via `onActionRequired`, the\n * seam a frontend notification center plugs into.\n *\n * A failed auto-claim is RETRIED with backoff. Each retry first re-verifies\n * against chain (a forced `applyHint`) and only re-runs if the swap is still\n * claimable — so we never blindly re-submit a claim (which on EVM would waste gas\n * or double-submit). After the attempt budget is spent the swap is surfaced via\n * `onActionRequired`, so it is never silently stuck.\n */\nimport type { SwapActionId, SwapActions } from \"../actions/types.js\";\n\n/** The tracker surface the worker drives. */\nexport interface WorkerTracker {\n trackedSwapIds(): string[];\n /** Reconcile a swap's legs from chain, then recompute. `force` re-emits even if unchanged. */\n applyHint(swapId: string, opts?: { force?: boolean }): Promise<void>;\n subscribeToActions(\n cb: (swapId: string, actions: SwapActions) => void,\n ): () => void;\n}\n\n/** The hint feed surface the worker consumes (`WsStatusSource` satisfies it). */\nexport interface WorkerHintSource {\n start(): void;\n stop(): void;\n subscribe(swapIds: string[]): void;\n unsubscribe(swapIds: string[]): void;\n onStatus(cb: (update: { swapId: string }) => void): () => void;\n /**\n * Optional: fires when the feed comes back up after an outage, during which\n * pushed transitions were lost for good. The worker answers by re-verifying\n * every tracked swap against the chain.\n */\n onReconnect?(cb: () => void): () => void;\n}\n\n/** Backoff policy for retrying a failed auto-claim. */\nexport type SwapWorkerRetryOptions = {\n /** Attempts before giving up and surfacing the swap. Default 5. */\n maxAttempts?: number;\n /** Base backoff (ms), doubled each attempt. Default 2000. */\n baseDelayMs?: number;\n /** Backoff ceiling (ms). Default 30000. */\n maxDelayMs?: number;\n};\n\nexport type SwapWorkerOptions = {\n tracker: WorkerTracker;\n hintSource: WorkerHintSource;\n /**\n * Run the recommended auto action for a swap (today: claim). Reject to signal\n * failure — the worker re-verifies against chain and retries with backoff.\n * Resolve once submitted. OMIT for observe-only mode: hints still trigger\n * chain re-verifies (fast, cheap derivations), but nothing is ever run.\n */\n execute?: (swapId: string, actionId: SwapActionId) => Promise<void>;\n /**\n * Surface an action that needs the user rather than being auto-run — a manual\n * `fund`, a refund the user must confirm, or a claim whose retries are spent.\n * Where a UI notification hooks in.\n */\n onActionRequired?: (swapId: string, actions: SwapActions) => void;\n /** Auto-claim retry policy; sensible defaults otherwise. */\n retry?: SwapWorkerRetryOptions;\n};\n\n/** Actions the worker runs automatically. `wait`/`none` are auto but no-ops. */\nconst AUTO_EXECUTABLE: ReadonlySet<SwapActionId> = new Set<SwapActionId>([\n \"claim\",\n]);\n\n/** In-flight retry bookkeeping for one swap. */\ntype Retry = { attempts: number; timer: ReturnType<typeof setTimeout> };\n\nexport class SwapWorker {\n readonly #tracker: WorkerTracker;\n readonly #hintSource: WorkerHintSource;\n readonly #execute: SwapWorkerOptions[\"execute\"];\n readonly #onActionRequired: SwapWorkerOptions[\"onActionRequired\"];\n readonly #maxAttempts: number;\n readonly #baseDelayMs: number;\n readonly #maxDelayMs: number;\n\n /** Swaps with an auto action in flight — guards against double-runs. */\n readonly #executing = new Set<string>();\n /** Swaps with a scheduled auto-claim retry (backoff timer + attempt count). */\n readonly #retries = new Map<string, Retry>();\n #unsubs: Array<() => void> = [];\n #started = false;\n\n constructor(options: SwapWorkerOptions) {\n this.#tracker = options.tracker;\n this.#hintSource = options.hintSource;\n this.#execute = options.execute;\n this.#onActionRequired = options.onActionRequired;\n this.#maxAttempts = options.retry?.maxAttempts ?? 5;\n this.#baseDelayMs = options.retry?.baseDelayMs ?? 2_000;\n this.#maxDelayMs = options.retry?.maxDelayMs ?? 30_000;\n }\n\n /** Watch tracked swaps on the hint feed, wire hints → verify, react to actions. */\n start(): void {\n if (this.#started) return;\n this.#started = true;\n\n this.#hintSource.subscribe(this.#tracker.trackedSwapIds());\n this.#unsubs.push(\n this.#hintSource.onStatus(({ swapId }) => {\n void this.#tracker.applyHint(swapId).catch((error) => {\n console.warn(`SwapWorker: applyHint(${swapId}) failed:`, error);\n });\n }),\n );\n // Hints were dead while the feed was down and pushed transitions are lost\n // for good — anything may have happened, including funding an `absent`\n // client leg, the one state nothing else re-reads (it is outside the\n // tracker's at-risk cadence). The server's resubscribe snapshot usually\n // re-triggers applyHint for subscribed ids, but this sweep is the\n // client-side guarantee: it doesn't depend on the server sending one, and\n // it also covers over-cap queued ids that never get hints.\n if (this.#hintSource.onReconnect)\n this.#unsubs.push(\n this.#hintSource.onReconnect(() => this.#reverifyAllTracked()),\n );\n this.#hintSource.start();\n\n this.#unsubs.push(\n this.#tracker.subscribeToActions((swapId, actions) =>\n this.#onActions(swapId, actions),\n ),\n );\n }\n\n /** Stop reacting, cancel pending retries, and close the hint feed. */\n stop(): void {\n this.#started = false;\n for (const unsub of this.#unsubs) unsub();\n this.#unsubs = [];\n this.#hintSource.stop();\n for (const { timer } of this.#retries.values()) clearTimeout(timer);\n this.#retries.clear();\n this.#executing.clear();\n }\n\n /** Chain-reconcile every tracked swap (the post-outage catch-up sweep). */\n #reverifyAllTracked(): void {\n for (const swapId of this.#tracker.trackedSwapIds()) {\n void this.#tracker.applyHint(swapId).catch((error) => {\n console.warn(\n `SwapWorker: post-reconnect re-verify of ${swapId} failed:`,\n error,\n );\n });\n }\n }\n\n #onActions(swapId: string, actions: SwapActions): void {\n const recommended = actions.recommended;\n // Terminal first: a finished swap needs no hints, and subscribing before\n // dropping it would briefly consume a subscription slot (or queue behind the\n // server cap) on a swap that is already done.\n if (recommended === \"none\") {\n this.#hintSource.unsubscribe([swapId]);\n this.#cancelRetry(swapId);\n return;\n }\n // Ensure the swap is on the hint feed (covers swaps whose first action only\n // appears after tracking started).\n this.#hintSource.subscribe([swapId]);\n\n // Observe-only mode (no execute): fall through — an auto action is nobody's\n // to run, and it doesn't need surfacing either (subscribers see it live).\n if (recommended && AUTO_EXECUTABLE.has(recommended) && this.#execute) {\n this.#autoExecute(swapId, actions);\n return;\n }\n // No longer an auto-claim (moved to wait/fund/refund, or nothing): drop any\n // pending retry — a fresh derivation supersedes it.\n this.#cancelRetry(swapId);\n // Surface only actions that actually need the user — a `manual` fund or a\n // refund to `confirm`. `wait` is an auto no-op: nothing to notify about.\n if (!recommended) return;\n const automation = actions.actions.find(\n (a) => a.id === recommended,\n )?.automation;\n if (automation === \"manual\" || automation === \"confirm\")\n this.#onActionRequired?.(swapId, actions);\n }\n\n #autoExecute(swapId: string, actions: SwapActions): void {\n const execute = this.#execute;\n if (!execute) return; // observe-only mode\n if (this.#executing.has(swapId)) return; // one in-flight run per swap\n const actionId = actions.recommended;\n if (!actionId) return;\n this.#executing.add(swapId);\n void execute(swapId, actionId)\n .then(() => this.#cancelRetry(swapId)) // submitted — reset the attempt budget\n .catch((error) => {\n console.warn(`SwapWorker: ${actionId}(${swapId}) failed:`, error);\n this.#scheduleRetry(swapId, actions);\n })\n .finally(() => this.#executing.delete(swapId));\n }\n\n /** Schedule the next backoff retry, or give up and surface once attempts run out. */\n #scheduleRetry(swapId: string, actions: SwapActions): void {\n const attempts = (this.#retries.get(swapId)?.attempts ?? 0) + 1;\n if (attempts > this.#maxAttempts) {\n // Out of attempts: stop auto-running and hand it to the user (a UI can offer\n // a manual claim). Better than silently stuck.\n console.warn(\n `SwapWorker: auto-claim for ${swapId} gave up after ${this.#maxAttempts} attempts`,\n );\n this.#cancelRetry(swapId);\n this.#onActionRequired?.(swapId, actions);\n return;\n }\n this.#clearTimer(swapId);\n const delay = Math.min(\n this.#baseDelayMs * 2 ** (attempts - 1),\n this.#maxDelayMs,\n );\n const timer = setTimeout(() => this.#retry(swapId, actions), delay);\n this.#retries.set(swapId, { attempts, timer });\n }\n\n /**\n * A retry fires: re-verify against chain (forced re-emit), which drives\n * {@link #onActions} to re-run the claim only if it is still recommended. If the\n * re-verify itself fails we can't safely retry, so back off and try the cycle\n * again.\n */\n #retry(swapId: string, actions: SwapActions): void {\n void this.#tracker.applyHint(swapId, { force: true }).catch((error) => {\n console.warn(\n `SwapWorker: re-verify before retrying ${swapId} failed:`,\n error,\n );\n this.#scheduleRetry(swapId, actions);\n });\n }\n\n #cancelRetry(swapId: string): void {\n this.#clearTimer(swapId);\n this.#retries.delete(swapId);\n }\n\n #clearTimer(swapId: string): void {\n const existing = this.#retries.get(swapId);\n if (existing) clearTimeout(existing.timer);\n }\n}\n","/**\n * A standalone subscriber to the server's swap-status WebSocket (`GET /ws`).\n *\n * Holds a single reconnecting connection, tracks a live set of subscribed swap\n * ids, and emits `{ swapId, status }` on every status transition the server\n * pushes — including the snapshot the server sends right after a subscribe. This\n * is a *hint* feed only: it neither trusts nor acts on the status. A consumer is\n * expected to verify against the chain (the contract managers) before doing\n * anything with it.\n *\n * Protocol (see `swap/src/api/ws.rs`):\n * → {\"op\":\"subscribe\",\"channel\":\"swap_status\",\"args\":[<uuid>,...]} (≤64 ids/frame)\n * ← {\"channel\":\"swap_status\",\"data\":{\"<uuid>\":\"<status>\",...}}\n * ← {\"op\":\"subscribed\",...} (ack) ← {\"op\":...,\"message\":...} (error)\n * A single connection carries up to 256 subscriptions; that cap is enforced\n * client-side (see {@link WsStatusSource.subscribe}) because the server silently\n * drops the overflow.\n */\nimport type { SwapStatus } from \"@lendasat/lendaswap-sdk-pure\";\n\n/** A status update pushed by the server for one swap. */\nexport type SwapStatusUpdate = {\n swapId: string;\n status: SwapStatus;\n};\n\n/** The minimal WebSocket surface the source needs (the global `WebSocket` satisfies it). */\nexport interface WebSocketLike {\n send(data: string): void;\n close(): void;\n onopen: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: (() => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\nexport type WsStatusSourceOptions = {\n /** Server base URL (http/https); the `ws(s)://…/ws` endpoint is derived from it. */\n serverUrl: string;\n /** WebSocket factory — defaults to the global `WebSocket` (browsers, Node ≥ 22). */\n webSocketFactory?: (url: string) => WebSocketLike;\n /** Reconnect backoff floor/ceiling (ms). Defaults: 1_000 / 30_000. */\n reconnectMinMs?: number;\n reconnectMaxMs?: number;\n};\n\n/** Server limits, mirrored from `swap/src/api/ws.rs`. */\nconst MAX_ARGS_PER_FRAME = 64;\nconst MAX_SUBSCRIPTIONS = 256;\n\nexport class WsStatusSource {\n readonly #url: string;\n readonly #factory: (url: string) => WebSocketLike;\n readonly #reconnectMinMs: number;\n readonly #reconnectMaxMs: number;\n\n /**\n * Ids actually subscribed on the wire — held at or below the server's\n * per-connection cap, and resent in full on every (re)connect.\n */\n readonly #active = new Set<string>();\n /**\n * Ids we want but have no slot for. Promoted FIFO as slots free (the worker\n * unsubscribes each swap when it reaches a terminal action, so they do free).\n * A queued swap is not broken, only slower: it gets no hints, so it advances\n * on the tracker's at-risk reconciles and the worker's reconnect sweep.\n */\n readonly #pending = new Set<string>();\n readonly #listeners = new Set<(update: SwapStatusUpdate) => void>();\n readonly #reconnectListeners = new Set<() => void>();\n\n #ws: WebSocketLike | undefined;\n #open = false;\n #stopped = false;\n /** A connect failed or the socket dropped — the next open is a RE-connect. */\n #hadDisruption = false;\n #backoffMs: number;\n #reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\n constructor(options: WsStatusSourceOptions) {\n this.#url = toWsUrl(options.serverUrl);\n this.#factory = options.webSocketFactory ?? defaultWebSocketFactory;\n this.#reconnectMinMs = options.reconnectMinMs ?? 1_000;\n this.#reconnectMaxMs = options.reconnectMaxMs ?? 30_000;\n this.#backoffMs = this.#reconnectMinMs;\n }\n\n /** Open the connection and begin (re)subscribing. Idempotent. */\n start(): void {\n if (this.#ws || this.#reconnectTimer) return;\n this.#stopped = false;\n this.#connect();\n }\n\n /** Close the connection and stop reconnecting. */\n stop(): void {\n this.#stopped = true;\n if (this.#reconnectTimer) clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = undefined;\n this.#open = false;\n this.#ws?.close();\n this.#ws = undefined;\n }\n\n /** Emit for each pushed status update. Returns an unsubscribe fn. */\n onStatus(cb: (update: SwapStatusUpdate) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n /**\n * Emit whenever the socket comes (back) up after a disruption — a drop, or a\n * first connect that had to be retried. NOT emitted on a pristine first open:\n * hints were never live before it, so there is no gap to recover from. While\n * the socket was down, pushed transitions were lost for good (the server only\n * snapshots on subscribe), so a consumer should re-verify its world against\n * the chain on this signal.\n */\n onReconnect(cb: () => void): () => void {\n this.#reconnectListeners.add(cb);\n return () => this.#reconnectListeners.delete(cb);\n }\n\n /**\n * Track these swap ids — subscribe on the socket now if open, else on next open.\n *\n * Enforces the server's per-connection cap locally. The server silently stops\n * adding once it is full (it breaks out of the loop but still acks every id),\n * so sending more would leave us believing ids are subscribed that will never\n * receive an update. Overflow is queued instead and promoted as slots free.\n */\n subscribe(swapIds: string[]): void {\n const fresh = swapIds.filter(\n (id) => !this.#active.has(id) && !this.#pending.has(id),\n );\n if (fresh.length === 0) return;\n\n const admitted: string[] = [];\n for (const id of fresh) {\n if (this.#active.size < MAX_SUBSCRIPTIONS) {\n this.#active.add(id);\n admitted.push(id);\n } else {\n this.#pending.add(id);\n }\n }\n if (this.#pending.size > 0)\n console.warn(\n `WsStatusSource: at the server cap of ${MAX_SUBSCRIPTIONS} subscriptions; ${this.#pending.size} swap(s) queued for a slot and will advance on the chain poll until then`,\n );\n if (this.#open && admitted.length > 0)\n this.#sendFrame(\"subscribe\", admitted);\n }\n\n /** Stop tracking these swap ids, promoting queued ones into the freed slots. */\n unsubscribe(swapIds: string[]): void {\n const dropped: string[] = [];\n for (const id of swapIds) {\n if (this.#active.delete(id)) dropped.push(id);\n else this.#pending.delete(id);\n }\n if (this.#open && dropped.length > 0)\n this.#sendFrame(\"unsubscribe\", dropped);\n this.#promotePending();\n }\n\n /** Move queued ids into any free slots, subscribing them if the socket is open. */\n #promotePending(): void {\n if (this.#pending.size === 0) return;\n const promoted: string[] = [];\n for (const id of this.#pending) {\n if (this.#active.size >= MAX_SUBSCRIPTIONS) break;\n this.#pending.delete(id);\n this.#active.add(id);\n promoted.push(id);\n }\n if (this.#open && promoted.length > 0)\n this.#sendFrame(\"subscribe\", promoted);\n }\n\n #connect(): void {\n this.#reconnectTimer = undefined;\n const ws = this.#factory(this.#url);\n this.#ws = ws;\n ws.onopen = () => {\n this.#backoffMs = this.#reconnectMinMs; // reset backoff on a good connection\n // Promote while still marked closed, so the queued ids ride along in the\n // single full resend below instead of being sent twice.\n this.#promotePending();\n this.#open = true;\n if (this.#active.size > 0)\n this.#sendFrame(\"subscribe\", [...this.#active]);\n if (this.#hadDisruption) {\n this.#hadDisruption = false;\n for (const cb of this.#reconnectListeners) cb();\n }\n };\n ws.onmessage = (event) => this.#handleMessage(event.data);\n ws.onclose = () => this.#onDisconnect();\n ws.onerror = () => this.#ws?.close(); // a `close` follows and drives reconnect\n }\n\n #onDisconnect(): void {\n this.#open = false;\n this.#ws = undefined;\n this.#hadDisruption = true;\n if (this.#stopped || this.#reconnectTimer) return;\n const delay = this.#backoffMs;\n this.#backoffMs = Math.min(this.#backoffMs * 2, this.#reconnectMaxMs);\n this.#reconnectTimer = setTimeout(() => this.#connect(), delay);\n }\n\n #handleMessage(data: unknown): void {\n if (typeof data !== \"string\") return;\n let frame: unknown;\n try {\n frame = JSON.parse(data);\n } catch {\n return;\n }\n if (!frame || typeof frame !== \"object\") return;\n const f = frame as Record<string, unknown>;\n\n // Data frame: { channel, data: { <swapId>: <status> } }\n if (f.data && typeof f.data === \"object\") {\n for (const [swapId, status] of Object.entries(\n f.data as Record<string, SwapStatus>,\n ))\n this.#emit({ swapId, status });\n return;\n }\n // Error frame: { op, message }. Ack frames (no data, no message) are ignored.\n if (typeof f.message === \"string\")\n console.warn(`WsStatusSource: server error: ${f.message}`);\n }\n\n #emit(update: SwapStatusUpdate): void {\n for (const cb of this.#listeners) cb(update);\n }\n\n #sendFrame(op: \"subscribe\" | \"unsubscribe\", ids: string[]): void {\n for (let i = 0; i < ids.length; i += MAX_ARGS_PER_FRAME) {\n const args = ids.slice(i, i + MAX_ARGS_PER_FRAME);\n this.#ws?.send(JSON.stringify({ op, channel: \"swap_status\", args }));\n }\n }\n}\n\n/** Derive the `ws(s)://host[/path]/ws` endpoint from an http(s) server base URL. */\nfunction toWsUrl(serverUrl: string): string {\n const url = new URL(serverUrl);\n const scheme = url.protocol === \"https:\" ? \"wss\" : \"ws\";\n const path = url.pathname.replace(/\\/+$/, \"\");\n return `${scheme}://${url.host}${path}/ws`;\n}\n\nfunction defaultWebSocketFactory(url: string): WebSocketLike {\n const WS = (globalThis as { WebSocket?: new (url: string) => WebSocketLike })\n .WebSocket;\n if (!WS)\n throw new Error(\n \"no global WebSocket available — pass options.webSocketFactory (Node < 22)\",\n );\n return new WS(url);\n}\n","/**\n * Build the Arkade {@link HtlcRef} for a swap's VHTLC leg from the plain swap\n * fields, deriving the pkScript and serialized contract params the\n * `ArkadeContractManager` needs to register it for watching.\n *\n * This is the one place that reconstructs the VHTLC from a swap, mirroring the\n * SDK VHTLC construction, so the mapper and manager stay free of VHTLC internals.\n */\n\nimport { VHTLCContractHandler } from \"@arkade-os/sdk\";\nimport {\n ARKADE_HTLC_SCRIPT_VERSION_STRICT,\n StrictVhtlcScript,\n} from \"@lendasat/lendaswap-sdk-pure\";\nimport { ripemd160 } from \"@noble/hashes/legacy.js\";\nimport { hex } from \"@scure/base\";\nimport type { HtlcRef } from \"./types.js\";\n\ntype ArkadeRef = Extract<HtlcRef, { ledger: \"arkade\" }>;\n\nexport type ArkadeVhtlcInput = {\n /** Pubkey hex (x-only or compressed) for each VHTLC role. */\n senderPk: string;\n receiverPk: string;\n serverPk: string;\n /**\n * The swap's `hash_lock`, hex, `0x`-prefixed or not. Either `sha256(preimage)`\n * (32 bytes — most directions) or the `ripemd160(sha256(preimage))` HASH160 the\n * VHTLC commits to directly (20 bytes — `btc_to_arkade`). Length disambiguates.\n */\n hashLock: string;\n /** The VHTLC address the server reported. */\n address: string;\n /** Absolute CLTV refund locktime (unix seconds). */\n refundLocktime: number;\n /** Relative unilateral delays (seconds). */\n unilateralClaimDelay: number;\n unilateralRefundDelay: number;\n unilateralRefundWithoutReceiverDelay: number;\n /** Expected funding amount in sats (a short funding is `invalid`, not confirmed). */\n expectedSats: number;\n /** Arkade HTLC script version. Missing means strict v1. */\n arkadeHtlcScriptVersion?: number;\n};\n\nfunction strip0x(value: string): string {\n return value.startsWith(\"0x\") ? value.slice(2) : value;\n}\n\n/** Normalize a pubkey hex to the 32-byte x-only form the VHTLC expects. */\nfunction xOnly(pubKeyHex: string): Uint8Array {\n const bytes = hex.decode(strip0x(pubKeyHex));\n if (bytes.length === 33) return bytes.slice(1);\n if (bytes.length === 32) return bytes;\n throw new Error(`invalid public key length: ${bytes.length}`);\n}\n\nconst seconds = (value: number) =>\n ({ type: \"seconds\", value: BigInt(value) }) as const;\n\n/**\n * The VHTLC always commits to `ripemd160(sha256(preimage))` (HASH160). When\n * `hashLock` is `sha256(preimage)` (32 bytes) we hash it once more to get that;\n * when it is already the HASH160 (20 bytes, `btc_to_arkade`) we use it as-is. The\n * ref keeps `preimageHash` as the raw `hashLock` — whatever length — and the spend\n * classifier verifies a revealed preimage against it, auto-detecting the algorithm\n * from that length.\n */\nexport function buildArkadeVhtlcRef(input: ArkadeVhtlcInput): ArkadeRef {\n const hashLockBytes = hex.decode(strip0x(input.hashLock));\n const params = {\n sender: xOnly(input.senderPk),\n receiver: xOnly(input.receiverPk),\n server: xOnly(input.serverPk),\n preimageHash:\n hashLockBytes.length === 20 ? hashLockBytes : ripemd160(hashLockBytes),\n refundLocktime: BigInt(input.refundLocktime),\n unilateralClaimDelay: seconds(input.unilateralClaimDelay),\n unilateralRefundDelay: seconds(input.unilateralRefundDelay),\n unilateralRefundWithoutReceiverDelay: seconds(\n input.unilateralRefundWithoutReceiverDelay,\n ),\n };\n if (\n input.arkadeHtlcScriptVersion !== undefined &&\n input.arkadeHtlcScriptVersion !== ARKADE_HTLC_SCRIPT_VERSION_STRICT\n ) {\n throw new Error(\n `Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`,\n );\n }\n const vhtlc = new StrictVhtlcScript(params);\n return {\n ledger: \"arkade\",\n script: hex.encode(vhtlc.pkScript),\n address: input.address,\n preimageHash: strip0x(input.hashLock),\n expectedSats: input.expectedSats,\n params: VHTLCContractHandler.serializeParams(params),\n };\n}\n","/**\n * Map a stored swap to the {@link TrackedSwap} the {@link SwapTracker} watches.\n *\n * The recovery bundle (`StoredSwap.response`) is a discriminated union over swap\n * directions; each carries the two HTLC legs and their refund locktimes. This\n * extracts the client-funded and server-funded HTLCs plus their locktimes so the\n * pure pipeline can derive the next action.\n *\n * Directions whose both legs are observable today (Arkade↔EVM, Bitcoin↔EVM) are\n * mapped; others return `undefined` until their ledger managers exist, rather than\n * producing a half-watchable swap.\n */\nimport type { StoredSwap } from \"@lendasat/lendaswap-sdk-pure\";\nimport { buildArkadeVhtlcRef } from \"../contracts/arkade-vhtlc.js\";\nimport type { HtlcRef } from \"../contracts/types.js\";\nimport type { TrackedSwap } from \"./swap-tracker.js\";\n\n/** Locktimes are unix seconds on the wire; the resolver works in ms. */\nconst ms = (seconds: number): number => seconds * 1000;\n\n/**\n * The BTC-pegged token the coordinator locks in HTLCs per MAINNET chain\n * (mirrors `config.mainnet.yaml` `tokens.wbtc`): WBTC on Polygon, tBTC on\n * Ethereum/Arbitrum. TEMPORARY fallback for the responses that don't expose\n * the locked token (`evm_to_arkade`, `evm_to_lightning`) so their isActive\n * tuple is complete; delete once the server returns the token there.\n * Gated to mainnet: a dev deployment's mock token lives at another address,\n * and guessing wrong would flag valid fundings as `invalid`.\n */\nconst MAINNET_LOCKED_TOKEN: Record<number, `0x${string}`> = {\n 1: \"0x18084fba666a33d37592fa2633fd49a74dd93a88\", // tBTC\n 42161: \"0x6c84a8f1c29108F47a79964b5Fe888D4f4D0dE40\", // tBTC\n 137: \"0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6\", // WBTC\n};\n\nfunction lockedTokenFallback(\n chainId: number,\n network: string | undefined,\n): `0x${string}` | undefined {\n const net = network?.toLowerCase();\n return net === \"bitcoin\" || net === \"mainnet\"\n ? MAINNET_LOCKED_TOKEN[chainId]\n : undefined;\n}\n\nconst ensure0x = (value: string): `0x${string}` =>\n (value.startsWith(\"0x\") ? value : `0x${value}`) as `0x${string}`;\nconst strip0x = (value: string): string =>\n value.startsWith(\"0x\") ? value.slice(2) : value;\n\n/**\n * The Arkade VHTLC leg. The three pubkeys the response reports are the exact keys\n * the server used to build the on-chain VHTLC, so feeding them back into\n * `VHTLC.Script` reproduces the funded pkScript regardless of which side funds or\n * claims — the manager watches by that script. `address` names the field the\n * response carries it in (`btc_vhtlc_address` for Arkade↔EVM, `arkade_vhtlc_address`\n * for Arkade↔Lightning).\n */\ntype ArkadeVhtlcFields = {\n sender_pk: string;\n receiver_pk: string;\n arkade_server_pk: string;\n hash_lock: string;\n vhtlc_refund_locktime: number;\n unilateral_claim_delay: number;\n unilateral_refund_delay: number;\n unilateral_refund_without_receiver_delay: number;\n arkade_htlc_script_version?: number;\n};\n\nfunction arkadeLeg(\n r: ArkadeVhtlcFields,\n address: string,\n expectedSats: number,\n): HtlcRef {\n return buildArkadeVhtlcRef({\n senderPk: r.sender_pk,\n receiverPk: r.receiver_pk,\n serverPk: r.arkade_server_pk,\n hashLock: r.hash_lock,\n address,\n refundLocktime: r.vhtlc_refund_locktime,\n unilateralClaimDelay: r.unilateral_claim_delay,\n unilateralRefundDelay: r.unilateral_refund_delay,\n unilateralRefundWithoutReceiverDelay:\n r.unilateral_refund_without_receiver_delay,\n expectedSats,\n arkadeHtlcScriptVersion: r.arkade_htlc_script_version,\n });\n}\n\n/**\n * The EVM HTLC leg. `claimAddress` is who can claim (the client for a\n * server-funded leg, the server for the client's own); `expectedSats`/`token`\n * are what the HTLC must lock (the leg is `invalid` otherwise).\n */\nfunction evmLeg(args: {\n chainId: number;\n htlc: string;\n hashLock: string;\n claimAddress: string;\n expectedSats: string;\n token?: string;\n /** The funder / refund address — completes the isActive tuple when known. */\n sender?: string;\n /** Refund timelock in unix seconds, exactly as funded on-chain. */\n timelockSec?: number;\n /** Swap creation time (ISO) — lower-bounds log scans. */\n createdAt?: string;\n}): HtlcRef {\n const createdAtMs = args.createdAt ? Date.parse(args.createdAt) : Number.NaN;\n return {\n ledger: \"evm\",\n chainId: args.chainId,\n htlc: ensure0x(args.htlc),\n preimageHash: ensure0x(args.hashLock),\n claimAddress: ensure0x(args.claimAddress),\n expectedAmount: BigInt(args.expectedSats),\n expectedToken: args.token ? ensure0x(args.token) : undefined,\n sender: args.sender ? ensure0x(args.sender) : undefined,\n timelockSec: args.timelockSec,\n createdAtMs: Number.isNaN(createdAtMs) ? undefined : createdAtMs,\n };\n}\n\n/**\n * The Bitcoin HTLC leg. `hashLock` is the SHA-256 preimage hash (`evm_hash_lock`);\n * the on-chain script commits `ripemd160` of it, but the classifier verifies a\n * revealed preimage as `sha256(preimage) === hashLock`.\n */\n/**\n * Confirmations a CLIENT-funded Bitcoin HTLC needs before it reads as funded.\n *\n * The server does not act on a client funding until it has a blocktime, so\n * observing it sooner would report the swap as funded while the other side is\n * still waiting for a block. A server-funded leg keeps the reader's 0-conf\n * default, which is what lets a claim go out without waiting ~10 minutes.\n */\nconst CLIENT_FUNDING_MIN_CONFIRMATIONS = 1;\n\nfunction bitcoinLeg(\n address: string,\n hashLock: string,\n expectedSats: number,\n minConfirmations?: number,\n): HtlcRef {\n return {\n ledger: \"bitcoin\",\n address,\n preimageHash: strip0x(hashLock),\n expectedSats,\n minConfirmations,\n };\n}\n\nexport function swapToTracked(stored: StoredSwap): TrackedSwap | undefined {\n const r = stored.response;\n switch (r.direction) {\n // Client sends Arkade (funds the VHTLC with source_amount sats) and claims EVM.\n case \"arkade_to_evm\":\n return {\n swapId: r.id,\n clientHtlc: arkadeLeg(r, r.btc_vhtlc_address, Number(r.source_amount)),\n serverHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.hash_lock,\n claimAddress: r.client_evm_address, // the client claims the server's EVM HTLC\n expectedSats: r.evm_expected_sats,\n token: r.wbtc_address,\n sender: r.server_evm_address, // the server funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n clientRefundLocktime: ms(r.vhtlc_refund_locktime),\n serverRefundLocktime: ms(r.evm_refund_locktime),\n };\n // Client sends EVM and claims the Arkade VHTLC (server funds it with target_amount sats).\n case \"evm_to_arkade\":\n return {\n swapId: r.id,\n clientHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.hash_lock,\n claimAddress: r.server_evm_address, // the server claims the client's EVM HTLC\n expectedSats: r.evm_expected_sats,\n // evm_to_arkade doesn't expose the locked token — fall back to the\n // per-chain mainnet constant so the isActive tuple is complete.\n token: lockedTokenFallback(r.evm_chain_id, r.network),\n sender: r.client_evm_address, // the client funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n serverHtlc: arkadeLeg(r, r.btc_vhtlc_address, Number(r.target_amount)),\n clientRefundLocktime: ms(r.evm_refund_locktime),\n serverRefundLocktime: ms(r.vhtlc_refund_locktime),\n };\n // Client sends BTC (funds the on-chain HTLC with source_amount sats) and claims EVM.\n case \"bitcoin_to_evm\":\n return {\n swapId: r.id,\n clientHtlc: bitcoinLeg(\n r.btc_htlc_address,\n r.evm_hash_lock,\n Number(r.source_amount),\n CLIENT_FUNDING_MIN_CONFIRMATIONS,\n ),\n serverHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.evm_hash_lock,\n claimAddress: r.client_evm_address, // the client claims the server's EVM HTLC\n expectedSats: r.evm_expected_sats,\n token: r.wbtc_address,\n sender: r.server_evm_address, // the server funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n clientRefundLocktime: ms(r.btc_refund_locktime),\n serverRefundLocktime: ms(r.evm_refund_locktime),\n };\n // Client sends EVM and claims the BTC HTLC (server funds it with target_amount sats).\n case \"evm_to_bitcoin\":\n return {\n swapId: r.id,\n clientHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.evm_hash_lock,\n claimAddress: r.server_evm_address, // the server claims the client's EVM HTLC\n expectedSats: r.evm_expected_sats,\n token: r.wbtc_address,\n sender: r.client_evm_address, // the client funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n serverHtlc: bitcoinLeg(\n r.btc_htlc_address,\n r.evm_hash_lock,\n Number(r.target_amount),\n ),\n clientRefundLocktime: ms(r.evm_refund_locktime),\n serverRefundLocktime: ms(r.btc_refund_locktime),\n };\n\n // Client sends BTC (funds the on-chain HTLC) and claims the Arkade VHTLC (the\n // server funds it). Unlike the other Arkade directions, the VHTLC's receiver is\n // the client's own key (not in the response) and its funder is `server_vhtlc_pk`;\n // the hash lock is a 20-byte HASH160, which buildArkadeVhtlcRef takes as-is.\n case \"btc_to_arkade\":\n return {\n swapId: r.id,\n clientHtlc: bitcoinLeg(\n r.btc_htlc_address,\n r.hash_lock,\n Number(r.source_amount),\n CLIENT_FUNDING_MIN_CONFIRMATIONS,\n ),\n serverHtlc: buildArkadeVhtlcRef({\n senderPk: r.server_vhtlc_pk, // the server funds the VHTLC\n receiverPk: stored.publicKey, // the client claims it\n serverPk: r.arkade_server_pk,\n hashLock: r.hash_lock,\n address: r.arkade_vhtlc_address,\n refundLocktime: r.vhtlc_refund_locktime,\n unilateralClaimDelay: r.unilateral_claim_delay,\n unilateralRefundDelay: r.unilateral_refund_delay,\n unilateralRefundWithoutReceiverDelay:\n r.unilateral_refund_without_receiver_delay,\n expectedSats: Number(r.target_amount),\n arkadeHtlcScriptVersion: r.arkade_htlc_script_version,\n }),\n clientRefundLocktime: ms(r.btc_refund_locktime),\n serverRefundLocktime: ms(r.vhtlc_refund_locktime),\n };\n\n // ─── Lightning: one on-chain leg, one off-chain LN payment ───────────────\n\n // Pay-on-Lightning: the client pays a Lightning invoice (off-chain, nothing to\n // watch) and claims an on-chain HTLC. No client-funded leg — the client's\n // claim completes the swap; a hold invoice that never settles auto-unwinds.\n\n // Client pays the invoice, then claims the Arkade VHTLC (target_amount).\n case \"lightning_to_arkade\":\n return {\n swapId: r.id,\n serverHtlc: arkadeLeg(\n r,\n r.arkade_vhtlc_address,\n Number(r.target_amount),\n ),\n clientRefundLocktime: 0, // no on-chain client leg\n serverRefundLocktime: ms(r.vhtlc_refund_locktime),\n };\n\n // Client funds the Arkade VHTLC (source_amount); the server pays the\n // Lightning invoice off-chain and claims the VHTLC — no server leg to\n // watch.\n case \"arkade_to_lightning\":\n return {\n swapId: r.id,\n clientHtlc: arkadeLeg(\n r,\n r.arkade_vhtlc_address,\n Number(r.source_amount),\n ),\n clientRefundLocktime: ms(r.vhtlc_refund_locktime),\n serverRefundLocktime: 0, // no on-chain server leg\n };\n default:\n return undefined;\n }\n}\n","/**\n * The reactive orchestration.\n *\n * A {@link SwapTracker} watches each tracked swap's on-chain HTLC leg(s) via\n * per-ledger {@link ContractManager}s (two legs for chain↔chain swaps, one for\n * Lightning). On any change it recomputes the next action\n * (`deriveSwapStatus` → `deriveSwapActions`), and — deduped — notifies\n * subscribers, dropping a swap once it reaches a terminal state.\n *\n * This is the observe-mode layer: it *notifies*. Auto-execution (recover/auto\n * mode running the action) is a policy layer built on top of these same\n * notifications, gated by each action's `automation`.\n */\nimport { deriveSwapActions } from \"../actions/derive.js\";\nimport { deriveSwapStatus } from \"../actions/status.js\";\nimport type { HtlcObservation, SwapActions } from \"../actions/types.js\";\nimport type { ContractManager, HtlcRef, Ledger } from \"../contracts/types.js\";\n\n/**\n * A swap the tracker watches: its HTLC legs + refund locktimes (from the recovery\n * bundle). Chain↔chain swaps have both legs; Lightning swaps have exactly one\n * on-chain leg (the other side is an off-chain Lightning payment) — see\n * {@link SwapObservations}.\n */\nexport type TrackedSwap = {\n swapId: string;\n clientHtlc?: HtlcRef;\n serverHtlc?: HtlcRef;\n clientRefundLocktime: number;\n serverRefundLocktime: number;\n};\n\nexport type ActionSubscriber = (swapId: string, actions: SwapActions) => void;\n\nexport type SwapTrackerOptions = {\n /**\n * The local tick interval (ms). Each tick recomputes every tracked swap from\n * extrapolated chain clocks — pure local work, NO chain reads — so timelock\n * flips (a refund unlocking, a stale pending swap turning reapable) surface\n * between hints. `0` disables the timer (tests drive `refresh` manually).\n */\n refreshIntervalMs?: number;\n /**\n * Minimum ms between chain reconciles of AT-RISK swaps — those whose client\n * leg holds funds on-chain (≥ clientfunded, unresolved), where refund safety\n * must not depend on the server. Everything else advances on server hints\n * only ({@link applyHint}); a swap with nothing at stake costs zero chain\n * reads. Default 60s.\n */\n atRiskReconcileIntervalMs?: number;\n};\n\nexport class SwapTracker {\n readonly #managers: Map<Ledger, ContractManager>;\n readonly #swaps = new Map<string, TrackedSwap>();\n /** Last actions emitted per swap — for dedupe and the subscribe-time snapshot. */\n readonly #lastActions = new Map<string, SwapActions>();\n readonly #subscribers = new Set<ActionSubscriber>();\n readonly #refreshIntervalMs: number;\n readonly #atRiskReconcileIntervalMs: number;\n #lastAtRiskReconcileAt = 0;\n #eventUnsubs: Array<() => void> = [];\n #timer: ReturnType<typeof setInterval> | undefined;\n\n constructor(\n managers: Map<Ledger, ContractManager>,\n options?: SwapTrackerOptions,\n ) {\n this.#managers = managers;\n this.#refreshIntervalMs = options?.refreshIntervalMs ?? 0;\n this.#atRiskReconcileIntervalMs =\n options?.atRiskReconcileIntervalMs ?? 60_000;\n }\n\n /**\n * Whether every on-chain leg of `swap` is observable — i.e. the tracker can\n * derive its status from chain. `false` means a leg is on a ledger/chain this\n * client can't reach (no manager, or an EVM chain with no RPC), so the swap\n * can't be tracked. Pure predicate — callers decide the policy (skip vs reject).\n */\n canObserve(swap: TrackedSwap): boolean {\n return legsOf(swap).every(\n (leg) => this.#managers.get(leg.ledger)?.canObserve(leg) ?? false,\n );\n }\n\n /** Register each swap's on-chain leg(s), subscribe to manager events, and seed state. */\n async startTracking(swaps: TrackedSwap[]): Promise<void> {\n for (const swap of swaps) {\n // Skip a swap with an unreachable leg rather than letting register() throw\n // and abort tracking for every other swap too. It simply goes untracked.\n if (!this.canObserve(swap)) {\n console.warn(\n `SwapTracker: skipping swap ${swap.swapId} — a leg is on a chain this client can't reach (configure it to track this swap)`,\n );\n continue;\n }\n this.#swaps.set(swap.swapId, swap);\n for (const leg of legsOf(swap)) await this.#managerFor(leg).register(leg);\n }\n for (const manager of new Set(this.#managers.values())) {\n this.#eventUnsubs.push(manager.onEvent(() => this.#recomputeAll()));\n }\n // Prime each manager: seeds its clock and does a full reconcile, so the first\n // recompute has both observations AND clocks. Without this a ledger whose\n // clock is only populated on refresh (Arkade's MTP) stays `undefined` and the\n // recompute bails forever — nothing is ever emitted.\n await this.#refreshManagers();\n this.#recomputeAll();\n // Tick onward: local recomputes off extrapolated clocks, plus the gated\n // at-risk chain reconcile. The prime above counts as the first reconcile.\n this.#lastAtRiskReconcileAt = Date.now();\n if (this.#refreshIntervalMs > 0)\n this.#timer = setInterval(\n () => void this.#tick(),\n this.#refreshIntervalMs,\n );\n }\n\n /**\n * Track one more swap after {@link startTracking} — register its on-chain\n * leg(s), seed the managers they touch, and derive its first action. Idempotent:\n * a swap already tracked, or one already seen through to a terminal action, is\n * ignored. New swaps are picked up by the existing per-manager event listeners\n * and poll timer, so nothing else needs re-wiring. Lets a swap created after\n * start (e.g. via `createSwap`) be tracked without a restart.\n */\n async track(swap: TrackedSwap): Promise<void> {\n // Already active, or already completed (lastActions is retained past\n // terminal) — either way, don't re-register.\n if (this.#swaps.has(swap.swapId) || this.#lastActions.has(swap.swapId))\n return;\n // Unreachable leg: skip rather than throw. (The create path rejects such a\n // swap up front; a pre-existing one reaching here just goes untracked.)\n if (!this.canObserve(swap)) {\n console.warn(\n `SwapTracker: skipping swap ${swap.swapId} — a leg is on a chain this client can't reach (configure it to track this swap)`,\n );\n return;\n }\n this.#swaps.set(swap.swapId, swap);\n\n const registered: HtlcRef[] = [];\n try {\n for (const leg of legsOf(swap)) {\n await this.#managerFor(leg).register(leg);\n registered.push(leg);\n }\n } catch (error) {\n // Roll back a partial registration. Without this the swap stays latched in\n // `#swaps` — so the guard above rejects every later retry — with one leg\n // watched and the other not, deriving nothing until a full stop/start.\n // A register failure is not self-healing (the poll can't set up a watch it\n // never made), so undo it and let a later sync try again cleanly.\n this.#swaps.delete(swap.swapId);\n for (const leg of registered) void this.#managerFor(leg).unregister(leg);\n throw error;\n }\n\n // Seed/advance just the managers this swap touches (clock + observations),\n // then derive its first action — mirrors startTracking's prime step. Unlike\n // registration, a failure here IS self-healing: the swap is fully registered\n // and unseeded legs count as at-risk, so the tick reconciles them. Leave it\n // tracked.\n const managers = new Set(legsOf(swap).map((leg) => this.#managerFor(leg)));\n await Promise.all([...managers].map((manager) => manager.refresh()));\n this.#recompute(swap);\n }\n\n /**\n * The periodic tick: recompute every swap from extrapolated clocks (pure\n * local work), and — at most every `atRiskReconcileIntervalMs` — re-read the\n * chain for at-risk swaps. This is the trust boundary: the happy path rides\n * server hints, but once the client's funds sit in an HTLC, refund\n * availability (and the counterparty leg's state) is verified against the\n * chain on a cadence the server can't influence.\n */\n async #tick(): Promise<void> {\n const now = Date.now();\n if (now - this.#lastAtRiskReconcileAt >= this.#atRiskReconcileIntervalMs) {\n this.#lastAtRiskReconcileAt = now;\n const legs = [...this.#swaps.values()]\n .filter((swap) => this.#isAtRisk(swap))\n .flatMap((swap) => legsOf(swap));\n await Promise.all(\n legs.map((leg) =>\n this.#managerFor(leg)\n .reconcile(leg)\n .catch((error) => {\n console.warn(\"SwapTracker: at-risk reconcile failed:\", error);\n }),\n ),\n );\n }\n this.#recomputeAll();\n }\n\n /**\n * Whether a swap needs chain-verified monitoring independent of the server:\n * its client leg holds (or held) funds on-chain, so knowing when a refund\n * unlocks — or that the counterparty resolved a leg — must not depend on a\n * hint arriving. An unfunded client leg (`absent`) is server-hint territory;\n * an unseeded one (`undefined`, e.g. the prime failed on a flaky RPC) counts\n * as at-risk so the tick heals it. A swap with no on-chain client leg\n * (paying via Lightning) is at-risk while tracked: once the invoice is paid\n * there is no on-chain trace of the client's exposure, and the server leg is\n * the only thing left to watch.\n */\n #isAtRisk(swap: TrackedSwap): boolean {\n if (!swap.clientHtlc) return true;\n const obs = this.#managerFor(swap.clientHtlc).getState(swap.clientHtlc);\n return obs !== \"absent\";\n }\n\n /**\n * Refresh the managers that have a registered leg — seeding their clocks and\n * reconciling observations (the on-demand full read backing startTracking's\n * prime and track()'s seed). Managers with nothing tracked are skipped:\n * refreshing them still hits their clock source (e.g. Arkade/Bitcoin MTP via\n * the API), so a swap that doesn't touch a ledger can't fail on that\n * ledger's endpoint being down (the default client builds every manager).\n */\n async #refreshManagers(): Promise<void> {\n const active = new Set<Ledger>();\n for (const swap of this.#swaps.values())\n for (const leg of legsOf(swap)) active.add(leg.ledger);\n // Several ledgers can share a manager, so dedupe on the manager while\n // keeping a ledger name to report a failure against.\n const byManager = new Map<ContractManager, Ledger>();\n for (const ledger of active) {\n const manager = this.#managers.get(ledger);\n if (manager && !byManager.has(manager)) byManager.set(manager, ledger);\n }\n\n // Settled, not all: one ledger's endpoint failing must not deny every other\n // swap its observations. A manager that throws simply contributes none this\n // round — a missing observation already reads as \"not seen yet\" — and the\n // next refresh retries it. Rejecting instead would propagate out of\n // `startTracking`, whose prime awaits this, leaving every tracked swap on\n // every ledger with no derived actions at all.\n const ledgers = [...byManager.values()];\n const results = await Promise.allSettled(\n [...byManager.keys()].map((manager) => manager.refresh()),\n );\n results.forEach((result, index) => {\n if (result.status === \"rejected\")\n console.warn(\n `SwapTracker: refreshing ${ledgers[index]} failed:`,\n result.reason,\n );\n });\n }\n\n /** The swap ids currently tracked — e.g. to subscribe them to a hint feed. */\n trackedSwapIds(): string[] {\n return [...this.#swaps.keys()];\n }\n\n /**\n * Re-verify one swap against the chain right now — the hook a faster server\n * status hint drives. Reconciles just that swap's legs (targeted, on the\n * managers), then recomputes its action. The hint is only a *trigger*: the\n * chain stays the source of truth, so a premature hint that the chain doesn't\n * yet reflect changes nothing. A no-op for an untracked swap.\n */\n async applyHint(swapId: string, opts?: { force?: boolean }): Promise<void> {\n const swap = this.#swaps.get(swapId);\n if (!swap) return;\n // Settled, not all: a hint is the fast path, and rejecting here would throw\n // it away for BOTH legs because one was unreadable — dropping the swap onto\n // the at-risk poller, which re-reads on a cadence measured in minutes. A leg\n // that fails keeps its previous observation and is recomputed from whatever\n // did land; the poller remains the backstop that heals it.\n const legs = legsOf(swap);\n const results = await Promise.allSettled(\n legs.map((leg) => this.#managerFor(leg).reconcile(leg)),\n );\n results.forEach((result, index) => {\n if (result.status === \"rejected\")\n console.warn(\n `SwapTracker: hint reconcile failed for ${legs[index].ledger}:`,\n result.reason,\n );\n });\n // `force` re-notifies even when the derived action is unchanged — the hook a\n // failed auto-execution uses to re-verify against chain and get retried, since\n // an unchanged action would otherwise be deduped and never re-trigger.\n this.#recompute(swap, opts?.force ?? false);\n }\n\n /** Notify `cb` of the current action for each tracked swap, then on every change. */\n subscribeToActions(cb: ActionSubscriber): () => void {\n this.#subscribers.add(cb);\n for (const [swapId, actions] of this.#lastActions) cb(swapId, actions);\n return () => this.#subscribers.delete(cb);\n }\n\n /**\n * Stop tracking: unregister every still-tracked leg (releasing its manager's\n * watch — e.g. the EVM per-chain block watch that only `unregister` tears down),\n * drop the poll timer, and detach event listeners + subscribers. Mirrors\n * `startTracking`'s `register`, so no manager keeps watching a stopped tracker's\n * swaps. The managers themselves are not owned here, so they are unregistered but\n * never disposed.\n */\n stop(): void {\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = undefined;\n for (const unsub of this.#eventUnsubs) unsub();\n this.#eventUnsubs = [];\n for (const swap of this.#swaps.values())\n for (const leg of legsOf(swap))\n void this.#managerFor(leg).unregister(leg);\n this.#swaps.clear();\n this.#subscribers.clear();\n }\n\n #managerFor(ref: HtlcRef): ContractManager {\n const manager = this.#managers.get(ref.ledger);\n if (!manager)\n throw new Error(`no ContractManager for ledger '${ref.ledger}'`);\n return manager;\n }\n\n #recomputeAll(): void {\n for (const swap of this.#swaps.values()) this.#recompute(swap);\n }\n\n #recompute(swap: TrackedSwap, force = false): void {\n // A Lightning swap has one on-chain leg; the absent leg stays `undefined` (its\n // status is derived from the leg that exists). Gate only on present legs — a\n // leg with no observation or clock yet means \"not enough known\", so bail.\n let clientHtlc: HtlcObservation | undefined;\n let serverHtlc: HtlcObservation | undefined;\n let clientChainNow = 0;\n let serverChainNow = 0;\n\n if (swap.clientHtlc) {\n const m = this.#managerFor(swap.clientHtlc);\n clientHtlc = m.getState(swap.clientHtlc);\n const now = m.chainNow(swap.clientHtlc);\n if (clientHtlc === undefined || now === undefined) return;\n clientChainNow = now;\n }\n if (swap.serverHtlc) {\n const m = this.#managerFor(swap.serverHtlc);\n serverHtlc = m.getState(swap.serverHtlc);\n const now = m.chainNow(swap.serverHtlc);\n if (serverHtlc === undefined || now === undefined) return;\n serverChainNow = now;\n }\n\n const status = deriveSwapStatus({ clientHtlc, serverHtlc });\n if (status === undefined) return; // contradictory observations\n\n const actions = {\n ...deriveSwapActions({\n status,\n clientChainNow,\n serverChainNow,\n clientRefundLocktime: swap.clientRefundLocktime,\n serverRefundLocktime: swap.serverRefundLocktime,\n // Pay-on-Lightning swaps have no client-funded on-chain leg.\n clientFunds: swap.clientHtlc !== undefined,\n }),\n // Expose the raw observations so consumers can render progress from\n // chain facts (which leg is funded/spent) even when the server is\n // stale/unreachable — without interpreting the SwapStatus enum.\n observations: { clientHtlc, serverHtlc },\n };\n\n const previous = this.#lastActions.get(swap.swapId);\n if (\n !force &&\n previous &&\n JSON.stringify(previous) === JSON.stringify(actions)\n )\n return;\n\n this.#lastActions.set(swap.swapId, actions);\n for (const cb of this.#subscribers) cb(swap.swapId, actions);\n\n // Terminal: stop watching (retain lastActions so late subscribers still see it).\n if (actions.recommended === \"none\") this.#untrack(swap);\n }\n\n #untrack(swap: TrackedSwap): void {\n this.#swaps.delete(swap.swapId);\n for (const leg of legsOf(swap)) void this.#managerFor(leg).unregister(leg);\n }\n}\n\n/** The on-chain legs actually present on a swap (one for Lightning, two otherwise). */\nfunction legsOf(swap: TrackedSwap): HtlcRef[] {\n const legs: HtlcRef[] = [];\n if (swap.clientHtlc) legs.push(swap.clientHtlc);\n if (swap.serverHtlc) legs.push(swap.serverHtlc);\n return legs;\n}\n"],"mappings":"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","names":["refund","hex","hex","hex","hex","hex","esploraReader","electrumReader","hex","refs","ripemd160","hex","seconds","strip0x"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/actions/derive.ts","../src/actions/status.ts","../src/client.ts","../src/contracts/arkade-manager.ts","../src/contracts/arkade.ts","../src/contracts/preimage.ts","../src/contracts/arkade-network.ts","../src/contracts/bitcoin-manager.ts","../src/contracts/bitcoin.ts","../src/contracts/evm.ts","../src/contracts/types.ts","../src/contracts/evm-manager.ts","../src/contracts/evm-reader-viem.ts","../src/hints/hint-tracker.ts","../src/hints/swap-worker.ts","../src/hints/ws-status-source.ts","../src/contracts/arkade-vhtlc.ts","../src/tracker/from-swap.ts","../src/tracker/swap-tracker.ts"],"sourcesContent":["/**\n * @satora/swap — the Satora swap client.\n *\n * Re-exports the legacy `@lendasat/lendaswap-sdk-pure` bundle (types, signer\n * helpers, storage, etc.), but shadows `Client` and `ClientBuilder` with the\n * Satora-native versions from `./client`. Those are drop-in replacements — same\n * public surface, forwarding to an internal legacy client for now — and are the\n * seam for migrating implementation into `@satora/*` and adding new features.\n *\n * Explicit named exports below take precedence over the `export *` star for the\n * same names, so consumers importing `{ Client, ClientBuilder }` get the new\n * ones while everything else stays legacy.\n */\nexport * from \"@lendasat/lendaswap-sdk-pure\";\n// Satora-native features.\nexport { deriveSwapActions } from \"./actions/derive.js\";\nexport { deriveSwapStatus } from \"./actions/status.js\";\nexport type * from \"./actions/types.js\";\nexport { Client, ClientBuilder } from \"./client.js\";\n// Observe-mode tracking: per-ledger monitors + the recovery-bundle mapper.\nexport {\n ArkadeContractManager,\n type ArkadeContractManagerDeps,\n type ArkadeCreateConfig,\n} from \"./contracts/arkade-manager.js\";\nexport { defaultArkadeServerUrl } from \"./contracts/arkade-network.js\";\nexport {\n type ArkadeVhtlcInput,\n buildArkadeVhtlcRef,\n} from \"./contracts/arkade-vhtlc.js\";\nexport {\n type BitcoinChainReader,\n BitcoinContractManager,\n type BitcoinContractManagerDeps,\n type BitcoinCreateConfig,\n} from \"./contracts/bitcoin-manager.js\";\nexport {\n type ElectrumReaderNetwork,\n type ElectrumRpc,\n electrumReader,\n} from \"./contracts/bitcoin-reader-electrum.js\";\nexport {\n type BitcoinConfirmationPolicy,\n DEFAULT_ESPLORA_URLS,\n esploraReader,\n htlcFactsFromEsploraTxs,\n} from \"./contracts/bitcoin-reader-esplora.js\";\nexport {\n type EvmChainReader,\n EvmContractManager,\n type EvmContractManagerDeps,\n type EvmHtlcQuery,\n htlcQueryKey,\n} from \"./contracts/evm-manager.js\";\nexport {\n createEvmRpcReader,\n DEFAULT_EVM_RPCS,\n defaultEvmReaders,\n type EvmLogClient,\n evmReaderFromClient,\n type RawLog,\n} from \"./contracts/evm-reader-viem.js\";\nexport type * from \"./contracts/types.js\";\nexport { htlcKey } from \"./contracts/types.js\";\nexport {\n HintTracker,\n type HintTrackerOptions,\n} from \"./hints/hint-tracker.js\";\nexport {\n SwapWorker,\n type SwapWorkerOptions,\n type WorkerHintSource,\n type WorkerTracker,\n} from \"./hints/swap-worker.js\";\nexport {\n type SwapStatusUpdate,\n type WebSocketLike,\n WsStatusSource,\n type WsStatusSourceOptions,\n} from \"./hints/ws-status-source.js\";\nexport { swapToTracked } from \"./tracker/from-swap.js\";\nexport {\n type ActionSubscriber,\n SwapTracker,\n type TrackedSwap,\n} from \"./tracker/swap-tracker.js\";\n","import type {\n ClaimAction,\n FundAction,\n NoneAction,\n RefundUnilateralAction,\n SwapActionInput,\n SwapActions,\n WaitAction,\n} from \"./types.js\";\n\n/**\n * How long past the client refund locktime a `pending` swap keeps being watched\n * before it is declared dead. Covers observation lag on a deadline-straddling\n * funding tx (EVM has no mempool-level observation, and clocks are partly\n * extrapolated); one that stays invisible longer than this is treated as never\n * sent. A later `startTracking` still picks the swap up from storage if a\n * funding somehow surfaces afterwards.\n */\nexport const FUNDING_REAP_GRACE_MS = 60 * 60 * 1000;\n\nexport function deriveSwapActions(input: SwapActionInput): SwapActions {\n const {\n status,\n clientChainNow,\n serverChainNow,\n clientRefundLocktime,\n serverRefundLocktime,\n clientFunds = true,\n } = input;\n\n switch (status) {\n case \"pending\": {\n // Pay-on-Lightning: the client's \"funding\" is paying the invoice off-chain\n // (handled by their Lightning wallet), not an on-chain deposit — there is\n // nothing for us to recommend funding, so just wait for the invoice to land\n // as a funded HTLC we can claim.\n if (!clientFunds) {\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"client_payment\",\n recommended: true,\n automation: \"auto\",\n reason: \"Waiting for the Lightning payment to be locked in.\",\n };\n return { recommended: \"wait\", actions: [wait] };\n }\n\n // Funding is \"too late\" once past the client's refund locktime (the funded\n // deposit would be immediately refundable). But `pending` only means the\n // last chain read saw NO deposit — a funding broadcast near the deadline\n // can still surface after it (and would need the refund path). So keep\n // watching through a grace window; only once it has clearly stayed unfunded\n // derive `none`, letting the tracker reap the dead swap instead of\n // watching it forever.\n if (clientChainNow >= clientRefundLocktime + FUNDING_REAP_GRACE_MS) {\n const done: NoneAction = {\n id: \"none\",\n outcome: \"expired\",\n recommended: true,\n automation: \"auto\",\n reason: \"The funding window closed.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n if (clientChainNow >= clientRefundLocktime) return { actions: [] };\n\n const fund: FundAction = {\n id: \"fund\",\n recommended: true,\n // Funding spends new user money, so it is never auto-run.\n automation: \"manual\",\n reason: \"Send the deposit to start the swap.\",\n };\n return { recommended: \"fund\", actions: [fund] };\n }\n\n case \"clientfundingseen\": {\n // Funding tx seen but not yet confirmed — wait for confirmation (then the\n // server funds its side). Nothing to refund until the HTLC confirms.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"client_funding_confirmation\",\n recommended: true,\n automation: \"auto\",\n reason: \"Waiting for your funding transaction to confirm.\",\n };\n return { recommended: \"wait\", actions: [wait] };\n }\n\n case \"clientfunded\": {\n const refundUnlocked = clientChainNow >= clientRefundLocktime;\n\n if (refundUnlocked) {\n // Timelock passed: reclaiming the deposit is the recommended way out.\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: true,\n automation: \"confirm\",\n reason: \"Reclaim your deposit.\",\n };\n return { recommended: \"refund_unilateral\", actions: [refund] };\n }\n\n // Still within the timelock: wait for the server; refund is a valid but\n // not-yet-runnable option, surfaced so the UI can show when it unlocks.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"server_funding\",\n recommended: true,\n automation: \"auto\",\n reason: \"Waiting for the server to fund the swap.\",\n };\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: false,\n automation: \"confirm\",\n reason: \"Refund becomes available once the timelock passes.\",\n blockedBy: {\n kind: \"timelock_not_expired\",\n message: \"The refund timelock has not passed yet.\",\n },\n };\n return { recommended: \"wait\", actions: [wait, refund] };\n }\n\n case \"serverfunded\": {\n // The client created the swap, so it always holds the preimage.\n const claimWindowClosed = serverChainNow >= serverRefundLocktime;\n\n if (!claimWindowClosed) {\n // Server can't refund yet, so revealing the preimage by claiming is safe.\n // Claiming secures the user's funds, so it is safe to auto-run.\n const claim: ClaimAction = {\n id: \"claim\",\n recommended: true,\n automation: \"auto\",\n reason: \"Redeem the swap with your preimage.\",\n };\n return { recommended: \"claim\", actions: [claim] };\n }\n\n // Pay-on-Lightning: the client has no on-chain deposit at risk, so there is\n // nothing to protect by keeping the secret and nothing to refund. Once the\n // claim window closed the leg is (being) reclaimed by the server; the\n // client's Lightning payment unwinds on its own. Terminal.\n if (!clientFunds) {\n const done: NoneAction = {\n id: \"none\",\n outcome: \"refunded\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"The claim window passed — your Lightning payment will be refunded automatically.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n // Claim window closed: the server can now refund its HTLC, and claiming\n // would reveal the preimage — letting the server also take the client's\n // deposit. Never claim now; refund instead (keeping the secret). Refund\n // unlocks at the client's own, later timelock.\n const refundUnlocked = clientChainNow >= clientRefundLocktime;\n\n if (refundUnlocked) {\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: true,\n automation: \"confirm\",\n reason:\n \"Reclaim your deposit — do not claim, as revealing the preimage would let the server take it.\",\n };\n return { recommended: \"refund_unilateral\", actions: [refund] };\n }\n\n // Refund not unlocked yet: wait for the client timelock, then refund. Still\n // must not claim.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"refund_timelock\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"Wait for your refund timelock — do not claim, as revealing the preimage would let the server take your deposit.\",\n };\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: false,\n automation: \"confirm\",\n reason: \"Refund becomes available once the timelock passes.\",\n blockedBy: {\n kind: \"timelock_not_expired\",\n message: \"The refund timelock has not passed yet.\",\n },\n };\n return { recommended: \"wait\", actions: [wait, refund] };\n }\n\n case \"clientredeeming\": {\n // The client's claim (redeem) is in flight — wait for it to confirm, then\n // the funds are received.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"claim_confirmation\",\n recommended: true,\n automation: \"auto\",\n reason: \"Redeeming your funds — waiting for the claim to confirm.\",\n };\n return { recommended: \"wait\", actions: [wait] };\n }\n\n case \"clientredeemed\":\n case \"serverredeemed\": {\n // The client claimed its funds — done from the client's side. The server\n // will sweep the client's deposit with the now-public preimage (and\n // `serverredeemed` means it already has); the client must not try to refund\n // it. Both are terminal successes — nothing to do.\n const done: NoneAction = {\n id: \"none\",\n outcome: \"completed\",\n recommended: true,\n automation: \"auto\",\n reason: \"Swap complete — you've received your funds.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"expired\": {\n // \"Pending → Expired\": the funding window passed without the client\n // funding, so nothing is locked and there is nothing to refund. Terminal.\n const done: NoneAction = {\n id: \"none\",\n outcome: \"expired\",\n recommended: true,\n automation: \"auto\",\n reason: \"The swap expired without funding — nothing was locked.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"clientrefunded\":\n case \"clientrefundedserverrefunded\":\n case \"clientrefundedserverfunded\": {\n // Terminal: the client's deposit was refunded. (`*serverrefunded` also\n // returned the server's; `clientrefundedserverfunded` is the backend's\n // \"should never occur\" error where the server funded after the client\n // refunded — the server's problem, not the client's.) Nothing to do.\n const done: NoneAction = {\n id: \"none\",\n outcome: \"refunded\",\n recommended: true,\n automation: \"auto\",\n reason: \"Refunded — your deposit was returned.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"clientredeemedandclientrefunded\": {\n // Error state: the client both redeemed and refunded (took both sides).\n // Anomalous but terminal — nothing for the client to do.\n const done: NoneAction = {\n id: \"none\",\n // The client holds both sides' funds — \"completed\" from its viewpoint.\n outcome: \"completed\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"This swap ended in an anomalous state (redeemed and refunded); no action needed.\",\n };\n return { recommended: \"none\", actions: [done] };\n }\n\n case \"clientfundedserverrefunded\":\n case \"serverwontfund\":\n case \"clientfundedtoolate\":\n case \"clientinvalidfunded\": {\n // The swap failed with the client's deposit funded — the only path is to\n // reclaim it via the client's refund timelock. (No happy-path wait: unlike\n // `clientfunded`, the swap will not complete.)\n const refundUnlocked = clientChainNow >= clientRefundLocktime;\n\n if (refundUnlocked) {\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: true,\n automation: \"confirm\",\n reason: \"Reclaim your deposit — the swap did not complete.\",\n };\n return { recommended: \"refund_unilateral\", actions: [refund] };\n }\n\n // Refund not unlocked yet: wait for the client timelock, then refund.\n const wait: WaitAction = {\n id: \"wait\",\n waitingOn: \"refund_timelock\",\n recommended: true,\n automation: \"auto\",\n reason:\n \"The swap did not complete; waiting for the refund timelock to reclaim your deposit.\",\n };\n const refund: RefundUnilateralAction = {\n id: \"refund_unilateral\",\n recommended: false,\n automation: \"confirm\",\n reason: \"Refund becomes available once the timelock passes.\",\n blockedBy: {\n kind: \"timelock_not_expired\",\n message: \"The refund timelock has not passed yet.\",\n },\n };\n return { recommended: \"wait\", actions: [wait, refund] };\n }\n\n default:\n return assertNever(status);\n }\n}\n\n/** Exhaustiveness guard: a compile error here means a `SwapStatus` case is missing. */\nfunction assertNever(status: never): never {\n throw new Error(`unhandled swap status: ${String(status)}`);\n}\n","/**\n * Derive a swap's lifecycle status from on-chain observations alone — the\n * trusted, server-independent source of truth.\n *\n * Pure: `deriveSwapStatus(observations) -> SwapStatus | undefined`. The wrapper\n * gathers the {@link SwapObservations} from chain (the server's status is only a\n * hint used to fetch efficiently), then cross-checks: if this disagrees with the\n * server, act on this. If the server is unavailable, derive from the local\n * recovery bundle's HTLC addresses — recovery still works.\n *\n * Returns `undefined` for contradictory/indeterminate observations (e.g. the\n * server's HTLC swept-by-claim while the client's HTLC was never involved — the\n * server can't hold the preimage before the client reveals it).\n *\n * Scope: this maps *settled* HTLC state to a status. Time- or param-dependent\n * refinements aren't derivable from HTLC state alone and are layered on\n * afterwards (or collapse into the state here):\n * - `expired` / `clientfundedtoolate`: `pending`/`clientfunded` + a clock check.\n * - `clientinvalidfunded`: needs the expected HTLC params, not just \"confirmed\".\n * - `serverwontfund`: a server intention, invisible on-chain — acts like\n * `clientfunded` (refund after the timelock), so it never needs deriving.\n * - `clientredeeming`: an in-flight (mempool) claim — a transient, not modelled\n * from settled state here.\n */\nimport type { SwapStatus } from \"@lendasat/lendaswap-sdk-pure\";\nimport type { HtlcObservation, SwapObservations } from \"./types.js\";\n\nexport function deriveSwapStatus(\n obs: SwapObservations,\n): SwapStatus | undefined {\n const { clientHtlc, serverHtlc } = obs;\n\n // Lightning swaps watch a single on-chain leg; the other side is an off-chain\n // Lightning payment with no HTLC (see SwapObservations). Derive from the leg\n // that exists.\n if (serverHtlc === undefined && clientHtlc !== undefined) {\n return receiveOnLightningStatus(clientHtlc); // *_to_lightning\n }\n if (clientHtlc === undefined && serverHtlc !== undefined) {\n return payOnLightningStatus(serverHtlc); // lightning_to_*\n }\n if (clientHtlc === undefined || serverHtlc === undefined) return undefined;\n\n // A spend on either HTLC has resolved (part of) the swap — check these first.\n if (clientHtlc === \"spent_claim\" && serverHtlc === \"spent_claim\") {\n return \"serverredeemed\"; // client redeemed, server swept — fully complete\n }\n if (clientHtlc === \"spent_refund\" && serverHtlc === \"spent_claim\") {\n return \"clientredeemedandclientrefunded\"; // client took both sides (anomaly)\n }\n if (clientHtlc === \"spent_refund\" && serverHtlc === \"spent_refund\") {\n return \"clientrefundedserverrefunded\";\n }\n if (clientHtlc === \"spent_refund\" && serverHtlc === \"confirmed\") {\n return \"clientrefundedserverfunded\"; // error state per the backend\n }\n if (\n clientHtlc === \"spent_refund\" &&\n (serverHtlc === \"absent\" || serverHtlc === \"mempool\")\n ) {\n return \"clientrefunded\";\n }\n if (clientHtlc === \"confirmed\" && serverHtlc === \"spent_claim\") {\n return \"clientredeemed\"; // client claimed the server's HTLC\n }\n if (clientHtlc === \"confirmed\" && serverHtlc === \"spent_refund\") {\n return \"clientfundedserverrefunded\"; // server let its HTLC time out\n }\n\n // Client funded, but not on the swap's terms (e.g. under the expected amount) —\n // the swap won't complete honestly, so reclaim the deposit.\n if (clientHtlc === \"invalid\") return \"clientinvalidfunded\";\n\n // No relevant spend — funding progression. `serverfunded` requires the server's\n // HTLC to be `confirmed` (correct amount/terms); an `invalid` server funding is\n // deliberately NOT serverfunded, so the client never claims a bad server leg —\n // it stays `clientfunded` and the client refunds after its timelock.\n if (clientHtlc === \"absent\") return \"pending\";\n if (clientHtlc === \"mempool\") return \"clientfundingseen\";\n if (clientHtlc === \"confirmed\") {\n return serverHtlc === \"confirmed\" ? \"serverfunded\" : \"clientfunded\";\n }\n\n // Contradictory (e.g. the client's HTLC swept-by-claim while the server's HTLC\n // isn't also claimed — impossible without the preimage having been revealed).\n return undefined;\n}\n\n/**\n * Receive-on-Lightning (`arkade_to_lightning`, `evm_to_lightning`): the client\n * funds a single on-chain HTLC and receives via Lightning. There is no\n * client-claimed server leg — the server sweeping the client's HTLC with the\n * preimage (it obtained by paying the client's Lightning invoice) is the success\n * signal. So the whole lifecycle derives from the client's leg alone.\n */\nfunction receiveOnLightningStatus(clientHtlc: HtlcObservation): SwapStatus {\n switch (clientHtlc) {\n case \"spent_claim\":\n return \"serverredeemed\"; // server swept the deposit ⟹ it paid the invoice\n case \"spent_refund\":\n return \"clientrefunded\";\n case \"invalid\":\n return \"clientinvalidfunded\";\n case \"confirmed\":\n return \"clientfunded\"; // funded; waiting for the server to pay + sweep\n case \"mempool\":\n return \"clientfundingseen\";\n case \"absent\":\n return \"pending\";\n }\n}\n\n/**\n * Pay-on-Lightning (`lightning_to_arkade`, `lightning_to_evm`): the client pays a\n * Lightning invoice (off-chain, nothing for us to watch) and claims a single\n * on-chain HTLC. That claim completes the swap. The client has no on-chain\n * deposit at risk, so there is never a refund — a hold invoice that never settles\n * is auto-cancelled by the Lightning wallet.\n */\nfunction payOnLightningStatus(serverHtlc: HtlcObservation): SwapStatus {\n switch (serverHtlc) {\n case \"spent_claim\":\n return \"clientredeemed\"; // client claimed the on-chain leg ⟹ complete\n case \"confirmed\":\n return \"serverfunded\"; // ready to claim\n case \"spent_refund\":\n // The server reclaimed its leg — the swap failed; the client's Lightning\n // payment unwinds on its own. Terminal, nothing on-chain for the client.\n return \"clientrefunded\";\n case \"invalid\":\n case \"mempool\":\n case \"absent\":\n // Nothing (correct) to claim yet — keep waiting for the invoice to land as a\n // funded HTLC. `invalid` (wrong amount/terms) also stays a wait: the client\n // must not claim a bad leg, and the Lightning payment will unwind.\n return \"pending\";\n }\n}\n","/**\n * @satora/swap — the Satora swap client.\n *\n * A standalone `Client` with the same public surface as the legacy\n * `@lendasat/lendaswap-sdk-pure` client, so it is a drop-in replacement. For\n * now every legacy method is forwarded to an internally-owned legacy client\n * instance (see the `@deprecated` delegators below). New Satora-native features\n * are added directly on this class, and\n * individual delegators get replaced with native implementations over time.\n */\nimport {\n DEFAULT_ELECTRUM_WS_URLS,\n Client as LegacyClient,\n type ClientBuilder as LegacyClientBuilder,\n type SwapStatus,\n} from \"@lendasat/lendaswap-sdk-pure\";\nimport type { SwapActions } from \"./actions/types.js\";\nimport { ArkadeContractManager } from \"./contracts/arkade-manager.js\";\nimport { defaultArkadeServerUrl } from \"./contracts/arkade-network.js\";\nimport { BitcoinContractManager } from \"./contracts/bitcoin-manager.js\";\nimport { DEFAULT_ESPLORA_URLS } from \"./contracts/bitcoin-reader-esplora.js\";\nimport { EvmContractManager } from \"./contracts/evm-manager.js\";\nimport { defaultEvmReaders } from \"./contracts/evm-reader-viem.js\";\nimport type { ContractManager, Ledger } from \"./contracts/types.js\";\nimport { HintTracker } from \"./hints/hint-tracker.js\";\nimport { SwapWorker } from \"./hints/swap-worker.js\";\nimport { WsStatusSource } from \"./hints/ws-status-source.js\";\nimport { swapToTracked } from \"./tracker/from-swap.js\";\nimport {\n type ActionSubscriber,\n SwapTracker,\n type TrackedSwap,\n} from \"./tracker/swap-tracker.js\";\n\n/**\n * Hint-driven auto-execution, opt-in via {@link ClientBuilder.withAutoClaim}.\n *\n * When set, tracking also opens the server status WebSocket (a faster trigger\n * than the chain poll) and runs a {@link SwapWorker}: it auto-claims a swap the\n * moment the chain confirms it is claimable, and surfaces the rest — a manual\n * `fund`, or a refund the user must confirm — via {@link onActionRequired}.\n */\ntype AutoClaimConfig = {\n /**\n * Surface an action that needs the user rather than being auto-run — a manual\n * `fund`, or a refund to confirm. The seam a frontend notification center\n * plugs into. Omit to only auto-claim and ignore the rest.\n */\n onActionRequired?: (swapId: string, actions: SwapActions) => void;\n};\n\n/** How the client should set up observe-mode tracking. */\ntype TrackingConfig = {\n /** Whether tracking is enabled at all (default on; `withoutTracking()` clears). */\n enabled: boolean;\n /**\n * The swap server URL (from `withBaseUrl`). When set, tracking opens the\n * server's status WebSocket as a HINT feed: a pushed transition triggers a\n * targeted chain verify, so the chain pollers can stay slow. Chain reads\n * remain the source of truth.\n */\n serverUrl?: string;\n /** Explicit per-ledger managers, bypassing auto-construction (advanced/testing). */\n managers?: Map<Ledger, ContractManager>;\n /** Ark server URL override; defaults to the mainnet server when unset. */\n arkadeServerUrl?: string;\n /** Per-chain EVM RPC overrides; the EVM manager is auto-built either way. */\n evmRpcUrls?: Record<number, string>;\n /** Esplora REST URL override for the Bitcoin manager; defaults to mainnet mempool.space. */\n esploraUrl?: string;\n /**\n * Electrum WebSocket URL (Fulcrum). When set, the Bitcoin manager reads via\n * Electrum with scripthash-subscription push reconciles; esplora stays the\n * fallback on Electrum errors.\n */\n electrumWsUrl?: string;\n /**\n * Network the Bitcoin HTLC addresses live on — needed by the Electrum\n * reader to compute scripthashes. Defaults to mainnet; a signet/regtest\n * deployment must set it or Electrum address decoding fails.\n */\n bitcoinNetwork?: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\";\n /**\n * Confirmations a Bitcoin funding tx needs before it observes as\n * `confirmed` (gating e.g. the evm→bitcoin claim). Default `0`: accept\n * 0-conf, trusting the funder not to double-spend. `1` = wait for a block.\n */\n bitcoinMinConfirmations?: number;\n /**\n * Local tick interval (ms) — recomputes actions off extrapolated clocks, no\n * chain reads. Chain reads happen on server hints and, for swaps whose\n * client leg holds funds, on the tracker's at-risk cadence. Defaults to 5s.\n */\n refreshIntervalMs?: number;\n /** Opt-in hint-driven auto-claim; unset leaves tracking observe-only. */\n autoClaim?: AutoClaimConfig;\n /**\n * Verify against the chain instead of trusting server hints. Off by default\n * (TEMP while the chain monitors are buggy): tracking then runs a\n * {@link HintTracker} that derives actions from the server's pushed status,\n * with zero chain access. Setting explicit `managers` implies chain-verified\n * tracking (they exist for nothing else).\n */\n chainVerified?: boolean;\n};\n\nexport class Client {\n /** The wrapped legacy client. Calls are forwarded here until migrated. */\n readonly #legacy: LegacyClient;\n /** Tracking configuration; managers are built lazily from it on first use. */\n readonly #tracking: TrackingConfig;\n /** Per-ledger monitors, resolved once from {@link #tracking}. */\n #managers: Map<Ledger, ContractManager> | undefined;\n #tracker: SwapTracker | HintTracker | undefined;\n /** Hint-driven auto-claim worker; only built when `withAutoClaim` opted in. */\n #worker: SwapWorker | undefined;\n #startPromise?: Promise<void>;\n /** Swap ids whose settled status was already re-fetched this session. */\n readonly #settledSyncDone = new Set<string>();\n\n /**\n * Creates a new Client instance.\n *\n * Prefer {@link Client.builder} for new code.\n */\n constructor(...args: ConstructorParameters<typeof LegacyClient>);\n /** @internal Wrap an already-built legacy client, optionally with tracking. */\n constructor(legacy: LegacyClient, tracking?: TrackingConfig);\n constructor(\n ...args:\n | ConstructorParameters<typeof LegacyClient>\n | [legacy: LegacyClient, tracking?: TrackingConfig]\n ) {\n if (args[0] instanceof LegacyClient) {\n this.#legacy = args[0];\n this.#tracking = (args[1] as TrackingConfig | undefined) ?? {\n enabled: false,\n };\n } else {\n this.#legacy = new LegacyClient(\n ...(args as ConstructorParameters<typeof LegacyClient>),\n );\n // A raw (non-builder) client has no tracking config to build from.\n this.#tracking = { enabled: false };\n }\n }\n\n /** Start building a {@link Client}. */\n static builder(): ClientBuilder {\n return new ClientBuilder();\n }\n\n /** Escape hatch: the underlying legacy client, during migration. */\n get legacy(): LegacyClient {\n return this.#legacy;\n }\n\n // --- delegated legacy surface (getters) ---\n\n /** Delegated to the legacy client (migration checkpoint). */\n get api(): LegacyClient[\"api\"] {\n return this.#legacy.api;\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n get baseUrl(): LegacyClient[\"baseUrl\"] {\n return this.#legacy.baseUrl;\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n get cctpInbound(): LegacyClient[\"cctpInbound\"] {\n return this.#legacy.cctpInbound;\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n get swapStorage(): LegacyClient[\"swapStorage\"] {\n return this.#legacy.swapStorage;\n }\n\n // --- delegated legacy surface (methods) ---\n\n /** Delegated to the legacy client (migration checkpoint). */\n subscribeToSwaps(\n ...args: Parameters<LegacyClient[\"subscribeToSwaps\"]>\n ): ReturnType<LegacyClient[\"subscribeToSwaps\"]> {\n return this.#legacy.subscribeToSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n unsubscribeFromSwaps(\n ...args: Parameters<LegacyClient[\"unsubscribeFromSwaps\"]>\n ): ReturnType<LegacyClient[\"unsubscribeFromSwaps\"]> {\n return this.#legacy.unsubscribeFromSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n closeSwapStatusSocket(\n ...args: Parameters<LegacyClient[\"closeSwapStatusSocket\"]>\n ): ReturnType<LegacyClient[\"closeSwapStatusSocket\"]> {\n return this.#legacy.closeSwapStatusSocket(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getMnemonic(\n ...args: Parameters<LegacyClient[\"getMnemonic\"]>\n ): ReturnType<LegacyClient[\"getMnemonic\"]> {\n return this.#legacy.getMnemonic(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n loadMnemonic(\n ...args: Parameters<LegacyClient[\"loadMnemonic\"]>\n ): ReturnType<LegacyClient[\"loadMnemonic\"]> {\n return this.#legacy.loadMnemonic(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getNostrKeyHex(\n ...args: Parameters<LegacyClient[\"getNostrKeyHex\"]>\n ): ReturnType<LegacyClient[\"getNostrKeyHex\"]> {\n return this.#legacy.getNostrKeyHex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getUserIdXpub(\n ...args: Parameters<LegacyClient[\"getUserIdXpub\"]>\n ): ReturnType<LegacyClient[\"getUserIdXpub\"]> {\n return this.#legacy.getUserIdXpub(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n deriveSwapParams(\n ...args: Parameters<LegacyClient[\"deriveSwapParams\"]>\n ): ReturnType<LegacyClient[\"deriveSwapParams\"]> {\n return this.#legacy.deriveSwapParams(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n deriveSwapParamsAtIndex(\n ...args: Parameters<LegacyClient[\"deriveSwapParamsAtIndex\"]>\n ): ReturnType<LegacyClient[\"deriveSwapParamsAtIndex\"]> {\n return this.#legacy.deriveSwapParamsAtIndex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getEvmAddress(\n ...args: Parameters<LegacyClient[\"getEvmAddress\"]>\n ): ReturnType<LegacyClient[\"getEvmAddress\"]> {\n return this.#legacy.getEvmAddress(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getKeyIndex(\n ...args: Parameters<LegacyClient[\"getKeyIndex\"]>\n ): ReturnType<LegacyClient[\"getKeyIndex\"]> {\n return this.#legacy.getKeyIndex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n setKeyIndex(\n ...args: Parameters<LegacyClient[\"setKeyIndex\"]>\n ): ReturnType<LegacyClient[\"setKeyIndex\"]> {\n return this.#legacy.setKeyIndex(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getStatus(\n ...args: Parameters<LegacyClient[\"getStatus\"]>\n ): ReturnType<LegacyClient[\"getStatus\"]> {\n return this.#legacy.getStatus(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n healthCheck(\n ...args: Parameters<LegacyClient[\"healthCheck\"]>\n ): ReturnType<LegacyClient[\"healthCheck\"]> {\n return this.#legacy.healthCheck(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getVersion(\n ...args: Parameters<LegacyClient[\"getVersion\"]>\n ): ReturnType<LegacyClient[\"getVersion\"]> {\n return this.#legacy.getVersion(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSupportAgents(\n ...args: Parameters<LegacyClient[\"getSupportAgents\"]>\n ): ReturnType<LegacyClient[\"getSupportAgents\"]> {\n return this.#legacy.getSupportAgents(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getMtp(\n ...args: Parameters<LegacyClient[\"getMtp\"]>\n ): ReturnType<LegacyClient[\"getMtp\"]> {\n return this.#legacy.getMtp(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getTokens(\n ...args: Parameters<LegacyClient[\"getTokens\"]>\n ): ReturnType<LegacyClient[\"getTokens\"]> {\n return this.#legacy.getTokens(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSwapPairs(\n ...args: Parameters<LegacyClient[\"getSwapPairs\"]>\n ): ReturnType<LegacyClient[\"getSwapPairs\"]> {\n return this.#legacy.getSwapPairs(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getChainConfig(\n ...args: Parameters<LegacyClient[\"getChainConfig\"]>\n ): ReturnType<LegacyClient[\"getChainConfig\"]> {\n return this.#legacy.getChainConfig(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getReferralFee(\n ...args: Parameters<LegacyClient[\"getReferralFee\"]>\n ): ReturnType<LegacyClient[\"getReferralFee\"]> {\n return this.#legacy.getReferralFee(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getNetworkFees(\n ...args: Parameters<LegacyClient[\"getNetworkFees\"]>\n ): ReturnType<LegacyClient[\"getNetworkFees\"]> {\n return this.#legacy.getNetworkFees(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getDexQuote(\n ...args: Parameters<LegacyClient[\"getDexQuote\"]>\n ): ReturnType<LegacyClient[\"getDexQuote\"]> {\n return this.#legacy.getDexQuote(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n composeQuote(\n ...args: Parameters<LegacyClient[\"composeQuote\"]>\n ): ReturnType<LegacyClient[\"composeQuote\"]> {\n return this.#legacy.composeQuote(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getQuote(\n ...args: Parameters<LegacyClient[\"getQuote\"]>\n ): ReturnType<LegacyClient[\"getQuote\"]> {\n return this.#legacy.getQuote(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSwap(\n ...args: Parameters<LegacyClient[\"getSwap\"]>\n ): ReturnType<LegacyClient[\"getSwap\"]> {\n return this.#legacy.getSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getBulkStatus(\n ...args: Parameters<LegacyClient[\"getBulkStatus\"]>\n ): ReturnType<LegacyClient[\"getBulkStatus\"]> {\n return this.#legacy.getBulkStatus(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getStoredSwap(\n ...args: Parameters<LegacyClient[\"getStoredSwap\"]>\n ): ReturnType<LegacyClient[\"getStoredSwap\"]> {\n return this.#legacy.getStoredSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n listAllSwaps(\n ...args: Parameters<LegacyClient[\"listAllSwaps\"]>\n ): ReturnType<LegacyClient[\"listAllSwaps\"]> {\n return this.#legacy.listAllSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n deleteSwap(\n ...args: Parameters<LegacyClient[\"deleteSwap\"]>\n ): ReturnType<LegacyClient[\"deleteSwap\"]> {\n return this.#legacy.deleteSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n clearSwapStorage(\n ...args: Parameters<LegacyClient[\"clearSwapStorage\"]>\n ): ReturnType<LegacyClient[\"clearSwapStorage\"]> {\n return this.#legacy.clearSwapStorage(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n recoverSwaps(\n ...args: Parameters<LegacyClient[\"recoverSwaps\"]>\n ): ReturnType<LegacyClient[\"recoverSwaps\"]> {\n return this.#legacy.recoverSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n recoverAllSwaps(\n ...args: Parameters<LegacyClient[\"recoverAllSwaps\"]>\n ): ReturnType<LegacyClient[\"recoverAllSwaps\"]> {\n return this.#legacy.recoverAllSwaps(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n amountsForSwap(\n ...args: Parameters<LegacyClient[\"amountsForSwap\"]>\n ): ReturnType<LegacyClient[\"amountsForSwap\"]> {\n return this.#legacy.amountsForSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n claim(\n ...args: Parameters<LegacyClient[\"claim\"]>\n ): ReturnType<LegacyClient[\"claim\"]> {\n return this.#legacy.claim(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n claimViaGasless(\n ...args: Parameters<LegacyClient[\"claimViaGasless\"]>\n ): ReturnType<LegacyClient[\"claimViaGasless\"]> {\n return this.#legacy.claimViaGasless(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n claimArkade(\n ...args: Parameters<LegacyClient[\"claimArkade\"]>\n ): ReturnType<LegacyClient[\"claimArkade\"]> {\n return this.#legacy.claimArkade(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n continueArkadeClaimSwap(\n ...args: Parameters<LegacyClient[\"continueArkadeClaimSwap\"]>\n ): ReturnType<LegacyClient[\"continueArkadeClaimSwap\"]> {\n return this.#legacy.continueArkadeClaimSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n refundSwap(\n ...args: Parameters<LegacyClient[\"refundSwap\"]>\n ): ReturnType<LegacyClient[\"refundSwap\"]> {\n return this.#legacy.refundSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getCollabRefundEvmParams(\n ...args: Parameters<LegacyClient[\"getCollabRefundEvmParams\"]>\n ): ReturnType<LegacyClient[\"getCollabRefundEvmParams\"]> {\n return this.#legacy.getCollabRefundEvmParams(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n buildCollabRefundEvmTypedData(\n ...args: Parameters<LegacyClient[\"buildCollabRefundEvmTypedData\"]>\n ): ReturnType<LegacyClient[\"buildCollabRefundEvmTypedData\"]> {\n return this.#legacy.buildCollabRefundEvmTypedData(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n collabRefundEvmSwap(\n ...args: Parameters<LegacyClient[\"collabRefundEvmSwap\"]>\n ): ReturnType<LegacyClient[\"collabRefundEvmSwap\"]> {\n return this.#legacy.collabRefundEvmSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n submitCollabRefundEvm(\n ...args: Parameters<LegacyClient[\"submitCollabRefundEvm\"]>\n ): ReturnType<LegacyClient[\"submitCollabRefundEvm\"]> {\n return this.#legacy.submitCollabRefundEvm(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createSwap(\n ...args: Parameters<LegacyClient[\"createSwap\"]>\n ): ReturnType<LegacyClient[\"createSwap\"]> {\n return this.#trackAfterCreate(this.#legacy.createSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createArkadeToEvmSwapGeneric(\n ...args: Parameters<LegacyClient[\"createArkadeToEvmSwapGeneric\"]>\n ): ReturnType<LegacyClient[\"createArkadeToEvmSwapGeneric\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createArkadeToEvmSwapGeneric(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createBitcoinToEvmSwap(\n ...args: Parameters<LegacyClient[\"createBitcoinToEvmSwap\"]>\n ): ReturnType<LegacyClient[\"createBitcoinToEvmSwap\"]> {\n return this.#trackAfterCreate(this.#legacy.createBitcoinToEvmSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createBitcoinToArkadeSwap(\n ...args: Parameters<LegacyClient[\"createBitcoinToArkadeSwap\"]>\n ): ReturnType<LegacyClient[\"createBitcoinToArkadeSwap\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createBitcoinToArkadeSwap(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createLightningToArkadeSwap(\n ...args: Parameters<LegacyClient[\"createLightningToArkadeSwap\"]>\n ): ReturnType<LegacyClient[\"createLightningToArkadeSwap\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createLightningToArkadeSwap(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createArkadeToLightningSwap(\n ...args: Parameters<LegacyClient[\"createArkadeToLightningSwap\"]>\n ): ReturnType<LegacyClient[\"createArkadeToLightningSwap\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createArkadeToLightningSwap(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createEvmToArkadeSwapGeneric(\n ...args: Parameters<LegacyClient[\"createEvmToArkadeSwapGeneric\"]>\n ): ReturnType<LegacyClient[\"createEvmToArkadeSwapGeneric\"]> {\n return this.#trackAfterCreate(\n this.#legacy.createEvmToArkadeSwapGeneric(...args),\n );\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n createEvmToBitcoinSwap(\n ...args: Parameters<LegacyClient[\"createEvmToBitcoinSwap\"]>\n ): ReturnType<LegacyClient[\"createEvmToBitcoinSwap\"]> {\n return this.#trackAfterCreate(this.#legacy.createEvmToBitcoinSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getCoordinatorFundingCallDataPermit2(\n ...args: Parameters<LegacyClient[\"getCoordinatorFundingCallDataPermit2\"]>\n ): ReturnType<LegacyClient[\"getCoordinatorFundingCallDataPermit2\"]> {\n return this.#legacy.getCoordinatorFundingCallDataPermit2(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getPermit2FundingParamsUnsigned(\n ...args: Parameters<LegacyClient[\"getPermit2FundingParamsUnsigned\"]>\n ): ReturnType<LegacyClient[\"getPermit2FundingParamsUnsigned\"]> {\n return this.#legacy.getPermit2FundingParamsUnsigned(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n fundSwap(\n ...args: Parameters<LegacyClient[\"fundSwap\"]>\n ): ReturnType<LegacyClient[\"fundSwap\"]> {\n return this.#legacy.fundSwap(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n refundEvmWithSigner(\n ...args: Parameters<LegacyClient[\"refundEvmWithSigner\"]>\n ): ReturnType<LegacyClient[\"refundEvmWithSigner\"]> {\n return this.#legacy.refundEvmWithSigner(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n collabRefundEvmWithSigner(\n ...args: Parameters<LegacyClient[\"collabRefundEvmWithSigner\"]>\n ): ReturnType<LegacyClient[\"collabRefundEvmWithSigner\"]> {\n return this.#legacy.collabRefundEvmWithSigner(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n fundSwapGasless(\n ...args: Parameters<LegacyClient[\"fundSwapGasless\"]>\n ): ReturnType<LegacyClient[\"fundSwapGasless\"]> {\n return this.#legacy.fundSwapGasless(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getSwapDepositorKey(\n ...args: Parameters<LegacyClient[\"getSwapDepositorKey\"]>\n ): ReturnType<LegacyClient[\"getSwapDepositorKey\"]> {\n return this.#legacy.getSwapDepositorKey(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getEvmDepositorKey(\n ...args: Parameters<LegacyClient[\"getEvmDepositorKey\"]>\n ): ReturnType<LegacyClient[\"getEvmDepositorKey\"]> {\n return this.#legacy.getEvmDepositorKey(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n getRefundedEvmSwapContinuation(\n ...args: Parameters<LegacyClient[\"getRefundedEvmSwapContinuation\"]>\n ): ReturnType<LegacyClient[\"getRefundedEvmSwapContinuation\"]> {\n return this.#legacy.getRefundedEvmSwapContinuation(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n continueRefundedEvmSwap(\n ...args: Parameters<LegacyClient[\"continueRefundedEvmSwap\"]>\n ): ReturnType<LegacyClient[\"continueRefundedEvmSwap\"]> {\n return this.#trackAfterRetry(this.#legacy.continueRefundedEvmSwap(...args));\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n recoverGaslessFunds(\n ...args: Parameters<LegacyClient[\"recoverGaslessFunds\"]>\n ): ReturnType<LegacyClient[\"recoverGaslessFunds\"]> {\n return this.#legacy.recoverGaslessFunds(...args);\n }\n\n /** Delegated to the legacy client (migration checkpoint). */\n hasReceivedVtxo(\n ...args: Parameters<LegacyClient[\"hasReceivedVtxo\"]>\n ): ReturnType<LegacyClient[\"hasReceivedVtxo\"]> {\n return this.#legacy.hasReceivedVtxo(...args);\n }\n\n // --- Satora-native features go below ---\n\n /**\n * Start observing the user's active swaps and deriving each one's next action.\n *\n * Loads stored swaps, maps them to {@link TrackedSwap}s, and derives each\n * one's actions — by default from the server's status hints alone (TEMP while\n * the chain monitors are buggy; see\n * {@link ClientBuilder.withChainVerifiedTracking} to re-enable the per-ledger\n * chain monitors). Idempotent. Requires\n * the client to have been built with {@link ClientBuilder.withTracking}.\n * Subscribe with {@link subscribeToActions}; release with\n * {@link stopTracking}.\n */\n startTracking(): Promise<void> {\n if (!this.#tracking.enabled)\n return Promise.reject(\n new Error(\n \"tracking is disabled — remove .withoutTracking() to enable it\",\n ),\n );\n // Cache the in-flight promise, not a boolean: a concurrent caller must\n // await the first call's completion, or it would be told \"started\" while\n // `#tracker` is still unset and crash in `subscribeToActions`.\n this.#startPromise ??= this.#doStartTracking().catch((error) => {\n this.#startPromise = undefined; // let a later call retry cleanly\n throw error;\n });\n return this.#startPromise;\n }\n\n async #doStartTracking(): Promise<void> {\n try {\n // Explicit managers imply chain verification — they exist for nothing\n // else, and every pre-existing withContractManagers caller expects it.\n const chainVerified =\n this.#tracking.chainVerified || this.#tracking.managers !== undefined;\n const tracker = chainVerified\n ? new SwapTracker(await this.#ensureManagers(), {\n refreshIntervalMs: this.#tracking.refreshIntervalMs ?? 5_000,\n })\n : new HintTracker({\n fetchStatus: async (swapId) =>\n (await this.getSwap(swapId, { updateStorage: true })).status,\n refreshIntervalMs: this.#tracking.refreshIntervalMs ?? 5_000,\n });\n this.#tracker = tracker;\n await tracker.startTracking(await this.#loadTrackedSwaps());\n // Keep STORED statuses converging on chain truth: without this, a swap\n // that settled while the app was closed keeps a stale active status and\n // #loadTrackedSwaps re-tracks it on every future start. Dies with the\n // tracker (stop() drops its subscribers), so no unsub bookkeeping.\n tracker.subscribeToActions((swapId, actions) =>\n this.#syncSettledStatus(swapId, actions),\n );\n this.#startWorker(tracker);\n } catch (error) {\n // A partway failure (e.g. a ledger register/refresh erroring on an RPC or\n // indexer hiccup) left `#tracker`/`#worker` set and some legs registered.\n // Tear them down so `subscribeToActions` still reports \"not started\" and a\n // retry begins clean, instead of leaking registrations/listeners/sockets.\n this.#worker?.stop();\n this.#worker = undefined;\n this.#tracker?.stop();\n this.#tracker = undefined;\n throw error;\n }\n }\n\n /**\n * The chain says this swap is finished (`none`) — if the STORED status still\n * reads active, re-fetch and persist it so the next session's\n * {@link #loadTrackedSwaps} settled-filter catches it (instead of tracking\n * and scanning the swap once per session forever). Fire-and-forget with a\n * per-session dedupe: a failed fetch just means one more one-scan round next\n * session, which is exactly the current behavior. A swap whose stored status\n * is already settled costs nothing (no server call).\n */\n #syncSettledStatus(swapId: string, actions: SwapActions): void {\n if (actions.recommended !== \"none\") return;\n if (this.#settledSyncDone.has(swapId)) return;\n this.#settledSyncDone.add(swapId);\n void (async () => {\n const stored = (await this.listAllSwaps()).find(\n (s) => s.response.id === swapId,\n );\n if (!stored || SETTLED_STORED_STATUSES.has(stored.response.status))\n return;\n await this.getSwap(swapId, { updateStorage: true });\n })().catch((error) => {\n console.warn(\n `Client: refreshing settled status of swap ${swapId} failed:`,\n error,\n );\n });\n }\n\n /** Every stored swap whose ledgers are observable, mapped to a {@link TrackedSwap}. */\n async #loadTrackedSwaps(): Promise<TrackedSwap[]> {\n const swaps = await this.listAllSwaps();\n return swaps\n .filter((s) => !SETTLED_STORED_STATUSES.has(s.response.status))\n .map((s): TrackedSwap | undefined => {\n // Isolate per swap: one unmappable stored swap (e.g. a legacy record\n // with an unsupported script version) must not prevent every other\n // swap from being tracked and auto-claimed.\n try {\n const tracked = swapToTracked(s);\n if (!tracked) return undefined;\n // Seed hints-only tracking with the last stored status (a possibly\n // stale first derivation; the WS snapshot corrects it on subscribe).\n return { ...tracked, storedStatus: s.response.status };\n } catch (error) {\n console.warn(\n `Client: skipping untrackable stored swap ${s.response.id}:`,\n error,\n );\n return undefined;\n }\n })\n .filter((s): s is TrackedSwap => s !== undefined);\n }\n\n /**\n * Run a create, then fold the new swap into tracking.\n *\n * When tracking is active, first REJECT a swap this client can't reach: a leg on\n * an unconfigured chain can be neither observed nor claimed, so creating it would\n * strand funds. Throwing here (before the caller funds it) is deliberately\n * stricter than {@link startTracking}, which only *skips* such a pre-existing\n * swap. Otherwise fold the new swap(s) in — fire-and-forget, so a tracking hiccup\n * never fails or delays the create; a no-op when tracking isn't running (a later\n * {@link startTracking} picks it up from storage).\n */\n async #trackAfterCreate<T>(op: Promise<T>): Promise<T> {\n const result = await op;\n const tracker = this.#tracker;\n if (!tracker) return result;\n\n let swaps: TrackedSwap[];\n try {\n swaps = await this.#loadTrackedSwaps();\n } catch (error) {\n // A storage-read hiccup must not fail a create that already succeeded; the\n // next startTracking reconciles from storage anyway.\n console.warn(\"Client: loading swaps after create failed:\", error);\n return result;\n }\n\n const created = swaps.find((s) => s.swapId === createdSwapId(result));\n if (created && !tracker.canObserve(created))\n throw new Error(\n `Refusing swap ${created.swapId}: a leg is on a chain this client can't reach, ` +\n `so it can be neither tracked nor claimed. Configure the chain ` +\n `(e.g. ClientBuilder.withEvmRpcUrls) before creating it.`,\n );\n void this.#trackAll(tracker, swaps);\n return result;\n }\n\n /**\n * Run a retry, then fold its replacement swap into tracking.\n *\n * Unlike a create there is no unreachable-leg\n * rejection: by the time the retry returns, the old VHTLC has already been\n * collab-refunded into the new one, so refusing here can't protect any funds\n * — and {@link SwapTracker.track} skips a swap it can't observe anyway.\n */\n async #trackAfterRetry<T>(op: Promise<T>): Promise<T> {\n const result = await op;\n const tracker = this.#tracker;\n if (!tracker) return result;\n\n try {\n void this.#trackAll(tracker, await this.#loadTrackedSwaps());\n } catch (error) {\n // Same rationale as #trackAfterCreate: the retry already succeeded.\n console.warn(\"Client: loading swaps after retry failed:\", error);\n }\n return result;\n }\n\n /** Track each given swap the tracker isn't already watching (isolated per swap). */\n async #trackAll(\n tracker: SwapTracker | HintTracker,\n swaps: TrackedSwap[],\n ): Promise<void> {\n // Isolate per swap: one that fails to register (and rolls itself back) must\n // not stop the rest of the batch from being tracked.\n for (const swap of swaps) {\n try {\n await tracker.track(swap);\n } catch (error) {\n console.warn(`Client: tracking swap ${swap.swapId} failed:`, error);\n }\n }\n }\n\n /**\n * Wire the server status WebSocket into a {@link SwapWorker} whenever a server\n * URL is configured: pushed status transitions trigger a targeted chain\n * re-verify (`applyHint`), which is what lets the chain pollers stay slow.\n * Auto-EXECUTION on top of that is opt-in: only with `withAutoClaim` does the\n * worker get an `execute` (claims) and an `onActionRequired` surface —\n * otherwise it runs observe-only, hints in, no spends.\n */\n #startWorker(tracker: SwapTracker | HintTracker): void {\n // Fall back to the URL the legacy REST client resolved to (its mainnet\n // default when withBaseUrl was never called): tracking must open the hint\n // feed against the same server the REST calls go to — without this, the\n // default-URL builder flow gets a HintTracker but no hints, freezing\n // tracking at each swap's stored status. A legacy-like stand-in whose\n // `baseUrl` getter throws (no internal config) simply yields no fallback,\n // keeping the old no-worker behavior.\n const serverUrl = this.#tracking.serverUrl ?? legacyBaseUrl(this.#legacy);\n if (!serverUrl) return;\n const autoClaim = this.#tracking.autoClaim;\n\n const hintSource = new WsStatusSource({ serverUrl });\n const worker = new SwapWorker({\n tracker,\n hintSource,\n execute: autoClaim\n ? async (swapId, actionId) => {\n // The worker only ever auto-runs claims (its AUTO_EXECUTABLE set).\n if (actionId !== \"claim\")\n throw new Error(`refusing to auto-run action '${actionId}'`);\n const result = await this.claim(swapId);\n if (!result.success)\n throw new Error(result.message ?? \"claim failed\");\n }\n : undefined,\n onActionRequired: autoClaim?.onActionRequired,\n });\n this.#worker = worker;\n worker.start();\n }\n\n /**\n * Subscribe to next-action updates: `cb` fires with the current action for each\n * tracked swap immediately, then on every change. Call after\n * {@link startTracking}. Returns an unsubscribe fn.\n */\n subscribeToActions(cb: ActionSubscriber): () => void {\n if (!this.#tracker)\n throw new Error(\"call startTracking() before subscribeToActions()\");\n return this.#tracker.subscribeToActions(cb);\n }\n\n /** Stop tracking and drop subscribers. The managers themselves are not disposed. */\n stopTracking(): void {\n this.#worker?.stop();\n this.#worker = undefined;\n this.#tracker?.stop();\n this.#tracker = undefined;\n this.#startPromise = undefined;\n }\n\n /**\n * Resolve the per-ledger managers once — the explicit override if given, else\n * auto-built from config: an {@link ArkadeContractManager} from the configured\n * Ark server URL (defaulting to the mainnet server), clocked by `getMtp`, and an\n * {@link EvmContractManager} from the tested default RPCs.\n */\n async #ensureManagers(): Promise<Map<Ledger, ContractManager>> {\n if (this.#managers) return this.#managers;\n if (this.#tracking.managers) {\n this.#managers = this.#tracking.managers;\n return this.#managers;\n }\n const managers = new Map<Ledger, ContractManager>();\n const {\n arkadeServerUrl,\n evmRpcUrls,\n esploraUrl,\n electrumWsUrl,\n bitcoinNetwork,\n bitcoinMinConfirmations,\n } = this.#tracking;\n // Arkade + Bitcoin share the Bitcoin MTP clock.\n const chainTime = async () => (await this.getMtp()).mtp * 1000;\n\n // Default to mainnet; dev/other networks override via withArkadeServerUrl.\n const arkadeUrl = arkadeServerUrl ?? defaultArkadeServerUrl(\"bitcoin\");\n if (arkadeUrl) {\n managers.set(\n \"arkade\",\n await ArkadeContractManager.create({ serverUrl: arkadeUrl, chainTime }),\n );\n }\n // EVM tracks out of the box via tested default RPCs; overrides take priority.\n const readers = defaultEvmReaders(evmRpcUrls);\n if (readers.size > 0)\n managers.set(\"evm\", EvmContractManager.fromDeps({ readers }));\n // Bitcoin observes on-chain HTLCs via esplora. Default to the public pair\n // (mempool.space + blockstream.info) with rotation/failover; an explicit URL\n // replaces them (a dev/regtest node must not fail over to mainnet).\n // Electrum defaults per network (Satora's Fulcrum on mainnet, nothing\n // elsewhere); an explicit URL always wins. Esplora stays the fallback,\n // so the default endpoint being down degrades to plain Esplora reads.\n const network = bitcoinNetwork ?? \"mainnet\";\n managers.set(\n \"bitcoin\",\n await BitcoinContractManager.create({\n esploraUrl: esploraUrl ? [esploraUrl] : DEFAULT_ESPLORA_URLS,\n electrumWsUrl: electrumWsUrl ?? DEFAULT_ELECTRUM_WS_URLS[network],\n network,\n chainTime,\n minConfirmations: bitcoinMinConfirmations,\n }),\n );\n this.#managers = managers;\n return managers;\n }\n}\n\n/**\n * Builds a {@link Client}. Mirrors the legacy `ClientBuilder` fluent surface and\n * constructs the legacy client internally, then wraps it.\n */\nexport class ClientBuilder {\n readonly #inner: LegacyClientBuilder = LegacyClient.builder();\n // Observe-mode tracking is on by default; the built client auto-builds its\n // managers from config unless disabled or given an explicit override.\n #trackingEnabled = true;\n #serverUrl: string | undefined;\n #arkadeServerUrl: string | undefined;\n #esploraUrl: string | undefined;\n #electrumWsUrl: string | undefined;\n #bitcoinNetwork: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\" | undefined;\n #bitcoinMinConfirmations: number | undefined;\n #evmRpcUrls: Record<number, string> | undefined;\n #managers: Map<Ledger, ContractManager> | undefined;\n #autoClaim: AutoClaimConfig | undefined;\n #chainVerified = false;\n\n /**\n * Override the EVM JSON-RPC endpoint per chainId. Optional — tracking uses\n * tested public defaults otherwise; an override is tried first, with the\n * defaults kept as fallbacks.\n */\n withEvmRpcUrls(urls: Record<number, string>): this {\n this.#evmRpcUrls = urls;\n return this;\n }\n\n /**\n * Opt in to hint-driven auto-claim. Tracking then also subscribes to the\n * server status WebSocket (a faster trigger than the chain poll) and, when the\n * chain confirms a swap is claimable, claims it automatically. Actions that\n * need the user — a manual `fund`, or a refund to confirm — are surfaced via\n * `onActionRequired` instead of being run. Off by default: this auto-spends on\n * the user's behalf, so it must be explicit.\n */\n withAutoClaim(options?: {\n onActionRequired?: (swapId: string, actions: SwapActions) => void;\n }): this {\n this.#autoClaim = { onActionRequired: options?.onActionRequired };\n return this;\n }\n\n /** Turn observe-mode tracking off. */\n withoutTracking(): this {\n this.#trackingEnabled = false;\n return this;\n }\n\n /**\n * Verify swap state against the chain (per-ledger contract managers) instead\n * of trusting the server's status hints. TEMPORARILY off by default while the\n * chain monitors are being fixed — without it, tracking derives actions from\n * the server's pushed status alone, with zero chain access.\n */\n withChainVerifiedTracking(): this {\n this.#chainVerified = true;\n return this;\n }\n\n /**\n * Advanced: supply per-ledger {@link ContractManager}s explicitly instead of\n * letting the client auto-build them from config (useful for tests or custom\n * chain sources).\n */\n withContractManagers(managers: Map<Ledger, ContractManager>): this {\n this.#managers = managers;\n return this;\n }\n\n withBaseUrl(...args: Parameters<LegacyClientBuilder[\"withBaseUrl\"]>): this {\n this.#inner.withBaseUrl(...args);\n this.#serverUrl = args[0];\n return this;\n }\n\n withReferralCode(\n ...args: Parameters<LegacyClientBuilder[\"withReferralCode\"]>\n ): this {\n this.#inner.withReferralCode(...args);\n return this;\n }\n\n withOrgCode(...args: Parameters<LegacyClientBuilder[\"withOrgCode\"]>): this {\n this.#inner.withOrgCode(...args);\n return this;\n }\n\n withDefaultHeaders(\n ...args: Parameters<LegacyClientBuilder[\"withDefaultHeaders\"]>\n ): this {\n this.#inner.withDefaultHeaders(...args);\n return this;\n }\n\n /**\n * Override the Esplora REST URL. Optional for tracking — the Bitcoin manager\n * otherwise defaults to mainnet mempool.space; set this for a dev/regtest node.\n */\n withEsploraUrl(url: string): this {\n this.#inner.withEsploraUrl(url);\n this.#esploraUrl = url;\n return this;\n }\n\n /**\n * Set an Electrum server WebSocket URL (a Fulcrum `ws`/`wss` port) for\n * Bitcoin claim/refund chain access. When set, the underlying client\n * prefers it over Esplora for lookups and broadcasts, and waits for HTLC\n * fundings via `blockchain.scripthash.subscribe` push instead of polling.\n * Esplora remains the fallback whenever the Electrum server errors.\n */\n withElectrumWsUrl(url: string): this {\n this.#inner.withElectrumWsUrl(url);\n this.#electrumWsUrl = url;\n return this;\n }\n\n /**\n * Declare the Bitcoin network of this deployment's HTLC addresses. Only\n * needed together with {@link withElectrumWsUrl} on a non-mainnet\n * deployment — the Electrum reader decodes addresses to scripthashes and\n * defaults to mainnet parameters.\n */\n withBitcoinNetwork(\n network: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\",\n ): this {\n this.#bitcoinNetwork = network;\n return this;\n }\n\n /**\n * Confirmations a Bitcoin funding tx needs before it observes as `confirmed`\n * (gating e.g. the evm→bitcoin claim). Default `0`: claim as soon as the\n * funding hits the mempool, which TRUSTS the funder not to double-spend it\n * (claiming publishes the preimage, so a funder that then replaced its\n * funding could take both legs). Set `1` (or more) for a block-depth policy\n * that doesn't rely on the funder's good behaviour.\n */\n withBitcoinMinConfirmations(minConfirmations: number): this {\n this.#bitcoinMinConfirmations = minConfirmations;\n return this;\n }\n\n /**\n * Override the Ark server URL. Optional for tracking — it otherwise defaults to\n * the mainnet server; set this to track a non-mainnet (dev/signet) deployment.\n */\n withArkadeServerUrl(url: string): this {\n this.#inner.withArkadeServerUrl(url);\n this.#arkadeServerUrl = url;\n return this;\n }\n\n withSignerStorage(\n ...args: Parameters<LegacyClientBuilder[\"withSignerStorage\"]>\n ): this {\n this.#inner.withSignerStorage(...args);\n return this;\n }\n\n withSwapStorage(\n ...args: Parameters<LegacyClientBuilder[\"withSwapStorage\"]>\n ): this {\n this.#inner.withSwapStorage(...args);\n return this;\n }\n\n withMnemonic(...args: Parameters<LegacyClientBuilder[\"withMnemonic\"]>): this {\n this.#inner.withMnemonic(...args);\n return this;\n }\n\n withXprv(...args: Parameters<LegacyClientBuilder[\"withXprv\"]>): this {\n this.#inner.withXprv(...args);\n return this;\n }\n\n withAa(...args: Parameters<LegacyClientBuilder[\"withAa\"]>): this {\n this.#inner.withAa(...args);\n return this;\n }\n\n withLogger(...args: Parameters<LegacyClientBuilder[\"withLogger\"]>): this {\n this.#inner.withLogger(...args);\n return this;\n }\n\n withLogLevel(...args: Parameters<LegacyClientBuilder[\"withLogLevel\"]>): this {\n this.#inner.withLogLevel(...args);\n return this;\n }\n\n async build(): Promise<Client> {\n return new Client(await this.#inner.build(), {\n enabled: this.#trackingEnabled,\n serverUrl: this.#serverUrl,\n managers: this.#managers,\n arkadeServerUrl: this.#arkadeServerUrl,\n esploraUrl: this.#esploraUrl,\n electrumWsUrl: this.#electrumWsUrl,\n bitcoinNetwork: this.#bitcoinNetwork,\n bitcoinMinConfirmations: this.#bitcoinMinConfirmations,\n evmRpcUrls: this.#evmRpcUrls,\n autoClaim: this.#autoClaim,\n chainVerified: this.#chainVerified,\n });\n }\n}\n\n/**\n * Stored statuses where the client's money is fully settled — already received,\n * already refunded, or (for `expired`) never deposited: the server only marks a\n * swap expired while it is unfunded (a late-funded one becomes\n * `clientfundedtoolate`, which is NOT in this set). Safe to skip registering\n * entirely, so tracking doesn't re-scan every historical swap on each start;\n * anything ambiguous stays chain-verified.\n */\n/** The legacy client's resolved base URL, or `undefined` if it can't provide one. */\nfunction legacyBaseUrl(legacy: LegacyClient): string | undefined {\n try {\n return legacy.baseUrl;\n } catch {\n return undefined;\n }\n}\n\nconst SETTLED_STORED_STATUSES = new Set<SwapStatus>([\n \"serverredeemed\",\n \"clientrefunded\",\n \"clientrefundedserverfunded\",\n \"clientrefundedserverrefunded\",\n \"clientredeemedandclientrefunded\",\n \"expired\",\n]);\n\n/**\n * The swap id from a create result. Every `create*` result carries the created\n * swap under `response.id`; used to find the just-created swap in storage.\n */\nfunction createdSwapId(result: unknown): string | undefined {\n const id = (result as { response?: { id?: unknown } } | null)?.response?.id;\n return typeof id === \"string\" ? id : undefined;\n}\n","/**\n * The Arkade {@link ContractManager} — the stateful I/O adapter that observes a\n * swap's VHTLC via the Ark indexer.\n *\n * We deliberately do NOT use `@arkade-os/sdk`'s `ContractManager`: its vtxo\n * annotation calls `script.forfeit()` on every contract, but a VHTLC script has\n * no `forfeit()` leaf (it exposes `claim()`/`refund()`), so watching a VHTLC\n * through it throws. Instead we read vtxos directly by pkScript — exactly what\n * the legacy SDK's claim/refund paths do — and map them with the pure helpers in\n * `./arkade.js`.\n *\n * Observation is on-demand: {@link refresh} (tracker start / newly tracked\n * swap) and targeted {@link reconcile}s (server hints, the tracker's at-risk\n * safety net). There is no standing subscription or periodic poll.\n */\nimport { RestIndexerProvider, type VirtualCoin } from \"@arkade-os/sdk\";\nimport { hex } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport type { VirtualTxSource } from \"./arkade.js\";\nimport { arkadeObservation, fetchArkadeSpend } from \"./arkade.js\";\nimport type { ContractManager, HtlcRef, Ledger } from \"./types.js\";\n\n/**\n * The Ark indexer surface the observer needs (RestIndexerProvider satisfies it).\n */\nexport type ArkadeIndexer = VirtualTxSource & {\n getVtxos(opts: {\n scripts: string[];\n spendableOnly?: boolean;\n }): Promise<{ vtxos: VirtualCoin[] }>;\n};\n\nexport type ArkadeCreateConfig = {\n /** Ark server base URL — used for the indexer. */\n serverUrl: string;\n /**\n * The current Bitcoin MTP (ms) for evaluating VHTLC timelocks. Typically\n * `async () => (await client.getMtp()).mtp * 1000`.\n */\n chainTime?: () => Promise<number>;\n};\n\nexport type ArkadeContractManagerDeps = {\n indexer: ArkadeIndexer;\n /**\n * The current Bitcoin MTP (ms) — arkade CLTV timelocks are evaluated against\n * Bitcoin's clock, not the Ark server's. Optional: until wired, `chainNow()`\n * reports `undefined` and the tracker holds swaps as provisional.\n */\n chainTime?: () => Promise<number>;\n};\n\n/** True when a vtxo is a live funding of the contract (not yet spent). */\nfunction isFunded(vtxo: VirtualCoin): boolean {\n const { state } = vtxo.virtualStatus;\n return state === \"preconfirmed\" || state === \"settled\";\n}\n\n/**\n * The offchain txid that spent this contract's VHTLC, if any. Mirrors the backend\n * watcher (`unified_watcher.rs`), which keys spend detection on `arkTxId` — the\n * offchain Arkade tx whose input[0] condition witness carries the revealed\n * preimage. Falls back to `spentBy` (the checkpoint tx, which also reveals it) so\n * a spend is caught under either field: reading only `spentBy` silently missed\n * spent vtxos that expose the spend solely via `arkTxId`, leaving them classified\n * as `confirmed` forever (never emitting spent_claim/spent_refund).\n */\nfunction spendTxid(vtxos: VirtualCoin[]): string | undefined {\n const spent = vtxos.find(\n (v) =>\n v.arkTxId || v.spentBy || v.isSpent || v.virtualStatus.state === \"spent\",\n );\n return spent?.arkTxId ?? spent?.spentBy;\n}\n\nexport class ArkadeContractManager implements ContractManager {\n readonly ledger: Ledger = \"arkade\";\n\n readonly #indexer: ArkadeIndexer;\n readonly #chainTime?: () => Promise<number>;\n\n /** pkScript → the ref we're tracking. */\n readonly #refs = new Map<string, Extract<HtlcRef, { ledger: \"arkade\" }>>();\n /** pkScript → last known observation. */\n readonly #obs = new Map<string, HtlcObservation>();\n /** pkScript → the verified preimage recovered from a claim spend. */\n readonly #preimages = new Map<string, Uint8Array>();\n readonly #listeners = new Set<\n (ref: HtlcRef, state: HtlcObservation) => void\n >();\n\n /** Last MTP reading (ms) + when it was fetched, for extrapolation. */\n #now: { mtpMs: number; fetchedAtMs: number } | undefined;\n\n private constructor(deps: ArkadeContractManagerDeps) {\n this.#indexer = deps.indexer;\n this.#chainTime = deps.chainTime;\n }\n\n static fromDeps(deps: ArkadeContractManagerDeps): ArkadeContractManager {\n return new ArkadeContractManager(deps);\n }\n\n /** Construct a manager backed by a `RestIndexerProvider` over the Ark server. */\n static async create(\n config: ArkadeCreateConfig,\n ): Promise<ArkadeContractManager> {\n const indexer = new RestIndexerProvider(config.serverUrl);\n return ArkadeContractManager.fromDeps({\n indexer,\n chainTime: config.chainTime,\n });\n }\n\n canObserve(ref: HtlcRef): boolean {\n // The Arkade server is always configured (default or override), so any Arkade\n // ref is observable.\n return ref.ledger === \"arkade\";\n }\n\n async register(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"arkade\")\n throw new Error(\n `ArkadeContractManager can't track a '${ref.ledger}' HTLC`,\n );\n this.#refs.set(ref.script, ref);\n }\n\n async unregister(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"arkade\") return;\n this.#refs.delete(ref.script);\n this.#obs.delete(ref.script);\n this.#preimages.delete(ref.script);\n }\n\n getState(ref: HtlcRef): HtlcObservation | undefined {\n return ref.ledger === \"arkade\" ? this.#obs.get(ref.script) : undefined;\n }\n\n chainNow(_ref: HtlcRef): number | undefined {\n // Arkade CLTV timelocks share one clock (Bitcoin MTP), so the ref is unused.\n if (!this.#now) return undefined;\n // Extrapolate between reads: MTP advances with wall-clock (lagging it by a\n // roughly constant margin), so the clock stays current without polling.\n // The worker re-verifies on-chain before acting on any flip this causes.\n return this.#now.mtpMs + Math.max(0, Date.now() - this.#now.fetchedAtMs);\n }\n\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n /**\n * Full reconcile of every tracked ref (clock + vtxos). Called on demand\n * (tracker start / newly tracked swap), NOT periodically: onward, hints and\n * the tracker's at-risk safety net drive targeted `reconcile`s.\n */\n async refresh(): Promise<void> {\n await this.#scanAll();\n }\n\n /** Targeted verify (hint / pre-action path) — never gated. */\n async reconcile(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"arkade\") return;\n const tracked = this.#refs.get(ref.script);\n if (!tracked) return;\n await this.#readClock();\n await this.#reconcileRef(tracked);\n }\n\n dispose(): void {\n this.#listeners.clear();\n }\n\n /** The verified preimage recovered from a claim spend, if one was seen. */\n getPreimage(ref: HtlcRef): Uint8Array | undefined {\n return ref.ledger === \"arkade\"\n ? this.#preimages.get(ref.script)\n : undefined;\n }\n\n /** Read and store the MTP clock (with fetch time, for extrapolation). */\n async #readClock(): Promise<void> {\n if (!this.#chainTime) return;\n this.#now = { mtpMs: await this.#chainTime(), fetchedAtMs: Date.now() };\n }\n\n /** Full scan of every tracked ref (clock + vtxos). */\n async #scanAll(): Promise<void> {\n await this.#readClock();\n await Promise.all(\n [...this.#refs.values()].map((ref) => this.#reconcileRef(ref)),\n );\n }\n\n /** Read the VHTLC's vtxos and map them to an observation. */\n async #reconcileRef(\n ref: Extract<HtlcRef, { ledger: \"arkade\" }>,\n ): Promise<void> {\n const { vtxos } = await this.#indexer.getVtxos({ scripts: [ref.script] });\n const spend = spendTxid(vtxos);\n if (spend) {\n const resolved = await fetchArkadeSpend(\n this.#indexer,\n spend,\n hex.decode(ref.preimageHash),\n );\n if (resolved) {\n if (resolved.spend === \"claim\")\n this.#preimages.set(ref.script, resolved.preimage);\n this.#set(\n ref.script,\n arkadeObservation({ funded: true, spend: resolved.spend }),\n );\n return;\n }\n }\n const funded = vtxos.filter(isFunded);\n const total = funded.reduce((sum, vtxo) => sum + vtxo.value, 0);\n this.#set(\n ref.script,\n arkadeObservation({\n funded: funded.length > 0,\n sufficient: total >= ref.expectedSats,\n }),\n );\n }\n\n /** Update an observation and notify on change; never downgrade a resolved spend. */\n #set(script: string, observation: HtlcObservation): void {\n const ref = this.#refs.get(script);\n if (!ref) return;\n const current = this.#obs.get(script);\n if (current === observation) return;\n // A resolved spend is terminal for the HTLC — a later funded/absent poll\n // (e.g. spent vtxos dropping out of the listing) must not undo it.\n const spendStates: HtlcObservation[] = [\"spent_claim\", \"spent_refund\"];\n if (\n current &&\n spendStates.includes(current) &&\n !spendStates.includes(observation)\n )\n return;\n this.#obs.set(script, observation);\n for (const listener of this.#listeners) listener(ref, observation);\n }\n}\n","/**\n * The `ArkadeContractManager`'s pure core.\n *\n * The manager (built on `@arkade-os/sdk`'s ContractManager) gathers facts about a\n * VHTLC — is it funded, and if spent, how — and this maps them to an\n * {@link HtlcObservation} the swap pipeline consumes.\n */\nimport {\n ConditionWitness,\n getArkPsbtFields,\n Transaction,\n} from \"@arkade-os/sdk\";\nimport { base64 } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { preimageMatches } from \"./preimage.js\";\n\n/** Facts about a VHTLC gathered from the Ark indexer/manager. */\nexport type ArkadeVtxoFacts = {\n /** A vtxo exists at the VHTLC address (it was funded). */\n funded: boolean;\n /**\n * Whether the funding meets the swap's expected amount. Only consulted when\n * `funded`; `false` → the VHTLC is funded but short → `invalid`, not confirmed.\n */\n sufficient?: boolean;\n /**\n * If the VHTLC output was spent, which path the spending tx took — resolved\n * from the spend tx's `ConditionWitness` (preimage present → `claim`).\n */\n spend?: \"claim\" | \"refund\";\n};\n\n/**\n * Map gathered VHTLC facts to an on-chain observation.\n *\n * Note: there is no `mempool` here. An Arkade `preconfirmed` vtxo is server-\n * signed (a far stronger trust assumption than a droppable Bitcoin mempool tx),\n * so a funded VHTLC — preconfirmed or settled — is treated as `confirmed`.\n * `mempool` is reserved for Bitcoin on-chain observers.\n */\nexport function arkadeObservation(facts: ArkadeVtxoFacts): HtlcObservation {\n if (facts.spend === \"claim\") return \"spent_claim\";\n if (facts.spend === \"refund\") return \"spent_refund\";\n if (!facts.funded) return \"absent\";\n return facts.sufficient === false ? \"invalid\" : \"confirmed\";\n}\n\n/** How a VHTLC output was spent. A claim also yields the verified preimage. */\nexport type ArkadeSpend =\n | { spend: \"claim\"; preimage: Uint8Array }\n | { spend: \"refund\" };\n\n/**\n * Classify how a VHTLC output was spent from the spending tx's PSBT.\n *\n * The claim path finalizes the VHTLC's condition-multisig closure, so the spend\n * tx witnesses the preimage in the `ConditionWitness` proprietary field; the\n * unilateral refund path (CSV timelock) never carries it. But we don't trust the\n * field's mere presence — we verify a revealed element actually hashes to *this*\n * swap's `paymentHash` (the SHA-256 preimage hash, i.e. the Lightning\n * payment_hash). That turns \"looks like a claim\" into \"this is the claim of our\n * swap, and here is the verified preimage\" — which the caller needs to settle the\n * counterparty leg (the same reason the backend extracts it in\n * `unified_watcher.rs`). Anything else — no field, or a non-matching field — is a\n * refund.\n *\n * Operates on the already-fetched, base64-encoded PSBT so it stays pure and\n * unit-testable; {@link fetchArkadeSpend} does the indexer I/O.\n */\nexport function classifyArkadeSpend(\n encodedSpendTx: string,\n paymentHash: Uint8Array,\n): ArkadeSpend {\n const tx = Transaction.fromPSBT(base64.decode(encodedSpendTx));\n // Multi-input Arkade spends may reveal the VHTLC condition witness on any\n // input. The condition witness is a stack of elements, one of which (on a\n // claim) is the 32-byte preimage.\n for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {\n for (const witness of getArkPsbtFields(tx, inputIndex, ConditionWitness)) {\n for (const element of witness) {\n if (element.length === 32 && preimageMatches(element, paymentHash)) {\n return { spend: \"claim\", preimage: element };\n }\n }\n }\n }\n return { spend: \"refund\" };\n}\n\n/** The slice of `@arkade-os/sdk`'s indexer the spend classifier needs. */\nexport type VirtualTxSource = {\n getVirtualTxs(txids: string[]): Promise<{ txs: string[] }>;\n};\n\n/**\n * Fetch the tx that spent a VHTLC and classify its path. Returns `undefined`\n * when the indexer can't yet return the spending tx (e.g. not indexed), so the\n * caller keeps the output as `confirmed` rather than guessing.\n */\nexport async function fetchArkadeSpend(\n source: VirtualTxSource,\n spentByTxid: string,\n paymentHash: Uint8Array,\n): Promise<ArkadeSpend | undefined> {\n const { txs } = await source.getVirtualTxs([spentByTxid]);\n const encoded = txs[0];\n return encoded ? classifyArkadeSpend(encoded, paymentHash) : undefined;\n}\n","/**\n * Verify a revealed witness element is the preimage for a swap's hash lock.\n *\n * The hash length selects the algorithm, so callers don't need to know the swap\n * direction: a 20-byte lock is HASH160 = `ripemd160(sha256(preimage))`\n * (`btc_to_arkade`, matching Bitcoin's `OP_HASH160` and the Arkade VHTLC); any\n * other length is `sha256(preimage)` (32 bytes, the EVM/Lightning directions,\n * i.e. the Lightning payment_hash).\n */\nimport { ripemd160 } from \"@noble/hashes/legacy.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { hex } from \"@scure/base\";\n\nexport function preimageMatches(\n element: Uint8Array,\n hashLock: Uint8Array,\n): boolean {\n const digest =\n hashLock.length === 20 ? ripemd160(sha256(element)) : sha256(element);\n return hex.encode(digest) === hex.encode(hashLock);\n}\n","/**\n * Default Arkade server URLs by Bitcoin network, mirroring the legacy SDK's\n * `arkade-network.ts`. Used so tracking can auto-build the Arkade manager without\n * an explicit server URL; a caller can still override via\n * `ClientBuilder.withArkadeServerUrl`.\n */\nconst DEFAULT_ARKADE_URLS: Record<string, string> = {\n bitcoin: \"https://arkade.computer\",\n mainnet: \"https://arkade.computer\",\n signet: \"https://mutinynet.arkade.sh\",\n mutinynet: \"https://mutinynet.arkade.sh\",\n};\n\n/**\n * The default Ark server URL for a Bitcoin network name (case-insensitive), or\n * `undefined` for networks without a public default (e.g. regtest/testnet),\n * where a URL must be supplied explicitly.\n */\nexport function defaultArkadeServerUrl(network: string): string | undefined {\n return DEFAULT_ARKADE_URLS[network.toLowerCase()];\n}\n","/**\n * The Bitcoin {@link ContractManager} — the stateful I/O adapter that observes a\n * swap's on-chain HTLC by polling esplora.\n *\n * Modeled on the Arkade manager: reading is delegated to an injected\n * {@link BitcoinChainReader} (so the package stays free of a bundled esplora\n * client and the adapter is testable against a fake). Observation is on-demand —\n * {@link refresh} at seed time, targeted {@link reconcile}s from hints and the\n * tracker's at-risk safety net. The clock is Bitcoin MTP (the same source the\n * Arkade manager uses), so {@link chainNow} is ref-less.\n */\nimport { hex } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { type BitcoinHtlcFacts, bitcoinObservation } from \"./bitcoin.js\";\nimport type { ContractManager, HtlcRef, Ledger } from \"./types.js\";\n\n/** The chain surface the observer needs (the esplora and electrum readers satisfy it). */\nexport type BitcoinChainReader = {\n /**\n * Funding + spend facts for an HTLC witness-script address. `minConfirmations`\n * overrides the reader's default for this address alone — the two legs of a\n * swap do not share one rule.\n */\n getHtlcFacts(\n address: string,\n minConfirmations?: number,\n ): Promise<BitcoinHtlcFacts>;\n /**\n * Optional push capability: invoke `onChange` whenever the address's tx\n * history changes (e.g. an Electrum scripthash subscription). Returns an\n * unsubscribe function. When present, the manager reconciles on push\n * instead of relying solely on the tracker's poll cadence.\n */\n subscribe?(address: string, onChange: () => void): () => void;\n};\n\nexport type BitcoinCreateConfig = {\n /** One or more esplora REST base URLs; several are tried in rotation with failover. */\n esploraUrl: string | string[];\n /**\n * Optional Electrum WebSocket URL (Fulcrum `ws`/`wss` port). When set, the\n * manager reads via Electrum (fresher than public explorers) and gets push\n * reconciles via scripthash subscriptions; the esplora reader remains the\n * fallback on Electrum errors.\n */\n electrumWsUrl?: string;\n /** Network the HTLC addresses live on (for Electrum scripthashes); default mainnet. */\n network?: \"mainnet\" | \"testnet\" | \"signet\" | \"regtest\";\n /** The current Bitcoin MTP (ms); typically `async () => (await client.getMtp()).mtp * 1000`. */\n chainTime?: () => Promise<number>;\n /**\n * Confirmations a funding tx needs before it observes as `confirmed`.\n * Default `0`: accept an unconfirmed funding, trusting the funder not to\n * double-spend it (the claim reveals the preimage against it). `1` = wait\n * for a block.\n */\n minConfirmations?: number;\n};\n\nexport type BitcoinContractManagerDeps = {\n reader: BitcoinChainReader;\n chainTime?: () => Promise<number>;\n};\n\nexport class BitcoinContractManager implements ContractManager {\n readonly ledger: Ledger = \"bitcoin\";\n\n readonly #reader: BitcoinChainReader;\n readonly #chainTime?: () => Promise<number>;\n\n /** address → the ref we're tracking. */\n readonly #refs = new Map<string, Extract<HtlcRef, { ledger: \"bitcoin\" }>>();\n /** address → last known observation. */\n readonly #obs = new Map<string, HtlcObservation>();\n /** address → the preimage a claim revealed. */\n readonly #preimages = new Map<string, Uint8Array>();\n /** address → push-subscription teardown (only when the reader supports push). */\n readonly #subscriptions = new Map<string, () => void>();\n readonly #listeners = new Set<\n (ref: HtlcRef, state: HtlcObservation) => void\n >();\n\n /** Last MTP reading (ms) + when it was fetched, for extrapolation. */\n #now: { mtpMs: number; fetchedAtMs: number } | undefined;\n\n private constructor(deps: BitcoinContractManagerDeps) {\n this.#reader = deps.reader;\n this.#chainTime = deps.chainTime;\n }\n\n static fromDeps(deps: BitcoinContractManagerDeps): BitcoinContractManager {\n return new BitcoinContractManager(deps);\n }\n\n static async create(\n config: BitcoinCreateConfig,\n ): Promise<BitcoinContractManager> {\n const { esploraReader } = await import(\"./bitcoin-reader-esplora.js\");\n let reader: BitcoinChainReader = esploraReader(\n config.esploraUrl,\n undefined,\n { minConfirmations: config.minConfirmations },\n );\n if (config.electrumWsUrl) {\n const [{ electrumReader }, { ElectrumWsClient }] = await Promise.all([\n import(\"./bitcoin-reader-electrum.js\"),\n import(\"@lendasat/lendaswap-sdk-pure\"),\n ]);\n reader = electrumReader(new ElectrumWsClient(config.electrumWsUrl), {\n network: config.network,\n minConfirmations: config.minConfirmations,\n fallback: reader,\n });\n }\n return BitcoinContractManager.fromDeps({\n reader,\n chainTime: config.chainTime,\n });\n }\n\n canObserve(ref: HtlcRef): boolean {\n // Esplora is always configured (default or override), so any Bitcoin ref is\n // observable.\n return ref.ledger === \"bitcoin\";\n }\n\n async register(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"bitcoin\")\n throw new Error(\n `BitcoinContractManager can't track a '${ref.ledger}' HTLC`,\n );\n this.#refs.set(ref.address, ref);\n // Push: reconcile the instant the address's history changes (a reader\n // without the capability leaves the tracker's poll cadence in charge).\n if (this.#reader.subscribe && !this.#subscriptions.has(ref.address)) {\n this.#subscriptions.set(\n ref.address,\n this.#reader.subscribe(ref.address, () => {\n this.#reconcileRef(ref).catch((error) => {\n console.warn(\n \"BitcoinContractManager: push reconcile failed:\",\n error instanceof Error ? error.message : error,\n );\n });\n }),\n );\n }\n await this.#reconcileRef(ref);\n }\n\n async unregister(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"bitcoin\") return;\n this.#subscriptions.get(ref.address)?.();\n this.#subscriptions.delete(ref.address);\n this.#refs.delete(ref.address);\n this.#obs.delete(ref.address);\n this.#preimages.delete(ref.address);\n }\n\n getState(ref: HtlcRef): HtlcObservation | undefined {\n return ref.ledger === \"bitcoin\" ? this.#obs.get(ref.address) : undefined;\n }\n\n chainNow(_ref: HtlcRef): number | undefined {\n // Bitcoin timelocks share one clock (MTP), so the ref is unused.\n if (!this.#now) return undefined;\n // Extrapolate between reads: MTP advances with wall-clock (lagging it by a\n // roughly constant margin), so the clock stays current without polling.\n // The worker re-verifies on-chain before acting on any flip this causes.\n return this.#now.mtpMs + Math.max(0, Date.now() - this.#now.fetchedAtMs);\n }\n\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n async refresh(): Promise<void> {\n await this.#readClock();\n await Promise.all(\n [...this.#refs.values()].map((ref) => this.#reconcileRef(ref)),\n );\n }\n\n async reconcile(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"bitcoin\") return;\n const tracked = this.#refs.get(ref.address);\n if (!tracked) return;\n await this.#readClock();\n await this.#reconcileRef(tracked);\n }\n\n /** Read and store the MTP clock (with fetch time, for extrapolation). */\n async #readClock(): Promise<void> {\n if (!this.#chainTime) return;\n this.#now = { mtpMs: await this.#chainTime(), fetchedAtMs: Date.now() };\n }\n\n dispose(): void {\n for (const unsubscribe of this.#subscriptions.values()) unsubscribe();\n this.#subscriptions.clear();\n this.#listeners.clear();\n }\n\n /** The preimage a claim revealed on this HTLC, if one was seen. */\n getPreimage(ref: HtlcRef): Uint8Array | undefined {\n return ref.ledger === \"bitcoin\"\n ? this.#preimages.get(ref.address)\n : undefined;\n }\n\n async #reconcileRef(\n ref: Extract<HtlcRef, { ledger: \"bitcoin\" }>,\n ): Promise<void> {\n const facts = await this.#reader.getHtlcFacts(\n ref.address,\n ref.minConfirmations,\n );\n const { observation, preimage } = bitcoinObservation(\n facts,\n hex.decode(ref.preimageHash),\n ref.expectedSats,\n );\n if (preimage) this.#preimages.set(ref.address, preimage);\n this.#set(ref.address, observation);\n }\n\n /** Update an observation and notify on change; never downgrade a resolved spend. */\n #set(address: string, observation: HtlcObservation): void {\n const ref = this.#refs.get(address);\n if (!ref) return;\n const current = this.#obs.get(address);\n if (current === observation) return;\n const spendStates: HtlcObservation[] = [\"spent_claim\", \"spent_refund\"];\n if (\n current &&\n spendStates.includes(current) &&\n !spendStates.includes(observation)\n )\n return;\n this.#obs.set(address, observation);\n for (const listener of this.#listeners) listener(ref, observation);\n }\n}\n","/**\n * The `BitcoinContractManager`'s pure core.\n *\n * A Bitcoin HTLC is an on-chain output at a witness-script address. It's observed\n * from esplora: an output paying the address means it's funded; a spend of that\n * output resolves it. The spend's witness reveals the preimage on a claim (a\n * timelock refund does not), so — script-agnostically, as on Arkade/EVM — a spend\n * is a claim iff a witness element hashes to the swap's `paymentHash`.\n */\nimport { hex } from \"@scure/base\";\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { preimageMatches } from \"./preimage.js\";\n\n/** On-chain funding state of the HTLC address. */\nexport type BitcoinFunding = \"absent\" | \"mempool\" | \"confirmed\";\n\n/** Facts gathered for an HTLC address from esplora. */\nexport type BitcoinHtlcFacts = {\n funding: BitcoinFunding;\n /** Total sats paid to the HTLC address by the funding tx (0 when absent). */\n fundedSats: number;\n /**\n * The witness stack (hex items) of the input that spent the HTLC output, if it\n * was spent. Absent while the output is still unspent.\n */\n spendWitness?: string[];\n};\n\n/** An HTLC's observation, plus the preimage if a claim revealed it. */\nexport type BitcoinHtlcState = {\n observation: HtlcObservation;\n preimage?: Uint8Array;\n};\n\n/**\n * Classify a spend from its witness: a claim path reveals the preimage in the\n * witness (verified against `paymentHash` — SHA-256, or HASH160 for the\n * `btc_to_arkade` script; {@link preimageMatches} picks by length); a timelock\n * refund path does not.\n */\nexport function classifyBitcoinSpend(\n witness: string[],\n paymentHash: Uint8Array,\n): { spend: \"claim\"; preimage: Uint8Array } | { spend: \"refund\" } {\n for (const item of witness) {\n // The preimage is 32 bytes (64 hex chars); skip signatures, pubkeys, scripts.\n if (item.length !== 64) continue;\n const bytes = hex.decode(item);\n if (preimageMatches(bytes, paymentHash))\n return { spend: \"claim\", preimage: bytes };\n }\n return { spend: \"refund\" };\n}\n\n/**\n * Reduce gathered HTLC facts to an observation (+ the preimage on a claim). A\n * confirmed funding below `expectedSats` is `invalid` (funded, but not on the\n * swap's terms) rather than `confirmed`, so the client never claims a\n * short-funded server leg. An unconfirmed (`mempool`) funding isn't actionable\n * yet, so its amount is re-checked once it confirms.\n */\nexport function bitcoinObservation(\n facts: BitcoinHtlcFacts,\n paymentHash: Uint8Array,\n expectedSats: number,\n): BitcoinHtlcState {\n if (facts.spendWitness) {\n const spend = classifyBitcoinSpend(facts.spendWitness, paymentHash);\n return spend.spend === \"claim\"\n ? { observation: \"spent_claim\", preimage: spend.preimage }\n : { observation: \"spent_refund\" };\n }\n if (facts.funding === \"confirmed\" && facts.fundedSats < expectedSats) {\n return { observation: \"invalid\" };\n }\n return { observation: facts.funding };\n}\n","/**\n * The `EvmContractManager`'s pure core.\n *\n * An EVM HTLC (an `HTLCErc20` swap) exposes its lifecycle as three events, all\n * indexed by the swap's `preimageHash`: `SwapCreated` (funded), `SwapRedeemed`\n * (claimed — reveals the preimage), and `SwapRefunded` (reclaimed after the\n * timelock). This maps a decoded event history to an {@link HtlcObservation}.\n */\nimport type { HtlcObservation } from \"../actions/types.js\";\n\n/** One decoded `HTLCErc20` lifecycle event for a single swap's `preimageHash`. */\nexport type EvmHtlcEvent =\n | { kind: \"created\"; amount: bigint; token: `0x${string}` }\n | { kind: \"redeemed\"; preimage: `0x${string}` }\n | { kind: \"refunded\" };\n\n/** An EVM HTLC's observation, plus the preimage if a claim revealed it. */\nexport type EvmHtlcState = {\n observation: HtlcObservation;\n preimage?: `0x${string}`;\n};\n\n/** What a `SwapCreated` must satisfy to be `confirmed` (else `invalid`). */\nexport type EvmExpectation = {\n /** Minimum locked amount (token's smallest unit). */\n amount: bigint;\n /** Expected token address; verified when provided. */\n token?: `0x${string}`;\n};\n\n/**\n * Reduce an HTLC's on-chain event history to an observation.\n *\n * A terminal spend wins over funding regardless of ordering: `SwapRedeemed`\n * reveals the preimage → `spent_claim` (plus the recovered preimage, which the\n * caller needs to settle the counterparty leg — as on Arkade); `SwapRefunded` →\n * `spent_refund`. A `SwapCreated` is `confirmed` only if it locks at least\n * `expected.amount` of the expected token — otherwise `invalid`, so the client\n * never claims a leg funded on the wrong terms. `pending` marks a broadcast-but-\n * unmined funding tx (`mempool`); otherwise `absent`.\n */\nexport function evmObservation(\n events: EvmHtlcEvent[],\n expected: EvmExpectation,\n pending = false,\n): EvmHtlcState {\n const redeemed = events.find((e) => e.kind === \"redeemed\");\n if (redeemed?.kind === \"redeemed\")\n return { observation: \"spent_claim\", preimage: redeemed.preimage };\n if (events.some((e) => e.kind === \"refunded\"))\n return { observation: \"spent_refund\" };\n const created = events.find((e) => e.kind === \"created\");\n if (created?.kind === \"created\") {\n const amountOk = created.amount >= expected.amount;\n const tokenOk =\n expected.token === undefined ||\n created.token.toLowerCase() === expected.token.toLowerCase();\n return { observation: amountOk && tokenOk ? \"confirmed\" : \"invalid\" };\n }\n return { observation: pending ? \"mempool\" : \"absent\" };\n}\n","/**\n * The contract-monitoring layer — modeled on @arkade-os/sdk's\n * `ContractManager`.\n *\n * A {@link ContractManager} is the per-ledger, stateful I/O layer that watches a\n * set of HTLCs and maps their on-chain state to {@link HtlcObservation}. The pure\n * pipeline (`deriveSwapStatus` → `deriveSwapActions`) consumes the observations\n * it produces; a `SwapTracker` (built later) combines the two ledgers of each\n * swap and notifies subscribers of the next action.\n */\nimport type { HtlcObservation } from \"../actions/types.js\";\n\n/** Ledgers a swap HTLC can live on. */\nexport type Ledger = \"arkade\" | \"bitcoin\" | \"evm\" | \"lightning\";\n\n/**\n * Identifies and parameterises one HTLC on its ledger — enough for a\n * {@link ContractManager} to observe it. Sourced from the swap's recovery\n * bundle. Discriminated by `ledger`.\n */\nexport type HtlcRef =\n | {\n ledger: \"arkade\";\n /** VHTLC pkScript (hex) — the contract's unique id. */\n script: string;\n address: string;\n /** SHA256 of the preimage, to tell a claim spend from a refund spend. */\n preimageHash: string;\n /**\n * Expected funding amount in sats. A funding below this is `invalid` (not on\n * the swap's terms) rather than `confirmed` — so the client never claims a\n * server leg the server short-funded.\n */\n expectedSats: number;\n /**\n * Serialized VHTLC parameters (`@arkade-os/sdk`'s `Contract.params`) — the\n * sender/receiver/server keys, preimage hash, and timelocks needed to\n * register the contract for watching. Opaque here: the Client serializes\n * it from the swap's recovery bundle so this layer stays VHTLC-agnostic.\n */\n params: Record<string, string>;\n }\n | {\n ledger: \"bitcoin\";\n /** HTLC witness-script address — the contract's unique id. */\n address: string;\n preimageHash: string;\n /** Expected funding amount in sats; a funding below this is `invalid`. */\n expectedSats: number;\n /**\n * Confirmations this leg's funding needs before it observes as\n * `confirmed`, overriding the reader's default.\n *\n * Set per leg because the two directions are not symmetric. A funding the\n * SERVER made can be taken at 0-conf: the server does not double-spend its\n * own HTLC, and waiting a block would stall every claim. A funding the\n * CLIENT made is read back under the rule the server applies to it — the\n * server does not act until the tx has a blocktime, so calling it funded\n * sooner reports progress the other side has not agreed to.\n */\n minConfirmations?: number;\n }\n | {\n ledger: \"evm\";\n chainId: number;\n /** The HTLCErc20 contract holding this leg — where its events are emitted. */\n htlc: `0x${string}`;\n /**\n * SHA256 preimage hash (`0x`-prefixed) — the indexed topic the contract's\n * `SwapCreated`/`SwapRedeemed`/`SwapRefunded` events are filtered by. It\n * narrows a log scan but identifies no single HTLC: any number can share\n * one hash, so a swap is told apart by the rest of the tuple below.\n */\n preimageHash: `0x${string}`;\n /**\n * The address that can claim this HTLC — the second indexed topic used to\n * filter `SwapCreated`, so only the HTLC actually claimable on the swap's\n * terms is observed (an HTLC with our hash but a different recipient is\n * never seen). Client's EVM address for a server-funded leg; the server's\n * for the client's own leg.\n */\n claimAddress: `0x${string}`;\n /**\n * Expected locked amount (token's smallest unit). A `SwapCreated` below this\n * → `invalid`, so the client never claims a short-funded leg.\n */\n expectedAmount: bigint;\n /** Expected token address; the funding is `invalid` if it locks another token. */\n expectedToken?: `0x${string}`;\n /**\n * The funder (the HTLC's refund address). Together with the other fields it\n * completes the contract's swap-key tuple, enabling the cheap `isActive`\n * eth_call fast path (which verifies the terms implicitly — the key hashes\n * amount/token/addresses/timelock). Legs whose response omits a tuple field\n * fall back to log scans.\n */\n sender?: `0x${string}`;\n /** The HTLC refund timelock in unix SECONDS, exactly as funded on-chain. */\n timelockSec?: number;\n /**\n * Swap creation time (ms). Lower-bounds log scans: the HTLC can have no\n * events before the swap existed, so the scan starts near the corresponding\n * block instead of genesis.\n */\n createdAtMs?: number;\n }\n | {\n ledger: \"lightning\";\n /** Payment hash — the contract's unique id. */\n paymentHash: string;\n };\n\n/** Stable, cross-ledger unique key for an {@link HtlcRef}. */\nexport function htlcKey(ref: HtlcRef): string {\n switch (ref.ledger) {\n case \"arkade\":\n return `arkade:${ref.script}`;\n case \"bitcoin\":\n return `bitcoin:${ref.address}`;\n case \"evm\": {\n // Not the hash alone: any number of HTLCs can carry one preimage hash, so\n // the contract and the rest of the swap-key tuple are what separate them.\n // A leg whose response omitted a tuple field falls back to contract + hash\n // and stays ambiguous — which is why its settlements are not attributed.\n const base = `evm:${ref.chainId}:${ref.htlc.toLowerCase()}:${ref.preimageHash.toLowerCase()}`;\n if (!ref.expectedToken || !ref.sender || ref.timelockSec === undefined) {\n return base;\n }\n return [\n base,\n ref.expectedAmount,\n ref.expectedToken.toLowerCase(),\n ref.sender.toLowerCase(),\n ref.claimAddress.toLowerCase(),\n ref.timelockSec,\n ].join(\":\");\n }\n case \"lightning\":\n return `lightning:${ref.paymentHash}`;\n }\n}\n\n/**\n * Per-ledger contract monitor. Implementations keep an internal, event-driven\n * cache: `register` seeds it, `onEvent` pushes changes, `getState` reads it\n * synchronously, `dispose` tears down subscriptions/loops.\n *\n * One manager exists per ledger actually in use and watches every tracked HTLC\n * on that ledger at once (one connection, not one per HTLC).\n */\nexport interface ContractManager {\n /** The ledger this manager observes. */\n readonly ledger: Ledger;\n\n /**\n * Whether this manager can observe `ref` at all — a permanent capability check,\n * not a transient health probe. `false` means the ledger/chain is not configured\n * (e.g. an EVM chain with no RPC), so the ref can never be observed and the\n * tracker should skip the swap rather than {@link register} it (which throws).\n * Distinct from a register failure, which is a recoverable error on an\n * observable ref.\n */\n canObserve(ref: HtlcRef): boolean;\n\n /** Start tracking an HTLC (idempotent); seeds its state from the ledger. */\n register(ref: HtlcRef): Promise<void>;\n\n /** Stop tracking an HTLC. */\n unregister(ref: HtlcRef): Promise<void>;\n\n /**\n * Current known observation for a tracked HTLC, or `undefined` if it isn't\n * tracked or its state isn't known yet.\n */\n getState(ref: HtlcRef): HtlcObservation | undefined;\n\n /**\n * The current clock (ms) for evaluating this HTLC's timelock — MTP for\n * Bitcoin/Arkade, `block.timestamp` for EVM. Ref-scoped because one ledger can\n * span several chains with independent clocks (EVM chains); managers whose\n * ledger has a single clock (Arkade) ignore the ref. `undefined` until known.\n */\n chainNow(ref: HtlcRef): number | undefined;\n\n /** Subscribe to observation changes for any tracked HTLC. Returns unsubscribe. */\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void;\n\n /** Force a reconcile from the ledger. */\n refresh(): Promise<void>;\n\n /**\n * Re-observe a single tracked HTLC from the ledger now — a targeted refresh, so\n * a faster server hint can trigger an immediate on-chain verification of just\n * that swap's legs without re-scanning every tracked HTLC. A no-op if the ref\n * isn't tracked.\n */\n reconcile(ref: HtlcRef): Promise<void>;\n\n /** Tear down subscriptions and background work. */\n dispose(): void;\n}\n","/**\n * The EVM {@link ContractManager} — the stateful I/O adapter that observes\n * `HTLCErc20` swaps across one or more EVM chains.\n *\n * Reading chain state is delegated to an injected {@link EvmChainReader} (one per\n * chainId) rather than a bundled viem/ethers client, so the swap package stays\n * chain-library-agnostic and this adapter is testable against a fake. The pure\n * event→observation mapping lives in `./evm.js`.\n *\n * Unlike Arkade, EVM spans multiple chains with independent `block.timestamp`\n * clocks, so this manager is chain-aware: it routes each ref by `chainId` and\n * {@link chainNow} is ref-scoped.\n */\nimport type { HtlcObservation } from \"../actions/types.js\";\nimport { type EvmHtlcEvent, evmObservation } from \"./evm.js\";\nimport {\n type ContractManager,\n type HtlcRef,\n htlcKey,\n type Ledger,\n} from \"./types.js\";\n\ntype EvmRef = Extract<HtlcRef, { ledger: \"evm\" }>;\n\n/** Identifies one HTLC to read: contract + preimageHash (+ the claim guard). */\nexport type EvmHtlcQuery = {\n htlc: `0x${string}`;\n preimageHash: `0x${string}`;\n /** A `SwapCreated` counts only when it pays this address (term check). */\n claimAddress: `0x${string}`;\n /**\n * The rest of the swap-key tuple, when the swap response exposed it. It makes\n * {@link htlcQueryKey} unique to this HTLC, so two swaps sharing a preimage\n * hash hold separate entries rather than one overwriting the other. It does\n * not change how settlements are attributed — those go by hash either way.\n */\n terms?: EvmHtlcTerms;\n};\n\n/**\n * The result key for one query in a batch.\n *\n * `preimageHash` identifies no single HTLC — any number can share one — so the\n * rest of the swap-key tuple is what separates two HTLCs on the same contract.\n * A caller that knows the tuple gets a key unique to its HTLC; one that does not\n * falls back to contract + hash, which is ambiguous by construction — two such\n * callers on one hash share an entry.\n */\nexport function htlcQueryKey(q: {\n htlc: `0x${string}`;\n preimageHash: `0x${string}`;\n claimAddress?: `0x${string}`;\n terms?: EvmHtlcTerms;\n}): string {\n const base = `${q.htlc.toLowerCase()}:${q.preimageHash.toLowerCase()}`;\n if (!q.terms || !q.claimAddress) return base;\n const { amount, token, sender, timelockSec } = q.terms;\n return [\n base,\n amount,\n token.toLowerCase(),\n sender.toLowerCase(),\n q.claimAddress.toLowerCase(),\n timelockSec,\n ].join(\":\");\n}\n\n/** The swap-key tuple fields beyond contract, hash and claim address. */\nexport type EvmHtlcTerms = {\n amount: bigint;\n token: `0x${string}`;\n sender: `0x${string}`;\n timelockSec: number;\n};\n\n/** The batch key for a tracked ref, carrying its terms when the swap exposed them. */\nexport function refQueryKey(ref: EvmRef): string {\n const full = activeQuery(ref);\n return htlcQueryKey({\n htlc: ref.htlc,\n preimageHash: ref.preimageHash,\n claimAddress: ref.claimAddress,\n terms: full && {\n amount: full.amount,\n token: full.token,\n sender: full.sender,\n timelockSec: full.timelockSec,\n },\n });\n}\n\n/**\n * The contract's full swap-key tuple for one HTLC. `isActive` hashes all of it\n * into the swap key, so an `isActive == true` with these EXPECTED values also\n * proves the HTLC was funded on exactly the expected terms.\n */\nexport type EvmActiveQuery = {\n htlc: `0x${string}`;\n preimageHash: `0x${string}`;\n amount: bigint;\n token: `0x${string}`;\n sender: `0x${string}`;\n claimAddress: `0x${string}`;\n timelockSec: number;\n};\n\n/** Reads `HTLCErc20` state for one EVM chain. Implemented over viem/ethers/etc. */\nexport type EvmChainReader = {\n /**\n * The decoded lifecycle events for every queried HTLC, keyed by\n * {@link htlcQueryKey}. Batched so a whole chain scan costs one RPC request\n * regardless of how many swaps are tracked; a queried HTLC with no events maps\n * to an empty array. `fromBlock` lower-bounds the scan (default genesis) —\n * pass the swaps' creation-block estimate so providers never see an\n * unbounded log query.\n */\n getHtlcEventsBatch(\n queries: EvmHtlcQuery[],\n fromBlock?: bigint,\n ): Promise<Map<string, EvmHtlcEvent[]>>;\n /**\n * Whether each queried HTLC is currently open, keyed by {@link htlcQueryKey}.\n * The cheap routine check (`eth_call`, batched via Multicall3): `true` means\n * funded-and-unspent on exactly the queried terms; `false` is ambiguous\n * (never funded / claimed / refunded) and needs {@link getHtlcEventsBatch}\n * to classify.\n */\n isActiveBatch(queries: EvmActiveQuery[]): Promise<Map<string, boolean>>;\n /** The latest block's timestamp (ms) and number. */\n getLatestBlock(): Promise<{ timeMs: number; number: bigint }>;\n};\n\nexport type EvmContractManagerDeps = {\n /** A chain reader per EVM `chainId` this manager serves. */\n readers: Map<number, EvmChainReader>;\n};\n\n/** A chain clock reading: block.timestamp/number plus when we fetched it. */\ntype ChainClock = {\n blockTimeMs: number;\n blockNumber: bigint;\n fetchedAtMs: number;\n};\n\n/**\n * Rough average block time per supported chain, for estimating \"the block\n * around a swap's creation time\". Only a LOWER bound for log scans, so being\n * generous (scanning further back) is safe; the ×1.5 factor in the estimate\n * absorbs historical variance. An unlisted chain falls back to a genesis scan.\n */\nconst AVG_BLOCK_MS: Record<number, number> = {\n 1: 12_000,\n 137: 2_100,\n 42161: 250,\n};\n\n/** Extra blocks to over-scan past the estimate (reorgs + estimate slack). */\nconst SCAN_MARGIN_BLOCKS = 1_000n;\n\nexport class EvmContractManager implements ContractManager {\n readonly ledger: Ledger = \"evm\";\n\n readonly #readers: Map<number, EvmChainReader>;\n /** htlcKey → the ref we're tracking. */\n readonly #refs = new Map<string, EvmRef>();\n /** htlcKey → last known observation. */\n readonly #obs = new Map<string, HtlcObservation>();\n /** htlcKey → the preimage a claim revealed. */\n readonly #preimages = new Map<string, `0x${string}`>();\n /** chainId → its last clock reading (extrapolated in {@link chainNow}). */\n readonly #now = new Map<number, ChainClock>();\n readonly #listeners = new Set<\n (ref: HtlcRef, state: HtlcObservation) => void\n >();\n\n private constructor(deps: EvmContractManagerDeps) {\n this.#readers = deps.readers;\n }\n\n static fromDeps(deps: EvmContractManagerDeps): EvmContractManager {\n return new EvmContractManager(deps);\n }\n\n canObserve(ref: HtlcRef): boolean {\n return ref.ledger === \"evm\" && this.#readers.has(ref.chainId);\n }\n\n async register(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"evm\")\n throw new Error(`EvmContractManager can't track a '${ref.ledger}' HTLC`);\n // Fail loudly on an unconfigured chain rather than storing a ref we can never\n // observe (its state/clock would stay undefined and the tracker would wait on\n // it forever). The caller must add the chain via ClientBuilder.withEvmRpcUrls().\n if (!this.#readers.has(ref.chainId))\n throw new Error(\n `no EVM reader for chain ${ref.chainId} — configure it via ClientBuilder.withEvmRpcUrls()`,\n );\n this.#refs.set(htlcKey(ref), ref);\n }\n\n async unregister(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"evm\") return;\n const key = htlcKey(ref);\n this.#refs.delete(key);\n this.#obs.delete(key);\n this.#preimages.delete(key);\n // Drop the chain's state once nothing on it is tracked anymore.\n if (![...this.#refs.values()].some((r) => r.chainId === ref.chainId))\n this.#now.delete(ref.chainId);\n }\n\n getState(ref: HtlcRef): HtlcObservation | undefined {\n return ref.ledger === \"evm\" ? this.#obs.get(htlcKey(ref)) : undefined;\n }\n\n chainNow(ref: HtlcRef): number | undefined {\n if (ref.ledger !== \"evm\") return undefined;\n const clock = this.#now.get(ref.chainId);\n if (!clock) return undefined;\n // Extrapolate between reads: EVM block.timestamp tracks wall-clock closely,\n // so the clock stays current without polling `getBlock`. This is what lets a\n // timelock flip (e.g. refund unlocking) surface between slow scans; the\n // worker re-verifies against the real chain before ever acting on it.\n return clock.blockTimeMs + Math.max(0, Date.now() - clock.fetchedAtMs);\n }\n\n onEvent(cb: (ref: HtlcRef, state: HtlcObservation) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n /**\n * Full reconcile of every tracked chain — seeds clocks + observations. Called\n * on demand (tracker start / newly tracked swap), NOT periodically: onward,\n * hints drive targeted `reconcile`s and the tracker's at-risk safety net\n * re-reads only swaps with client funds on the line.\n */\n async refresh(): Promise<void> {\n const chainIds = [\n ...new Set([...this.#refs.values()].map((r) => r.chainId)),\n ];\n // Settled, not all: every EVM chain shares this one manager, so a single\n // endpoint failing would otherwise leave the HTLCs on every OTHER chain\n // unobserved — a swap would stop deriving because an unrelated swap's chain\n // is unreachable. A chain that throws keeps its previous observations and is\n // retried on the next reconcile.\n const results = await Promise.allSettled(\n chainIds.map((c) => this.#reconcileChain(c)),\n );\n results.forEach((result, index) => {\n if (result.status === \"rejected\")\n console.warn(\n `EVM reconcile failed for chain ${chainIds[index]}:`,\n result.reason,\n );\n });\n }\n\n /** Targeted verify (hint / pre-action path) — never gated. */\n async reconcile(ref: HtlcRef): Promise<void> {\n if (ref.ledger !== \"evm\") return;\n const tracked = this.#refs.get(htlcKey(ref));\n if (!tracked) return;\n const reader = this.#readers.get(tracked.chainId);\n if (!reader) return;\n await this.#readClock(reader, tracked.chainId);\n await this.#reconcileRefs(reader, tracked.chainId, [tracked]);\n }\n\n dispose(): void {\n this.#listeners.clear();\n }\n\n /** The preimage a claim revealed on this HTLC, if one was seen. */\n getPreimage(ref: HtlcRef): `0x${string}` | undefined {\n return ref.ledger === \"evm\" ? this.#preimages.get(htlcKey(ref)) : undefined;\n }\n\n /** Read and store a chain's clock (with its fetch time, for extrapolation). */\n async #readClock(reader: EvmChainReader, chainId: number): Promise<void> {\n const block = await reader.getLatestBlock();\n this.#now.set(chainId, {\n blockTimeMs: block.timeMs,\n blockNumber: block.number,\n fetchedAtMs: Date.now(),\n });\n }\n\n /** Refresh one chain's clock and re-observe every HTLC tracked on it. */\n async #reconcileChain(chainId: number): Promise<void> {\n const reader = this.#readers.get(chainId);\n if (!reader) return;\n await this.#readClock(reader, chainId);\n const refs = [...this.#refs.values()].filter((r) => r.chainId === chainId);\n await this.#reconcileRefs(reader, chainId, refs);\n }\n\n /**\n * Re-observe the given refs. Two tiers:\n *\n * 1. Refs with the full contract tuple get the cheap `isActive` check —\n * `true` proves open-and-on-expected-terms (the swap key hashes them) →\n * `confirmed`, no log scan at all. This is the common case.\n * 2. Everything else — an incomplete tuple, an inactive-and-unclassified\n * HTLC (never funded vs claimed vs refunded), or an `isActive` transport\n * failure (e.g. no Multicall3 on a dev chain) — is classified from one\n * batched log read, lower-bounded by the swaps' creation-block estimate.\n *\n * A ref already latched on a spend is skipped entirely: terminal per leg,\n * nothing left to learn.\n */\n async #reconcileRefs(\n reader: EvmChainReader,\n chainId: number,\n refs: EvmRef[],\n ): Promise<void> {\n const open = refs.filter((r) => !this.#isSpent(r));\n if (open.length === 0) return;\n\n const fast = open.filter((r) => activeQuery(r) !== undefined);\n let needLogs = open.filter((r) => activeQuery(r) === undefined);\n\n if (fast.length > 0) {\n try {\n const active = await reader.isActiveBatch(\n fast.map((r) => activeQuery(r) as EvmActiveQuery),\n );\n for (const ref of fast) {\n if (active.get(refQueryKey(ref)) === true)\n this.#set(htlcKey(ref), \"confirmed\");\n else needLogs.push(ref); // inactive: classify from logs\n }\n } catch (error) {\n console.warn(\n `EVM isActive check failed for chain ${chainId} — falling back to logs:`,\n error,\n );\n needLogs = open;\n }\n }\n if (needLogs.length === 0) return;\n\n // Split by whether the creation time is known: known-age refs share a scan\n // lower-bounded near the oldest creation block, unknown-age refs (legacy\n // stored swaps without created_at) get their own full-range scan. One\n // batch for both would force the WORSE bound on everyone — genesis for the\n // whole batch, or (if unknowns were just filtered from the estimate)\n // possibly missing an old ref's events and misreading it as never funded.\n const known = needLogs.filter((r) => r.createdAtMs !== undefined);\n const unknown = needLogs.filter((r) => r.createdAtMs === undefined);\n await Promise.all(\n (\n [\n [known, this.#estimateFromBlock(chainId, known)],\n [unknown, 0n],\n ] as const\n )\n .filter(([refs]) => refs.length > 0)\n .map(([refs, fromBlock]) =>\n this.#classifyFromLogs(reader, refs, fromBlock),\n ),\n );\n }\n\n /** Read the refs' lifecycle logs (from `fromBlock`) and set observations. */\n async #classifyFromLogs(\n reader: EvmChainReader,\n refs: EvmRef[],\n fromBlock: bigint,\n ): Promise<void> {\n const events = await reader.getHtlcEventsBatch(\n refs.map((r) => {\n const full = activeQuery(r);\n return {\n htlc: r.htlc,\n preimageHash: r.preimageHash,\n claimAddress: r.claimAddress,\n terms: full && {\n amount: full.amount,\n token: full.token,\n sender: full.sender,\n timelockSec: full.timelockSec,\n },\n };\n }),\n fromBlock,\n );\n for (const ref of refs) {\n const { observation, preimage } = evmObservation(\n events.get(refQueryKey(ref)) ?? [],\n { amount: ref.expectedAmount, token: ref.expectedToken },\n );\n const key = htlcKey(ref);\n if (preimage) this.#preimages.set(key, preimage);\n this.#set(key, observation);\n }\n }\n\n #isSpent(ref: EvmRef): boolean {\n const obs = this.#obs.get(htlcKey(ref));\n return obs === \"spent_claim\" || obs === \"spent_refund\";\n }\n\n /**\n * The block to scan logs from: roughly where the OLDEST of the given swaps\n * was created (an HTLC has no events before its swap existed). Only a lower\n * bound, so estimation errs early — ×1.5 on elapsed time plus a fixed block\n * margin. Falls back to genesis when the creation time, clock, or the\n * chain's block time is unknown.\n */\n #estimateFromBlock(chainId: number, refs: EvmRef[]): bigint {\n const clock = this.#now.get(chainId);\n const avgBlockMs = AVG_BLOCK_MS[chainId];\n const createdAts = refs.map((r) => r.createdAtMs ?? 0);\n const oldestMs = Math.min(...createdAts);\n if (!clock || !avgBlockMs || oldestMs <= 0) return 0n;\n const elapsedMs = Math.max(0, clock.blockTimeMs - oldestMs);\n const blocksBehind =\n BigInt(Math.ceil((elapsedMs / avgBlockMs) * 1.5)) + SCAN_MARGIN_BLOCKS;\n const from = clock.blockNumber - blocksBehind;\n return from > 0n ? from : 0n;\n }\n\n /** Update an observation and notify on change; never downgrade a resolved spend. */\n #set(key: string, observation: HtlcObservation): void {\n const ref = this.#refs.get(key);\n if (!ref) return;\n const current = this.#obs.get(key);\n if (current === observation) return;\n const spendStates: HtlcObservation[] = [\"spent_claim\", \"spent_refund\"];\n if (\n current &&\n spendStates.includes(current) &&\n !spendStates.includes(observation)\n )\n return;\n this.#obs.set(key, observation);\n for (const listener of this.#listeners) listener(ref, observation);\n }\n}\n\n/**\n * The complete `isActive` tuple for a ref, or `undefined` when the swap\n * response didn't expose a field (then only the log path can observe the leg).\n */\nfunction activeQuery(ref: EvmRef): EvmActiveQuery | undefined {\n if (\n ref.sender === undefined ||\n ref.expectedToken === undefined ||\n ref.timelockSec === undefined\n )\n return undefined;\n return {\n htlc: ref.htlc,\n preimageHash: ref.preimageHash,\n amount: ref.expectedAmount,\n token: ref.expectedToken,\n sender: ref.sender,\n claimAddress: ref.claimAddress,\n timelockSec: ref.timelockSec,\n };\n}\n","/**\n * A viem-backed {@link EvmChainReader} — the concrete chain source the\n * {@link EvmContractManager} uses in production.\n *\n * Kept separate from the manager so the manager itself stays free of any\n * chain-library dependency (and unit-testable against a fake reader). This module\n * is the only place that touches viem and the `HTLCErc20` ABI.\n *\n * All HTLCs on a chain are read with a SINGLE `eth_getLogs` call: the three\n * `HTLCErc20` lifecycle events all index `preimageHash` as topic1, so one filter\n * ORs the three event signatures on topic0 and the tracked preimage hashes on\n * topic1 across all HTLC contract addresses. Request count per scan is constant\n * in the number of tracked swaps — this matters, since public RPCs rate-limit by\n * request count.\n */\nimport {\n type Block,\n createPublicClient,\n decodeEventLog,\n decodeFunctionResult,\n encodeFunctionData,\n fallback,\n http,\n numberToHex,\n parseAbiItem,\n toEventSelector,\n} from \"viem\";\nimport type { EvmHtlcEvent } from \"./evm.js\";\nimport {\n type EvmActiveQuery,\n type EvmChainReader,\n type EvmHtlcQuery,\n htlcQueryKey,\n} from \"./evm-manager.js\";\n\n/**\n * Tested public RPC endpoints per supported chainId, tried in order via viem's\n * `fallback`. Mirrors the frontend's `evmTransport.ts` list — the client\n * uses these by default so Arkade↔EVM tracking works out of the box; a caller can\n * override per chain with `ClientBuilder.withEvmRpcUrls`.\n */\nexport const DEFAULT_EVM_RPCS: Record<number, string[]> = {\n // Polygon — viem's default (polygon.drpc.org) misbehaves for some calls, so\n // list working public RPCs explicitly.\n 137: [\n \"https://polygon.drpc.org\",\n \"https://tenderly.rpc.polygon.community\",\n \"https://polygon-bor-rpc.publicnode.com\",\n ],\n // No publicnode: it rate-limits by IP across its whole fleet (403s), and as\n // a PRIMARY that costs a full retry cycle per read before failing over.\n // Both entries verified for eth_getLogs + eth_call from a browser origin.\n 1: [\"https://eth.drpc.org\", \"https://rpc.mevblocker.io\"],\n // The official gateway first; publicnode last (see above).\n 42161: [\n \"https://arb1.arbitrum.io/rpc\",\n \"https://arbitrum.drpc.org\",\n \"https://arbitrum-one-rpc.publicnode.com\",\n ],\n};\n\n// The three `HTLCErc20` lifecycle events, each indexed by `preimageHash` and each\n// carrying `key` — the commitment to the swap's full parameter set, and the only\n// field that says which swap an event belongs to.\nconst SWAP_CREATED = parseAbiItem(\n \"event SwapCreated(bytes32 indexed preimageHash, address indexed refundAddress, address indexed claimAddress, address token, uint256 amount, uint256 timelock, bytes32 key)\",\n);\nconst SWAP_REDEEMED = parseAbiItem(\n \"event SwapRedeemed(bytes32 indexed preimageHash, bytes32 indexed key, bytes32 preimage)\",\n);\nconst SWAP_REFUNDED = parseAbiItem(\n \"event SwapRefunded(bytes32 indexed preimageHash, bytes32 indexed key)\",\n);\n\nconst HTLC_EVENTS_ABI = [SWAP_CREATED, SWAP_REDEEMED, SWAP_REFUNDED] as const;\n/** topic0 of the three lifecycle events — the batched getLogs OR-filter. */\nconst HTLC_EVENT_TOPICS = HTLC_EVENTS_ABI.map(toEventSelector);\n\n// The contract's open-check: the args hash into the swap key, so `true` with\n// the EXPECTED values also verifies the funded terms.\nconst IS_ACTIVE = parseAbiItem(\n \"function isActive(bytes32 preimageHash, uint256 amount, address token, address sender, address claimAddress, uint256 timelock) view returns (bool)\",\n);\n\n// Multicall3 — same address on every major EVM chain; aggregates the per-HTLC\n// isActive calls into one eth_call.\nexport const MULTICALL3_ADDRESS =\n \"0xcA11bde05977b3631167028862bE2a173976CA11\" as const;\nconst AGGREGATE3 = parseAbiItem(\n \"function aggregate3((address target, bool allowFailure, bytes callData)[] calls) payable returns ((bool success, bytes returnData)[] returnData)\",\n);\n\n/** An `EvmActiveQuery` carries the whole tuple, so its key always includes terms. */\nfunction activeKey(q: EvmActiveQuery): string {\n return htlcQueryKey({\n htlc: q.htlc,\n preimageHash: q.preimageHash,\n claimAddress: q.claimAddress,\n terms: {\n amount: q.amount,\n token: q.token,\n sender: q.sender,\n timelockSec: q.timelockSec,\n },\n });\n}\n\n/** The undecoded log shape `eth_getLogs` returns. */\nexport type RawLog = {\n address: `0x${string}`;\n topics: [`0x${string}`, ...`0x${string}`[]];\n data: `0x${string}`;\n};\n\n/** The viem surface the reader needs — a seam so it can be faked in tests. */\nexport type EvmLogClient = {\n request(args: {\n method: \"eth_getLogs\";\n params: [\n {\n address: `0x${string}`[];\n topics: (`0x${string}`[] | null)[];\n // Hex quantity, not \"earliest\": strict RPCs (e.g. arb1.arbitrum.io)\n // reject the tag on eth_getLogs.\n fromBlock: `0x${string}`;\n },\n ];\n }): Promise<RawLog[]>;\n /** A read-only `eth_call`. */\n call(args: {\n to: `0x${string}`;\n data: `0x${string}`;\n }): Promise<{ data?: `0x${string}` }>;\n getBlock(): Promise<Pick<Block, \"timestamp\" | \"number\">>;\n};\n\n/** Build an {@link EvmChainReader} over an existing viem-like client. */\nexport function evmReaderFromClient(client: EvmLogClient): EvmChainReader {\n return {\n async getHtlcEventsBatch(queries, fromBlock = 0n) {\n const results = new Map<string, EvmHtlcEvent[]>();\n if (queries.length === 0) return results;\n for (const q of queries) results.set(htlcQueryKey(q), []);\n\n const logs = await client.request({\n method: \"eth_getLogs\",\n params: [\n {\n address: unique(queries.map((q) => q.htlc)),\n topics: [\n HTLC_EVENT_TOPICS,\n unique(queries.map((q) => q.preimageHash)),\n ],\n fromBlock: numberToHex(fromBlock),\n },\n ],\n });\n\n // A log can only be grouped by what it carries on its own — contract and\n // hash — and several HTLCs can share that. So group, then let every query\n // filter the whole group through its own terms. Matching each log to a\n // single query up front would let two queries sharing a hash collapse into\n // one, dropping the other's settlements or attributing them wrongly.\n const grouped = new Map<string, DecodedHtlcLog[]>();\n for (const log of logs) {\n const decoded = tryDecode(log);\n if (!decoded) continue;\n const group = `${log.address.toLowerCase()}:${decoded.preimageHash.toLowerCase()}`;\n const existing = grouped.get(group);\n if (existing) existing.push(decoded);\n else grouped.set(group, [decoded]);\n }\n\n for (const query of queries) {\n const group = `${query.htlc.toLowerCase()}:${query.preimageHash.toLowerCase()}`;\n const events = results.get(htlcQueryKey(query));\n if (!events) continue;\n for (const decoded of grouped.get(group) ?? []) {\n const event = toHtlcEvent(decoded, query);\n if (event) events.push(event);\n }\n }\n return results;\n },\n async isActiveBatch(queries) {\n const results = new Map<string, boolean>();\n if (queries.length === 0) return results;\n const calldatas = queries.map((q) =>\n encodeFunctionData({\n abi: [IS_ACTIVE],\n args: [\n q.preimageHash,\n q.amount,\n q.token,\n q.sender,\n q.claimAddress,\n BigInt(q.timelockSec),\n ],\n }),\n );\n\n const callOne = async (i: number): Promise<void> => {\n const { data } = await client.call({\n to: queries[i].htlc,\n data: calldatas[i],\n });\n results.set(activeKey(queries[i]), decodeIsActive(data));\n };\n\n if (queries.length === 1) {\n await callOne(0);\n return results;\n }\n try {\n const { data } = await client.call({\n to: MULTICALL3_ADDRESS,\n data: encodeFunctionData({\n abi: [AGGREGATE3],\n args: [\n queries.map((q, i) => ({\n target: q.htlc,\n allowFailure: true,\n callData: calldatas[i],\n })),\n ],\n }),\n });\n if (data === undefined || data === \"0x\")\n throw new Error(\"empty multicall result\");\n const decoded = decodeFunctionResult({\n abi: [AGGREGATE3],\n data,\n }) as readonly { success: boolean; returnData: `0x${string}` }[];\n queries.forEach((q, i) => {\n const r = decoded[i];\n // A failed inner call reads as \"not active\" — the caller's log\n // fallback then classifies it, so nothing is silently trusted.\n results.set(\n activeKey(q),\n r?.success === true && decodeIsActive(r.returnData),\n );\n });\n } catch {\n // No Multicall3 on this chain (e.g. a bare dev node) — per-HTLC calls.\n await Promise.all(queries.map((_, i) => callOne(i)));\n }\n return results;\n },\n async getLatestBlock() {\n const block = await client.getBlock();\n return {\n timeMs: Number(block.timestamp) * 1000,\n number: block.number ?? 0n,\n };\n },\n };\n}\n\n/** Decode an `isActive` eth_call result; empty data (no contract) → false. */\nfunction decodeIsActive(data: `0x${string}` | undefined): boolean {\n if (data === undefined || data === \"0x\") return false;\n return decodeFunctionResult({ abi: [IS_ACTIVE], data }) === true;\n}\n\n/**\n * The decoded fields this reader acts on.\n *\n * All three events also carry `key`. It is deliberately not surfaced: nothing\n * here can check it against a query yet, and a field decoded but never read\n * reads as though attribution were key-based. See {@link toHtlcEvent}.\n */\ntype DecodedHtlcLog =\n | {\n eventName: \"SwapCreated\";\n preimageHash: `0x${string}`;\n claimAddress: `0x${string}`;\n token: `0x${string}`;\n amount: bigint;\n }\n | {\n eventName: \"SwapRedeemed\";\n preimageHash: `0x${string}`;\n preimage: `0x${string}`;\n }\n | {\n eventName: \"SwapRefunded\";\n preimageHash: `0x${string}`;\n };\n\n/** Decode one raw log against the three-event ABI; undefined for foreign logs. */\nfunction tryDecode(log: RawLog): DecodedHtlcLog | undefined {\n try {\n const { eventName, args } = decodeEventLog({\n abi: HTLC_EVENTS_ABI,\n data: log.data,\n topics: log.topics,\n });\n return { eventName, ...args } as DecodedHtlcLog;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Map a decoded log to the manager's event, applying the per-query guards the\n * batched filter can't express per-hash.\n *\n * Settlements are attributed by `preimageHash` alone, which identifies no single\n * HTLC — any number may share one hash, each with its own terms and lifecycle.\n * Two queries on the same hash and contract therefore both see the same terminal\n * event. Attribution here is ambiguous by construction; `evmObservation`'s\n * `isActive` check, not this mapping, is what keeps an open HTLC from reading as\n * settled, because the tuple it calls with does hold for the leg being tracked.\n *\n * The events carry `key`, the commitment to the full parameter set, which would\n * separate them — but a query cannot arrive at the funded HTLC's key on its own:\n * a coordinator locks its post-swap balance rather than the quoted amount, and is\n * recorded as `sender` in place of the funder named on the swap. Attributing\n * settlements exactly needs the key recorded at funding time to reach the client\n * rather than being re-derived here.\n *\n * `SwapCreated` is guarded on `claimAddress` for the same reason a key match will\n * not do: it is the event that first reports the funded amount, which may differ\n * from the expected one, so its key is not predictable either. `evmObservation`\n * term-checks it afterwards.\n */\nfunction toHtlcEvent(\n decoded: DecodedHtlcLog,\n query: EvmHtlcQuery,\n): EvmHtlcEvent | undefined {\n if (decoded.eventName === \"SwapCreated\") {\n if (decoded.claimAddress.toLowerCase() !== query.claimAddress.toLowerCase())\n return undefined;\n return { kind: \"created\", amount: decoded.amount, token: decoded.token };\n }\n\n return decoded.eventName === \"SwapRedeemed\"\n ? { kind: \"redeemed\", preimage: decoded.preimage }\n : { kind: \"refunded\" };\n}\n\nfunction unique<T>(values: T[]): T[] {\n return [...new Set(values)];\n}\n\n/**\n * Build an {@link EvmChainReader} over one or more EVM JSON-RPC endpoints. With\n * several, viem's `fallback` tries them in order and fails over on error.\n * Deliberately NOT ranked: ranking health-pings every listed endpoint on an\n * interval from every open tab, which is exactly the background burst that got\n * public RPCs rate-limiting us (publicnode 403s) — and it contradicts this\n * package's near-zero-RPC design.\n */\nexport function createEvmRpcReader(rpcUrls: string | string[]): EvmChainReader {\n const urls = Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls];\n const transport =\n urls.length > 1 ? fallback(urls.map((url) => http(url))) : http(urls[0]);\n const client = createPublicClient({ transport });\n return evmReaderFromClient(client as unknown as EvmLogClient);\n}\n\n/**\n * Resolve the per-chain readers used for tracking: the tested {@link\n * DEFAULT_EVM_RPCS} by default, with any `overrides` taking priority for their\n * chain (kept ahead of the defaults, which remain as fallbacks).\n */\nexport function defaultEvmReaders(\n overrides?: Record<number, string>,\n): Map<number, EvmChainReader> {\n const chainIds = new Set<number>([\n ...Object.keys(DEFAULT_EVM_RPCS).map(Number),\n ...Object.keys(overrides ?? {}).map(Number),\n ]);\n const readers = new Map<number, EvmChainReader>();\n for (const chainId of chainIds) {\n const override = overrides?.[chainId];\n const defaults = DEFAULT_EVM_RPCS[chainId] ?? [];\n const urls = override ? [override, ...defaults] : defaults;\n if (urls.length > 0) readers.set(chainId, createEvmRpcReader(urls));\n }\n return readers;\n}\n","/**\n * A server-trusting stand-in for {@link SwapTracker}: derives each swap's next\n * action from the SERVER's status instead of chain observations — zero chain\n * access (no EVM RPCs, no Esplora/Electrum, no Arkade reads).\n *\n * TEMPORARY default while the chain monitors are being fixed. Unlike\n * {@link SwapTracker} there is no independent verification: the client acts on\n * the server's word. The pushed status is the same `SwapStatus` enum the chain\n * derivation produces, so it feeds straight into {@link deriveSwapActions}.\n * Failure containment: a claim run on a false `serverfunded` fails locally\n * (nothing to spend / gas estimation reverts) rather than losing funds.\n *\n * Timelocks are evaluated against the wall clock, not chain clocks. The wall\n * clock runs AHEAD of Bitcoin's MTP (by up to ~an hour), so a refund can look\n * unlocked slightly early — the broadcast simply fails until the chain agrees,\n * and refunds are user-confirmed anyway — while the claim window closes\n * conservatively early, which is the safe direction.\n */\nimport type { SwapStatus } from \"@lendasat/lendaswap-sdk-pure\";\nimport { deriveSwapActions } from \"../actions/derive.js\";\nimport type { SwapActions } from \"../actions/types.js\";\nimport type { ActionSubscriber, TrackedSwap } from \"../tracker/swap-tracker.js\";\n\nexport type HintTrackerOptions = {\n /**\n * Fetch a swap's current status from the server — the fallback for hints\n * that don't carry one (the worker's retry re-verifies and its\n * post-reconnect sweep).\n */\n fetchStatus: (swapId: string) => Promise<SwapStatus>;\n /**\n * Local tick interval (ms): recomputes every swap against the wall clock so\n * timelock flips (a refund unlocking, a stale pending swap turning reapable)\n * surface between hints. Pure local work. `0` disables the timer (tests\n * drive recomputes via {@link applyHint}).\n */\n refreshIntervalMs?: number;\n};\n\nexport class HintTracker {\n readonly #fetchStatus: (swapId: string) => Promise<SwapStatus>;\n readonly #refreshIntervalMs: number;\n readonly #swaps = new Map<string, TrackedSwap>();\n /** Last server-reported status per swap (seeded from storage, updated by hints). */\n readonly #statuses = new Map<string, SwapStatus>();\n /** Last actions emitted per swap — for dedupe and the subscribe-time snapshot. */\n readonly #lastActions = new Map<string, SwapActions>();\n readonly #subscribers = new Set<ActionSubscriber>();\n #timer: ReturnType<typeof setInterval> | undefined;\n\n constructor(options: HintTrackerOptions) {\n this.#fetchStatus = options.fetchStatus;\n this.#refreshIntervalMs = options.refreshIntervalMs ?? 5_000;\n }\n\n /**\n * Always `true`: with no chain readers there is no reachability constraint.\n * (Claim/refund execution has its own chain configuration, independent of\n * tracking.)\n */\n canObserve(_swap: TrackedSwap): boolean {\n return true;\n }\n\n /** Register the swaps, derive each one's first action from its stored status. */\n async startTracking(swaps: TrackedSwap[]): Promise<void> {\n for (const swap of swaps) this.#admit(swap);\n this.#recomputeAll();\n if (this.#refreshIntervalMs > 0)\n this.#timer = setInterval(\n () => this.#recomputeAll(),\n this.#refreshIntervalMs,\n );\n }\n\n /**\n * Track one more swap after {@link startTracking}. Idempotent: a swap already\n * tracked, or one already seen through to a terminal action, is ignored.\n */\n async track(swap: TrackedSwap): Promise<void> {\n if (this.#swaps.has(swap.swapId) || this.#lastActions.has(swap.swapId))\n return;\n this.#admit(swap);\n this.#recompute(swap);\n }\n\n #admit(swap: TrackedSwap): void {\n this.#swaps.set(swap.swapId, swap);\n // The stored status may be stale — it only seeds the first derivation; the\n // server's subscribe-time snapshot (pushed right after the worker\n // subscribes) corrects it moments later.\n if (swap.storedStatus !== undefined && !this.#statuses.has(swap.swapId))\n this.#statuses.set(swap.swapId, swap.storedStatus);\n }\n\n /** The swap ids currently tracked — e.g. to subscribe them to the hint feed. */\n trackedSwapIds(): string[] {\n return [...this.#swaps.keys()];\n }\n\n /**\n * Apply a server status to one swap and recompute its action. A hint that\n * carries the pushed `status` is applied as-is; without one (the worker's\n * retry and post-reconnect paths) the status is re-fetched from the server.\n * A no-op for an untracked swap.\n */\n async applyHint(\n swapId: string,\n opts?: { force?: boolean; status?: SwapStatus },\n ): Promise<void> {\n const swap = this.#swaps.get(swapId);\n if (!swap) return;\n const status = opts?.status ?? (await this.#fetchStatus(swapId));\n // Re-check: the fetch is async and the swap may have gone terminal (and\n // been untracked) while it was in flight.\n if (!this.#swaps.has(swapId)) return;\n this.#statuses.set(swapId, status);\n this.#recompute(swap, opts?.force ?? false);\n }\n\n /** Notify `cb` of the current action for each tracked swap, then on every change. */\n subscribeToActions(cb: ActionSubscriber): () => void {\n this.#subscribers.add(cb);\n for (const [swapId, actions] of this.#lastActions) cb(swapId, actions);\n return () => this.#subscribers.delete(cb);\n }\n\n /** Stop tracking: drop the tick timer, tracked swaps, and subscribers. */\n stop(): void {\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = undefined;\n this.#swaps.clear();\n this.#subscribers.clear();\n }\n\n #recomputeAll(): void {\n for (const swap of this.#swaps.values()) this.#recompute(swap);\n }\n\n #recompute(swap: TrackedSwap, force = false): void {\n const status = this.#statuses.get(swap.swapId);\n // No status yet (nothing stored, no hint) — wait for the subscribe-time\n // snapshot rather than deriving from a guess.\n if (status === undefined) return;\n\n const now = Date.now();\n const actions = deriveSwapActions({\n status,\n clientChainNow: now,\n serverChainNow: now,\n clientRefundLocktime: swap.clientRefundLocktime,\n serverRefundLocktime: swap.serverRefundLocktime,\n // Pay-on-Lightning swaps have no client-funded on-chain leg.\n clientFunds: swap.clientHtlc !== undefined,\n });\n // No `observations`: nothing was observed on chain. Consumers treat the\n // field as optional.\n\n const previous = this.#lastActions.get(swap.swapId);\n if (\n !force &&\n previous &&\n JSON.stringify(previous) === JSON.stringify(actions)\n )\n return;\n\n this.#lastActions.set(swap.swapId, actions);\n for (const cb of this.#subscribers) cb(swap.swapId, actions);\n\n // Terminal: stop watching (retain lastActions so late subscribers still see it).\n if (actions.recommended === \"none\") {\n this.#swaps.delete(swap.swapId);\n this.#statuses.delete(swap.swapId);\n }\n }\n}\n","/**\n * Ties the hint feed and the action model together into auto-execution.\n *\n * On a server status hint it verifies the swap against the chain\n * (`tracker.applyHint`) — the hint only makes the derivation faster, the chain\n * stays the source of truth. Then, on every derived action, it AUTO-RUNS the\n * recommended action when it is auto-safe (a claim) and SURFACES the rest — a\n * manual `fund`, or a refund the user must confirm — via `onActionRequired`, the\n * seam a frontend notification center plugs into.\n *\n * A failed auto-claim is RETRIED with backoff. Each retry first re-verifies\n * against chain (a forced `applyHint`) and only re-runs if the swap is still\n * claimable — so we never blindly re-submit a claim (which on EVM would waste gas\n * or double-submit). After the attempt budget is spent the swap is surfaced via\n * `onActionRequired`, so it is never silently stuck.\n */\nimport type { SwapStatus } from \"@lendasat/lendaswap-sdk-pure\";\nimport type { SwapActionId, SwapActions } from \"../actions/types.js\";\n\n/** The tracker surface the worker drives. */\nexport interface WorkerTracker {\n trackedSwapIds(): string[];\n /**\n * Re-verify a swap, then recompute. `force` re-emits even if unchanged.\n * `status` is the hint's pushed status: a chain-verifying tracker ignores it\n * (the chain stays its source of truth); a hints-only tracker applies it\n * directly, saving a server round-trip.\n */\n applyHint(\n swapId: string,\n opts?: { force?: boolean; status?: SwapStatus },\n ): Promise<void>;\n subscribeToActions(\n cb: (swapId: string, actions: SwapActions) => void,\n ): () => void;\n}\n\n/** The hint feed surface the worker consumes (`WsStatusSource` satisfies it). */\nexport interface WorkerHintSource {\n start(): void;\n stop(): void;\n subscribe(swapIds: string[]): void;\n unsubscribe(swapIds: string[]): void;\n onStatus(\n cb: (update: { swapId: string; status?: SwapStatus }) => void,\n ): () => void;\n /**\n * Optional: fires when the feed comes back up after an outage, during which\n * pushed transitions were lost for good. The worker answers by re-verifying\n * every tracked swap against the chain.\n */\n onReconnect?(cb: () => void): () => void;\n}\n\n/** Backoff policy for retrying a failed auto-claim. */\nexport type SwapWorkerRetryOptions = {\n /** Attempts before giving up and surfacing the swap. Default 5. */\n maxAttempts?: number;\n /** Base backoff (ms), doubled each attempt. Default 2000. */\n baseDelayMs?: number;\n /** Backoff ceiling (ms). Default 30000. */\n maxDelayMs?: number;\n};\n\nexport type SwapWorkerOptions = {\n tracker: WorkerTracker;\n hintSource: WorkerHintSource;\n /**\n * Run the recommended auto action for a swap (today: claim). Reject to signal\n * failure — the worker re-verifies against chain and retries with backoff.\n * Resolve once submitted. OMIT for observe-only mode: hints still trigger\n * chain re-verifies (fast, cheap derivations), but nothing is ever run.\n */\n execute?: (swapId: string, actionId: SwapActionId) => Promise<void>;\n /**\n * Surface an action that needs the user rather than being auto-run — a manual\n * `fund`, a refund the user must confirm, or a claim whose retries are spent.\n * Where a UI notification hooks in.\n */\n onActionRequired?: (swapId: string, actions: SwapActions) => void;\n /** Auto-claim retry policy; sensible defaults otherwise. */\n retry?: SwapWorkerRetryOptions;\n};\n\n/** Actions the worker runs automatically. `wait`/`none` are auto but no-ops. */\nconst AUTO_EXECUTABLE: ReadonlySet<SwapActionId> = new Set<SwapActionId>([\n \"claim\",\n]);\n\n/** In-flight retry bookkeeping for one swap. */\ntype Retry = { attempts: number; timer: ReturnType<typeof setTimeout> };\n\nexport class SwapWorker {\n readonly #tracker: WorkerTracker;\n readonly #hintSource: WorkerHintSource;\n readonly #execute: SwapWorkerOptions[\"execute\"];\n readonly #onActionRequired: SwapWorkerOptions[\"onActionRequired\"];\n readonly #maxAttempts: number;\n readonly #baseDelayMs: number;\n readonly #maxDelayMs: number;\n\n /** Swaps with an auto action in flight — guards against double-runs. */\n readonly #executing = new Set<string>();\n /** Swaps with a scheduled auto-claim retry (backoff timer + attempt count). */\n readonly #retries = new Map<string, Retry>();\n #unsubs: Array<() => void> = [];\n #started = false;\n\n constructor(options: SwapWorkerOptions) {\n this.#tracker = options.tracker;\n this.#hintSource = options.hintSource;\n this.#execute = options.execute;\n this.#onActionRequired = options.onActionRequired;\n this.#maxAttempts = options.retry?.maxAttempts ?? 5;\n this.#baseDelayMs = options.retry?.baseDelayMs ?? 2_000;\n this.#maxDelayMs = options.retry?.maxDelayMs ?? 30_000;\n }\n\n /** Watch tracked swaps on the hint feed, wire hints → verify, react to actions. */\n start(): void {\n if (this.#started) return;\n this.#started = true;\n\n this.#hintSource.subscribe(this.#tracker.trackedSwapIds());\n this.#unsubs.push(\n this.#hintSource.onStatus(({ swapId, status }) => {\n void this.#tracker.applyHint(swapId, { status }).catch((error) => {\n console.warn(`SwapWorker: applyHint(${swapId}) failed:`, error);\n });\n }),\n );\n // Hints were dead while the feed was down and pushed transitions are lost\n // for good — anything may have happened, including funding an `absent`\n // client leg, the one state nothing else re-reads (it is outside the\n // tracker's at-risk cadence). The server's resubscribe snapshot usually\n // re-triggers applyHint for subscribed ids, but this sweep is the\n // client-side guarantee: it doesn't depend on the server sending one, and\n // it also covers over-cap queued ids that never get hints.\n if (this.#hintSource.onReconnect)\n this.#unsubs.push(\n this.#hintSource.onReconnect(() => this.#reverifyAllTracked()),\n );\n this.#hintSource.start();\n\n this.#unsubs.push(\n this.#tracker.subscribeToActions((swapId, actions) =>\n this.#onActions(swapId, actions),\n ),\n );\n }\n\n /** Stop reacting, cancel pending retries, and close the hint feed. */\n stop(): void {\n this.#started = false;\n for (const unsub of this.#unsubs) unsub();\n this.#unsubs = [];\n this.#hintSource.stop();\n for (const { timer } of this.#retries.values()) clearTimeout(timer);\n this.#retries.clear();\n this.#executing.clear();\n }\n\n /** Chain-reconcile every tracked swap (the post-outage catch-up sweep). */\n #reverifyAllTracked(): void {\n for (const swapId of this.#tracker.trackedSwapIds()) {\n void this.#tracker.applyHint(swapId).catch((error) => {\n console.warn(\n `SwapWorker: post-reconnect re-verify of ${swapId} failed:`,\n error,\n );\n });\n }\n }\n\n #onActions(swapId: string, actions: SwapActions): void {\n const recommended = actions.recommended;\n // Terminal first: a finished swap needs no hints, and subscribing before\n // dropping it would briefly consume a subscription slot (or queue behind the\n // server cap) on a swap that is already done.\n if (recommended === \"none\") {\n this.#hintSource.unsubscribe([swapId]);\n this.#cancelRetry(swapId);\n return;\n }\n // Ensure the swap is on the hint feed (covers swaps whose first action only\n // appears after tracking started).\n this.#hintSource.subscribe([swapId]);\n\n // Observe-only mode (no execute): fall through — an auto action is nobody's\n // to run, and it doesn't need surfacing either (subscribers see it live).\n if (recommended && AUTO_EXECUTABLE.has(recommended) && this.#execute) {\n this.#autoExecute(swapId, actions);\n return;\n }\n // No longer an auto-claim (moved to wait/fund/refund, or nothing): drop any\n // pending retry — a fresh derivation supersedes it.\n this.#cancelRetry(swapId);\n // Surface only actions that actually need the user — a `manual` fund or a\n // refund to `confirm`. `wait` is an auto no-op: nothing to notify about.\n if (!recommended) return;\n const automation = actions.actions.find(\n (a) => a.id === recommended,\n )?.automation;\n if (automation === \"manual\" || automation === \"confirm\")\n this.#onActionRequired?.(swapId, actions);\n }\n\n #autoExecute(swapId: string, actions: SwapActions): void {\n const execute = this.#execute;\n if (!execute) return; // observe-only mode\n if (this.#executing.has(swapId)) return; // one in-flight run per swap\n const actionId = actions.recommended;\n if (!actionId) return;\n this.#executing.add(swapId);\n void execute(swapId, actionId)\n .then(() => this.#cancelRetry(swapId)) // submitted — reset the attempt budget\n .catch((error) => {\n console.warn(`SwapWorker: ${actionId}(${swapId}) failed:`, error);\n this.#scheduleRetry(swapId, actions);\n })\n .finally(() => this.#executing.delete(swapId));\n }\n\n /** Schedule the next backoff retry, or give up and surface once attempts run out. */\n #scheduleRetry(swapId: string, actions: SwapActions): void {\n const attempts = (this.#retries.get(swapId)?.attempts ?? 0) + 1;\n if (attempts > this.#maxAttempts) {\n // Out of attempts: stop auto-running and hand it to the user (a UI can offer\n // a manual claim). Better than silently stuck.\n console.warn(\n `SwapWorker: auto-claim for ${swapId} gave up after ${this.#maxAttempts} attempts`,\n );\n this.#cancelRetry(swapId);\n this.#onActionRequired?.(swapId, actions);\n return;\n }\n this.#clearTimer(swapId);\n const delay = Math.min(\n this.#baseDelayMs * 2 ** (attempts - 1),\n this.#maxDelayMs,\n );\n const timer = setTimeout(() => this.#retry(swapId, actions), delay);\n this.#retries.set(swapId, { attempts, timer });\n }\n\n /**\n * A retry fires: re-verify against chain (forced re-emit), which drives\n * {@link #onActions} to re-run the claim only if it is still recommended. If the\n * re-verify itself fails we can't safely retry, so back off and try the cycle\n * again.\n */\n #retry(swapId: string, actions: SwapActions): void {\n void this.#tracker.applyHint(swapId, { force: true }).catch((error) => {\n console.warn(\n `SwapWorker: re-verify before retrying ${swapId} failed:`,\n error,\n );\n this.#scheduleRetry(swapId, actions);\n });\n }\n\n #cancelRetry(swapId: string): void {\n this.#clearTimer(swapId);\n this.#retries.delete(swapId);\n }\n\n #clearTimer(swapId: string): void {\n const existing = this.#retries.get(swapId);\n if (existing) clearTimeout(existing.timer);\n }\n}\n","/**\n * A standalone subscriber to the server's swap-status WebSocket (`GET /ws`).\n *\n * Holds a single reconnecting connection, tracks a live set of subscribed swap\n * ids, and emits `{ swapId, status }` on every status transition the server\n * pushes — including the snapshot the server sends right after a subscribe. This\n * is a *hint* feed only: it neither trusts nor acts on the status. A consumer is\n * expected to verify against the chain (the contract managers) before doing\n * anything with it.\n *\n * Protocol (see `swap/src/api/ws.rs`):\n * → {\"op\":\"subscribe\",\"channel\":\"swap_status\",\"args\":[<uuid>,...]} (≤64 ids/frame)\n * ← {\"channel\":\"swap_status\",\"data\":{\"<uuid>\":\"<status>\",...}}\n * ← {\"op\":\"subscribed\",...} (ack) ← {\"op\":...,\"message\":...} (error)\n * A single connection carries up to 256 subscriptions; that cap is enforced\n * client-side (see {@link WsStatusSource.subscribe}) because the server silently\n * drops the overflow.\n */\nimport type { SwapStatus } from \"@lendasat/lendaswap-sdk-pure\";\n\n/** A status update pushed by the server for one swap. */\nexport type SwapStatusUpdate = {\n swapId: string;\n status: SwapStatus;\n};\n\n/** The minimal WebSocket surface the source needs (the global `WebSocket` satisfies it). */\nexport interface WebSocketLike {\n send(data: string): void;\n close(): void;\n onopen: (() => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: (() => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\nexport type WsStatusSourceOptions = {\n /** Server base URL (http/https); the `ws(s)://…/ws` endpoint is derived from it. */\n serverUrl: string;\n /** WebSocket factory — defaults to the global `WebSocket` (browsers, Node ≥ 22). */\n webSocketFactory?: (url: string) => WebSocketLike;\n /** Reconnect backoff floor/ceiling (ms). Defaults: 1_000 / 30_000. */\n reconnectMinMs?: number;\n reconnectMaxMs?: number;\n};\n\n/** Server limits, mirrored from `swap/src/api/ws.rs`. */\nconst MAX_ARGS_PER_FRAME = 64;\nconst MAX_SUBSCRIPTIONS = 256;\n\nexport class WsStatusSource {\n readonly #url: string;\n readonly #factory: (url: string) => WebSocketLike;\n readonly #reconnectMinMs: number;\n readonly #reconnectMaxMs: number;\n\n /**\n * Ids actually subscribed on the wire — held at or below the server's\n * per-connection cap, and resent in full on every (re)connect.\n */\n readonly #active = new Set<string>();\n /**\n * Ids we want but have no slot for. Promoted FIFO as slots free (the worker\n * unsubscribes each swap when it reaches a terminal action, so they do free).\n * A queued swap is not broken, only slower: it gets no hints, so it advances\n * on the tracker's at-risk reconciles and the worker's reconnect sweep.\n */\n readonly #pending = new Set<string>();\n readonly #listeners = new Set<(update: SwapStatusUpdate) => void>();\n readonly #reconnectListeners = new Set<() => void>();\n\n #ws: WebSocketLike | undefined;\n #open = false;\n #stopped = false;\n /** A connect failed or the socket dropped — the next open is a RE-connect. */\n #hadDisruption = false;\n #backoffMs: number;\n #reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\n constructor(options: WsStatusSourceOptions) {\n this.#url = toWsUrl(options.serverUrl);\n this.#factory = options.webSocketFactory ?? defaultWebSocketFactory;\n this.#reconnectMinMs = options.reconnectMinMs ?? 1_000;\n this.#reconnectMaxMs = options.reconnectMaxMs ?? 30_000;\n this.#backoffMs = this.#reconnectMinMs;\n }\n\n /** Open the connection and begin (re)subscribing. Idempotent. */\n start(): void {\n if (this.#ws || this.#reconnectTimer) return;\n this.#stopped = false;\n this.#connect();\n }\n\n /** Close the connection and stop reconnecting. */\n stop(): void {\n this.#stopped = true;\n if (this.#reconnectTimer) clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = undefined;\n this.#open = false;\n this.#ws?.close();\n this.#ws = undefined;\n }\n\n /** Emit for each pushed status update. Returns an unsubscribe fn. */\n onStatus(cb: (update: SwapStatusUpdate) => void): () => void {\n this.#listeners.add(cb);\n return () => this.#listeners.delete(cb);\n }\n\n /**\n * Emit whenever the socket comes (back) up after a disruption — a drop, or a\n * first connect that had to be retried. NOT emitted on a pristine first open:\n * hints were never live before it, so there is no gap to recover from. While\n * the socket was down, pushed transitions were lost for good (the server only\n * snapshots on subscribe), so a consumer should re-verify its world against\n * the chain on this signal.\n */\n onReconnect(cb: () => void): () => void {\n this.#reconnectListeners.add(cb);\n return () => this.#reconnectListeners.delete(cb);\n }\n\n /**\n * Track these swap ids — subscribe on the socket now if open, else on next open.\n *\n * Enforces the server's per-connection cap locally. The server silently stops\n * adding once it is full (it breaks out of the loop but still acks every id),\n * so sending more would leave us believing ids are subscribed that will never\n * receive an update. Overflow is queued instead and promoted as slots free.\n */\n subscribe(swapIds: string[]): void {\n const fresh = swapIds.filter(\n (id) => !this.#active.has(id) && !this.#pending.has(id),\n );\n if (fresh.length === 0) return;\n\n const admitted: string[] = [];\n for (const id of fresh) {\n if (this.#active.size < MAX_SUBSCRIPTIONS) {\n this.#active.add(id);\n admitted.push(id);\n } else {\n this.#pending.add(id);\n }\n }\n if (this.#pending.size > 0)\n console.warn(\n `WsStatusSource: at the server cap of ${MAX_SUBSCRIPTIONS} subscriptions; ${this.#pending.size} swap(s) queued for a slot and will advance on the chain poll until then`,\n );\n if (this.#open && admitted.length > 0)\n this.#sendFrame(\"subscribe\", admitted);\n }\n\n /** Stop tracking these swap ids, promoting queued ones into the freed slots. */\n unsubscribe(swapIds: string[]): void {\n const dropped: string[] = [];\n for (const id of swapIds) {\n if (this.#active.delete(id)) dropped.push(id);\n else this.#pending.delete(id);\n }\n if (this.#open && dropped.length > 0)\n this.#sendFrame(\"unsubscribe\", dropped);\n this.#promotePending();\n }\n\n /** Move queued ids into any free slots, subscribing them if the socket is open. */\n #promotePending(): void {\n if (this.#pending.size === 0) return;\n const promoted: string[] = [];\n for (const id of this.#pending) {\n if (this.#active.size >= MAX_SUBSCRIPTIONS) break;\n this.#pending.delete(id);\n this.#active.add(id);\n promoted.push(id);\n }\n if (this.#open && promoted.length > 0)\n this.#sendFrame(\"subscribe\", promoted);\n }\n\n #connect(): void {\n this.#reconnectTimer = undefined;\n const ws = this.#factory(this.#url);\n this.#ws = ws;\n ws.onopen = () => {\n this.#backoffMs = this.#reconnectMinMs; // reset backoff on a good connection\n // Promote while still marked closed, so the queued ids ride along in the\n // single full resend below instead of being sent twice.\n this.#promotePending();\n this.#open = true;\n if (this.#active.size > 0)\n this.#sendFrame(\"subscribe\", [...this.#active]);\n if (this.#hadDisruption) {\n this.#hadDisruption = false;\n for (const cb of this.#reconnectListeners) cb();\n }\n };\n ws.onmessage = (event) => this.#handleMessage(event.data);\n ws.onclose = () => this.#onDisconnect();\n ws.onerror = () => this.#ws?.close(); // a `close` follows and drives reconnect\n }\n\n #onDisconnect(): void {\n this.#open = false;\n this.#ws = undefined;\n this.#hadDisruption = true;\n if (this.#stopped || this.#reconnectTimer) return;\n const delay = this.#backoffMs;\n this.#backoffMs = Math.min(this.#backoffMs * 2, this.#reconnectMaxMs);\n this.#reconnectTimer = setTimeout(() => this.#connect(), delay);\n }\n\n #handleMessage(data: unknown): void {\n if (typeof data !== \"string\") return;\n let frame: unknown;\n try {\n frame = JSON.parse(data);\n } catch {\n return;\n }\n if (!frame || typeof frame !== \"object\") return;\n const f = frame as Record<string, unknown>;\n\n // Data frame: { channel, data: { <swapId>: <status> } }\n if (f.data && typeof f.data === \"object\") {\n for (const [swapId, status] of Object.entries(\n f.data as Record<string, SwapStatus>,\n ))\n this.#emit({ swapId, status });\n return;\n }\n // Error frame: { op, message }. Ack frames (no data, no message) are ignored.\n if (typeof f.message === \"string\")\n console.warn(`WsStatusSource: server error: ${f.message}`);\n }\n\n #emit(update: SwapStatusUpdate): void {\n for (const cb of this.#listeners) cb(update);\n }\n\n #sendFrame(op: \"subscribe\" | \"unsubscribe\", ids: string[]): void {\n for (let i = 0; i < ids.length; i += MAX_ARGS_PER_FRAME) {\n const args = ids.slice(i, i + MAX_ARGS_PER_FRAME);\n this.#ws?.send(JSON.stringify({ op, channel: \"swap_status\", args }));\n }\n }\n}\n\n/** Derive the `ws(s)://host[/path]/ws` endpoint from an http(s) server base URL. */\nfunction toWsUrl(serverUrl: string): string {\n const url = new URL(serverUrl);\n const scheme = url.protocol === \"https:\" ? \"wss\" : \"ws\";\n const path = url.pathname.replace(/\\/+$/, \"\");\n return `${scheme}://${url.host}${path}/ws`;\n}\n\nfunction defaultWebSocketFactory(url: string): WebSocketLike {\n const WS = (globalThis as { WebSocket?: new (url: string) => WebSocketLike })\n .WebSocket;\n if (!WS)\n throw new Error(\n \"no global WebSocket available — pass options.webSocketFactory (Node < 22)\",\n );\n return new WS(url);\n}\n","/**\n * Build the Arkade {@link HtlcRef} for a swap's VHTLC leg from the plain swap\n * fields, deriving the pkScript and serialized contract params the\n * `ArkadeContractManager` needs to register it for watching.\n *\n * This is the one place that reconstructs the VHTLC from a swap, mirroring the\n * SDK VHTLC construction, so the mapper and manager stay free of VHTLC internals.\n */\n\nimport { VHTLCContractHandler } from \"@arkade-os/sdk\";\nimport {\n ARKADE_HTLC_SCRIPT_VERSION_STRICT,\n StrictVhtlcScript,\n} from \"@lendasat/lendaswap-sdk-pure\";\nimport { ripemd160 } from \"@noble/hashes/legacy.js\";\nimport { hex } from \"@scure/base\";\nimport type { HtlcRef } from \"./types.js\";\n\ntype ArkadeRef = Extract<HtlcRef, { ledger: \"arkade\" }>;\n\nexport type ArkadeVhtlcInput = {\n /** Pubkey hex (x-only or compressed) for each VHTLC role. */\n senderPk: string;\n receiverPk: string;\n serverPk: string;\n /**\n * The swap's `hash_lock`, hex, `0x`-prefixed or not. Either `sha256(preimage)`\n * (32 bytes — most directions) or the `ripemd160(sha256(preimage))` HASH160 the\n * VHTLC commits to directly (20 bytes — `btc_to_arkade`). Length disambiguates.\n */\n hashLock: string;\n /** The VHTLC address the server reported. */\n address: string;\n /** Absolute CLTV refund locktime (unix seconds). */\n refundLocktime: number;\n /** Relative unilateral delays (seconds). */\n unilateralClaimDelay: number;\n unilateralRefundDelay: number;\n unilateralRefundWithoutReceiverDelay: number;\n /** Expected funding amount in sats (a short funding is `invalid`, not confirmed). */\n expectedSats: number;\n /** Arkade HTLC script version. Missing means strict v1. */\n arkadeHtlcScriptVersion?: number;\n};\n\nfunction strip0x(value: string): string {\n return value.startsWith(\"0x\") ? value.slice(2) : value;\n}\n\n/** Normalize a pubkey hex to the 32-byte x-only form the VHTLC expects. */\nfunction xOnly(pubKeyHex: string): Uint8Array {\n const bytes = hex.decode(strip0x(pubKeyHex));\n if (bytes.length === 33) return bytes.slice(1);\n if (bytes.length === 32) return bytes;\n throw new Error(`invalid public key length: ${bytes.length}`);\n}\n\nconst seconds = (value: number) =>\n ({ type: \"seconds\", value: BigInt(value) }) as const;\n\n/**\n * The VHTLC always commits to `ripemd160(sha256(preimage))` (HASH160). When\n * `hashLock` is `sha256(preimage)` (32 bytes) we hash it once more to get that;\n * when it is already the HASH160 (20 bytes, `btc_to_arkade`) we use it as-is. The\n * ref keeps `preimageHash` as the raw `hashLock` — whatever length — and the spend\n * classifier verifies a revealed preimage against it, auto-detecting the algorithm\n * from that length.\n */\nexport function buildArkadeVhtlcRef(input: ArkadeVhtlcInput): ArkadeRef {\n const hashLockBytes = hex.decode(strip0x(input.hashLock));\n const params = {\n sender: xOnly(input.senderPk),\n receiver: xOnly(input.receiverPk),\n server: xOnly(input.serverPk),\n preimageHash:\n hashLockBytes.length === 20 ? hashLockBytes : ripemd160(hashLockBytes),\n refundLocktime: BigInt(input.refundLocktime),\n unilateralClaimDelay: seconds(input.unilateralClaimDelay),\n unilateralRefundDelay: seconds(input.unilateralRefundDelay),\n unilateralRefundWithoutReceiverDelay: seconds(\n input.unilateralRefundWithoutReceiverDelay,\n ),\n };\n if (\n input.arkadeHtlcScriptVersion !== undefined &&\n input.arkadeHtlcScriptVersion !== ARKADE_HTLC_SCRIPT_VERSION_STRICT\n ) {\n throw new Error(\n `Unsupported Arkade HTLC script version: ${input.arkadeHtlcScriptVersion}`,\n );\n }\n const vhtlc = new StrictVhtlcScript(params);\n return {\n ledger: \"arkade\",\n script: hex.encode(vhtlc.pkScript),\n address: input.address,\n preimageHash: strip0x(input.hashLock),\n expectedSats: input.expectedSats,\n params: VHTLCContractHandler.serializeParams(params),\n };\n}\n","/**\n * Map a stored swap to the {@link TrackedSwap} the {@link SwapTracker} watches.\n *\n * The recovery bundle (`StoredSwap.response`) is a discriminated union over swap\n * directions; each carries the two HTLC legs and their refund locktimes. This\n * extracts the client-funded and server-funded HTLCs plus their locktimes so the\n * pure pipeline can derive the next action.\n *\n * Directions whose both legs are observable today (Arkade↔EVM, Bitcoin↔EVM) are\n * mapped; others return `undefined` until their ledger managers exist, rather than\n * producing a half-watchable swap.\n */\nimport type { StoredSwap } from \"@lendasat/lendaswap-sdk-pure\";\nimport { buildArkadeVhtlcRef } from \"../contracts/arkade-vhtlc.js\";\nimport type { HtlcRef } from \"../contracts/types.js\";\nimport type { TrackedSwap } from \"./swap-tracker.js\";\n\n/** Locktimes are unix seconds on the wire; the resolver works in ms. */\nconst ms = (seconds: number): number => seconds * 1000;\n\n/**\n * The BTC-pegged token the coordinator locks in HTLCs per MAINNET chain\n * (mirrors `config.mainnet.yaml` `tokens.wbtc`): WBTC on Polygon, tBTC on\n * Ethereum/Arbitrum. TEMPORARY fallback for the responses that don't expose\n * the locked token (`evm_to_arkade`, `evm_to_lightning`) so their isActive\n * tuple is complete; delete once the server returns the token there.\n * Gated to mainnet: a dev deployment's mock token lives at another address,\n * and guessing wrong would flag valid fundings as `invalid`.\n */\nconst MAINNET_LOCKED_TOKEN: Record<number, `0x${string}`> = {\n 1: \"0x18084fba666a33d37592fa2633fd49a74dd93a88\", // tBTC\n 42161: \"0x6c84a8f1c29108F47a79964b5Fe888D4f4D0dE40\", // tBTC\n 137: \"0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6\", // WBTC\n};\n\nfunction lockedTokenFallback(\n chainId: number,\n network: string | undefined,\n): `0x${string}` | undefined {\n const net = network?.toLowerCase();\n return net === \"bitcoin\" || net === \"mainnet\"\n ? MAINNET_LOCKED_TOKEN[chainId]\n : undefined;\n}\n\nconst ensure0x = (value: string): `0x${string}` =>\n (value.startsWith(\"0x\") ? value : `0x${value}`) as `0x${string}`;\nconst strip0x = (value: string): string =>\n value.startsWith(\"0x\") ? value.slice(2) : value;\n\n/**\n * The Arkade VHTLC leg. The three pubkeys the response reports are the exact keys\n * the server used to build the on-chain VHTLC, so feeding them back into\n * `VHTLC.Script` reproduces the funded pkScript regardless of which side funds or\n * claims — the manager watches by that script. `address` names the field the\n * response carries it in (`btc_vhtlc_address` for Arkade↔EVM, `arkade_vhtlc_address`\n * for Arkade↔Lightning).\n */\ntype ArkadeVhtlcFields = {\n sender_pk: string;\n receiver_pk: string;\n arkade_server_pk: string;\n hash_lock: string;\n vhtlc_refund_locktime: number;\n unilateral_claim_delay: number;\n unilateral_refund_delay: number;\n unilateral_refund_without_receiver_delay: number;\n arkade_htlc_script_version?: number;\n};\n\nfunction arkadeLeg(\n r: ArkadeVhtlcFields,\n address: string,\n expectedSats: number,\n): HtlcRef {\n return buildArkadeVhtlcRef({\n senderPk: r.sender_pk,\n receiverPk: r.receiver_pk,\n serverPk: r.arkade_server_pk,\n hashLock: r.hash_lock,\n address,\n refundLocktime: r.vhtlc_refund_locktime,\n unilateralClaimDelay: r.unilateral_claim_delay,\n unilateralRefundDelay: r.unilateral_refund_delay,\n unilateralRefundWithoutReceiverDelay:\n r.unilateral_refund_without_receiver_delay,\n expectedSats,\n arkadeHtlcScriptVersion: r.arkade_htlc_script_version,\n });\n}\n\n/**\n * The EVM HTLC leg. `claimAddress` is who can claim (the client for a\n * server-funded leg, the server for the client's own); `expectedSats`/`token`\n * are what the HTLC must lock (the leg is `invalid` otherwise).\n */\nfunction evmLeg(args: {\n chainId: number;\n htlc: string;\n hashLock: string;\n claimAddress: string;\n expectedSats: string;\n token?: string;\n /** The funder / refund address — completes the isActive tuple when known. */\n sender?: string;\n /** Refund timelock in unix seconds, exactly as funded on-chain. */\n timelockSec?: number;\n /** Swap creation time (ISO) — lower-bounds log scans. */\n createdAt?: string;\n}): HtlcRef {\n const createdAtMs = args.createdAt ? Date.parse(args.createdAt) : Number.NaN;\n return {\n ledger: \"evm\",\n chainId: args.chainId,\n htlc: ensure0x(args.htlc),\n preimageHash: ensure0x(args.hashLock),\n claimAddress: ensure0x(args.claimAddress),\n expectedAmount: BigInt(args.expectedSats),\n expectedToken: args.token ? ensure0x(args.token) : undefined,\n sender: args.sender ? ensure0x(args.sender) : undefined,\n timelockSec: args.timelockSec,\n createdAtMs: Number.isNaN(createdAtMs) ? undefined : createdAtMs,\n };\n}\n\n/**\n * The Bitcoin HTLC leg. `hashLock` is the SHA-256 preimage hash (`evm_hash_lock`);\n * the on-chain script commits `ripemd160` of it, but the classifier verifies a\n * revealed preimage as `sha256(preimage) === hashLock`.\n */\n/**\n * Confirmations a CLIENT-funded Bitcoin HTLC needs before it reads as funded.\n *\n * The server does not act on a client funding until it has a blocktime, so\n * observing it sooner would report the swap as funded while the other side is\n * still waiting for a block. A server-funded leg keeps the reader's 0-conf\n * default, which is what lets a claim go out without waiting ~10 minutes.\n */\nconst CLIENT_FUNDING_MIN_CONFIRMATIONS = 1;\n\nfunction bitcoinLeg(\n address: string,\n hashLock: string,\n expectedSats: number,\n minConfirmations?: number,\n): HtlcRef {\n return {\n ledger: \"bitcoin\",\n address,\n preimageHash: strip0x(hashLock),\n expectedSats,\n minConfirmations,\n };\n}\n\nexport function swapToTracked(stored: StoredSwap): TrackedSwap | undefined {\n const r = stored.response;\n switch (r.direction) {\n // Client sends Arkade (funds the VHTLC with source_amount sats) and claims EVM.\n case \"arkade_to_evm\":\n return {\n swapId: r.id,\n clientHtlc: arkadeLeg(r, r.btc_vhtlc_address, Number(r.source_amount)),\n serverHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.hash_lock,\n claimAddress: r.client_evm_address, // the client claims the server's EVM HTLC\n expectedSats: r.evm_expected_sats,\n token: r.wbtc_address,\n sender: r.server_evm_address, // the server funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n clientRefundLocktime: ms(r.vhtlc_refund_locktime),\n serverRefundLocktime: ms(r.evm_refund_locktime),\n };\n // Client sends EVM and claims the Arkade VHTLC (server funds it with target_amount sats).\n case \"evm_to_arkade\":\n return {\n swapId: r.id,\n clientHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.hash_lock,\n claimAddress: r.server_evm_address, // the server claims the client's EVM HTLC\n expectedSats: r.evm_expected_sats,\n // evm_to_arkade doesn't expose the locked token — fall back to the\n // per-chain mainnet constant so the isActive tuple is complete.\n token: lockedTokenFallback(r.evm_chain_id, r.network),\n sender: r.client_evm_address, // the client funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n serverHtlc: arkadeLeg(r, r.btc_vhtlc_address, Number(r.target_amount)),\n clientRefundLocktime: ms(r.evm_refund_locktime),\n serverRefundLocktime: ms(r.vhtlc_refund_locktime),\n };\n // Client sends BTC (funds the on-chain HTLC with source_amount sats) and claims EVM.\n case \"bitcoin_to_evm\":\n return {\n swapId: r.id,\n clientHtlc: bitcoinLeg(\n r.btc_htlc_address,\n r.evm_hash_lock,\n Number(r.source_amount),\n CLIENT_FUNDING_MIN_CONFIRMATIONS,\n ),\n serverHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.evm_hash_lock,\n claimAddress: r.client_evm_address, // the client claims the server's EVM HTLC\n expectedSats: r.evm_expected_sats,\n token: r.wbtc_address,\n sender: r.server_evm_address, // the server funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n clientRefundLocktime: ms(r.btc_refund_locktime),\n serverRefundLocktime: ms(r.evm_refund_locktime),\n };\n // Client sends EVM and claims the BTC HTLC (server funds it with target_amount sats).\n case \"evm_to_bitcoin\":\n return {\n swapId: r.id,\n clientHtlc: evmLeg({\n chainId: r.evm_chain_id,\n htlc: r.evm_htlc_address,\n hashLock: r.evm_hash_lock,\n claimAddress: r.server_evm_address, // the server claims the client's EVM HTLC\n expectedSats: r.evm_expected_sats,\n token: r.wbtc_address,\n sender: r.client_evm_address, // the client funded it\n timelockSec: r.evm_refund_locktime,\n createdAt: r.created_at,\n }),\n serverHtlc: bitcoinLeg(\n r.btc_htlc_address,\n r.evm_hash_lock,\n Number(r.target_amount),\n ),\n clientRefundLocktime: ms(r.evm_refund_locktime),\n serverRefundLocktime: ms(r.btc_refund_locktime),\n };\n\n // Client sends BTC (funds the on-chain HTLC) and claims the Arkade VHTLC (the\n // server funds it). Unlike the other Arkade directions, the VHTLC's receiver is\n // the client's own key (not in the response) and its funder is `server_vhtlc_pk`;\n // the hash lock is a 20-byte HASH160, which buildArkadeVhtlcRef takes as-is.\n case \"btc_to_arkade\":\n return {\n swapId: r.id,\n clientHtlc: bitcoinLeg(\n r.btc_htlc_address,\n r.hash_lock,\n Number(r.source_amount),\n CLIENT_FUNDING_MIN_CONFIRMATIONS,\n ),\n serverHtlc: buildArkadeVhtlcRef({\n senderPk: r.server_vhtlc_pk, // the server funds the VHTLC\n receiverPk: stored.publicKey, // the client claims it\n serverPk: r.arkade_server_pk,\n hashLock: r.hash_lock,\n address: r.arkade_vhtlc_address,\n refundLocktime: r.vhtlc_refund_locktime,\n unilateralClaimDelay: r.unilateral_claim_delay,\n unilateralRefundDelay: r.unilateral_refund_delay,\n unilateralRefundWithoutReceiverDelay:\n r.unilateral_refund_without_receiver_delay,\n expectedSats: Number(r.target_amount),\n arkadeHtlcScriptVersion: r.arkade_htlc_script_version,\n }),\n clientRefundLocktime: ms(r.btc_refund_locktime),\n serverRefundLocktime: ms(r.vhtlc_refund_locktime),\n };\n\n // ─── Lightning: one on-chain leg, one off-chain LN payment ───────────────\n\n // Pay-on-Lightning: the client pays a Lightning invoice (off-chain, nothing to\n // watch) and claims an on-chain HTLC. No client-funded leg — the client's\n // claim completes the swap; a hold invoice that never settles auto-unwinds.\n\n // Client pays the invoice, then claims the Arkade VHTLC (target_amount).\n case \"lightning_to_arkade\":\n return {\n swapId: r.id,\n serverHtlc: arkadeLeg(\n r,\n r.arkade_vhtlc_address,\n Number(r.target_amount),\n ),\n clientRefundLocktime: 0, // no on-chain client leg\n serverRefundLocktime: ms(r.vhtlc_refund_locktime),\n };\n\n // Client funds the Arkade VHTLC (source_amount); the server pays the\n // Lightning invoice off-chain and claims the VHTLC — no server leg to\n // watch.\n case \"arkade_to_lightning\":\n return {\n swapId: r.id,\n clientHtlc: arkadeLeg(\n r,\n r.arkade_vhtlc_address,\n Number(r.source_amount),\n ),\n clientRefundLocktime: ms(r.vhtlc_refund_locktime),\n serverRefundLocktime: 0, // no on-chain server leg\n };\n default:\n return undefined;\n }\n}\n","/**\n * The reactive orchestration.\n *\n * A {@link SwapTracker} watches each tracked swap's on-chain HTLC leg(s) via\n * per-ledger {@link ContractManager}s (two legs for chain↔chain swaps, one for\n * Lightning). On any change it recomputes the next action\n * (`deriveSwapStatus` → `deriveSwapActions`), and — deduped — notifies\n * subscribers, dropping a swap once it reaches a terminal state.\n *\n * This is the observe-mode layer: it *notifies*. Auto-execution (recover/auto\n * mode running the action) is a policy layer built on top of these same\n * notifications, gated by each action's `automation`.\n */\nimport type { SwapStatus } from \"@lendasat/lendaswap-sdk-pure\";\nimport { deriveSwapActions } from \"../actions/derive.js\";\nimport { deriveSwapStatus } from \"../actions/status.js\";\nimport type { HtlcObservation, SwapActions } from \"../actions/types.js\";\nimport type { ContractManager, HtlcRef, Ledger } from \"../contracts/types.js\";\n\n/**\n * A swap the tracker watches: its HTLC legs + refund locktimes (from the recovery\n * bundle). Chain↔chain swaps have both legs; Lightning swaps have exactly one\n * on-chain leg (the other side is an off-chain Lightning payment) — see\n * {@link SwapObservations}.\n */\nexport type TrackedSwap = {\n swapId: string;\n clientHtlc?: HtlcRef;\n serverHtlc?: HtlcRef;\n clientRefundLocktime: number;\n serverRefundLocktime: number;\n /**\n * The swap's last STORED server status, seeding hints-only tracking\n * (`HintTracker`) with a first derivation before any hint arrives. Ignored by\n * {@link SwapTracker}, which derives from chain observations only.\n */\n storedStatus?: SwapStatus;\n};\n\nexport type ActionSubscriber = (swapId: string, actions: SwapActions) => void;\n\nexport type SwapTrackerOptions = {\n /**\n * The local tick interval (ms). Each tick recomputes every tracked swap from\n * extrapolated chain clocks — pure local work, NO chain reads — so timelock\n * flips (a refund unlocking, a stale pending swap turning reapable) surface\n * between hints. `0` disables the timer (tests drive `refresh` manually).\n */\n refreshIntervalMs?: number;\n /**\n * Minimum ms between chain reconciles of AT-RISK swaps — those whose client\n * leg holds funds on-chain (≥ clientfunded, unresolved), where refund safety\n * must not depend on the server. Everything else advances on server hints\n * only ({@link applyHint}); a swap with nothing at stake costs zero chain\n * reads. Default 60s.\n */\n atRiskReconcileIntervalMs?: number;\n};\n\nexport class SwapTracker {\n readonly #managers: Map<Ledger, ContractManager>;\n readonly #swaps = new Map<string, TrackedSwap>();\n /** Last actions emitted per swap — for dedupe and the subscribe-time snapshot. */\n readonly #lastActions = new Map<string, SwapActions>();\n readonly #subscribers = new Set<ActionSubscriber>();\n readonly #refreshIntervalMs: number;\n readonly #atRiskReconcileIntervalMs: number;\n #lastAtRiskReconcileAt = 0;\n #eventUnsubs: Array<() => void> = [];\n #timer: ReturnType<typeof setInterval> | undefined;\n\n constructor(\n managers: Map<Ledger, ContractManager>,\n options?: SwapTrackerOptions,\n ) {\n this.#managers = managers;\n this.#refreshIntervalMs = options?.refreshIntervalMs ?? 0;\n this.#atRiskReconcileIntervalMs =\n options?.atRiskReconcileIntervalMs ?? 60_000;\n }\n\n /**\n * Whether every on-chain leg of `swap` is observable — i.e. the tracker can\n * derive its status from chain. `false` means a leg is on a ledger/chain this\n * client can't reach (no manager, or an EVM chain with no RPC), so the swap\n * can't be tracked. Pure predicate — callers decide the policy (skip vs reject).\n */\n canObserve(swap: TrackedSwap): boolean {\n return legsOf(swap).every(\n (leg) => this.#managers.get(leg.ledger)?.canObserve(leg) ?? false,\n );\n }\n\n /** Register each swap's on-chain leg(s), subscribe to manager events, and seed state. */\n async startTracking(swaps: TrackedSwap[]): Promise<void> {\n for (const swap of swaps) {\n // Skip a swap with an unreachable leg rather than letting register() throw\n // and abort tracking for every other swap too. It simply goes untracked.\n if (!this.canObserve(swap)) {\n console.warn(\n `SwapTracker: skipping swap ${swap.swapId} — a leg is on a chain this client can't reach (configure it to track this swap)`,\n );\n continue;\n }\n this.#swaps.set(swap.swapId, swap);\n for (const leg of legsOf(swap)) await this.#managerFor(leg).register(leg);\n }\n for (const manager of new Set(this.#managers.values())) {\n this.#eventUnsubs.push(manager.onEvent(() => this.#recomputeAll()));\n }\n // Prime each manager: seeds its clock and does a full reconcile, so the first\n // recompute has both observations AND clocks. Without this a ledger whose\n // clock is only populated on refresh (Arkade's MTP) stays `undefined` and the\n // recompute bails forever — nothing is ever emitted.\n await this.#refreshManagers();\n this.#recomputeAll();\n // Tick onward: local recomputes off extrapolated clocks, plus the gated\n // at-risk chain reconcile. The prime above counts as the first reconcile.\n this.#lastAtRiskReconcileAt = Date.now();\n if (this.#refreshIntervalMs > 0)\n this.#timer = setInterval(\n () => void this.#tick(),\n this.#refreshIntervalMs,\n );\n }\n\n /**\n * Track one more swap after {@link startTracking} — register its on-chain\n * leg(s), seed the managers they touch, and derive its first action. Idempotent:\n * a swap already tracked, or one already seen through to a terminal action, is\n * ignored. New swaps are picked up by the existing per-manager event listeners\n * and poll timer, so nothing else needs re-wiring. Lets a swap created after\n * start (e.g. via `createSwap`) be tracked without a restart.\n */\n async track(swap: TrackedSwap): Promise<void> {\n // Already active, or already completed (lastActions is retained past\n // terminal) — either way, don't re-register.\n if (this.#swaps.has(swap.swapId) || this.#lastActions.has(swap.swapId))\n return;\n // Unreachable leg: skip rather than throw. (The create path rejects such a\n // swap up front; a pre-existing one reaching here just goes untracked.)\n if (!this.canObserve(swap)) {\n console.warn(\n `SwapTracker: skipping swap ${swap.swapId} — a leg is on a chain this client can't reach (configure it to track this swap)`,\n );\n return;\n }\n this.#swaps.set(swap.swapId, swap);\n\n const registered: HtlcRef[] = [];\n try {\n for (const leg of legsOf(swap)) {\n await this.#managerFor(leg).register(leg);\n registered.push(leg);\n }\n } catch (error) {\n // Roll back a partial registration. Without this the swap stays latched in\n // `#swaps` — so the guard above rejects every later retry — with one leg\n // watched and the other not, deriving nothing until a full stop/start.\n // A register failure is not self-healing (the poll can't set up a watch it\n // never made), so undo it and let a later sync try again cleanly.\n this.#swaps.delete(swap.swapId);\n for (const leg of registered) void this.#managerFor(leg).unregister(leg);\n throw error;\n }\n\n // Seed/advance just the managers this swap touches (clock + observations),\n // then derive its first action — mirrors startTracking's prime step. Unlike\n // registration, a failure here IS self-healing: the swap is fully registered\n // and unseeded legs count as at-risk, so the tick reconciles them. Leave it\n // tracked.\n const managers = new Set(legsOf(swap).map((leg) => this.#managerFor(leg)));\n await Promise.all([...managers].map((manager) => manager.refresh()));\n this.#recompute(swap);\n }\n\n /**\n * The periodic tick: recompute every swap from extrapolated clocks (pure\n * local work), and — at most every `atRiskReconcileIntervalMs` — re-read the\n * chain for at-risk swaps. This is the trust boundary: the happy path rides\n * server hints, but once the client's funds sit in an HTLC, refund\n * availability (and the counterparty leg's state) is verified against the\n * chain on a cadence the server can't influence.\n */\n async #tick(): Promise<void> {\n const now = Date.now();\n if (now - this.#lastAtRiskReconcileAt >= this.#atRiskReconcileIntervalMs) {\n this.#lastAtRiskReconcileAt = now;\n const legs = [...this.#swaps.values()]\n .filter((swap) => this.#isAtRisk(swap))\n .flatMap((swap) => legsOf(swap));\n await Promise.all(\n legs.map((leg) =>\n this.#managerFor(leg)\n .reconcile(leg)\n .catch((error) => {\n console.warn(\"SwapTracker: at-risk reconcile failed:\", error);\n }),\n ),\n );\n }\n this.#recomputeAll();\n }\n\n /**\n * Whether a swap needs chain-verified monitoring independent of the server:\n * its client leg holds (or held) funds on-chain, so knowing when a refund\n * unlocks — or that the counterparty resolved a leg — must not depend on a\n * hint arriving. An unfunded client leg (`absent`) is server-hint territory;\n * an unseeded one (`undefined`, e.g. the prime failed on a flaky RPC) counts\n * as at-risk so the tick heals it. A swap with no on-chain client leg\n * (paying via Lightning) is at-risk while tracked: once the invoice is paid\n * there is no on-chain trace of the client's exposure, and the server leg is\n * the only thing left to watch.\n */\n #isAtRisk(swap: TrackedSwap): boolean {\n if (!swap.clientHtlc) return true;\n const obs = this.#managerFor(swap.clientHtlc).getState(swap.clientHtlc);\n return obs !== \"absent\";\n }\n\n /**\n * Refresh the managers that have a registered leg — seeding their clocks and\n * reconciling observations (the on-demand full read backing startTracking's\n * prime and track()'s seed). Managers with nothing tracked are skipped:\n * refreshing them still hits their clock source (e.g. Arkade/Bitcoin MTP via\n * the API), so a swap that doesn't touch a ledger can't fail on that\n * ledger's endpoint being down (the default client builds every manager).\n */\n async #refreshManagers(): Promise<void> {\n const active = new Set<Ledger>();\n for (const swap of this.#swaps.values())\n for (const leg of legsOf(swap)) active.add(leg.ledger);\n // Several ledgers can share a manager, so dedupe on the manager while\n // keeping a ledger name to report a failure against.\n const byManager = new Map<ContractManager, Ledger>();\n for (const ledger of active) {\n const manager = this.#managers.get(ledger);\n if (manager && !byManager.has(manager)) byManager.set(manager, ledger);\n }\n\n // Settled, not all: one ledger's endpoint failing must not deny every other\n // swap its observations. A manager that throws simply contributes none this\n // round — a missing observation already reads as \"not seen yet\" — and the\n // next refresh retries it. Rejecting instead would propagate out of\n // `startTracking`, whose prime awaits this, leaving every tracked swap on\n // every ledger with no derived actions at all.\n const ledgers = [...byManager.values()];\n const results = await Promise.allSettled(\n [...byManager.keys()].map((manager) => manager.refresh()),\n );\n results.forEach((result, index) => {\n if (result.status === \"rejected\")\n console.warn(\n `SwapTracker: refreshing ${ledgers[index]} failed:`,\n result.reason,\n );\n });\n }\n\n /** The swap ids currently tracked — e.g. to subscribe them to a hint feed. */\n trackedSwapIds(): string[] {\n return [...this.#swaps.keys()];\n }\n\n /**\n * Re-verify one swap against the chain right now — the hook a faster server\n * status hint drives. Reconciles just that swap's legs (targeted, on the\n * managers), then recomputes its action. The hint is only a *trigger*: the\n * chain stays the source of truth, so a premature hint that the chain doesn't\n * yet reflect changes nothing. A no-op for an untracked swap.\n */\n async applyHint(swapId: string, opts?: { force?: boolean }): Promise<void> {\n const swap = this.#swaps.get(swapId);\n if (!swap) return;\n // Settled, not all: a hint is the fast path, and rejecting here would throw\n // it away for BOTH legs because one was unreadable — dropping the swap onto\n // the at-risk poller, which re-reads on a cadence measured in minutes. A leg\n // that fails keeps its previous observation and is recomputed from whatever\n // did land; the poller remains the backstop that heals it.\n const legs = legsOf(swap);\n const results = await Promise.allSettled(\n legs.map((leg) => this.#managerFor(leg).reconcile(leg)),\n );\n results.forEach((result, index) => {\n if (result.status === \"rejected\")\n console.warn(\n `SwapTracker: hint reconcile failed for ${legs[index].ledger}:`,\n result.reason,\n );\n });\n // `force` re-notifies even when the derived action is unchanged — the hook a\n // failed auto-execution uses to re-verify against chain and get retried, since\n // an unchanged action would otherwise be deduped and never re-trigger.\n this.#recompute(swap, opts?.force ?? false);\n }\n\n /** Notify `cb` of the current action for each tracked swap, then on every change. */\n subscribeToActions(cb: ActionSubscriber): () => void {\n this.#subscribers.add(cb);\n for (const [swapId, actions] of this.#lastActions) cb(swapId, actions);\n return () => this.#subscribers.delete(cb);\n }\n\n /**\n * Stop tracking: unregister every still-tracked leg (releasing its manager's\n * watch — e.g. the EVM per-chain block watch that only `unregister` tears down),\n * drop the poll timer, and detach event listeners + subscribers. Mirrors\n * `startTracking`'s `register`, so no manager keeps watching a stopped tracker's\n * swaps. The managers themselves are not owned here, so they are unregistered but\n * never disposed.\n */\n stop(): void {\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = undefined;\n for (const unsub of this.#eventUnsubs) unsub();\n this.#eventUnsubs = [];\n for (const swap of this.#swaps.values())\n for (const leg of legsOf(swap))\n void this.#managerFor(leg).unregister(leg);\n this.#swaps.clear();\n this.#subscribers.clear();\n }\n\n #managerFor(ref: HtlcRef): ContractManager {\n const manager = this.#managers.get(ref.ledger);\n if (!manager)\n throw new Error(`no ContractManager for ledger '${ref.ledger}'`);\n return manager;\n }\n\n #recomputeAll(): void {\n for (const swap of this.#swaps.values()) this.#recompute(swap);\n }\n\n #recompute(swap: TrackedSwap, force = false): void {\n // A Lightning swap has one on-chain leg; the absent leg stays `undefined` (its\n // status is derived from the leg that exists). Gate only on present legs — a\n // leg with no observation or clock yet means \"not enough known\", so bail.\n let clientHtlc: HtlcObservation | undefined;\n let serverHtlc: HtlcObservation | undefined;\n let clientChainNow = 0;\n let serverChainNow = 0;\n\n if (swap.clientHtlc) {\n const m = this.#managerFor(swap.clientHtlc);\n clientHtlc = m.getState(swap.clientHtlc);\n const now = m.chainNow(swap.clientHtlc);\n if (clientHtlc === undefined || now === undefined) return;\n clientChainNow = now;\n }\n if (swap.serverHtlc) {\n const m = this.#managerFor(swap.serverHtlc);\n serverHtlc = m.getState(swap.serverHtlc);\n const now = m.chainNow(swap.serverHtlc);\n if (serverHtlc === undefined || now === undefined) return;\n serverChainNow = now;\n }\n\n const status = deriveSwapStatus({ clientHtlc, serverHtlc });\n if (status === undefined) return; // contradictory observations\n\n const actions = {\n ...deriveSwapActions({\n status,\n clientChainNow,\n serverChainNow,\n clientRefundLocktime: swap.clientRefundLocktime,\n serverRefundLocktime: swap.serverRefundLocktime,\n // Pay-on-Lightning swaps have no client-funded on-chain leg.\n clientFunds: swap.clientHtlc !== undefined,\n }),\n // Expose the raw observations so consumers can render progress from\n // chain facts (which leg is funded/spent) even when the server is\n // stale/unreachable — without interpreting the SwapStatus enum.\n observations: { clientHtlc, serverHtlc },\n };\n\n const previous = this.#lastActions.get(swap.swapId);\n if (\n !force &&\n previous &&\n JSON.stringify(previous) === JSON.stringify(actions)\n )\n return;\n\n this.#lastActions.set(swap.swapId, actions);\n for (const cb of this.#subscribers) cb(swap.swapId, actions);\n\n // Terminal: stop watching (retain lastActions so late subscribers still see it).\n if (actions.recommended === \"none\") this.#untrack(swap);\n }\n\n #untrack(swap: TrackedSwap): void {\n this.#swaps.delete(swap.swapId);\n for (const leg of legsOf(swap)) void this.#managerFor(leg).unregister(leg);\n }\n}\n\n/** The on-chain legs actually present on a swap (one for Lightning, two otherwise). */\nfunction legsOf(swap: TrackedSwap): HtlcRef[] {\n const legs: HtlcRef[] = [];\n if (swap.clientHtlc) legs.push(swap.clientHtlc);\n if (swap.serverHtlc) legs.push(swap.serverHtlc);\n return legs;\n}\n"],"mappings":"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","names":["refund","hex","hex","hex","hex","hex","esploraReader","electrumReader","hex","refs","ripemd160","hex","seconds","strip0x"]}
|