@arkade-os/swap 0.0.12 → 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -22
- package/dist/{chunk-XXWOODUE.js → chunk-OEKSNYJW.js} +107 -76
- package/dist/{chunk-6ZUS47GA.js → chunk-U7VWWTL6.js} +15 -1
- package/dist/index.cjs +1457 -312
- package/dist/index.d.cts +542 -46
- package/dist/index.d.ts +542 -46
- package/dist/index.js +1312 -230
- package/dist/node/index.cjs +322 -0
- package/dist/node/index.d.cts +1055 -0
- package/dist/node/index.d.ts +1055 -0
- package/dist/node/index.js +295 -0
- package/dist/nostr.cjs +4 -1
- package/dist/nostr.d.cts +1 -1
- package/dist/nostr.d.ts +1 -1
- package/dist/nostr.js +1 -1
- package/dist/repositories/realm/index.cjs +50 -2
- package/dist/repositories/realm/index.d.cts +55 -10
- package/dist/repositories/realm/index.d.ts +55 -10
- package/dist/repositories/realm/index.js +49 -3
- package/dist/repositories/sqlite/index.cjs +42 -1
- package/dist/repositories/sqlite/index.d.cts +8 -3
- package/dist/repositories/sqlite/index.d.ts +8 -3
- package/dist/repositories/sqlite/index.js +43 -2
- package/dist/{repository-DaK1RzRq.d.cts → repository-DoR-ahHc.d.ts} +1081 -84
- package/dist/{repository-DlLvj_y6.d.ts → repository-oeW8KZo1.d.cts} +1081 -84
- package/dist/{rfq-DzsmhXX3.d.cts → rfq-D0KGjmnn.d.cts} +42 -32
- package/dist/{rfq-DzsmhXX3.d.ts → rfq-D0KGjmnn.d.ts} +42 -32
- package/package.json +22 -6
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ProvisionedKey, ProvisionedClaimSecret, asset, RelativeTimelock, IWallet, arkade, RestIndexerProvider, Transaction, IContractManager, OnchainProvider, PaymentRail, VHTLC, Identity, ActivityResolver } from '@arkade-os/sdk';
|
|
2
|
-
import { S as SwapSecretsProjection, R as RfqSwapRecord, A as AssetSwapRepository, a as AssetSwap, M as MarketsCacheEntry,
|
|
3
|
-
export {
|
|
1
|
+
import { ProvisionedKey, ProvisionedClaimSecret, asset, RelativeTimelock, IWallet, arkade, RestIndexerProvider, Transaction, IContractManager, OnchainProvider, PaymentRail, PaymentHandle, RouterPreferences, Wallet, PaymentRouter, PaymentStatus, VHTLC, Identity, ActivityResolver } from '@arkade-os/sdk';
|
|
2
|
+
import { S as SwapSecretsProjection, R as RfqSwapRecord, A as AssetSwapRepository, a as AssetSwap, b as SwapRecord, M as MarketsCacheEntry, c as AssetSwapStatus, O as Outcome, d as Swap, Q as QuoteInput, e as RouteResolution, f as Quote, g as SwapUpdate, U as Unsubscribe, h as RefundIndexer, L as LockupVtxo, i as SwapOperator, j as RfqSwap, k as ArkadeRefundResult, l as LockupSpendIndexer, m as RfqSwapState, n as RfqSwapManager, o as RfqSwapManagerCallbacks } from './repository-oeW8KZo1.cjs';
|
|
3
|
+
export { p as AvailableRfqSwapManagerCallbacks, B as BTC_ASSET_ID, I as InMemoryAssetSwapRepository, q as LightningReceiveSwap, r as LightningSendSwap, s as LockupFate, t as LockupNeedsRecoveryError, u as LockupParams, v as LockupSpend, w as OnchainSendAction, x as OnchainSendSwap, P as PersistableRfqSwap, y as PreimageBlockedReason, z as PreimageNotRecoverableError, C as REFUND_MTP_LAG_SECONDS, D as RFQ_RESOLVED_STATES, E as RFQ_SWAP_RETENTION_SECONDS, F as RFQ_SWAP_TERMINAL_STATES, G as RefundOutcome, H as RfqRestoreFailure, J as RfqRestoreOptions, K as RfqRestoreResult, N as RfqSwapActionName, T as RfqSwapLockup, V as RfqSwapManagerConfig, W as RfqSwapManagerDeps, X as RfqSwapManagerEvents, Y as RfqSwapOrigin, Z as RfqSwapOriginRequired, _ as RfqSwapOutcome, $ as RfqSwapRecordStore, a0 as SwapContractRegistry, a1 as addAssetSwap, a2 as awaitRfqResolution, a3 as createRfqSwapRecord, a4 as findLockupVtxos, a5 as getAssetSwaps, a6 as getAssetSwapsOrThrow, a7 as isRfqSwapTerminal, a8 as isRfqTerminal, a9 as nextOnchainAction, aa as normalizeRfqSwapRecord, ab as preimageForSwapRecord, ac as pushRefundWithoutReceiver, ad as readLockupFate, ae as rebuildRfqSwap, af as refundIfUnresolved, ag as rfqSwapOriginOf, ah as shouldRetainRfqSwap, ai as swapSecretsToRecord, aj as updateAssetSwap, ak as updateAssetSwapBestEffort, al as updateRfqSwapRecord } from './repository-oeW8KZo1.cjs';
|
|
4
4
|
import { Network, LocalCardInput, DiscoveredMarket, Side, OfferPlan } from '@arkade-os/solver-discovery';
|
|
5
|
-
import { O as OnchainNetwork, a as OnchainHtlc, b as OnchainHtlcParams, C as ChainSource, R as RfqTransport, r as requestOnchainSend, I as InvoiceFacts, c as requestLightningSend } from './rfq-
|
|
6
|
-
export { A as ARKADE_ASSET,
|
|
5
|
+
import { O as OnchainNetwork, a as OnchainHtlc, b as OnchainHtlcParams, C as ChainSource, R as RfqTransport, r as requestOnchainSend, I as InvoiceFacts, c as requestLightningSend, d as requestLightningReceive } from './rfq-D0KGjmnn.cjs';
|
|
6
|
+
export { A as ARKADE_ASSET, e as ARKADE_BTC, f as AddressMismatch, g as ChainUtxo, H as HtlcUtxo, L as LIGHTNING_BTC, h as LIGHTNING_RECEIVE_PAIR, i as LIGHTNING_SEND_PAIR, j as LOCKTIME_THRESHOLD, k as LightningReceiveContractParams, l as LightningSendContractParams, M as MAX_MIN_CONFIRMATIONS, m as MIN_CLAIM_WINDOW_SECONDS, n as MIN_HEADROOM_SECONDS, o as ONCHAIN_BTC, p as ONCHAIN_CLAIM_MARGIN_SECONDS, q as ONCHAIN_DUST_SATS, s as ONCHAIN_ORDER_MARGIN_SECONDS, t as ONCHAIN_RECEIVE_PAIR, u as ONCHAIN_SECONDS_PER_BLOCK, v as ONCHAIN_SEND_PAIR, w as OnchainHtlcPhase, x as RFQ_TERMINAL_STATES, y as RelaySocket, z as RfqQuote, B as RfqRefusalReason, D as RfqStatus, S as SOLO_REFUND_HEADROOM_SECONDS, E as SwapRefusal, F as arkadeAssetLeg, G as arkadeSwapRequest, J as assertFundable, K as assertReceivable, N as awaitOnchainFill, P as buildHtlcClaim, Q as buildHtlcRefund, T as claimOnchainFill, U as classifyOnchainHtlc, V as deriveLightningReceive, W as deriveOnchainReceive, X as deriveOnchainSend, Y as extractPreimage, Z as httpTransport, _ as l1ScriptForAddress, $ as lightningReceiveContract, a0 as lightningReceiveRequest, a1 as lightningSendContract, a2 as lightningSendRequest, a3 as newPreimage, a4 as newRfqId, a5 as offerTermsFromQuote, a6 as onchainHtlcScript, a7 as onchainReceiveRequest, a8 as onchainSendRequest, a9 as paymentHashOf, aa as relayTransport, ab as requestOnchainReceive, ac as rfqPair, ad as unilateralClaimDelay, ae as unilateralRefundDelay, af as unilateralRefundWithoutReceiverDelay, ag as verifyLockupAddress, ah as verifyReceiveInvoice } from './rfq-D0KGjmnn.cjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Which wallet key signs this leg. Stored at `profile.signer`.
|
|
@@ -111,7 +111,7 @@ interface LightningReceiveProfile extends Record<string, unknown> {
|
|
|
111
111
|
* preimage that is already public, and a swap that did claim is relabelled
|
|
112
112
|
* `needs_counterparty` once its window shuts.
|
|
113
113
|
*/
|
|
114
|
-
|
|
114
|
+
claimTxid?: string;
|
|
115
115
|
}
|
|
116
116
|
/** `arkade:BTC->onchain:BTC`. */
|
|
117
117
|
interface OnchainSendProfile extends Record<string, unknown> {
|
|
@@ -228,7 +228,7 @@ interface Offer {
|
|
|
228
228
|
exitDelay?: RelativeTimelock;
|
|
229
229
|
}
|
|
230
230
|
/** Compile the offer's contract: program + args -> taproot tree. */
|
|
231
|
-
declare function
|
|
231
|
+
declare function offerContract(offer: Omit<Offer, "swapPkScript">, operatorPubkey: Uint8Array): InstanceType<typeof arkade.ArkadeProgramScript>;
|
|
232
232
|
/** Extension packet type tag for Arkade Intents offers. */
|
|
233
233
|
declare const OFFER_PACKET_TYPE = 3;
|
|
234
234
|
/** Serialize an offer to TLV bytes (the packet payload). */
|
|
@@ -240,11 +240,11 @@ declare function decodeOffer(data: Uint8Array): Offer;
|
|
|
240
240
|
* you deposit, embedding the returned extension, and the solver does the rest:
|
|
241
241
|
*
|
|
242
242
|
* // BTC -> asset
|
|
243
|
-
* const o = await createOffer(wallet,
|
|
243
|
+
* const o = await createOffer(wallet, { wantAmount: 1000n, wantAsset })
|
|
244
244
|
* await wallet.send({ address: o.address, amount: 1000, extensions: [o.extension] })
|
|
245
245
|
*
|
|
246
246
|
* // asset -> BTC (the sats are the VTXO carrier for the asset)
|
|
247
|
-
* const o = await createOffer(wallet,
|
|
247
|
+
* const o = await createOffer(wallet, { wantAmount: 1000n, offerAsset })
|
|
248
248
|
* await wallet.send({ address: o.address, amount: 500,
|
|
249
249
|
* assets: [{ assetId, amount: 1000n }],
|
|
250
250
|
* extensions: [o.extension] })
|
|
@@ -265,7 +265,7 @@ declare function decodeOffer(data: Uint8Array): Offer;
|
|
|
265
265
|
* so that exposure has no end. `noExit` opts out for a caller who wants the
|
|
266
266
|
* smaller tree and accepts the dependency.
|
|
267
267
|
*/
|
|
268
|
-
declare function createOffer(wallet: IWallet,
|
|
268
|
+
declare function createOffer(wallet: IWallet, params: {
|
|
269
269
|
wantAmount: bigint;
|
|
270
270
|
wantAsset?: asset.AssetId;
|
|
271
271
|
offerAsset?: asset.AssetId;
|
|
@@ -332,8 +332,8 @@ declare function createOffer(wallet: IWallet, arkServerUrl: string, params: {
|
|
|
332
332
|
* Identical offers derive the same address, so `fundingTxid` selects the exact
|
|
333
333
|
* deposit; without it the address must hold exactly one spendable VTXO — with
|
|
334
334
|
* several, cancel refuses to guess and throws.
|
|
335
|
-
* `swapAddress` (the funded address) pins the
|
|
336
|
-
* built with, so cancel keeps working across
|
|
335
|
+
* `swapAddress` (the funded address) pins the operator key the covenant was
|
|
336
|
+
* built with, so cancel keeps working across an operator signer rotation; without
|
|
337
337
|
* it a rotated key is detected and reported rather than reading as a missing
|
|
338
338
|
* VTXO.
|
|
339
339
|
*
|
|
@@ -354,12 +354,44 @@ declare function createOffer(wallet: IWallet, arkServerUrl: string, params: {
|
|
|
354
354
|
* returns, so a spend event that arrives in that window finds a `cancelling`
|
|
355
355
|
* record and classifies the spend by its covenant leaf instead — the same
|
|
356
356
|
* answer, one indexer read more.
|
|
357
|
+
*
|
|
358
|
+
* Takes no server URL, like every other entrypoint here: broadcast comes from
|
|
359
|
+
* `wallet.getArkadeBroadcaster()` and the indexer fallback from
|
|
360
|
+
* `wallet.getArkadeReader()`, so the wallet's own connection is the only one
|
|
361
|
+
* used (#734).
|
|
357
362
|
*/
|
|
358
|
-
declare function cancelOffer(wallet: IWallet,
|
|
363
|
+
declare function cancelOffer(wallet: IWallet, offerHex: string, opts: {
|
|
359
364
|
repository: AssetSwapRepository;
|
|
360
365
|
fundingTxid?: string;
|
|
361
366
|
swapAddress?: string;
|
|
362
367
|
}): Promise<string>;
|
|
368
|
+
/**
|
|
369
|
+
* No deposit left to cancel at the swap address.
|
|
370
|
+
*
|
|
371
|
+
* Almost always the fill winning the race — v1's documented
|
|
372
|
+
* throw-means-completed trap, which the v2 client's `cancel()` reconciles
|
|
373
|
+
* against the covenant's leaves instead of surfacing (see `client/cancel.ts`).
|
|
374
|
+
* Typed so that reconciliation is an `instanceof`, not a message match; the
|
|
375
|
+
* message is v1's, unchanged.
|
|
376
|
+
*/
|
|
377
|
+
declare class NoSpendableDepositError extends Error {
|
|
378
|
+
readonly name = "NoSpendableDepositError";
|
|
379
|
+
constructor(options?: ErrorOptions);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* The rebuilt covenant disagrees with the offer's own `swapPkScript`: the
|
|
383
|
+
* operator key the rebuild pinned is not the one the covenant was funded with.
|
|
384
|
+
*
|
|
385
|
+
* v1's fallback read this as a missing VTXO; with a pinned `swapAddress` the
|
|
386
|
+
* only remaining causes are a record corrupted or a `swapAddress` that is not
|
|
387
|
+
* the one funded — so the condition is typed rather than left to surface raw.
|
|
388
|
+
* Fires before any broadcast, where §7's law still governs.
|
|
389
|
+
*/
|
|
390
|
+
declare class OfferCovenantMismatchError extends Error {
|
|
391
|
+
readonly swapPkScript: string;
|
|
392
|
+
readonly name = "OfferCovenantMismatchError";
|
|
393
|
+
constructor(swapPkScript: string, options?: ErrorOptions);
|
|
394
|
+
}
|
|
363
395
|
|
|
364
396
|
/**
|
|
365
397
|
* Quoting: solver discovery and the pricing guardrails around a plan.
|
|
@@ -434,10 +466,9 @@ type PlanError = "insufficient-balance" | "side-disabled" | "below-min" | "above
|
|
|
434
466
|
/** Validate a plan against the user's balance and the server dust limit. */
|
|
435
467
|
declare const validatePlan: (plan: OfferPlan, giveBalance: bigint, dust: bigint) => PlanError | undefined;
|
|
436
468
|
|
|
437
|
-
/** Browser backend over the SDK's shared IndexedDB manager
|
|
438
|
-
* infrastructure the wallet already uses for its Boltz swap repository. */
|
|
469
|
+
/** Browser backend over the SDK's shared IndexedDB manager. */
|
|
439
470
|
declare class IndexedDbAssetSwapRepository implements AssetSwapRepository {
|
|
440
|
-
readonly version:
|
|
471
|
+
readonly version: 5;
|
|
441
472
|
private readonly connection;
|
|
442
473
|
constructor(dbName?: string);
|
|
443
474
|
private ensureDb;
|
|
@@ -452,6 +483,10 @@ declare class IndexedDbAssetSwapRepository implements AssetSwapRepository {
|
|
|
452
483
|
getRfqSwap(rfqId: string): Promise<RfqSwapRecord | undefined>;
|
|
453
484
|
getAllRfqSwaps(): Promise<RfqSwapRecord[]>;
|
|
454
485
|
removeRfqSwap(rfqId: string): Promise<void>;
|
|
486
|
+
saveSwapRecord(record: SwapRecord): Promise<void>;
|
|
487
|
+
getSwapRecord(id: string): Promise<SwapRecord | undefined>;
|
|
488
|
+
getAllSwapRecords(): Promise<SwapRecord[]>;
|
|
489
|
+
removeSwapRecord(id: string): Promise<void>;
|
|
455
490
|
getScannedTxids(): Promise<Set<string>>;
|
|
456
491
|
markTxidsScanned(txids: Iterable<string>): Promise<void>;
|
|
457
492
|
getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
|
|
@@ -524,12 +559,12 @@ type SpendKind = "cancelled" | "fulfilled" | "indeterminate";
|
|
|
524
559
|
* and they also survive batching: a solver filling several offers in one tx
|
|
525
560
|
* gives each input its own leaf.
|
|
526
561
|
*
|
|
527
|
-
* `
|
|
562
|
+
* `operatorPubkey` must be the key the covenant was *funded* against. If it has
|
|
528
563
|
* rotated since, the rebuilt script will not match the offer's own
|
|
529
564
|
* `swapPkScript` and this returns `indeterminate` rather than guessing —
|
|
530
565
|
* `cancelOffer` diagnoses the same mismatch the same way.
|
|
531
566
|
*/
|
|
532
|
-
declare function classifySpend(offer: Offer,
|
|
567
|
+
declare function classifySpend(offer: Offer, operatorPubkey: Uint8Array, spendTx: Transaction, deposit: {
|
|
533
568
|
txid: string;
|
|
534
569
|
vout: number;
|
|
535
570
|
}): SpendKind;
|
|
@@ -550,7 +585,7 @@ declare const spendTxidsOf: (vtxo: {
|
|
|
550
585
|
* settlement they may be the same id. Try each and take the first definite
|
|
551
586
|
* answer, so the classification does not depend on that distinction.
|
|
552
587
|
*/
|
|
553
|
-
declare function classifyDepositSpend(offer: Offer,
|
|
588
|
+
declare function classifyDepositSpend(offer: Offer, operatorPubkey: Uint8Array, spendTxs: Iterable<Transaction>, deposit: {
|
|
554
589
|
txid: string;
|
|
555
590
|
vout: number;
|
|
556
591
|
}): SpendKind;
|
|
@@ -575,18 +610,65 @@ declare function classifyDepositSpend(offer: Offer, serverPubkey: Uint8Array, sp
|
|
|
575
610
|
* does, so the `existingIds` escape hatch is no longer a correction mechanism
|
|
576
611
|
* for a wrong label — it is only a skip list.
|
|
577
612
|
*
|
|
578
|
-
* `
|
|
613
|
+
* `operatorPubkey` must be the operator key the covenants were funded against; a
|
|
579
614
|
* key that has rotated since makes every affected swap unclassifiable rather
|
|
580
615
|
* than misclassified.
|
|
581
616
|
*/
|
|
582
617
|
declare function restoreAssetSwaps(indexer: RestoreIndexer, txs: Tx[], existingIds: ReadonlySet<string>, opts: {
|
|
583
|
-
|
|
618
|
+
operatorPubkey: Uint8Array;
|
|
584
619
|
scanned?: ReadonlySet<string>;
|
|
585
620
|
}): Promise<{
|
|
586
621
|
restored: AssetSwap[];
|
|
587
622
|
scannedTxids: string[];
|
|
588
623
|
}>;
|
|
589
624
|
|
|
625
|
+
/**
|
|
626
|
+
* What this watcher needs to know about an offer swap.
|
|
627
|
+
*
|
|
628
|
+
* Structural, and narrower than {@link AssetSwap}, because two record families
|
|
629
|
+
* now answer the same question. v1 keys its records on the funding txid — the
|
|
630
|
+
* deposit IS the identity, so no record can exist before the money does — while
|
|
631
|
+
* the v2 client keys on the quote id and writes the record BEFORE funding, with
|
|
632
|
+
* `fundingTxid` a later best-effort write. Projecting the second onto the first
|
|
633
|
+
* is impossible for the whole pre-funding window, so the parameter widens
|
|
634
|
+
* instead: `id` is whatever key the source stores under, and the spend is
|
|
635
|
+
* matched on `fundingTxid` and `swapPkScript`, which both families carry.
|
|
636
|
+
*
|
|
637
|
+
* `createdAt` is unix **milliseconds**, the unit `coverage.ts` marks issuance
|
|
638
|
+
* in; see {@link CoveredSwap}.
|
|
639
|
+
*/
|
|
640
|
+
interface OfferSwapFacts {
|
|
641
|
+
readonly id: string;
|
|
642
|
+
readonly status: AssetSwapStatus;
|
|
643
|
+
/** The TLV offer, hex — what {@link classifyDepositSpend} needs. */
|
|
644
|
+
readonly offerHex: string;
|
|
645
|
+
readonly swapPkScript: string;
|
|
646
|
+
readonly fundingTxid?: string;
|
|
647
|
+
readonly spentTxid?: string;
|
|
648
|
+
readonly createdAt: number;
|
|
649
|
+
}
|
|
650
|
+
/** The record change a classified spend implies. */
|
|
651
|
+
interface OfferSpendChanges {
|
|
652
|
+
readonly status: AssetSwapStatus;
|
|
653
|
+
readonly spentTxid: string;
|
|
654
|
+
readonly completedAt?: number;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Where the watcher reads offer records and writes their spends.
|
|
658
|
+
*
|
|
659
|
+
* One seam, two implementations: {@link assetSwapSource} over v1's store, and
|
|
660
|
+
* the v2 client's own over its quote-id-keyed record store. `apply` returns
|
|
661
|
+
* both halves for the same reason `updateAssetSwapBestEffort` does — a lost
|
|
662
|
+
* write must not retire a script, and the post-update view is what the liveness
|
|
663
|
+
* check reads without a third round trip.
|
|
664
|
+
*/
|
|
665
|
+
interface OfferSwapSource<S extends OfferSwapFacts = OfferSwapFacts> {
|
|
666
|
+
list(): Promise<S[]>;
|
|
667
|
+
apply(swap: S, changes: OfferSpendChanges): Promise<{
|
|
668
|
+
persisted: boolean;
|
|
669
|
+
swaps: S[];
|
|
670
|
+
}>;
|
|
671
|
+
}
|
|
590
672
|
/**
|
|
591
673
|
* The record change a classified spend implies, or `undefined` when it implies
|
|
592
674
|
* none — an already-resolved swap, or a spend nobody could classify.
|
|
@@ -595,25 +677,26 @@ declare function restoreAssetSwaps(indexer: RestoreIndexer, txs: Tx[], existingI
|
|
|
595
677
|
* taking the watcher, and so re-delivery of an event is a no-op rather than a
|
|
596
678
|
* rewrite.
|
|
597
679
|
*/
|
|
598
|
-
declare function spendUpdate(swap:
|
|
680
|
+
declare function spendUpdate(swap: Pick<OfferSwapFacts, "status">, spend: {
|
|
599
681
|
txid: string;
|
|
600
682
|
kind: SpendKind;
|
|
601
683
|
at?: number;
|
|
602
|
-
}):
|
|
684
|
+
}): OfferSpendChanges | undefined;
|
|
603
685
|
/** A running watcher. `idle()` exists because the writes are async: shutdown
|
|
604
686
|
* and tests both need to know when in-flight updates have settled. */
|
|
605
687
|
interface OfferSwapWatcher {
|
|
606
688
|
stop(): void;
|
|
607
689
|
idle(): Promise<void>;
|
|
608
690
|
}
|
|
609
|
-
interface WatchOfferSwapsParams {
|
|
691
|
+
interface WatchOfferSwapsParams<S extends OfferSwapFacts = OfferSwapFacts> {
|
|
610
692
|
wallet: IWallet;
|
|
611
|
-
/**
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
693
|
+
/** v1's record source. Ignored when {@link source} is given. */
|
|
694
|
+
repository?: AssetSwapRepository;
|
|
695
|
+
/** Where records are read and spends written. Defaults to
|
|
696
|
+
* {@link assetSwapSource} over `repository`. */
|
|
697
|
+
source?: OfferSwapSource<S>;
|
|
615
698
|
/** Called after a change is persisted. A notification, not a store. */
|
|
616
|
-
onUpdate?: (swap:
|
|
699
|
+
onUpdate?: (swap: S) => void;
|
|
617
700
|
}
|
|
618
701
|
/**
|
|
619
702
|
* Subscribe to the wallet's contract events and drive offer swap status.
|
|
@@ -626,7 +709,16 @@ interface WatchOfferSwapsParams {
|
|
|
626
709
|
* provide an `EventSource` implementation or use a runtime where it is enabled;
|
|
627
710
|
* otherwise live updates do not arrive and restore remains the fallback.
|
|
628
711
|
*/
|
|
629
|
-
declare function watchOfferSwaps(
|
|
712
|
+
declare function watchOfferSwaps(params: {
|
|
713
|
+
wallet: IWallet;
|
|
714
|
+
repository: AssetSwapRepository;
|
|
715
|
+
onUpdate?: (swap: AssetSwap) => void;
|
|
716
|
+
}): Promise<OfferSwapWatcher>;
|
|
717
|
+
declare function watchOfferSwaps<S extends OfferSwapFacts>(params: {
|
|
718
|
+
wallet: IWallet;
|
|
719
|
+
source: OfferSwapSource<S>;
|
|
720
|
+
onUpdate?: (swap: S) => void;
|
|
721
|
+
}): Promise<OfferSwapWatcher>;
|
|
630
722
|
|
|
631
723
|
/**
|
|
632
724
|
* Coverage: whether an offer's script is in the wallet's watched set.
|
|
@@ -659,6 +751,25 @@ declare function watchOfferSwaps({ wallet, arkServerUrl, repository, onUpdate, }
|
|
|
659
751
|
* the same backstop every other best-effort step here relies on.
|
|
660
752
|
*/
|
|
661
753
|
|
|
754
|
+
/**
|
|
755
|
+
* What coverage needs to know about a swap, and nothing else.
|
|
756
|
+
*
|
|
757
|
+
* Structural rather than `AssetSwap`, because two record families now ask the
|
|
758
|
+
* same question: v1's offer swaps and the v2 client's `OfferSwapRecord`s, which
|
|
759
|
+
* are keyed on a quote id and carry none of `AssetSwap`'s other fields.
|
|
760
|
+
* Liveness is a property of the status and the funding time, so those are the
|
|
761
|
+
* whole parameter.
|
|
762
|
+
*
|
|
763
|
+
* `createdAt` is unix **milliseconds** — the unit {@link promoteOfferContract}
|
|
764
|
+
* marks issuance in. A v2 record stores seconds, so its adapter converts;
|
|
765
|
+
* comparing seconds against a `Date.now()` mark would leave every issued script
|
|
766
|
+
* outstanding forever and never retire one.
|
|
767
|
+
*/
|
|
768
|
+
interface CoveredSwap {
|
|
769
|
+
readonly swapPkScript: string;
|
|
770
|
+
readonly status: AssetSwapStatus;
|
|
771
|
+
readonly createdAt: number;
|
|
772
|
+
}
|
|
662
773
|
/** The one contract-manager capability changing coverage needs. */
|
|
663
774
|
type OfferContractRetirer = Pick<IContractManager, "setContractWatchState">;
|
|
664
775
|
/**
|
|
@@ -669,7 +780,7 @@ type OfferContractRetirer = Pick<IContractManager, "setContractWatchState">;
|
|
|
669
780
|
* Takes the caller's full record list: liveness is a property of all records at
|
|
670
781
|
* a script, so a partial list would retire a script another record still holds.
|
|
671
782
|
*/
|
|
672
|
-
declare function retireSettledOfferContracts(manager: OfferContractRetirer, swaps:
|
|
783
|
+
declare function retireSettledOfferContracts(manager: OfferContractRetirer, swaps: readonly CoveredSwap[]): Promise<void>;
|
|
673
784
|
|
|
674
785
|
/** `network` is `requestOnchainSend`'s `l1Network`, and only decodes scripts to
|
|
675
786
|
* addresses — the provider decides which chain is read, so mismatching the two
|
|
@@ -720,7 +831,6 @@ type SolverOnchainSend = Awaited<ReturnType<typeof requestOnchainSend>> & {
|
|
|
720
831
|
payoutPkScript: Uint8Array;
|
|
721
832
|
};
|
|
722
833
|
interface SolverOnchainRailDeps {
|
|
723
|
-
arkServerUrl: string;
|
|
724
834
|
l1Network: OnchainNetwork;
|
|
725
835
|
/** x-only L1 key that AUTHORISES the claim — not where it pays. */
|
|
726
836
|
payoutPubkey: Uint8Array;
|
|
@@ -759,7 +869,6 @@ type SolverLightningSend = Awaited<ReturnType<typeof requestLightningSend>> & {
|
|
|
759
869
|
};
|
|
760
870
|
/** Mirrors {@link SolverOnchainRailDeps}; see there for the shared seams. */
|
|
761
871
|
interface SolverLightningRailDeps {
|
|
762
|
-
arkServerUrl: string;
|
|
763
872
|
/** A decoder that throws drops the rail rather than taking the router
|
|
764
873
|
* down — correct, since an undecodable invoice cannot be paid. */
|
|
765
874
|
decodeInvoice(bolt11: string): InvoiceFacts;
|
|
@@ -776,6 +885,289 @@ interface SolverLightningRailDeps {
|
|
|
776
885
|
declare const solverLightningRendezvous: (markets: DiscoveredMarket[], amountSats: number, fallbackEmulatorPubkey?: Uint8Array) => SolverRendezvous | undefined;
|
|
777
886
|
declare function solverLightningRail(deps: SolverLightningRailDeps): PaymentRail;
|
|
778
887
|
|
|
888
|
+
/**
|
|
889
|
+
* What the two v2 swap rails share: the client seam, the quote arithmetic, and
|
|
890
|
+
* the handle that observes a swap through core's four-state stream.
|
|
891
|
+
*
|
|
892
|
+
* A rail here is a thin adapter and deliberately nothing more. Persistence,
|
|
893
|
+
* refunds, recovery and the outcome vocabulary all live behind the v2 client;
|
|
894
|
+
* the rail's whole job is to turn a `PaymentRequest` into a `QuoteInput`, a
|
|
895
|
+
* `Quote` into a receiver-exact `RouteQuote`, and a swap's outcome stream into
|
|
896
|
+
* a `PaymentHandle`.
|
|
897
|
+
*
|
|
898
|
+
* Two boundaries are crossed here and both are named. Amounts narrow from P3's
|
|
899
|
+
* `bigint` to core's `number` (see `client/sats.ts`). And a ranking estimate
|
|
900
|
+
* meets a binding quote: `RouteQuote.fee` is documented as "a display and
|
|
901
|
+
* ranking figure, not a guarantee" while a v2 `Quote` is verified before return
|
|
902
|
+
* and expires. `PaymentOption.quote()` is lazy and `options()` never calls it,
|
|
903
|
+
* so ranking is free — but a swap rail's `quote()` is an RFQ round trip that
|
|
904
|
+
* discloses an invoice and an amount, and an app that ranks then quotes several
|
|
905
|
+
* options would disclose to every solver. `available()` therefore **never
|
|
906
|
+
* quotes**: it resolves, which is network-free against the cached snapshot.
|
|
907
|
+
*
|
|
908
|
+
* A held `RouteQuote` can outlive its `Quote` — core's shape carries no
|
|
909
|
+
* `expiresAt` — and then `send()` throws `QuoteExpired` inside `makeHandle`'s
|
|
910
|
+
* run, which core turns into a terminal `failed`. That stays: re-quoting inside
|
|
911
|
+
* `send()` is the silent re-quote §3.2 forbids.
|
|
912
|
+
*/
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* The slice of `SwapClient` a rail uses.
|
|
916
|
+
*
|
|
917
|
+
* Structural and minimal, so what a rail can reach is visible in one place and
|
|
918
|
+
* a test can stand in for it without a wallet, a repository or a solver behind
|
|
919
|
+
* it. A full `SwapClient` satisfies it.
|
|
920
|
+
*/
|
|
921
|
+
interface SwapRailClient {
|
|
922
|
+
resolve(input: QuoteInput): Promise<RouteResolution>;
|
|
923
|
+
quote(input: QuoteInput): Promise<Quote>;
|
|
924
|
+
accept(quote: Quote): Promise<Swap>;
|
|
925
|
+
onUpdate(fn: (update: SwapUpdate) => void): Unsubscribe;
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* A payment that ended in something other than success.
|
|
929
|
+
*
|
|
930
|
+
* Not a member of §7's taxonomy and suffixed to say so: that taxonomy is thrown
|
|
931
|
+
* *before* value moves, and this is an outcome reported after it did. It exists
|
|
932
|
+
* to carry the {@link Outcome} onto the handle's terminal `error`, which is
|
|
933
|
+
* where `refunded` and `lapsed` stay distinguishable after the four-state
|
|
934
|
+
* projection has mapped both to `failed`.
|
|
935
|
+
*/
|
|
936
|
+
declare class SwapPaymentFailedError extends Error {
|
|
937
|
+
readonly railId: string;
|
|
938
|
+
readonly outcome: Outcome;
|
|
939
|
+
readonly swap: Swap;
|
|
940
|
+
readonly name = "SwapPaymentFailedError";
|
|
941
|
+
constructor(railId: string, outcome: Outcome, swap: Swap);
|
|
942
|
+
}
|
|
943
|
+
/** `eligible > 0` for this input, or false for anything that will not route. */
|
|
944
|
+
declare const railAvailable: (client: SwapRailClient, input: QuoteInput) => Promise<boolean>;
|
|
945
|
+
/**
|
|
946
|
+
* The three receiver-exact numbers, checked against each other.
|
|
947
|
+
*
|
|
948
|
+
* `total === amount + fee` is core's contract and this is where a rail proves
|
|
949
|
+
* it rather than asserting it in a comment: `amount` is what the recipient
|
|
950
|
+
* gets, `fee` is what the rail and its counterparty charge on top, and `total`
|
|
951
|
+
* is what leaves the wallet. A `Quote` whose give leg does not equal the take
|
|
952
|
+
* leg plus the spread has broken an invariant M3 owns, and surfacing it here
|
|
953
|
+
* beats quoting a number that would rank this rail against the collaborative
|
|
954
|
+
* exit on a fee it does not charge.
|
|
955
|
+
*/
|
|
956
|
+
declare const receiverExact: (railId: string, parts: {
|
|
957
|
+
amount: bigint;
|
|
958
|
+
fee: bigint;
|
|
959
|
+
total: bigint;
|
|
960
|
+
}) => {
|
|
961
|
+
amount: number;
|
|
962
|
+
fee: number;
|
|
963
|
+
total: number;
|
|
964
|
+
};
|
|
965
|
+
/**
|
|
966
|
+
* Accept the quote, then observe it to its first terminal outcome.
|
|
967
|
+
*
|
|
968
|
+
* The handle's contract, stated once: it observes the payment up to and
|
|
969
|
+
* including the first terminal outcome, and every recovery past that point is
|
|
970
|
+
* observed on `client.onUpdate` — keyed by the tagged `RouteResult.swapId`, so
|
|
971
|
+
* the two views join. `client.onUpdate` replays synchronously on subscribe, so
|
|
972
|
+
* a swap that was already terminal by the time we subscribed still resolves.
|
|
973
|
+
*/
|
|
974
|
+
declare const swapHandle: (railId: string, client: SwapRailClient, quote: Quote) => Promise<PaymentHandle>;
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* `lightning` — pay a BOLT11 invoice out of an Arkade balance, through the v2
|
|
978
|
+
* swap client.
|
|
979
|
+
*
|
|
980
|
+
* The rail id is a registry key with published history: the factory deleted
|
|
981
|
+
* with `packages/boltz-swap` registered `"lightning"`, and a key is what an
|
|
982
|
+
* app's `priority` array and `disabled` list name. It therefore stays
|
|
983
|
+
* `lightning` even though Q12 renamed the lightning *asset* namespace to
|
|
984
|
+
* `bolt11` — the divergence between a registry key and an asset vocabulary is
|
|
985
|
+
* recorded here, not resolved by a rename that would break every consumer's
|
|
986
|
+
* preferences.
|
|
987
|
+
*
|
|
988
|
+
* The whole rail is an adapter. The invoice fixes the amount, the client
|
|
989
|
+
* verifies and prices the route, `accept()` persists before it funds, and the
|
|
990
|
+
* drive owns everything after — so there is no `persist`, no `awaitSettlement`
|
|
991
|
+
* and no solver selection to configure here.
|
|
992
|
+
*/
|
|
993
|
+
|
|
994
|
+
declare const LIGHTNING_RAIL = "lightning";
|
|
995
|
+
/**
|
|
996
|
+
* Register alongside core's rails. The deleted factory's ranking was
|
|
997
|
+
* `["ark", "lightning", "onchain-swap", "onchain"]`; see
|
|
998
|
+
* {@link createSwapPaymentRouter}.
|
|
999
|
+
*/
|
|
1000
|
+
declare function lightningRail(client: SwapRailClient): PaymentRail;
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* `onchain-swap` — pay an L1 address out of an Arkade balance through a solver,
|
|
1004
|
+
* via the v2 swap client.
|
|
1005
|
+
*
|
|
1006
|
+
* Registered alongside core's `onchain` rail (the collaborative exit) and
|
|
1007
|
+
* ranked ahead of it, with both left registered: the default is a *preference*,
|
|
1008
|
+
* not a restriction. The preference self-heals by amount — an out-of-range
|
|
1009
|
+
* request drops this rail at `available()` and `onchain` wins with no error.
|
|
1010
|
+
*
|
|
1011
|
+
* The id is the deleted factory's published registry key, `"onchain-swap"`,
|
|
1012
|
+
* kept for the reason the `lightning` rail keeps its own: a rail id is what an
|
|
1013
|
+
* app's `priority` array names.
|
|
1014
|
+
*
|
|
1015
|
+
* **The claim fee is this rail's whole subtlety.** On `arkade -> onchain` the
|
|
1016
|
+
* trader claims the solver's L1 HTLC itself, and that claim's fee comes out of
|
|
1017
|
+
* the HTLC output — `payout = utxo.amount - fee`. So the sats the recipient
|
|
1018
|
+
* actually receives are less than the sats the solver locks, by an amount the
|
|
1019
|
+
* swap quote knows nothing about. A rail that quoted the solver's spread alone
|
|
1020
|
+
* would be reporting a fee it does not charge, and would win a ranking against
|
|
1021
|
+
* the collaborative exit that it should lose.
|
|
1022
|
+
*
|
|
1023
|
+
* This rail therefore takes a claim fee-rate policy at construction and grosses
|
|
1024
|
+
* the swap up by the estimate: it asks the solver for `amount + claimFee` on the
|
|
1025
|
+
* take leg, so what lands after the claim is the `amount` the caller asked for,
|
|
1026
|
+
* and it reports the estimate inside `fee` where a ranking can see it. A fee
|
|
1027
|
+
* rate is environment-specific and is nobody's to default — the same reason
|
|
1028
|
+
* `CorridorOverrides.onchain.claim` has no default.
|
|
1029
|
+
*/
|
|
1030
|
+
|
|
1031
|
+
declare const ONCHAIN_SWAP_RAIL = "onchain-swap";
|
|
1032
|
+
interface OnchainSwapRailDeps {
|
|
1033
|
+
/**
|
|
1034
|
+
* Sat/vB the trader's L1 claim will be built at — the rate the corridor's
|
|
1035
|
+
* own `claim` dep will use.
|
|
1036
|
+
*
|
|
1037
|
+
* No default, and the rail refuses to exist without it: the fee comes out
|
|
1038
|
+
* of the recipient's payout, so a rail with no rate either quotes a fee it
|
|
1039
|
+
* does not charge or short-pays the recipient. Neither is a default.
|
|
1040
|
+
*/
|
|
1041
|
+
readonly claimFeeRateSatVb: number;
|
|
1042
|
+
/** vsize the claim is priced at. Defaults to {@link ONCHAIN_CLAIM_VSIZE}. */
|
|
1043
|
+
readonly claimVsize?: number;
|
|
1044
|
+
}
|
|
1045
|
+
/** What the trader's L1 claim will cost, rounded up as the builder rounds it. */
|
|
1046
|
+
declare const claimFeeSats: (deps: OnchainSwapRailDeps) => bigint;
|
|
1047
|
+
/**
|
|
1048
|
+
* Register alongside core's rails, ranked ahead of `onchain`:
|
|
1049
|
+
* `["ark", "lightning", "onchain-swap", "onchain"]`. See
|
|
1050
|
+
* {@link createSwapPaymentRouter}.
|
|
1051
|
+
*/
|
|
1052
|
+
declare function onchainSwapRail(client: SwapRailClient, deps: OnchainSwapRailDeps): PaymentRail;
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* The router that puts lightning and `arkade -> onchain` back into a payment
|
|
1056
|
+
* app's reach.
|
|
1057
|
+
*
|
|
1058
|
+
* Core's `createDefaultPaymentRouter(wallet)` registers `ark` and `onchain` and
|
|
1059
|
+
* nothing else. The two swap rails never lived there — they were in
|
|
1060
|
+
* `packages/boltz-swap`, behind a second overload with the priority
|
|
1061
|
+
* `["ark", "lightning", "onchain-swap", "onchain"]`, and ts-sdk #811 removed
|
|
1062
|
+
* them along with the package. This is that overload's shape, rebuilt on the v2
|
|
1063
|
+
* client.
|
|
1064
|
+
*
|
|
1065
|
+
* **The rails close over their own dependencies, and `RouterContext` is not
|
|
1066
|
+
* re-opened.** Widening core's context was the alternative and it is the wrong
|
|
1067
|
+
* half: it edits a published payment type, re-imports the `swaps?: unknown`
|
|
1068
|
+
* smell that was deliberately deleted, and asks core to name things it cannot —
|
|
1069
|
+
* a repository, a transport, a discovery index. A rail factory over an
|
|
1070
|
+
* already-constructed `SwapClient` names none of that in core.
|
|
1071
|
+
*
|
|
1072
|
+
* This factory stays out of core's own, which is pinned at exactly
|
|
1073
|
+
* `["ark", "onchain"]`. Registering the swap rails is the app's decision, and
|
|
1074
|
+
* the ranking it gets is a *preference*: both `onchain-swap` and `onchain` stay
|
|
1075
|
+
* registered, so an amount outside the solver's range drops the swap rail at
|
|
1076
|
+
* `available()` and the collaborative exit wins with no error.
|
|
1077
|
+
*/
|
|
1078
|
+
|
|
1079
|
+
/** The deleted factory's ranking, and the one this factory ships. */
|
|
1080
|
+
declare const SWAP_ROUTER_PRIORITY: readonly string[];
|
|
1081
|
+
interface SwapPaymentRouterConfig extends OnchainSwapRailDeps {
|
|
1082
|
+
/** Overrides the shipped ranking; `disabled`, `caps` and `tieBreak` pass through. */
|
|
1083
|
+
readonly prefs?: RouterPreferences;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Core's four rails plus the two this package supplies, ranked as the deleted
|
|
1087
|
+
* factory ranked them.
|
|
1088
|
+
*
|
|
1089
|
+
* Takes the concrete `Wallet` because that is what `RouterContext` holds — core
|
|
1090
|
+
* types it as the class, not `IWallet` — and the already-constructed client,
|
|
1091
|
+
* because building one here would put storage, discovery and corridor policy
|
|
1092
|
+
* behind a payment-router call that has no business deciding any of them.
|
|
1093
|
+
*/
|
|
1094
|
+
declare function createSwapPaymentRouter(wallet: Wallet, client: SwapRailClient, config: SwapPaymentRouterConfig): PaymentRouter;
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* Fourteen outcomes onto four payment statuses — the only artefact M7 mints.
|
|
1098
|
+
*
|
|
1099
|
+
* Declared **total** even though a send rail cannot reach every member, because
|
|
1100
|
+
* a partial map is where the collapse hides: an outcome with no row does not
|
|
1101
|
+
* fail loudly, it renders as whatever the lookup happens to return.
|
|
1102
|
+
*
|
|
1103
|
+
* The projection is lossy by construction and the loss is recoverable, which is
|
|
1104
|
+
* the whole design. `refunded` and `lapsed` both land on `failed` — P6 exists so
|
|
1105
|
+
* those two never become one word, and the difference survives on the update's
|
|
1106
|
+
* `error`, which core's `PaymentHandle` docblock already reserves for it. No
|
|
1107
|
+
* fifth `PaymentStatus` is proposed: a status is what a payment UI branches on,
|
|
1108
|
+
* and "the trader's value came back" versus "the incoming payment never arrived"
|
|
1109
|
+
* is a sentence, not a branch.
|
|
1110
|
+
*
|
|
1111
|
+
* **Where the rail goes terminal is a decision, not a rounding.** It goes
|
|
1112
|
+
* terminal at `refunding`, not at `refunded`: `makeHandle` clears its subscriber
|
|
1113
|
+
* set on a terminal update and replays-without-registering afterwards, so the
|
|
1114
|
+
* `refunded` that follows could not reach that handle anyway — and holding
|
|
1115
|
+
* terminality back until the refund resolves would hang every
|
|
1116
|
+
* `settled({ timeoutMs })` caller for a whole refund window. The refund is
|
|
1117
|
+
* observed through `client.onUpdate` and `swaps()`, keyed by the tagged
|
|
1118
|
+
* `RouteResult.swapId`.
|
|
1119
|
+
*
|
|
1120
|
+
* The same rule answers the `unblock` backslide. `needs_recovery -> funded` is
|
|
1121
|
+
* a legal re-entry that crosses the terminality boundary the rail just drew; it
|
|
1122
|
+
* is emitted rather than swallowed, because the drive's idempotence key is the
|
|
1123
|
+
* DERIVED outcome and the second `funded` is a new key — delivered on
|
|
1124
|
+
* `client.onUpdate`, never through the handle, which stays terminal. A handle
|
|
1125
|
+
* observes a payment up to and including its first terminal outcome; every
|
|
1126
|
+
* recovery past that point is observed on the client's update stream.
|
|
1127
|
+
*/
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* The projection, total over {@link Outcome}.
|
|
1131
|
+
*
|
|
1132
|
+
* The `on the rail` column of M7's table is not encoded here: a send rail
|
|
1133
|
+
* cannot reach `open`, `filled`, `cancelling`, `cancelled` or `lapsed`, but a
|
|
1134
|
+
* map that refused them would be a map with holes, and the point of totality is
|
|
1135
|
+
* that there are none.
|
|
1136
|
+
*/
|
|
1137
|
+
declare const PAYMENT_STATUS: {
|
|
1138
|
+
/** Persisted, funding not broadcast. */
|
|
1139
|
+
readonly accepted: "pending";
|
|
1140
|
+
readonly funding: "pending";
|
|
1141
|
+
/** The lockup is funded. Nothing has emitted `"sent"` in core since the
|
|
1142
|
+
* `onchain-swap` rail left with `packages/boltz-swap`. */
|
|
1143
|
+
readonly funded: "sent";
|
|
1144
|
+
/** Asset swaps only: an unfilled offer. */
|
|
1145
|
+
readonly open: "pending";
|
|
1146
|
+
/** Asset swaps only. */
|
|
1147
|
+
readonly filled: "settled";
|
|
1148
|
+
/** The trader's L1 claim on `arkade -> onchain`. */
|
|
1149
|
+
readonly claimed: "settled";
|
|
1150
|
+
/** Terminal success on `arkade -> lightning`: the solver's hash-verified
|
|
1151
|
+
* spend IS the invoice being paid. */
|
|
1152
|
+
readonly paid: "settled";
|
|
1153
|
+
readonly cancelling: "pending";
|
|
1154
|
+
/** Value returned, and the router has no non-loss terminal to say so with. */
|
|
1155
|
+
readonly cancelled: "failed";
|
|
1156
|
+
/** Terminal HERE, not at `refunded` — see the module docblock. */
|
|
1157
|
+
readonly refunding: "failed";
|
|
1158
|
+
/** The trader's value came back. Reaches `onUpdate`, not the handle. */
|
|
1159
|
+
readonly refunded: "failed";
|
|
1160
|
+
/** The solver reclaimed a receive-leg lockup: a loss, and not `refunded`. */
|
|
1161
|
+
readonly lapsed: "failed";
|
|
1162
|
+
/** Never retried silently; `client.recover` drives it. */
|
|
1163
|
+
readonly needs_recovery: "failed";
|
|
1164
|
+
readonly failed: "failed";
|
|
1165
|
+
};
|
|
1166
|
+
/** Where a payment stands, in core's four-state vocabulary. */
|
|
1167
|
+
declare const paymentStatusOf: (outcome: Outcome) => PaymentStatus;
|
|
1168
|
+
/** Whether this status ends the handle's observation. Core's own rule. */
|
|
1169
|
+
declare const isTerminalStatus: (status: PaymentStatus) => boolean;
|
|
1170
|
+
|
|
779
1171
|
interface SealedClaimPacket {
|
|
780
1172
|
/** `ephPub(33) ‖ nonce(12) ‖ ciphertext`, base64 — wire-ready. This is
|
|
781
1173
|
* the whole packet: the RFQ request's `claim_packet` field carries
|
|
@@ -799,8 +1191,6 @@ interface ClaimPacketInput {
|
|
|
799
1191
|
*/
|
|
800
1192
|
declare function sealClaimPacket(input: ClaimPacketInput): Promise<SealedClaimPacket>;
|
|
801
1193
|
|
|
802
|
-
/** The Ark surface the claim push needs — the same seam the refund push uses. */
|
|
803
|
-
type ClaimArkProvider = RefundArkProvider;
|
|
804
1194
|
/**
|
|
805
1195
|
* The lockup is funded for less than the swap agreed.
|
|
806
1196
|
*
|
|
@@ -843,9 +1233,9 @@ declare class LockupAmountMismatchError extends Error {
|
|
|
843
1233
|
* server at submit — but it turns "reported claimed, nothing landed, the
|
|
844
1234
|
* solver refunds hours later" into an immediate failure.
|
|
845
1235
|
*/
|
|
846
|
-
declare function pushClaim(
|
|
847
|
-
/** The receive-direction covenant (see `
|
|
848
|
-
|
|
1236
|
+
declare function pushClaim(operator: SwapOperator, input: {
|
|
1237
|
+
/** The receive-direction covenant (see `lightningReceiveContract`). */
|
|
1238
|
+
contract: InstanceType<typeof VHTLC.ScriptV2>;
|
|
849
1239
|
/** The trader's `receiver` signer. Build it from the swap's `secrets`
|
|
850
1240
|
* with `contractSigner` — on an HD wallet that resolves
|
|
851
1241
|
* from the seed, with no stored key bytes anywhere. */
|
|
@@ -865,7 +1255,7 @@ declare function pushClaim(ark: ClaimArkProvider, input: {
|
|
|
865
1255
|
* the remainder. */
|
|
866
1256
|
partiallyClaimed?: boolean;
|
|
867
1257
|
}): Promise<{
|
|
868
|
-
|
|
1258
|
+
txid: string;
|
|
869
1259
|
amount: number;
|
|
870
1260
|
}>;
|
|
871
1261
|
/**
|
|
@@ -892,13 +1282,13 @@ declare function awaitLockupFunding(indexer: RefundIndexer, swapPkScript: Uint8A
|
|
|
892
1282
|
* rest lands is safe — and that is also the answer to a genuinely underfunded
|
|
893
1283
|
* lockup, which never gets past the gate at all.
|
|
894
1284
|
*/
|
|
895
|
-
declare function claimReceiveLockup(indexer: RefundIndexer,
|
|
1285
|
+
declare function claimReceiveLockup(indexer: RefundIndexer, operator: SwapOperator, input: Parameters<typeof pushClaim>[1] & {
|
|
896
1286
|
/** The covenant's scriptPubKey, from the request flow's `swapPkScript`. */
|
|
897
1287
|
swapPkScript: Uint8Array;
|
|
898
1288
|
pollMs?: number;
|
|
899
1289
|
deadline?: number;
|
|
900
1290
|
}): Promise<{
|
|
901
|
-
|
|
1291
|
+
txid: string;
|
|
902
1292
|
amount: number;
|
|
903
1293
|
}>;
|
|
904
1294
|
|
|
@@ -965,7 +1355,7 @@ declare function senderIdentityForSwapRecord(wallet: IWallet, record: {
|
|
|
965
1355
|
*/
|
|
966
1356
|
|
|
967
1357
|
interface ArkadeRefunderDeps {
|
|
968
|
-
|
|
1358
|
+
operator: SwapOperator;
|
|
969
1359
|
indexer: RefundIndexer;
|
|
970
1360
|
/** Asked for the descriptor's signer; never asked to mint a key. */
|
|
971
1361
|
wallet: IWallet;
|
|
@@ -981,7 +1371,7 @@ interface ArkadeRefunderDeps {
|
|
|
981
1371
|
*
|
|
982
1372
|
* @example
|
|
983
1373
|
* manager.setCallbacks({
|
|
984
|
-
* refundArkade: arkadeRefunder({
|
|
1374
|
+
* refundArkade: arkadeRefunder({ operator, indexer, wallet, repository }),
|
|
985
1375
|
* saveSwap,
|
|
986
1376
|
* });
|
|
987
1377
|
*/
|
|
@@ -1109,7 +1499,7 @@ interface RfqSwapActivityDeps {
|
|
|
1109
1499
|
repository: Pick<AssetSwapRepository, "getAllRfqSwaps">;
|
|
1110
1500
|
/**
|
|
1111
1501
|
* Consulted only for what a record cannot answer: a record written before
|
|
1112
|
-
* `
|
|
1502
|
+
* `fundingTxid` existed, and the counterparty's spend on a swap that
|
|
1113
1503
|
* ended without a refund of ours.
|
|
1114
1504
|
*
|
|
1115
1505
|
* Optional because the stored fields are the primary source — cheaper, and
|
|
@@ -1123,7 +1513,7 @@ interface RfqSwapActivityDeps {
|
|
|
1123
1513
|
* groups on.
|
|
1124
1514
|
*
|
|
1125
1515
|
* The txids come from four places, in order of preference: the record's own
|
|
1126
|
-
* `
|
|
1516
|
+
* `fundingTxid` and `refundTxid`, the corridor's `activityTxids` (the
|
|
1127
1517
|
* receive leg's Arkade claim, the onchain leg's L1 one), and — only when the
|
|
1128
1518
|
* first two cannot answer — one read of the lockup's VTXOs.
|
|
1129
1519
|
*
|
|
@@ -1133,4 +1523,110 @@ interface RfqSwapActivityDeps {
|
|
|
1133
1523
|
*/
|
|
1134
1524
|
declare function rfqSwapActivityInputs(deps: RfqSwapActivityDeps): Promise<SwapActivityInput[]>;
|
|
1135
1525
|
|
|
1136
|
-
|
|
1526
|
+
interface SwapQuoteInput {
|
|
1527
|
+
/** Which side of the market the trader deposits. */
|
|
1528
|
+
give: "base" | "quote";
|
|
1529
|
+
/** Size on the named side ("give" = exact-in, "receive" = exact-out).
|
|
1530
|
+
* Spot: display string or atomic bigint; corridors: sats. A lightning
|
|
1531
|
+
* send takes its amount from the invoice instead. */
|
|
1532
|
+
amount?: string | number | bigint;
|
|
1533
|
+
amountOn?: "give" | "receive";
|
|
1534
|
+
/** Required when the receive side is lightning. */
|
|
1535
|
+
invoice?: InvoiceFacts;
|
|
1536
|
+
/** Trader's x-only L1 claim key; required when the receive side is onchain. */
|
|
1537
|
+
payoutPubkey?: Uint8Array;
|
|
1538
|
+
/**
|
|
1539
|
+
* The L1 address the claim PAYS to, when the receive side is onchain.
|
|
1540
|
+
*
|
|
1541
|
+
* Distinct from {@link payoutPubkey}, which only AUTHORISES the claim: the
|
|
1542
|
+
* claim's output is the spender's own choice, and nothing on the wire names
|
|
1543
|
+
* it. Omitted, the claim pays the trader's own `payoutPubkey` as a key-path
|
|
1544
|
+
* P2TR — the conservative default, since that is the one destination the
|
|
1545
|
+
* trader is already known to hold the key for.
|
|
1546
|
+
*/
|
|
1547
|
+
payoutAddress?: string;
|
|
1548
|
+
preimage?: Uint8Array;
|
|
1549
|
+
maxPayAmount?: number;
|
|
1550
|
+
}
|
|
1551
|
+
interface SpotQuote {
|
|
1552
|
+
kind: "spot";
|
|
1553
|
+
market: DiscoveredMarket;
|
|
1554
|
+
plan: OfferPlan;
|
|
1555
|
+
}
|
|
1556
|
+
interface LightningSendQuote {
|
|
1557
|
+
kind: "ln_send";
|
|
1558
|
+
market: DiscoveredMarket;
|
|
1559
|
+
request: Awaited<ReturnType<typeof requestLightningSend>>;
|
|
1560
|
+
}
|
|
1561
|
+
interface LightningReceiveQuote {
|
|
1562
|
+
kind: "ln_receive";
|
|
1563
|
+
market: DiscoveredMarket;
|
|
1564
|
+
request: Awaited<ReturnType<typeof requestLightningReceive>>;
|
|
1565
|
+
/** The solver's hold invoice, for the payer. */
|
|
1566
|
+
invoice: string;
|
|
1567
|
+
}
|
|
1568
|
+
interface OnchainSendQuote {
|
|
1569
|
+
kind: "onchain_send";
|
|
1570
|
+
market: DiscoveredMarket;
|
|
1571
|
+
request: Awaited<ReturnType<typeof requestOnchainSend>>;
|
|
1572
|
+
/** Where the claim pays, resolved at quote time from `payoutAddress` (or
|
|
1573
|
+
* defaulted to the trader's own claim key) and persisted with the record:
|
|
1574
|
+
* `buildHtlcClaim` needs it and nothing else gives it back. */
|
|
1575
|
+
payoutPkScript: Uint8Array;
|
|
1576
|
+
}
|
|
1577
|
+
type SwapQuote = SpotQuote | LightningSendQuote | LightningReceiveQuote | OnchainSendQuote;
|
|
1578
|
+
type UnifiedSwap = {
|
|
1579
|
+
family: "offer";
|
|
1580
|
+
swap: AssetSwap;
|
|
1581
|
+
} | {
|
|
1582
|
+
family: "rfq";
|
|
1583
|
+
swap: RfqSwap;
|
|
1584
|
+
};
|
|
1585
|
+
interface SwapClientDeps {
|
|
1586
|
+
/** The server connection too: every provider below defaults to this
|
|
1587
|
+
* wallet's own, so no server URL is passed in (arkade-os/ts-sdk#734). */
|
|
1588
|
+
wallet: IWallet;
|
|
1589
|
+
repository: AssetSwapRepository;
|
|
1590
|
+
/** The rendezvous for a corridor market; never called for a spot one.
|
|
1591
|
+
*
|
|
1592
|
+
* The market's own `base_corridor`/`quote_corridor` are what route a swap
|
|
1593
|
+
* here rather than to the offer path, so whoever publishes the market
|
|
1594
|
+
* chooses which backend you are asked to open a transport to. Resolve one
|
|
1595
|
+
* only for markets from an index you trust — this client does not, and
|
|
1596
|
+
* cannot, check that a market named a corridor honestly. */
|
|
1597
|
+
transportFor: (market: DiscoveredMarket) => RfqTransport;
|
|
1598
|
+
discovery: Omit<DiscoverMarketsOptions, "repository">;
|
|
1599
|
+
/** BOLT11 decoder for the solver's hold invoice; required to quote lightning receives. */
|
|
1600
|
+
decodeInvoice?: (bolt11: string) => InvoiceFacts;
|
|
1601
|
+
/** covclaimd's 33-byte compressed pubkey — the lightning-receive claim
|
|
1602
|
+
* packet seals to it; required to quote lightning receives. */
|
|
1603
|
+
covclaimdPubkey?: Uint8Array;
|
|
1604
|
+
/** L1 access; required to quote onchain sends. */
|
|
1605
|
+
chain?: ChainSource;
|
|
1606
|
+
/** L1 claim callback (fee rate and signing are environment-specific);
|
|
1607
|
+
* without it the manager reports onchain claims as blocked. */
|
|
1608
|
+
claimOnchain?: RfqSwapManagerCallbacks["claimOnchain"];
|
|
1609
|
+
emulatorPubkey?: string;
|
|
1610
|
+
/** Overrides the wallet's own connection; for tests and for a caller that
|
|
1611
|
+
* must reach a different operator. */
|
|
1612
|
+
ark?: SwapOperator;
|
|
1613
|
+
indexer?: LockupSpendIndexer;
|
|
1614
|
+
}
|
|
1615
|
+
interface SwapClient {
|
|
1616
|
+
markets(useCache?: boolean): Promise<DiscoveredMarket[]>;
|
|
1617
|
+
quote(market: DiscoveredMarket, input: SwapQuoteInput): Promise<SwapQuote>;
|
|
1618
|
+
accept(quote: SwapQuote): Promise<UnifiedSwap>;
|
|
1619
|
+
/** Spot only: the covenant's cooperative reclaim. */
|
|
1620
|
+
cancel(fundingTxid: string): Promise<void>;
|
|
1621
|
+
/** Every swap this client knows of, both families and both live and ended
|
|
1622
|
+
* — not a live-only view. The RFQ half is bounded by what the manager has
|
|
1623
|
+
* loaded and by `RFQ_SWAP_RETENTION_SECONDS`. */
|
|
1624
|
+
swaps(): Promise<UnifiedSwap[]>;
|
|
1625
|
+
onUpdate(listener: (swap: UnifiedSwap) => void): () => void;
|
|
1626
|
+
start(): Promise<void>;
|
|
1627
|
+
stop(): Promise<void>;
|
|
1628
|
+
readonly manager: RfqSwapManager;
|
|
1629
|
+
}
|
|
1630
|
+
declare function createSwapClient(deps: SwapClientDeps): SwapClient;
|
|
1631
|
+
|
|
1632
|
+
export { ArkadeRefundResult, type ArkadeRefunderDeps, AssetSwap, AssetSwapRepository, AssetSwapStatus, ChainSource, type ClaimPacketInput, type DiscoverMarketsOptions, IndexedDbAssetSwapRepository, InvoiceFacts, LIGHTNING_RAIL, type LightningReceiveProfile, type LightningReceiveQuote, type LightningSendProfile, type LightningSendQuote, LockupAmountMismatchError, LockupContractMissing, type LockupContractReader, type LockupContractWriter, LockupRegistrationFailed, LockupSpendIndexer, LockupVtxo, MarketsCacheEntry, NoSpendableDepositError, OFFER_PACKET_TYPE, ONCHAIN_SWAP_RAIL, type Offer, type OfferContractRetirer, OfferCovenantMismatchError, type OfferSwapWatcher, OnchainHtlc, OnchainHtlcParams, OnchainNetwork, type OnchainSendProfile, type OnchainSendQuote, type OnchainSwapRailDeps, PAYMENT_STATUS, type PlanError, QUOTE_OPTIONS, type RefundBlockedReason, RefundIndexer, RefundNotLocallyPossibleError, type RestoreIndexer, type RfqClaimSecretProjection, type RfqHashlockProjection, type RfqSignerProjection, RfqSwap, type RfqSwapActivityDeps, RfqSwapManager, RfqSwapManagerCallbacks, RfqSwapRecord, RfqSwapState, RfqTransport, SOLVER_LIGHTNING_RAIL, SOLVER_ONCHAIN_RAIL, SWAP_LOCKUP_CONTRACT_KIND, SWAP_LOCKUP_CONTRACT_LABEL, SWAP_LOCKUP_CONTRACT_TYPE, SWAP_ROUTER_PRIORITY, type SealedClaimPacket, type SolverLightningRailDeps, type SolverLightningSend, type SolverOnchainRailDeps, type SolverOnchainSend, type SolverRendezvous, type SpendKind, type SpotQuote, type SwapActivityInput, type SwapClient, type SwapClientDeps, SwapOperator, SwapPaymentFailedError, type SwapPaymentRouterConfig, type SwapQuote, type SwapQuoteInput, type SwapRailClient, SwapSecretsProjection, type Tx, type UnifiedSwap, type WatchOfferSwapsParams, arkadeRefunder, awaitLockupFunding, cancelOffer, chainSourceFrom, claimFeeSats, claimReceiveLockup, classifyDepositSpend, classifySpend, createOffer, createSwapClient, createSwapPaymentRouter, decodeOffer, discoverMarkets, encodeOffer, findMarket, isTerminalStatus, lightningRail, lockupContractParams, makeCachedFeedFetch, offerContract, onchainSendProfile, onchainSwapRail, paymentStatusOf, pushClaim, railAvailable, receiverExact, registerLockupContract, requestLightningReceive, requestLightningSend, requestOnchainSend, restoreAssetSwaps, retireSettledOfferContracts, rfqClaimSecretOf, rfqSecretsProfile, rfqSignerOf, rfqSwapActivityInputs, sealClaimPacket, senderIdentityForSwapRecord, solverLightningRail, solverLightningRendezvous, solverOnchainRail, solverOnchainRendezvous, solverRendezvous, spendTxidsOf, spendUpdate, swapActivityResolver, swapHandle, swapPrograms, validatePlan, watchOfferSwaps };
|