@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
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DiscoveredMarket } from '@arkade-os/solver-discovery';
|
|
2
|
-
import { IWallet, ProvisionedKey, ProvisionedClaimSecret,
|
|
3
|
-
import {
|
|
1
|
+
import { Corridor as Corridor$1, DiscoveredMarket } from '@arkade-os/solver-discovery';
|
|
2
|
+
import { IWallet, ProvisionedKey, ProvisionedClaimSecret, ArkProvider, RestIndexerProvider, VHTLC, Identity, IContractManager, NetworkName } from '@arkade-os/sdk';
|
|
3
|
+
import { D as RfqStatus, R as RfqTransport, a as OnchainHtlc, g as ChainUtxo, C as ChainSource, w as OnchainHtlcPhase } from './rfq-D0KGjmnn.cjs';
|
|
4
4
|
|
|
5
5
|
type AssetSwapStatus = "pending" | "cancelling" | "fulfilled" | "cancelled" | "recoverable" | "awaiting_fill" | "claimable" | "claimed" | "refunded_l1";
|
|
6
6
|
/** The sentinel asset id for BTC itself, as opposed to a 68-hex asset id.
|
|
@@ -210,10 +210,17 @@ declare function awaitRfqResolution(transport: RfqTransport, rfqId: string, opti
|
|
|
210
210
|
pollMs?: number;
|
|
211
211
|
deadline?: number;
|
|
212
212
|
}): Promise<RfqStatus>;
|
|
213
|
-
/** The Ark surface the refund push needs — narrower than a full provider, and
|
|
214
|
-
* satisfied by {@link RestArkProvider}. Same seam style as `RestoreIndexer`. */
|
|
215
|
-
type RefundArkProvider = Pick<RestArkProvider, "getInfo" | "submitTx" | "finalizeTx">;
|
|
216
213
|
/** The indexer surface the lockup lookup needs. */
|
|
214
|
+
/**
|
|
215
|
+
* What a push needs from the operator: the broadcast pair, plus the info read
|
|
216
|
+
* that supplies `checkpointTapscript`.
|
|
217
|
+
*
|
|
218
|
+
* Narrow on purpose. A wallet's own connection satisfies it —
|
|
219
|
+
* `getArkadeBroadcaster()` for the pair and `getArkadeInfo()` for the read — so
|
|
220
|
+
* a plugin composes one from the wallet it already has and never opens a second
|
|
221
|
+
* connection of its own (#734). A full `ArkProvider` still fits, structurally.
|
|
222
|
+
*/
|
|
223
|
+
type SwapOperator = Pick<ArkProvider, "getInfo" | "submitTx" | "finalizeTx">;
|
|
217
224
|
type RefundIndexer = Pick<RestIndexerProvider, "getVtxos">;
|
|
218
225
|
/** A still-refundable virtual output sitting at the swap lockup. */
|
|
219
226
|
interface LockupVtxo {
|
|
@@ -237,12 +244,6 @@ interface LockupVtxo {
|
|
|
237
244
|
* Opposite, but not exhaustive: an unrolled output satisfies neither, and
|
|
238
245
|
* {@link findLockupVtxos} drops those before they reach this type at all.
|
|
239
246
|
*
|
|
240
|
-
* `packages/boltz-swap` splits on exactly this fact rather than working
|
|
241
|
-
* around it: `settleRefundWithoutReceiver` sends a live VTXO through an
|
|
242
|
-
* offchain tx and a recoverable one through `joinBatch` — "a swept
|
|
243
|
-
* (recoverable) VTXO is no longer a live leaf, so it can only be reclaimed
|
|
244
|
-
* by re-registering it into a batch".
|
|
245
|
-
*
|
|
246
247
|
* So the remedy is recovery (renewing the output into a fresh batch),
|
|
247
248
|
* after which the ordinary CLTV refund works again. This package does not
|
|
248
249
|
* build that round — see {@link pushRefundWithoutReceiver}, which refuses
|
|
@@ -260,8 +261,7 @@ interface LockupVtxo {
|
|
|
260
261
|
*
|
|
261
262
|
* **The remedy already exists; this package does not reimplement it.** The SDK
|
|
262
263
|
* recovers swept outputs by re-registering them into a fresh batch, through
|
|
263
|
-
* `IVtxoManager.recoverVtxos()
|
|
264
|
-
* reaches via its own `joinBatch`. It reads the wallet's registered-contract
|
|
264
|
+
* `IVtxoManager.recoverVtxos()`. It reads the wallet's registered-contract
|
|
265
265
|
* snapshot (`recoverVtxos` → `wallet.getVtxos({ withRecoverable: true })` →
|
|
266
266
|
* `contractSnapshot()` → `contractManager.getContractsWithVtxos()`), so it
|
|
267
267
|
* covers a swap lockup as soon as that lockup is registered as a contract —
|
|
@@ -278,8 +278,7 @@ interface LockupVtxo {
|
|
|
278
278
|
* recovery round including this VTXO earlier is rejected. `recoverVtxos`
|
|
279
279
|
* sweeps every recoverable output in ONE settlement and has no CLTV
|
|
280
280
|
* awareness, so recovering early can fail the whole batch rather than just
|
|
281
|
-
* this output.
|
|
282
|
-
* recoverable → skipped".
|
|
281
|
+
* this output.
|
|
283
282
|
*/
|
|
284
283
|
declare class LockupNeedsRecoveryError extends Error {
|
|
285
284
|
readonly name = "LockupNeedsRecoveryError";
|
|
@@ -292,11 +291,10 @@ declare class LockupNeedsRecoveryError extends Error {
|
|
|
292
291
|
* into one settlement with no CLTV awareness, so an early attempt can fail
|
|
293
292
|
* the whole batch — including unrelated outputs that were otherwise fine.
|
|
294
293
|
*
|
|
295
|
-
* Exposed as a value, not only inside the message, so a caller can
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
* not an exact alarm.
|
|
294
|
+
* Exposed as a value, not only inside the message, so a caller can skip
|
|
295
|
+
* pre-CLTV recoverable outputs without parsing prose. Seconds-based
|
|
296
|
+
* locktimes mature against the chain tip's timestamp rather than wall
|
|
297
|
+
* clock, so treat this as a floor to wait past, not an exact alarm.
|
|
300
298
|
*/
|
|
301
299
|
readonly recoverableAfter: bigint;
|
|
302
300
|
constructor(outpoints: string[], recoverableAfter: bigint);
|
|
@@ -315,8 +313,7 @@ declare class LockupNeedsRecoveryError extends Error {
|
|
|
315
313
|
* sat unresolved — which are precisely the ones most likely to have got there.
|
|
316
314
|
* Reading only the spendable set would report `nothing_to_refund` over money
|
|
317
315
|
* that is still sitting at the script, which is worse than an error: it looks
|
|
318
|
-
* like a resolved swap.
|
|
319
|
-
* the same reason (`arkade-swaps.ts`'s `refundableVtxos`).
|
|
316
|
+
* like a resolved swap.
|
|
320
317
|
*
|
|
321
318
|
* **Visible is not the same as refundable.** A `recoverable` output cannot be
|
|
322
319
|
* spent offchain at all — see {@link LockupVtxo.recoverable} — so this set is
|
|
@@ -370,7 +367,7 @@ interface LockupSpend {
|
|
|
370
367
|
checkpointTxid: string;
|
|
371
368
|
/** The ark transaction that spent the above checkpoint output. What
|
|
372
369
|
* history correlation matches on; absent when the indexer omitted it. */
|
|
373
|
-
|
|
370
|
+
txid?: string;
|
|
374
371
|
}
|
|
375
372
|
type LockupFate =
|
|
376
373
|
/** At least one output at the lockup is still unspent. Not over. */
|
|
@@ -501,8 +498,8 @@ declare function readLockupFate(indexer: LockupSpendIndexer, input: {
|
|
|
501
498
|
* outpoints, rather than submitted and rejected. Filtering them out silently
|
|
502
499
|
* would be worse still: it would report success over money that never moved.
|
|
503
500
|
*/
|
|
504
|
-
declare function pushRefundWithoutReceiver(
|
|
505
|
-
|
|
501
|
+
declare function pushRefundWithoutReceiver(operator: SwapOperator, input: {
|
|
502
|
+
contract: InstanceType<typeof VHTLC.ScriptV2>;
|
|
506
503
|
/** The `sender` signer. Build it from the swap record with
|
|
507
504
|
* {@link senderIdentityForSwapRecord} — on an HD wallet that resolves
|
|
508
505
|
* from the seed, with no stored key bytes anywhere, and every way the
|
|
@@ -514,7 +511,7 @@ declare function pushRefundWithoutReceiver(ark: RefundArkProvider, input: {
|
|
|
514
511
|
/** Defaults to the contract's own committed refund destination. */
|
|
515
512
|
refundPkScript?: Uint8Array;
|
|
516
513
|
}): Promise<{
|
|
517
|
-
|
|
514
|
+
txid: string;
|
|
518
515
|
amount: number;
|
|
519
516
|
}>;
|
|
520
517
|
/**
|
|
@@ -536,7 +533,7 @@ type RefundOutcome =
|
|
|
536
533
|
/** The trader took it back via `refundWithoutReceiver`. */
|
|
537
534
|
| {
|
|
538
535
|
outcome: "refunded";
|
|
539
|
-
|
|
536
|
+
txid: string;
|
|
540
537
|
amount: number;
|
|
541
538
|
status: RfqStatus | null;
|
|
542
539
|
}
|
|
@@ -562,7 +559,18 @@ type RefundOutcome =
|
|
|
562
559
|
* The lockup was unilaterally exited: its outputs sit onchain under the VHTLC
|
|
563
560
|
* script, where no offchain refund can reach them. Returned rather than
|
|
564
561
|
* retried: no amount of waiting changes where the money lives. Complete the
|
|
565
|
-
* unroll and spend the outputs onchain
|
|
562
|
+
* unroll and spend the named outputs onchain.
|
|
563
|
+
*
|
|
564
|
+
* **`outpoints` is not necessarily the whole lockup, and `exited` does not
|
|
565
|
+
* mean "nothing left to refund".** A lockup funded by more than one UTXO
|
|
566
|
+
* reports `exited` as soon as ANY of them has been exited, and `outpoints`
|
|
567
|
+
* names only those; a still-live sibling is left unrefunded, and this call
|
|
568
|
+
* will not come back for it — the outcome is terminal, so the caller's retry
|
|
569
|
+
* loop ends here. That is deliberate rather than an oversight: `RfqSwapManager`
|
|
570
|
+
* reports the same lockup `exited` on the same any-output rule, and the two
|
|
571
|
+
* must not disagree. It does make the surviving outputs the caller's to
|
|
572
|
+
* pursue, through an independent onchain or offchain recovery path that this
|
|
573
|
+
* function will not reach.
|
|
566
574
|
*
|
|
567
575
|
* Distinct from {@link RefundOutcome} `needs_recovery` on purpose: that
|
|
568
576
|
* variant's remedy is recovery into a fresh batch, which is a spend no batch
|
|
@@ -619,9 +627,9 @@ type RefundOutcome =
|
|
|
619
627
|
* well past the deadline skips straight to the push, and a lockup that is
|
|
620
628
|
* already empty comes back as `nothing_to_refund` instead of an error.
|
|
621
629
|
*/
|
|
622
|
-
declare function refundIfUnresolved(transport: RfqTransport,
|
|
630
|
+
declare function refundIfUnresolved(transport: RfqTransport, operator: SwapOperator, indexer: LockupSpendIndexer, input: {
|
|
623
631
|
rfqId: string;
|
|
624
|
-
|
|
632
|
+
contract: InstanceType<typeof VHTLC.ScriptV2>;
|
|
625
633
|
/** @see pushRefundWithoutReceiver */
|
|
626
634
|
sender: Identity;
|
|
627
635
|
/**
|
|
@@ -787,7 +795,7 @@ interface RfqSwapCommon {
|
|
|
787
795
|
createdAt: number;
|
|
788
796
|
updatedAt: number;
|
|
789
797
|
/** Set once the trader's own `refundWithoutReceiver` push landed. */
|
|
790
|
-
|
|
798
|
+
refundTxid?: string;
|
|
791
799
|
/**
|
|
792
800
|
* The ark transactions that SPENT the lockup, stamped from the chain read
|
|
793
801
|
* that ended the swap — `LockupFate.spends`, whichever verdict it reached.
|
|
@@ -796,8 +804,8 @@ interface RfqSwapCommon {
|
|
|
796
804
|
* a solver reclaim on a receive, and — the exception — the trader's own
|
|
797
805
|
* claim when a receive settles. What they have in common is that no local
|
|
798
806
|
* action produced them, so nothing else on this record can name them:
|
|
799
|
-
* {@link
|
|
800
|
-
* `
|
|
807
|
+
* {@link refundTxid} names only a push this wallet made, and
|
|
808
|
+
* `claimTxid` only a submission it made.
|
|
801
809
|
*
|
|
802
810
|
* Stamped so a terminal record answers "which transaction ended this" from
|
|
803
811
|
* storage. Without it the only source is another read of the lockup — a
|
|
@@ -805,12 +813,20 @@ interface RfqSwapCommon {
|
|
|
805
813
|
* has to pay on the offline-first path where it is least affordable.
|
|
806
814
|
*
|
|
807
815
|
* Absent when the swap ended without a chain verdict, or when the indexer
|
|
808
|
-
* named the checkpoint but not the ark transaction — the same `
|
|
816
|
+
* named the checkpoint but not the ark transaction — the same `txid`
|
|
809
817
|
* `LockupSpend` declares optional, for the same reason.
|
|
810
818
|
*/
|
|
811
|
-
|
|
819
|
+
lockupSpendTxids?: string[];
|
|
812
820
|
/** Why `state` is `failed`. */
|
|
813
821
|
failure?: string;
|
|
822
|
+
/**
|
|
823
|
+
* Last local claim error while the receive swap is still retryable.
|
|
824
|
+
*
|
|
825
|
+
* Distinct from terminal `failure`: this records a claim attempt that failed
|
|
826
|
+
* before the window closed. If the window later closes without a submitted
|
|
827
|
+
* claim, it becomes the terminal failure reason.
|
|
828
|
+
*/
|
|
829
|
+
claimFailure?: string;
|
|
814
830
|
/** Why `state` is `needs_counterparty`. Distinct from {@link failure},
|
|
815
831
|
* which means an action was attempted and did not work. */
|
|
816
832
|
blockedReason?: string;
|
|
@@ -875,7 +891,7 @@ interface LightningReceiveSwap extends RfqSwapCommon {
|
|
|
875
891
|
expectedAmount: number;
|
|
876
892
|
/** Our Arkade claim's txid, once submitted. Set from the callback's return
|
|
877
893
|
* and never from a chain read — the chain's answer is `settled`. */
|
|
878
|
-
|
|
894
|
+
claimTxid?: string;
|
|
879
895
|
}
|
|
880
896
|
/**
|
|
881
897
|
* A monitored swap.
|
|
@@ -886,8 +902,8 @@ interface LightningReceiveSwap extends RfqSwapCommon {
|
|
|
886
902
|
* writes and rebuilds these itself, through
|
|
887
903
|
* {@link RfqSwapManager.restoreFromRepository}. A caller keeping its own store
|
|
888
904
|
* projects it in {@link RfqSwapManagerCallbacks.saveSwap} instead, rebuilds it
|
|
889
|
-
* on restart the way it was made — `
|
|
890
|
-
* `
|
|
905
|
+
* on restart the way it was made — `lightningSendContract` /
|
|
906
|
+
* `lightningReceiveContract` / `onchainHtlcScript` over the quote's binding fields —
|
|
891
907
|
* and hands the result to {@link RfqSwapManager.start}.
|
|
892
908
|
*
|
|
893
909
|
* **`onchain:BTC->arkade:BTC` is deliberately not a member yet.** Its Arkade
|
|
@@ -945,7 +961,7 @@ declare function nextOnchainAction(input: {
|
|
|
945
961
|
/** What the trader's own `refundWithoutReceiver` push returned, or `null` when
|
|
946
962
|
* the lockup held nothing to return. */
|
|
947
963
|
type ArkadeRefundResult = {
|
|
948
|
-
|
|
964
|
+
txid: string;
|
|
949
965
|
amount: number;
|
|
950
966
|
} | null;
|
|
951
967
|
/**
|
|
@@ -996,7 +1012,7 @@ interface RfqSwapManagerCallbacks {
|
|
|
996
1012
|
* funding that arrived piecemeal can still be swept. */
|
|
997
1013
|
partiallyClaimed: boolean;
|
|
998
1014
|
}) => Promise<{
|
|
999
|
-
|
|
1015
|
+
txid: string;
|
|
1000
1016
|
amount: number;
|
|
1001
1017
|
}>;
|
|
1002
1018
|
/**
|
|
@@ -1110,7 +1126,7 @@ interface RfqSwapManagerConfig {
|
|
|
1110
1126
|
}
|
|
1111
1127
|
/** The contract-manager surface this needs, narrowed for injection — the same
|
|
1112
1128
|
* seam style as {@link LockupSpendIndexer} and `refund.ts`'s
|
|
1113
|
-
*
|
|
1129
|
+
* `RefundIndexer`, and satisfied structurally by a real
|
|
1114
1130
|
* `ContractManager` (`await wallet.getContractManager()`). */
|
|
1115
1131
|
type SwapContractRegistry = Pick<IContractManager, "createContract" | "getContracts" | "onContractEvent" | "setContractWatchState">;
|
|
1116
1132
|
/**
|
|
@@ -1311,20 +1327,6 @@ declare class RfqSwapManager {
|
|
|
1311
1327
|
* sign may well have been restored.
|
|
1312
1328
|
*/
|
|
1313
1329
|
private readonly refundRefused;
|
|
1314
|
-
/**
|
|
1315
|
-
* The last error a receive swap's claim callback threw, by rfqId.
|
|
1316
|
-
*
|
|
1317
|
-
* Kept only to tell two terminal outcomes apart once the claim window
|
|
1318
|
-
* shuts: a swap whose claim was attempted and kept failing ends `failed`
|
|
1319
|
-
* with that reason, while one that simply never became claimable ends
|
|
1320
|
-
* `refunded`. Without it a broken claim callback would resolve a caller's
|
|
1321
|
-
* {@link waitForSwapCompletion} as an ordinary unwind.
|
|
1322
|
-
*
|
|
1323
|
-
* Process-local, like {@link refundRefused}: after a restart the same swap
|
|
1324
|
-
* ends `refunded` instead, which costs the caller a reason and nothing else
|
|
1325
|
-
* — every throw was already reported through `onSwapFailed` as it happened.
|
|
1326
|
-
*/
|
|
1327
|
-
private readonly lastClaimError;
|
|
1328
1330
|
/**
|
|
1329
1331
|
* The lockup outpoints a receive swap's claim callback has already been
|
|
1330
1332
|
* handed, by rfqId.
|
|
@@ -1490,6 +1492,17 @@ declare class RfqSwapManager {
|
|
|
1490
1492
|
removeSwap(rfqId: string): Promise<void>;
|
|
1491
1493
|
/** Every swap still being monitored. */
|
|
1492
1494
|
getPendingSwaps(): Promise<RfqSwap[]>;
|
|
1495
|
+
/**
|
|
1496
|
+
* Every swap this manager holds — {@link getPendingSwaps} plus the ones
|
|
1497
|
+
* that already ended. The two sets are disjoint: a swap leaves `monitored`
|
|
1498
|
+
* as it enters `finished`.
|
|
1499
|
+
*
|
|
1500
|
+
* The finished half is what this process has seen, which after
|
|
1501
|
+
* {@link restoreFromRepository} is the stored history minus what retention
|
|
1502
|
+
* pruned. A manager that has restored nothing answers with the live swaps
|
|
1503
|
+
* alone.
|
|
1504
|
+
*/
|
|
1505
|
+
getAllSwaps(): Promise<RfqSwap[]>;
|
|
1493
1506
|
hasSwap(rfqId: string): Promise<boolean>;
|
|
1494
1507
|
/** True while an action for this swap holds the per-swap lock. */
|
|
1495
1508
|
isProcessing(rfqId: string): Promise<boolean>;
|
|
@@ -1651,7 +1664,7 @@ declare class RfqSwapManager {
|
|
|
1651
1664
|
/**
|
|
1652
1665
|
* Record which ark transactions ended the lockup.
|
|
1653
1666
|
*
|
|
1654
|
-
* Only the ones the indexer actually named: `LockupSpend.
|
|
1667
|
+
* Only the ones the indexer actually named: `LockupSpend.txid` is
|
|
1655
1668
|
* optional, and a checkpoint txid is not what history correlates on — a
|
|
1656
1669
|
* record carrying one would name a transaction the wallet's own activity
|
|
1657
1670
|
* never shows. Fewer txids is the right failure here.
|
|
@@ -1695,10 +1708,9 @@ declare class RfqSwapManager {
|
|
|
1695
1708
|
/**
|
|
1696
1709
|
* Drop a terminal swap from monitoring and report it exactly once.
|
|
1697
1710
|
*
|
|
1698
|
-
* `onSwapCompleted` and `onSwapFailed` are mutually exclusive here
|
|
1699
|
-
*
|
|
1700
|
-
*
|
|
1701
|
-
* also fires on failure is a trap worth not inheriting.
|
|
1711
|
+
* `onSwapCompleted` and `onSwapFailed` are mutually exclusive here: a
|
|
1712
|
+
* listener named "completed" that also fires on failure is a trap, so a
|
|
1713
|
+
* swap that leaves monitoring reports through exactly one of them.
|
|
1702
1714
|
*/
|
|
1703
1715
|
private finalize;
|
|
1704
1716
|
private settleWaiters;
|
|
@@ -1798,7 +1810,7 @@ interface RfqSwapOrigin {
|
|
|
1798
1810
|
* learns it. So it is written once at record creation, like {@link amount},
|
|
1799
1811
|
* and no corridor `project` emits it.
|
|
1800
1812
|
*/
|
|
1801
|
-
|
|
1813
|
+
fundingTxid?: string;
|
|
1802
1814
|
}
|
|
1803
1815
|
/** The stored record: the origin plus the manager's mutable state. */
|
|
1804
1816
|
interface RfqSwapRecord extends RfqSwapOrigin {
|
|
@@ -1806,14 +1818,39 @@ interface RfqSwapRecord extends RfqSwapOrigin {
|
|
|
1806
1818
|
state: RfqSwapState;
|
|
1807
1819
|
createdAt: number;
|
|
1808
1820
|
updatedAt: number;
|
|
1809
|
-
|
|
1821
|
+
refundTxid?: string;
|
|
1810
1822
|
/** The ark transactions that spent the lockup, stamped by the manager from
|
|
1811
1823
|
* the chain read that ended the swap. See
|
|
1812
|
-
* `RfqSwapCommon.
|
|
1813
|
-
|
|
1824
|
+
* `RfqSwapCommon.lockupSpendTxids`. */
|
|
1825
|
+
lockupSpendTxids?: string[];
|
|
1814
1826
|
failure?: string;
|
|
1827
|
+
/** Last local receive-claim error while the swap is still retryable. */
|
|
1828
|
+
claimFailure?: string;
|
|
1815
1829
|
blockedReason?: string;
|
|
1816
1830
|
}
|
|
1831
|
+
/**
|
|
1832
|
+
* A stored record read under the current field names.
|
|
1833
|
+
*
|
|
1834
|
+
* Four fields were renamed after `0.0.9`: `fundingArkTxid`, `refundArkTxid`,
|
|
1835
|
+
* `lockupSpendArkTxids`, and the receive corridor's `profile.claimArkTxid`. All
|
|
1836
|
+
* four arrived together with record persistence itself in `0.0.8`, so `0.0.8`
|
|
1837
|
+
* and `0.0.9` are the only versions that ever wrote them. Backends store the
|
|
1838
|
+
* record WHOLE, so such a store still holds the old names on disk and the
|
|
1839
|
+
* current code reads every one of them as `undefined`.
|
|
1840
|
+
*
|
|
1841
|
+
* Called by every function here that takes a record, so a consumer needs no
|
|
1842
|
+
* boot-time migration of its own; and because the old keys never reach the
|
|
1843
|
+
* object handed back, the next write persists the record without them.
|
|
1844
|
+
*
|
|
1845
|
+
* What it buys, in descending order of sharpness: a receive leg with a partial
|
|
1846
|
+
* claim already out keeps its `claimTxid`, so the value gate stays disarmed
|
|
1847
|
+
* rather than re-blocking the rest of a lockup whose preimage is public;
|
|
1848
|
+
* `refunded` swaps report an outcome txid again; and activity answers from the
|
|
1849
|
+
* record instead of falling back to a lockup read per query. It is NOT a
|
|
1850
|
+
* re-refund fix — `refunded` is terminal, and `restoreFromRepository` puts a
|
|
1851
|
+
* terminal record straight into `finished` without ever driving it.
|
|
1852
|
+
*/
|
|
1853
|
+
declare function normalizeRfqSwapRecord(record: RfqSwapRecord): RfqSwapRecord;
|
|
1817
1854
|
/** First write, at the moment the caller hands the swap to the manager. */
|
|
1818
1855
|
declare function createRfqSwapRecord(origin: RfqSwapOrigin, swap: PersistableRfqSwap): RfqSwapRecord;
|
|
1819
1856
|
/**
|
|
@@ -1822,8 +1859,9 @@ declare function createRfqSwapRecord(origin: RfqSwapOrigin, swap: PersistableRfq
|
|
|
1822
1859
|
* The mutable half is REPLACED, not merged. `managerState` omits a key the live
|
|
1823
1860
|
* swap no longer carries, so spreading it over the old record could only ever
|
|
1824
1861
|
* set these fields, never clear them. The manager clears them on purpose: it
|
|
1825
|
-
* deletes `blockedReason` when a swap leaves `needs_counterparty
|
|
1826
|
-
*
|
|
1862
|
+
* deletes `blockedReason` when a swap leaves `needs_counterparty` and
|
|
1863
|
+
* `claimFailure` when a swap becomes terminal, precisely because stale mutable
|
|
1864
|
+
* reasons read as live refusal or retry state.
|
|
1827
1865
|
*/
|
|
1828
1866
|
declare function updateRfqSwapRecord(record: RfqSwapRecord, swap: PersistableRfqSwap): RfqSwapRecord;
|
|
1829
1867
|
/**
|
|
@@ -1832,9 +1870,9 @@ declare function updateRfqSwapRecord(record: RfqSwapRecord, swap: PersistableRfq
|
|
|
1832
1870
|
* A record IS an origin plus manager state, so `record` where an
|
|
1833
1871
|
* {@link RfqSwapOrigin} is wanted type-checks — and is a bug. Spread into
|
|
1834
1872
|
* {@link createRfqSwapRecord} it carries the OLD state's `failure`,
|
|
1835
|
-
* `blockedReason` and `
|
|
1836
|
-
* the live swap no longer has and therefore cannot clear
|
|
1837
|
-
* trap {@link updateRfqSwapRecord} strips those
|
|
1873
|
+
* `blockedReason`, `claimFailure` and `refundTxid` past `managerState`, which omits
|
|
1874
|
+
* fields the live swap no longer has and therefore cannot clear them. That is the same
|
|
1875
|
+
* trap {@link updateRfqSwapRecord} strips those mutable fields to avoid; this is
|
|
1838
1876
|
* how a caller holding only a record gets an origin that is safe to keep.
|
|
1839
1877
|
*
|
|
1840
1878
|
* What `RfqSwapManager.restoreFromRepository` remembers for each record it
|
|
@@ -1869,6 +1907,924 @@ declare function rebuildRfqSwap(record: RfqSwapRecord, params: LockupParams): Pe
|
|
|
1869
1907
|
*/
|
|
1870
1908
|
declare function shouldRetainRfqSwap(record: RfqSwapRecord, now: number): boolean;
|
|
1871
1909
|
|
|
1910
|
+
declare const ATOMIC_DECIMAL: unique symbol;
|
|
1911
|
+
/** Atomic units written out: the form records and the wire hold. */
|
|
1912
|
+
type AtomicDecimal = string & {
|
|
1913
|
+
readonly [ATOMIC_DECIMAL]: true;
|
|
1914
|
+
};
|
|
1915
|
+
|
|
1916
|
+
/**
|
|
1917
|
+
* Asset identity for the v2 client: CAIP-19 with the rail as the CAIP-2
|
|
1918
|
+
* namespace — `<rail>:<network>/<asset-ns>:<reference>`.
|
|
1919
|
+
*
|
|
1920
|
+
* The rail is the namespace rather than the settlement chain because sameness
|
|
1921
|
+
* across rails is then a comparison on the asset part instead of a shared
|
|
1922
|
+
* string: `arkade:bitcoin/slip44:0` and `bitcoin:bitcoin/slip44:0` are one BTC
|
|
1923
|
+
* on two rails, and nothing has to agree on a single id for them. Arkade has no
|
|
1924
|
+
* CAIP-2 namespace and no bitcoin-chain identity to nest under, so
|
|
1925
|
+
* `bip122:…/arkade:…` would assert a relationship it does not have.
|
|
1926
|
+
*
|
|
1927
|
+
* These ids parse under CAIP-19 and resolve under no published namespace spec:
|
|
1928
|
+
* `arkade`, `bitcoin` and `bolt11` are registered in no CASA registry. That is
|
|
1929
|
+
* the price of naming rails instead of chains, and it costs nothing here — this
|
|
1930
|
+
* module is the grammar, and what an id *means* is the alias layer's job.
|
|
1931
|
+
*/
|
|
1932
|
+
|
|
1933
|
+
/**
|
|
1934
|
+
* A CAIP-2 namespace this client can spell. Closed rather than open to the
|
|
1935
|
+
* CAIP-2 character class, so a rail nobody implements is a parse failure here
|
|
1936
|
+
* rather than a lookup miss three layers down.
|
|
1937
|
+
*
|
|
1938
|
+
* `bolt11` is lightning: CAIP-2 caps a namespace at eight characters, which
|
|
1939
|
+
* `lightning` overruns, and floors it at three, which `ln` misses. It names the
|
|
1940
|
+
* instrument the rail carries today; BOLT12 is a separate corridor when it
|
|
1941
|
+
* ships.
|
|
1942
|
+
*
|
|
1943
|
+
* `eip155` is grammar and nothing else. §9's EVM corridor is deferred, and §9
|
|
1944
|
+
* exists to prove the seams hold, so its own examples have to parse; the
|
|
1945
|
+
* refusal belongs where a route is chosen, not where a string is read, and the
|
|
1946
|
+
* alias layer is where it happens.
|
|
1947
|
+
*/
|
|
1948
|
+
declare const RAILS: readonly ["arkade", "bitcoin", "bolt11", "eip155"];
|
|
1949
|
+
type Rail = (typeof RAILS)[number];
|
|
1950
|
+
/** The rails whose CAIP-2 reference is a bitcoin network rather than a chain id. */
|
|
1951
|
+
declare const BITCOIN_RAILS: readonly ["arkade", "bitcoin", "bolt11"];
|
|
1952
|
+
type BitcoinRail = (typeof BITCOIN_RAILS)[number];
|
|
1953
|
+
/**
|
|
1954
|
+
* The network half of a bitcoin-family chain part: core's own
|
|
1955
|
+
* {@link NetworkName}, because the wallet is the only source of the network —
|
|
1956
|
+
* v2 accepts no server URL anywhere — and this is the vocabulary a wallet
|
|
1957
|
+
* resolves to.
|
|
1958
|
+
*
|
|
1959
|
+
* It is one wider than discovery's `NETWORKS`, which omits `testnet`. That
|
|
1960
|
+
* difference belongs to the alias layer and not to the grammar: an asset on
|
|
1961
|
+
* testnet exists whether or not anyone publishes a market index for it, so
|
|
1962
|
+
* amputating the identity to match the index would make a network the SDK fully
|
|
1963
|
+
* supports unnameable.
|
|
1964
|
+
*/
|
|
1965
|
+
type NetworkRef = NetworkName;
|
|
1966
|
+
type BitcoinAssetId<R extends BitcoinRail> = `${R}:${NetworkRef}/${string}:${string}`;
|
|
1967
|
+
/**
|
|
1968
|
+
* A public asset id.
|
|
1969
|
+
*
|
|
1970
|
+
* A template literal type and not `string`, which is what makes the other three
|
|
1971
|
+
* asset spellings in this package — core's 68-hex `asset.AssetId#toString()`,
|
|
1972
|
+
* discovery's `AssetInfo.id`, the RFQ leg's `arkade:BTC` — a compile error in a
|
|
1973
|
+
* slot that wants a public id, rather than a wrong pair string three layers
|
|
1974
|
+
* down. The rail parameter carries that further: `AssetId<"arkade">` accepts no
|
|
1975
|
+
* `bitcoin:` string, which is what makes an endpoint whose corridor and asset
|
|
1976
|
+
* disagree a compile error (see `route.ts`).
|
|
1977
|
+
*
|
|
1978
|
+
* Deliberately not branded — `client.quote({ give: "arkade:bitcoin/slip44:0" })`
|
|
1979
|
+
* must stay writable, and the spec's own examples are written that way — so the
|
|
1980
|
+
* shape is what the type checks and {@link parseAssetId} is the gate for
|
|
1981
|
+
* everything else. A value out of a record or off the wire is a `string`: parse
|
|
1982
|
+
* it, never cast it.
|
|
1983
|
+
*/
|
|
1984
|
+
type AssetId<R extends Rail = Rail> = R extends BitcoinRail ? BitcoinAssetId<R> : `eip155:${number}/${string}:${string}`;
|
|
1985
|
+
/** The `<asset-ns>:<reference>` half of an id — what sameness compares. */
|
|
1986
|
+
type AssetPart = `${string}:${string}`;
|
|
1987
|
+
|
|
1988
|
+
/**
|
|
1989
|
+
* The corridor axis, and its bijection with the rail namespaces.
|
|
1990
|
+
*
|
|
1991
|
+
* One axis, two vocabularies: discovery speaks `arkade | lightning | onchain`
|
|
1992
|
+
* and an asset id's CAIP-2 namespace is `arkade | bolt11 | bitcoin`. They agree
|
|
1993
|
+
* on one member of three. Collapsing them was the alternative and it loses
|
|
1994
|
+
* either way — take the rail names and every market lookup translates on the
|
|
1995
|
+
* way out to discovery; take the corridor names and `lightning:` overruns
|
|
1996
|
+
* CAIP-2's eight-character namespace cap.
|
|
1997
|
+
*
|
|
1998
|
+
* So both stay, and the disagreement is spent once, here: `Corridor` is
|
|
1999
|
+
* discovery's type verbatim, {@link railOfCorridor} is total, and `route.ts`
|
|
2000
|
+
* ties an endpoint's corridor to its asset's rail in the type system so the two
|
|
2001
|
+
* cannot disagree in a value.
|
|
2002
|
+
*/
|
|
2003
|
+
|
|
2004
|
+
/**
|
|
2005
|
+
* The corridor a leg settles on.
|
|
2006
|
+
*
|
|
2007
|
+
* Aliased from discovery rather than re-declared: it is discovery's vocabulary,
|
|
2008
|
+
* the alias layer has to speak it, and a re-declaration would drift silently
|
|
2009
|
+
* the day discovery adds a corridor.
|
|
2010
|
+
*/
|
|
2011
|
+
type Corridor = Corridor$1;
|
|
2012
|
+
/**
|
|
2013
|
+
* A corridor id: the three implemented corridors plus §9's EVM chains.
|
|
2014
|
+
*
|
|
2015
|
+
* The template arm stays open against the registry's closed enum on purpose —
|
|
2016
|
+
* which chains a registry lists is a listing decision, not an id-grammar one.
|
|
2017
|
+
*/
|
|
2018
|
+
type CorridorId = Corridor | `eip155:${number}`;
|
|
2019
|
+
/** The rail namespace a corridor's assets are spelled on. */
|
|
2020
|
+
type RailOf<C extends CorridorId> = C extends "arkade" ? "arkade" : C extends "lightning" ? "bolt11" : C extends "onchain" ? "bitcoin" : "eip155";
|
|
2021
|
+
|
|
2022
|
+
/**
|
|
2023
|
+
* The two string aliases the v2 surface is written in.
|
|
2024
|
+
*
|
|
2025
|
+
* Aliases rather than branded types: they document what a field carries at the
|
|
2026
|
+
* places §3.1's `Quote` reads (`lock.hash`, `solver`) without making every
|
|
2027
|
+
* literal go through a constructor. Core exports neither, so nothing here is a
|
|
2028
|
+
* duplicate.
|
|
2029
|
+
*/
|
|
2030
|
+
/** Lowercase hex, no `0x` prefix — the encoding `@scure/base`'s `hex` emits. */
|
|
2031
|
+
type Hex = string;
|
|
2032
|
+
/** A secp256k1 public key as {@link Hex}: x-only (32 bytes) or compressed (33). */
|
|
2033
|
+
type Pubkey = Hex;
|
|
2034
|
+
|
|
2035
|
+
/**
|
|
2036
|
+
* One outcome vocabulary, and the translation that produces it.
|
|
2037
|
+
*
|
|
2038
|
+
* The protocol keeps two state machines — `RfqSwapState` for the corridors and
|
|
2039
|
+
* `AssetSwapStatus` for offers — and v1 handed both to every consumer, who then
|
|
2040
|
+
* wrote `family === "offer" ? swap.status : swap.state` and decided what each
|
|
2041
|
+
* word meant. {@link Outcome} is that decision, made once.
|
|
2042
|
+
*
|
|
2043
|
+
* **The axis is not send-versus-receive: it is whose lockup the pass reads.**
|
|
2044
|
+
* On `arkade -> lightning` and `arkade -> onchain` the arkade lockup is the
|
|
2045
|
+
* trader's, so a non-claim spend of it returns the trader's money. On
|
|
2046
|
+
* `lightning -> arkade` it is the solver's, so the identical chain read means
|
|
2047
|
+
* the trader's incoming payment never arrived. `refunded` and `lapsed` are that
|
|
2048
|
+
* one difference, and the reason this enum refuses to inherit the protocol's
|
|
2049
|
+
* word: on the receive leg the state the wire calls `refunded` is a LOSS.
|
|
2050
|
+
*
|
|
2051
|
+
* The ownership is not restated here. {@link LIGHTNING_DRIVE} and
|
|
2052
|
+
* {@link ONCHAIN_DRIVE} already declare it per route side — that is what M2
|
|
2053
|
+
* minted `CorridorPass` for — so this module reads it off them and a corridor
|
|
2054
|
+
* that changes its mind is a compile error rather than a second table to keep
|
|
2055
|
+
* in step.
|
|
2056
|
+
*
|
|
2057
|
+
* Three outcomes come from neither machine, and they are the record's and the
|
|
2058
|
+
* clock's: `accepted` and `funding` describe a record the drive holds no live
|
|
2059
|
+
* state for, and `refunding` a send leg past its deadline with a push in
|
|
2060
|
+
* flight. All three still carry the raw word their machine holds, because
|
|
2061
|
+
* {@link SwapUpdate.detail} is `RawState` and `RawState` is the raw word.
|
|
2062
|
+
*/
|
|
2063
|
+
|
|
2064
|
+
/**
|
|
2065
|
+
* What happened to a swap, in one vocabulary for both families.
|
|
2066
|
+
*
|
|
2067
|
+
* Trader-centric by definition. Fourteen members, §3.5's, and the two that
|
|
2068
|
+
* carry the whole point are `refunded` — the trader's value came back — and
|
|
2069
|
+
* `lapsed`, the solver reclaiming a lockup the trader failed to claim.
|
|
2070
|
+
*/
|
|
2071
|
+
type Outcome =
|
|
2072
|
+
/** Persisted, and its funding has not been broadcast. */
|
|
2073
|
+
"accepted"
|
|
2074
|
+
/** The funding is broadcast and the drive has not adopted the swap yet. */
|
|
2075
|
+
| "funding"
|
|
2076
|
+
/** The trader's lockup is funded and the swap is live. */
|
|
2077
|
+
| "funded"
|
|
2078
|
+
/** Waiting on the counterparty: an unpaid invoice, an unfilled offer. */
|
|
2079
|
+
| "open"
|
|
2080
|
+
/** An offer covenant was filled. */
|
|
2081
|
+
| "filled"
|
|
2082
|
+
/** The trader's own claim is confirmed on chain. */
|
|
2083
|
+
| "claimed"
|
|
2084
|
+
/** A lightning send settled: the solver's hash-verified spend IS the
|
|
2085
|
+
* invoice being paid. */
|
|
2086
|
+
| "paid" | "cancelling" | "cancelled"
|
|
2087
|
+
/** A send leg past `refundLocktime` with a refund push in flight. */
|
|
2088
|
+
| "refunding"
|
|
2089
|
+
/** The trader's value came back. Send legs only, ever. */
|
|
2090
|
+
| "refunded"
|
|
2091
|
+
/** A receive leg the trader never claimed: the solver took the lockup back
|
|
2092
|
+
* and the incoming payment never arrived. */
|
|
2093
|
+
| "lapsed"
|
|
2094
|
+
/** Surfaced, never silently retried, and never terminal. */
|
|
2095
|
+
| "needs_recovery"
|
|
2096
|
+
/** An action failed and its window closed. */
|
|
2097
|
+
| "failed";
|
|
2098
|
+
/**
|
|
2099
|
+
* The untranslated protocol word, for support and audit.
|
|
2100
|
+
*
|
|
2101
|
+
* A tagged union over the two families rather than a bare string, so M6's
|
|
2102
|
+
* cancel path reads the family split off the update one lookup earlier than a
|
|
2103
|
+
* repository read. The reason strings are deliberately NOT here: `failure` and
|
|
2104
|
+
* `blockedReason` arrive on the record as fields, not as vocabulary, and
|
|
2105
|
+
* {@link Swap} is where a consumer reads them.
|
|
2106
|
+
*
|
|
2107
|
+
* `wire`, `htlc` and `fate` are the supporting vocabularies a pass consults.
|
|
2108
|
+
* They are declared because the shape is fixed here rather than left to the
|
|
2109
|
+
* first consumer that wants one, and populated by nothing today: the manager
|
|
2110
|
+
* surfaces neither the L1 phase nor the lockup fate on its update callback, and
|
|
2111
|
+
* the solver's own `RfqStatus` is a self-report no drive pass reads at all.
|
|
2112
|
+
*/
|
|
2113
|
+
type RawState = {
|
|
2114
|
+
readonly family: "rfq";
|
|
2115
|
+
readonly state: RfqSwapState;
|
|
2116
|
+
readonly wire?: RfqStatus["state"];
|
|
2117
|
+
readonly htlc?: OnchainHtlcPhase;
|
|
2118
|
+
readonly fate?: LockupFate;
|
|
2119
|
+
} | {
|
|
2120
|
+
readonly family: "offer";
|
|
2121
|
+
readonly status: AssetSwapStatus;
|
|
2122
|
+
};
|
|
2123
|
+
/** What `onUpdate` delivers. */
|
|
2124
|
+
interface SwapUpdate {
|
|
2125
|
+
readonly swap: Swap;
|
|
2126
|
+
readonly outcome: Outcome;
|
|
2127
|
+
readonly detail: RawState;
|
|
2128
|
+
}
|
|
2129
|
+
/** What every listener registration hands back. */
|
|
2130
|
+
type Unsubscribe = () => void;
|
|
2131
|
+
|
|
2132
|
+
/**
|
|
2133
|
+
* A route is two endpoints, each an asset on a corridor, plus the instrument
|
|
2134
|
+
* that settles it.
|
|
2135
|
+
*
|
|
2136
|
+
* Two invariants live in the types rather than in a check. An endpoint's
|
|
2137
|
+
* corridor and its asset cannot disagree, because {@link Endpoint} is a union
|
|
2138
|
+
* with one member per corridor and each member types its asset as
|
|
2139
|
+
* {@link AssetOn} of that one corridor. And `onchain -> arkade` is not in
|
|
2140
|
+
* {@link Route} at all, so once a route has been resolved the misroute is a
|
|
2141
|
+
* compile error; before resolution it is `UnsupportedRoute`, thrown ahead of
|
|
2142
|
+
* RFQ disclosure, artifact creation, persistence and funding.
|
|
2143
|
+
*/
|
|
2144
|
+
|
|
2145
|
+
/**
|
|
2146
|
+
* A leg's concrete settlement locus. Direction comes from give versus take,
|
|
2147
|
+
* never from the instrument.
|
|
2148
|
+
*
|
|
2149
|
+
* `{ kind: "wallet" }` is the only instrument the SDK holds signing authority
|
|
2150
|
+
* over, which is why it is the only one nobody passes: `accept()` spends the
|
|
2151
|
+
* balance and lands the claim on wallet legs and merely watches the others. The
|
|
2152
|
+
* supply law is that the caller provides non-wallet take instruments (that is
|
|
2153
|
+
* what `to` is), the quote provides non-wallet give instruments (that is
|
|
2154
|
+
* exactly what the artifact is), and every remaining slot resolves to `wallet`.
|
|
2155
|
+
*
|
|
2156
|
+
* `wallet` is an explicit variant rather than an absent field because absence
|
|
2157
|
+
* would mean two unrelated things — wallet-by-default and not-yet-resolved —
|
|
2158
|
+
* and a receive leg lives in the second state until the quote returns.
|
|
2159
|
+
*/
|
|
2160
|
+
type Instrument = {
|
|
2161
|
+
kind: "wallet";
|
|
2162
|
+
} | {
|
|
2163
|
+
kind: "address";
|
|
2164
|
+
address: string;
|
|
2165
|
+
} | {
|
|
2166
|
+
kind: "invoice";
|
|
2167
|
+
bolt11: string;
|
|
2168
|
+
paymentHash: Hex;
|
|
2169
|
+
amount?: bigint;
|
|
2170
|
+
expiresAt: number;
|
|
2171
|
+
};
|
|
2172
|
+
/**
|
|
2173
|
+
* The asset ids corridor `C` can carry.
|
|
2174
|
+
*
|
|
2175
|
+
* On the three bitcoin-family corridors the corridor names no network, so the
|
|
2176
|
+
* tie is the rail alone. On an EVM corridor it is more than that: `eip155:8453`
|
|
2177
|
+
* *is* the CAIP-2 chain part its assets are spelled with, so the asset id has to
|
|
2178
|
+
* start with the corridor id verbatim. Enforcing only the rail there would admit
|
|
2179
|
+
* `eip155:1/erc20:…` on a Base corridor — the same near-miss `sameAsset` refuses
|
|
2180
|
+
* one layer up, an address being a chain's fact and not a token's.
|
|
2181
|
+
*/
|
|
2182
|
+
type AssetOn<C extends CorridorId> = C extends Corridor ? AssetId<RailOf<C>> : `${C}/${AssetPart}`;
|
|
2183
|
+
/**
|
|
2184
|
+
* An asset on a corridor, with the instrument that settles it.
|
|
2185
|
+
*
|
|
2186
|
+
* `corridor` is a cross-check rather than an input: every id already carries
|
|
2187
|
+
* its rail. Typing `asset` against that rail is what makes the cross-check free
|
|
2188
|
+
* — there is no value in which the two disagree.
|
|
2189
|
+
*
|
|
2190
|
+
* Distributed over `C` rather than written as one object whose two fields both
|
|
2191
|
+
* mention it: `{ corridor: C; asset: AssetOn<C> }` at `C = CorridorId` widens
|
|
2192
|
+
* *each field independently* to its own union, and correlates nothing —
|
|
2193
|
+
* `{ corridor: "arkade", asset: "bitcoin:…" }` satisfies it. The conditional
|
|
2194
|
+
* makes `Endpoint` the union of the four single-corridor shapes instead, so the
|
|
2195
|
+
* pairing survives the default type argument, which is the case every unwitnessed
|
|
2196
|
+
* `Endpoint` in a signature lands on.
|
|
2197
|
+
*/
|
|
2198
|
+
type Endpoint<C extends CorridorId = CorridorId> = C extends CorridorId ? {
|
|
2199
|
+
corridor: C;
|
|
2200
|
+
asset: AssetOn<C>;
|
|
2201
|
+
/** Resolved by the client, never constructed by callers. */
|
|
2202
|
+
instrument: Instrument;
|
|
2203
|
+
} : never;
|
|
2204
|
+
/** Shorthand for one corridor's endpoint, as the route union spells it. */
|
|
2205
|
+
type Ep<C extends CorridorId> = Endpoint<C>;
|
|
2206
|
+
/**
|
|
2207
|
+
* The implemented routes, as a closed union.
|
|
2208
|
+
*
|
|
2209
|
+
* `onchain -> arkade` is deliberately absent until the manager owns the
|
|
2210
|
+
* trader's L1 refund path end to end.
|
|
2211
|
+
*/
|
|
2212
|
+
type Route = {
|
|
2213
|
+
give: Ep<"arkade">;
|
|
2214
|
+
take: Ep<"arkade">;
|
|
2215
|
+
} | {
|
|
2216
|
+
give: Ep<"arkade">;
|
|
2217
|
+
take: Ep<"lightning">;
|
|
2218
|
+
} | {
|
|
2219
|
+
give: Ep<"lightning">;
|
|
2220
|
+
take: Ep<"arkade">;
|
|
2221
|
+
} | {
|
|
2222
|
+
give: Ep<"arkade">;
|
|
2223
|
+
take: Ep<"onchain">;
|
|
2224
|
+
};
|
|
2225
|
+
/**
|
|
2226
|
+
* The one thing a counterparty must see, when a route has one.
|
|
2227
|
+
*
|
|
2228
|
+
* The deposit variant carries no `chain` field. §3.4 reserved one and Q12 made
|
|
2229
|
+
* it redundant: the chain part lives inside `asset`, and a second, untyped
|
|
2230
|
+
* spelling of it is a fact two fields can disagree about with nothing checking.
|
|
2231
|
+
* `corridor` stays because it is the typed axis `route.ts` already ties to the
|
|
2232
|
+
* asset's rail — a cross-check, where `chain?: string | number` was a copy.
|
|
2233
|
+
*/
|
|
2234
|
+
type Artifact = {
|
|
2235
|
+
kind: "invoice";
|
|
2236
|
+
bolt11: string;
|
|
2237
|
+
} | DepositArtifact;
|
|
2238
|
+
/**
|
|
2239
|
+
* The deposit half of {@link Artifact}, distributed over the corridor for the
|
|
2240
|
+
* reason {@link Endpoint} is: `corridor` is only a cross-check if a value cannot
|
|
2241
|
+
* spell it against an asset from another corridor.
|
|
2242
|
+
*/
|
|
2243
|
+
type DepositArtifact<C extends CorridorId = CorridorId> = C extends CorridorId ? {
|
|
2244
|
+
kind: "deposit";
|
|
2245
|
+
corridor: C;
|
|
2246
|
+
address: string;
|
|
2247
|
+
asset: AssetOn<C>;
|
|
2248
|
+
amount: bigint;
|
|
2249
|
+
expiresAt?: number;
|
|
2250
|
+
} : never;
|
|
2251
|
+
|
|
2252
|
+
/** Which side of the trade an amount pins, in v2's vocabulary. */
|
|
2253
|
+
type AmountOn = "give" | "take";
|
|
2254
|
+
|
|
2255
|
+
/**
|
|
2256
|
+
* What a quote is: the order, fully resolved, plus the provenance that says who
|
|
2257
|
+
* priced it and where that card came from.
|
|
2258
|
+
*
|
|
2259
|
+
* M1 named these shapes and left them to whichever milestone decided their
|
|
2260
|
+
* semantics; this is that milestone, so `QuoteId`, `QuoteInput`, `Quote`,
|
|
2261
|
+
* `MarketRef` and `AuctionProvenance` are declared here rather than beside the
|
|
2262
|
+
* types they are built out of. Nothing in this module has behaviour — the quote
|
|
2263
|
+
* path assembles these, `accept()` (M4) consumes them.
|
|
2264
|
+
*
|
|
2265
|
+
* The one rule worth restating at the top: a `Quote` is binding terms plus the
|
|
2266
|
+
* evidence for them. Every field is either something the caller must act on
|
|
2267
|
+
* (the two obligations, the artifact, the deadline) or something they must be
|
|
2268
|
+
* able to audit afterwards (the market, the solver, the checks that passed).
|
|
2269
|
+
* Nothing internal rides along — no covenant, no secret, no transport.
|
|
2270
|
+
*/
|
|
2271
|
+
|
|
2272
|
+
/**
|
|
2273
|
+
* A quote's identity, minted by the client at quote time.
|
|
2274
|
+
*
|
|
2275
|
+
* Client-minted everywhere, not just where the wire offers no id: a feed-priced
|
|
2276
|
+
* offer quote has no solver-minted id at all, and `accept()` is idempotent by
|
|
2277
|
+
* quote id *and only* by quote id (§3.2), so an identity that exists on one
|
|
2278
|
+
* backend and not the other could not carry that rule. An alias rather than a
|
|
2279
|
+
* brand, matching `Hex` and `Pubkey` beside it.
|
|
2280
|
+
*/
|
|
2281
|
+
type QuoteId = string;
|
|
2282
|
+
/**
|
|
2283
|
+
* A caller's spelling of an asset: a public id, or a ticker the alias layer
|
|
2284
|
+
* canonicalizes against the registry.
|
|
2285
|
+
*
|
|
2286
|
+
* `AssetId | (string & {})` rather than `string`, so an editor still completes
|
|
2287
|
+
* the id form and a mistyped id still shows up as one — `AssetId | string`
|
|
2288
|
+
* collapses to `string` and takes both with it.
|
|
2289
|
+
*/
|
|
2290
|
+
type AssetRef = AssetId | (string & {});
|
|
2291
|
+
/**
|
|
2292
|
+
* Everything a caller supplies. §4's bottom line: two asset ids or one
|
|
2293
|
+
* destination string, one amount, which side it pins, and — on receives, where
|
|
2294
|
+
* no instrument exists yet — a corridor.
|
|
2295
|
+
*/
|
|
2296
|
+
interface QuoteInput {
|
|
2297
|
+
/** Omitted when the route determines it: the give leg is the wallet's. */
|
|
2298
|
+
give?: AssetRef;
|
|
2299
|
+
/** Omitted when `to` determines it — a corridor that carries BTC only does. */
|
|
2300
|
+
take?: AssetRef;
|
|
2301
|
+
/** A self-describing instrument: bolt11, an Arkade address, or `bc1…`. */
|
|
2302
|
+
to?: string;
|
|
2303
|
+
/** Receive flows only: names the corridor when no instrument can exist yet. */
|
|
2304
|
+
via?: CorridorId;
|
|
2305
|
+
/** Atomic units. Exactly one amount may be pinned — see `amountOn`. */
|
|
2306
|
+
amount?: bigint;
|
|
2307
|
+
/** Which leg `amount` pins. Required with `amount`, and refused with an
|
|
2308
|
+
* invoice, which pins one already. */
|
|
2309
|
+
amountOn?: AmountOn;
|
|
2310
|
+
}
|
|
2311
|
+
/** Which backend priced a quote. The card decides it; no client switch does. */
|
|
2312
|
+
type MarketBackend = "rfq" | "feed";
|
|
2313
|
+
/** Where a snapshot came from, and how fresh it is. */
|
|
2314
|
+
interface SnapshotRef {
|
|
2315
|
+
/** Unix ms the markets were read from their sources. */
|
|
2316
|
+
readonly fetchedAt: number;
|
|
2317
|
+
/**
|
|
2318
|
+
* The registry answered in this read, so the cards are registry-served
|
|
2319
|
+
* rather than replayed out of local storage.
|
|
2320
|
+
*
|
|
2321
|
+
* `false` is not an error: a stale snapshot still resolves and still prices
|
|
2322
|
+
* a feed-priced quote — it is marked, not refused. What it cannot do is
|
|
2323
|
+
* supply the key an addressed RFQ's responder is checked against, because
|
|
2324
|
+
* that field is unvalidated cache content (`isMarketShaped` revalidates
|
|
2325
|
+
* four fields and trusts the rest).
|
|
2326
|
+
*/
|
|
2327
|
+
readonly live: boolean;
|
|
2328
|
+
/** How the markets were obtained. */
|
|
2329
|
+
readonly source: "live" | "cache" | "injected";
|
|
2330
|
+
/** The registry URL behind it, or `undefined` for an injected snapshot. */
|
|
2331
|
+
readonly registry?: string;
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Which card priced a quote, and from which registry.
|
|
2335
|
+
*
|
|
2336
|
+
* A union rather than a bag of optionals, because §10's published RFQ is the
|
|
2337
|
+
* one place the sentence "the market picks the backend" stops: a quote closed
|
|
2338
|
+
* out of an open auction has a market *key* and no card behind it, so every
|
|
2339
|
+
* card-derived field is absent at once rather than one at a time. Sizing that
|
|
2340
|
+
* arm now costs a discriminant and keeps the addressed arm total.
|
|
2341
|
+
*/
|
|
2342
|
+
type MarketRef = CardMarketRef | AuctionMarketRef;
|
|
2343
|
+
interface CardMarketRef {
|
|
2344
|
+
readonly kind: "card";
|
|
2345
|
+
/**
|
|
2346
|
+
* The canonical market key, `<corridor>:<id>/<corridor>:<id>`, derived under
|
|
2347
|
+
* rfq-protocol.md §2's leg order — arkade first when exactly one leg is
|
|
2348
|
+
* arkade, lexicographic otherwise — and never read off the card's own
|
|
2349
|
+
* base/quote order. The two agree for every card the registry's reducer
|
|
2350
|
+
* validated, and a card published outside it is exactly where the silent
|
|
2351
|
+
* miss lives.
|
|
2352
|
+
*/
|
|
2353
|
+
readonly key: string;
|
|
2354
|
+
readonly backend: MarketBackend;
|
|
2355
|
+
/** The registry URL, or the label a locally pinned card was loaded under. */
|
|
2356
|
+
readonly source: string;
|
|
2357
|
+
readonly sourceType: "registry" | "local";
|
|
2358
|
+
/** The solver's name, as the card publishes it. Display, never identity. */
|
|
2359
|
+
readonly solver: string;
|
|
2360
|
+
/** The card's signing key. Absent on spot cards, which need no rendezvous. */
|
|
2361
|
+
readonly discoveryPubkey?: Pubkey;
|
|
2362
|
+
/** The card's display label, e.g. `BTC/lightning:BTC`. Display only. */
|
|
2363
|
+
readonly pair: string;
|
|
2364
|
+
readonly snapshot: SnapshotRef;
|
|
2365
|
+
}
|
|
2366
|
+
/** §10, reserved: a quote closed out of a published auction has no card. */
|
|
2367
|
+
interface AuctionMarketRef {
|
|
2368
|
+
readonly kind: "auction";
|
|
2369
|
+
readonly key: string;
|
|
2370
|
+
readonly backend: "rfq";
|
|
2371
|
+
}
|
|
2372
|
+
/**
|
|
2373
|
+
* One bid seen in a published auction (§10, Q9).
|
|
2374
|
+
*
|
|
2375
|
+
* Typed against ts-sdk #777's shipped draft rather than invented: a bid is a
|
|
2376
|
+
* counter-amount on the leg the client did not fix, attributed to the event key
|
|
2377
|
+
* that signed it — which is NOT the covenant's `solver_pubkey`, a role key the
|
|
2378
|
+
* quote fills from a different field.
|
|
2379
|
+
*/
|
|
2380
|
+
interface RankedBid {
|
|
2381
|
+
/** The event key that signed the bid. Attribution, not a covenant role. */
|
|
2382
|
+
readonly bidder: Pubkey;
|
|
2383
|
+
/** The counter-amount, on the leg the client left free. */
|
|
2384
|
+
readonly amount: bigint;
|
|
2385
|
+
readonly amountOn: AmountOn;
|
|
2386
|
+
readonly expiresAt: number;
|
|
2387
|
+
}
|
|
2388
|
+
/**
|
|
2389
|
+
* The auction a published-RFQ quote was closed out of (§10, Q9).
|
|
2390
|
+
*
|
|
2391
|
+
* Reserved and inert: `quote()` never populates it, and Q9 froze the name so
|
|
2392
|
+
* the shape it will take cannot be occupied by something else in the meantime.
|
|
2393
|
+
*/
|
|
2394
|
+
interface AuctionProvenance {
|
|
2395
|
+
/** The market key the open request was tagged with. */
|
|
2396
|
+
readonly marketKey: string;
|
|
2397
|
+
/** The bid that was closed with. */
|
|
2398
|
+
readonly winner: RankedBid;
|
|
2399
|
+
/** Every other bid seen before the window closed. */
|
|
2400
|
+
readonly losers: readonly RankedBid[];
|
|
2401
|
+
/** Unix seconds the bid window closed. */
|
|
2402
|
+
readonly closedAt: number;
|
|
2403
|
+
}
|
|
2404
|
+
/** One leg's obligation: an asset, and exactly how much of it. */
|
|
2405
|
+
interface QuoteLeg {
|
|
2406
|
+
readonly asset: AssetId;
|
|
2407
|
+
readonly amount: bigint;
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* The order, fully resolved.
|
|
2411
|
+
*
|
|
2412
|
+
* Both amounts are exact obligations with the fee already inside them, which is
|
|
2413
|
+
* what `fee` restates rather than adds: it is the spread, precomputed, so a
|
|
2414
|
+
* verb (M7) can compare it to a ceiling without re-deriving it from a price.
|
|
2415
|
+
*/
|
|
2416
|
+
interface Quote {
|
|
2417
|
+
readonly id: QuoteId;
|
|
2418
|
+
/** Both endpoints resolved, instruments included. */
|
|
2419
|
+
readonly route: Route;
|
|
2420
|
+
/** What the trader gives, fee included. */
|
|
2421
|
+
readonly give: QuoteLeg;
|
|
2422
|
+
/** What the trader takes. */
|
|
2423
|
+
readonly take: QuoteLeg;
|
|
2424
|
+
/** Corridor routes: the hash both covenants commit to. */
|
|
2425
|
+
readonly lock?: {
|
|
2426
|
+
readonly hash: Hex;
|
|
2427
|
+
};
|
|
2428
|
+
/** Which card priced this, from which registry, how fresh. */
|
|
2429
|
+
readonly market: MarketRef;
|
|
2430
|
+
/** RFQ routes: the committed counterparty, from the quote's covenant role. */
|
|
2431
|
+
readonly solver?: Pubkey;
|
|
2432
|
+
/** §10 only, and never populated today. */
|
|
2433
|
+
readonly auction?: AuctionProvenance;
|
|
2434
|
+
/** Unix seconds. Non-optional on both backends — a feed-priced quote has no
|
|
2435
|
+
* wire expiry to inherit, so the client mints one from the feed's freshness. */
|
|
2436
|
+
readonly expiresAt: number;
|
|
2437
|
+
/**
|
|
2438
|
+
* Corridor routes: when the trader's value comes back if the swap does not
|
|
2439
|
+
* complete.
|
|
2440
|
+
*
|
|
2441
|
+
* Optional on the type because an asset swap has no refund clock at all —
|
|
2442
|
+
* an offer covenant never expires — and non-optional in practice on every
|
|
2443
|
+
* corridor route, where the wire's own field is optional and the client
|
|
2444
|
+
* refuses a quote without it.
|
|
2445
|
+
*/
|
|
2446
|
+
readonly refundLocktime?: number;
|
|
2447
|
+
/** The one thing a counterparty must see, when this route has one. */
|
|
2448
|
+
readonly artifact?: Artifact;
|
|
2449
|
+
/** The spread, denominated on the leg where it is exact. */
|
|
2450
|
+
readonly fee: {
|
|
2451
|
+
readonly amount: bigint;
|
|
2452
|
+
readonly asset: AssetId;
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
/**
|
|
2456
|
+
* An endpoint as `resolve()` can answer for it, before any disclosure.
|
|
2457
|
+
*
|
|
2458
|
+
* Not an `Endpoint`: that type's instrument is non-optional, deliberately, and a
|
|
2459
|
+
* receive leg has none until the quote returns — the instrument IS the artifact
|
|
2460
|
+
* the solver mints. Absence here means exactly that one thing, because the
|
|
2461
|
+
* wallet case is spelled `{ kind: "wallet" }` rather than left out.
|
|
2462
|
+
*/
|
|
2463
|
+
interface ResolvedEndpoint {
|
|
2464
|
+
readonly corridor: Corridor;
|
|
2465
|
+
readonly asset: AssetId;
|
|
2466
|
+
/** Absent only while the leg's instrument does not exist yet. */
|
|
2467
|
+
readonly instrument?: Instrument;
|
|
2468
|
+
}
|
|
2469
|
+
/** The amount a caller (or an invoice) pinned, and which leg it pins. */
|
|
2470
|
+
interface PinnedAmount {
|
|
2471
|
+
readonly value: bigint;
|
|
2472
|
+
readonly on: AmountOn;
|
|
2473
|
+
/** What pinned it, for the diagnostic an `AmountMismatch` carries. */
|
|
2474
|
+
readonly source: "caller" | "invoice";
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* What `resolve()` answers: the route's shape, the market that would price it,
|
|
2478
|
+
* and what the active snapshot actually serves.
|
|
2479
|
+
*
|
|
2480
|
+
* `eligible` is reported alongside rather than folded into an error because
|
|
2481
|
+
* zero is not a failure of resolution: the destination parsed, the corridor pair
|
|
2482
|
+
* is implemented, and nothing about the route is wrong — there is simply no
|
|
2483
|
+
* market for it on this snapshot. `quote()` is where that becomes
|
|
2484
|
+
* `UnsupportedRoute`, since a quote cannot proceed past market selection.
|
|
2485
|
+
*/
|
|
2486
|
+
interface RouteResolution {
|
|
2487
|
+
readonly give: ResolvedEndpoint;
|
|
2488
|
+
readonly take: ResolvedEndpoint;
|
|
2489
|
+
/** The card that would price it: the first eligible one after policy. */
|
|
2490
|
+
readonly market?: MarketRef;
|
|
2491
|
+
/** How many markets serve this pair on the active snapshot, after policy. */
|
|
2492
|
+
readonly eligible: number;
|
|
2493
|
+
/** Where the market data came from, and how fresh it is. */
|
|
2494
|
+
readonly snapshot: SnapshotRef;
|
|
2495
|
+
/** The amount pinned so far, when the input pinned one. */
|
|
2496
|
+
readonly amount?: PinnedAmount;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* What survives a crash, and the public shape `accept()` hands back.
|
|
2501
|
+
*
|
|
2502
|
+
* Two types, one key. {@link SwapRecord} is the storage form — JSON-safe by
|
|
2503
|
+
* declaration, every amount a canonical decimal string — and {@link Swap} is the
|
|
2504
|
+
* answer a caller reads, with `bigint` amounts and the resolved `Route`. The
|
|
2505
|
+
* conversion between them is §D's record-boundary codec, and it is not a third
|
|
2506
|
+
* law: `toAtomicDecimal`/`fromAtomicDecimal` already ship in `./amount`, minted
|
|
2507
|
+
* by M1 and used by M3's wire adapter, so both sites emit the same canonical
|
|
2508
|
+
* form — atomic units, unsigned, no leading zeros, never a scaled display
|
|
2509
|
+
* decimal.
|
|
2510
|
+
*
|
|
2511
|
+
* **The key is the quote id, on every route.** That is what makes persist-first
|
|
2512
|
+
* representable at all: v1's `AssetSwap.id` *is* the funding txid
|
|
2513
|
+
* (`store.ts:70-71`), so a record could not exist before the money did, and
|
|
2514
|
+
* `coverage.ts` carries a process-local issuance mark precisely to paper over
|
|
2515
|
+
* the gap. Here the record precedes the funding and `fundingTxid` is a later,
|
|
2516
|
+
* best-effort write.
|
|
2517
|
+
*
|
|
2518
|
+
* **JSON-safe means no `bigint` anywhere, at any depth.** The SQLite and Realm
|
|
2519
|
+
* backends `JSON.stringify` the record whole, so a `bigint` throws on two
|
|
2520
|
+
* backends and round-trips on the third — the asymmetry
|
|
2521
|
+
* `test/repository.test.ts` refuses to paper over. Every amount here is an
|
|
2522
|
+
* {@link AtomicDecimal}; `test/client/record.types.ts` proves the absence
|
|
2523
|
+
* structurally rather than by review.
|
|
2524
|
+
*/
|
|
2525
|
+
|
|
2526
|
+
/**
|
|
2527
|
+
* Which family a record belongs to, and the discriminant M5's `RawState` keys
|
|
2528
|
+
* on.
|
|
2529
|
+
*
|
|
2530
|
+
* Not a cosmetic tag. v1's two families occupied one `string` id space by
|
|
2531
|
+
* accident — an offer record's id a funding txid, a corridor record's an
|
|
2532
|
+
* `rfqId` — and keying both on `QuoteId` closes that collision (§B). The tag is
|
|
2533
|
+
* what M6's public id carries, and what lets M5 branch its outcome table
|
|
2534
|
+
* without a repository read.
|
|
2535
|
+
*/
|
|
2536
|
+
type SwapFamily = "offer" | "rfq";
|
|
2537
|
+
/**
|
|
2538
|
+
* The public swap id: the family tag over the client-minted quote id,
|
|
2539
|
+
* `offer:<QuoteId>` / `rfq:<QuoteId>`, as {@link Swap.id} carries it.
|
|
2540
|
+
*
|
|
2541
|
+
* The tag is presentation, not a second identity — storage, the drive and
|
|
2542
|
+
* `accept()`'s idempotency stay keyed on the bare quote id. But a branded
|
|
2543
|
+
* string erases at runtime, so the tag is not cosmetic either: it makes
|
|
2544
|
+
* `NotCancellable` a parse of the prefix — a corridor id refused with no
|
|
2545
|
+
* repository read, an offer id distinguished from a corridor id the way the v1
|
|
2546
|
+
* read could not be — and it retypes the id a caller hands around, so
|
|
2547
|
+
* `cancel(swap.id)` and `recover(swap.id)` stop being two spellings of one
|
|
2548
|
+
* thing. An id arriving untagged from `/protocol`'s re-exported readers takes
|
|
2549
|
+
* the same one read the tagged form takes.
|
|
2550
|
+
*/
|
|
2551
|
+
type AssetSwapId = `${SwapFamily}:${QuoteId}`;
|
|
2552
|
+
/** One leg's obligation, in the form a record holds it. */
|
|
2553
|
+
interface RecordedLeg {
|
|
2554
|
+
readonly asset: AssetId;
|
|
2555
|
+
/** Atomic units as a canonical decimal string — never a `bigint`. */
|
|
2556
|
+
readonly amount: AtomicDecimal;
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2559
|
+
* An endpoint as the record holds it: the corridor, the asset, the instrument.
|
|
2560
|
+
*
|
|
2561
|
+
* `Instrument`'s invoice arm carries a `bigint` amount, so it cannot be stored
|
|
2562
|
+
* as declared — {@link RecordedInstrument} is the same union with that one field
|
|
2563
|
+
* in decimal form. The rest is field for field identical, which is what keeps the
|
|
2564
|
+
* comparison in `acceptConflict` honest.
|
|
2565
|
+
*/
|
|
2566
|
+
interface RecordedEndpoint {
|
|
2567
|
+
readonly corridor: CorridorId;
|
|
2568
|
+
readonly asset: AssetId;
|
|
2569
|
+
readonly instrument: RecordedInstrument;
|
|
2570
|
+
}
|
|
2571
|
+
/** {@link Instrument}, with the invoice arm's amount in decimal form. */
|
|
2572
|
+
type RecordedInstrument = {
|
|
2573
|
+
readonly kind: "wallet";
|
|
2574
|
+
} | {
|
|
2575
|
+
readonly kind: "address";
|
|
2576
|
+
readonly address: string;
|
|
2577
|
+
} | {
|
|
2578
|
+
readonly kind: "invoice";
|
|
2579
|
+
readonly bolt11: string;
|
|
2580
|
+
readonly paymentHash: Hex;
|
|
2581
|
+
readonly amount?: AtomicDecimal;
|
|
2582
|
+
readonly expiresAt: number;
|
|
2583
|
+
};
|
|
2584
|
+
/** {@link Artifact}, with the deposit arm's amount in decimal form. */
|
|
2585
|
+
type RecordedArtifact = {
|
|
2586
|
+
readonly kind: "invoice";
|
|
2587
|
+
readonly bolt11: string;
|
|
2588
|
+
} | {
|
|
2589
|
+
readonly kind: "deposit";
|
|
2590
|
+
readonly corridor: CorridorId;
|
|
2591
|
+
readonly address: string;
|
|
2592
|
+
readonly asset: AssetId;
|
|
2593
|
+
readonly amount: AtomicDecimal;
|
|
2594
|
+
readonly expiresAt?: number;
|
|
2595
|
+
};
|
|
2596
|
+
/**
|
|
2597
|
+
* The half both families carry.
|
|
2598
|
+
*
|
|
2599
|
+
* Every field is either something `AcceptConflict` compares (§3.2's list), or
|
|
2600
|
+
* something M5 named as a cross-milestone ask, or the two timestamps. Nothing
|
|
2601
|
+
* is here for display: a record carries what no covenant and no chain read can
|
|
2602
|
+
* give back, which is the same rule `RfqSwapRecord` is arranged by.
|
|
2603
|
+
*/
|
|
2604
|
+
interface SwapRecordCommon {
|
|
2605
|
+
/** The client-minted quote id — the primary key, per C1 and §B. */
|
|
2606
|
+
readonly id: QuoteId;
|
|
2607
|
+
readonly family: SwapFamily;
|
|
2608
|
+
/**
|
|
2609
|
+
* Both endpoints, instruments included — `AcceptConflict` items 1 and 3.
|
|
2610
|
+
*
|
|
2611
|
+
* Nested under `route` so the record's field names are `Quote`'s field
|
|
2612
|
+
* names: the conflict check walks the two shapes together, and a record
|
|
2613
|
+
* that spelled the same fact differently would make every comparison a
|
|
2614
|
+
* translation.
|
|
2615
|
+
*/
|
|
2616
|
+
readonly route: {
|
|
2617
|
+
readonly give: RecordedEndpoint;
|
|
2618
|
+
readonly take: RecordedEndpoint;
|
|
2619
|
+
};
|
|
2620
|
+
/** The two obligations — `AcceptConflict` item 2. */
|
|
2621
|
+
readonly give: RecordedLeg;
|
|
2622
|
+
readonly take: RecordedLeg;
|
|
2623
|
+
/** The spread, as the quote precomputed it. */
|
|
2624
|
+
readonly fee: RecordedLeg;
|
|
2625
|
+
/**
|
|
2626
|
+
* Which card priced this, from which registry, how fresh — stored WHOLE.
|
|
2627
|
+
*
|
|
2628
|
+
* A trimmed projection could not rebuild the type {@link Swap.market}
|
|
2629
|
+
* promises: `CardMarketRef` requires `kind`, `pair` and `snapshot` beside
|
|
2630
|
+
* the fields a summary would keep. Every member is a string, number or
|
|
2631
|
+
* boolean, so the union round-trips through JSON untouched, and `snapshot`
|
|
2632
|
+
* is restated as read at accept rather than restamped — a past `fetchedAt`
|
|
2633
|
+
* beside the recorded `live` is the honest answer about how fresh the card
|
|
2634
|
+
* was when this swap was accepted.
|
|
2635
|
+
*/
|
|
2636
|
+
readonly market: MarketRef;
|
|
2637
|
+
/**
|
|
2638
|
+
* The committed counterparty, from the quote's covenant role.
|
|
2639
|
+
*
|
|
2640
|
+
* NOT `CardMarketRef.solver`, which is the card's display name. Two
|
|
2641
|
+
* different facts that v1 spelled with one word: this one is a key that
|
|
2642
|
+
* ends up in a covenant leaf, the other is a label. `AcceptConflict`
|
|
2643
|
+
* compares this one.
|
|
2644
|
+
*/
|
|
2645
|
+
readonly solver?: Pubkey;
|
|
2646
|
+
/** The quote's own deadline, unix seconds — what makes a stalled accept a
|
|
2647
|
+
* benign abandon rather than a live obligation. */
|
|
2648
|
+
readonly expiresAt: number;
|
|
2649
|
+
/**
|
|
2650
|
+
* The one thing a counterparty must see, when this route has one.
|
|
2651
|
+
*
|
|
2652
|
+
* Durable because a duplicate accept must return the SAME invoice, and the
|
|
2653
|
+
* invoice lives on the quote object — nowhere in the corridor profile. A
|
|
2654
|
+
* caller that re-accepts after a restart has no quote object left, so
|
|
2655
|
+
* without this field the only honest answer would be a second invoice,
|
|
2656
|
+
* which §3.2 forbids by name.
|
|
2657
|
+
*/
|
|
2658
|
+
readonly artifact?: RecordedArtifact;
|
|
2659
|
+
/**
|
|
2660
|
+
* The transaction that funded this swap, once known.
|
|
2661
|
+
*
|
|
2662
|
+
* A later, best-effort write, and the field that separates M5's `accepted`
|
|
2663
|
+
* from `funding`. Set-where-absent is a benign resume and never an
|
|
2664
|
+
* `AcceptConflict` — §3.2 says so by name.
|
|
2665
|
+
*/
|
|
2666
|
+
readonly fundingTxid?: string;
|
|
2667
|
+
/**
|
|
2668
|
+
* Last local receive-claim error while the swap is still retryable. If the
|
|
2669
|
+
* claim window later closes without a submitted claim, this becomes the
|
|
2670
|
+
* terminal failure reason after restore.
|
|
2671
|
+
*/
|
|
2672
|
+
readonly claimFailure?: string;
|
|
2673
|
+
/** Terminal failure reason. */
|
|
2674
|
+
readonly failure?: string;
|
|
2675
|
+
/** Refusal reason while `state` is `needs_counterparty`. */
|
|
2676
|
+
readonly blockedReason?: string;
|
|
2677
|
+
/** Unix **seconds**, both — the unit `RfqSwapRecord` carries and
|
|
2678
|
+
* `shouldRetainRfqSwap` compares against, not `AssetSwap`'s milliseconds. */
|
|
2679
|
+
readonly createdAt: number;
|
|
2680
|
+
readonly updatedAt: number;
|
|
2681
|
+
}
|
|
2682
|
+
/**
|
|
2683
|
+
* `arkade <-> arkade`: the offer covenant, and what cancels it.
|
|
2684
|
+
*
|
|
2685
|
+
* `offerHex` is the whole covenant — `cancelOffer` needs nothing else to
|
|
2686
|
+
* rebuild it — so this arm stores no tree parameters of its own.
|
|
2687
|
+
*/
|
|
2688
|
+
interface OfferSwapRecord extends SwapRecordCommon {
|
|
2689
|
+
readonly family: "offer";
|
|
2690
|
+
/** v1's raw status vocabulary, which M5's `RawState` reads verbatim. */
|
|
2691
|
+
readonly status: AssetSwapStatus;
|
|
2692
|
+
/** The TLV offer, hex. The only input `cancelOffer` needs. */
|
|
2693
|
+
readonly offerHex: string;
|
|
2694
|
+
readonly swapAddress: string;
|
|
2695
|
+
/** The covenant's scriptPubKey, hex — the indexer's monitoring key, and
|
|
2696
|
+
* what §F's reconcile matches a discovered deposit against. */
|
|
2697
|
+
readonly swapPkScript: string;
|
|
2698
|
+
readonly spentTxid?: string;
|
|
2699
|
+
readonly completedAt?: number;
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* The three corridor routes: a VHTLC lockup, its clocks and its secrets.
|
|
2703
|
+
*
|
|
2704
|
+
* **No covenant tree here.** Every lockup registers a contract row before its
|
|
2705
|
+
* address can be funded, and that row already holds the parameters, keyed by
|
|
2706
|
+
* the script they derive — a key `createContract` refuses to write unless the
|
|
2707
|
+
* params reproduce it. Storing the tree a second time would be two sources for
|
|
2708
|
+
* one covenant. `accept()` is what writes that row (see `./accept.ts`), which
|
|
2709
|
+
* is why a persisted record always has one.
|
|
2710
|
+
*/
|
|
2711
|
+
interface CorridorSwapRecord extends SwapRecordCommon {
|
|
2712
|
+
readonly family: "rfq";
|
|
2713
|
+
/** v1's raw state vocabulary, read verbatim by M5's `RawState`. */
|
|
2714
|
+
readonly state: RfqSwapState;
|
|
2715
|
+
/**
|
|
2716
|
+
* Which corridor, in the manager's own vocabulary.
|
|
2717
|
+
*
|
|
2718
|
+
* `PersistableRfqSwap["kind"]` rather than a `Corridor`: it is a route pair
|
|
2719
|
+
* — `lightning_send` and `lightning_receive` are one corridor from opposite
|
|
2720
|
+
* ends — and it is what resolves the handler that owns {@link profile}.
|
|
2721
|
+
*/
|
|
2722
|
+
readonly kind: PersistableRfqSwap["kind"];
|
|
2723
|
+
/** The solver's own id for the negotiation, echoed back on the wire. */
|
|
2724
|
+
readonly rfqId: string;
|
|
2725
|
+
/** The Arkade address that was funded, and the swap's handle on its
|
|
2726
|
+
* covenant row. */
|
|
2727
|
+
readonly lockupAddress: string;
|
|
2728
|
+
/** Its pkScript, hex — the row's key, and §F's matching key. */
|
|
2729
|
+
readonly lockupPkScript: string;
|
|
2730
|
+
/** The hash both covenants commit to. `sha256(P)`, hex. */
|
|
2731
|
+
readonly lock: {
|
|
2732
|
+
readonly hash: Hex;
|
|
2733
|
+
};
|
|
2734
|
+
/** When the trader's value comes back if the swap does not complete. */
|
|
2735
|
+
readonly refundLocktime: number;
|
|
2736
|
+
/**
|
|
2737
|
+
* The corridor's own half, as plain JSON.
|
|
2738
|
+
*
|
|
2739
|
+
* v1's opaque bag (`rfqRecord.ts:108-123`), written with `rfqSecretsProfile`
|
|
2740
|
+
* and read by `rfqCorridorHandlers.hydrate` — reused rather than
|
|
2741
|
+
* reinvented, so M5 rebuilds through machinery that already exists and a new
|
|
2742
|
+
* corridor still ships without touching this file. It is also what carries
|
|
2743
|
+
* `expectedAmount`, the claim value gate's request-time input.
|
|
2744
|
+
*
|
|
2745
|
+
* Amounts inside it follow v1's shapes (`expectedAmount` is a `number`),
|
|
2746
|
+
* which is JSON-safe and therefore fine: the decimal-string law governs
|
|
2747
|
+
* this record's OWN amount fields, not the bag it carries forward.
|
|
2748
|
+
*
|
|
2749
|
+
* **This is also where the swap's secrets live** — `profile.signer` and,
|
|
2750
|
+
* on a leg locked to a preimage, `profile.hashlock`. Deliberately not a
|
|
2751
|
+
* second copy at the record's top level: `rfqClaimSecretOf` and
|
|
2752
|
+
* `preimageForSwapRecord` already read them from here, and two homes for
|
|
2753
|
+
* one claim secret is two things to keep in step with one of them always
|
|
2754
|
+
* empty. At most one of `preimageHex`/`preimageSaltHex` is ever written,
|
|
2755
|
+
* and which arm exists is decided by the wallet's provisioning result, not
|
|
2756
|
+
* here.
|
|
2757
|
+
*/
|
|
2758
|
+
readonly profile: Record<string, unknown>;
|
|
2759
|
+
readonly refundTxid?: string;
|
|
2760
|
+
readonly lockupSpendTxids?: readonly string[];
|
|
2761
|
+
}
|
|
2762
|
+
/** Everything `accept()` persists, both families in one key space. */
|
|
2763
|
+
type SwapRecord = OfferSwapRecord | CorridorSwapRecord;
|
|
2764
|
+
/**
|
|
2765
|
+
* A swap, as a caller reads it.
|
|
2766
|
+
*
|
|
2767
|
+
* The quote's terms plus what has happened to them. `bigint` amounts and the
|
|
2768
|
+
* resolved `Route`, because this is the public answer and the record is the
|
|
2769
|
+
* storage form — §D's codec is the boundary between the two.
|
|
2770
|
+
*
|
|
2771
|
+
* `artifact` stays optional: M7's `ReceiveRequest` is `Swap & { artifact:
|
|
2772
|
+
* Artifact }`, and an intersection cannot narrow a field that is already
|
|
2773
|
+
* required. `id` is the tagged public form {@link AssetSwapId} — minted from
|
|
2774
|
+
* `record.family` by {@link swapOf} — while storage, the drive and `accept()`'s
|
|
2775
|
+
* idempotency stay keyed on the bare quote id.
|
|
2776
|
+
*
|
|
2777
|
+
* {@link outcome} and the two reason strings are M5's, and they are here rather
|
|
2778
|
+
* than on `SwapUpdate.detail` because `detail` is typed `RawState` — the raw
|
|
2779
|
+
* machine word and nothing else. The reasons live on `SwapRecordCommon`, the
|
|
2780
|
+
* internal record, so without this a consumer told a swap `needs_recovery` had
|
|
2781
|
+
* nowhere to read WHY.
|
|
2782
|
+
*/
|
|
2783
|
+
interface Swap {
|
|
2784
|
+
readonly id: AssetSwapId;
|
|
2785
|
+
readonly family: SwapFamily;
|
|
2786
|
+
/** Where this swap stands, in the one vocabulary both families share. */
|
|
2787
|
+
readonly outcome: Outcome;
|
|
2788
|
+
/** Both endpoints resolved, instruments included. */
|
|
2789
|
+
readonly route: Route;
|
|
2790
|
+
/** What the trader gives, fee included. */
|
|
2791
|
+
readonly give: QuoteLeg;
|
|
2792
|
+
/** What the trader takes. */
|
|
2793
|
+
readonly take: QuoteLeg;
|
|
2794
|
+
/** The spread, denominated on the leg where it is exact. */
|
|
2795
|
+
readonly fee: QuoteLeg;
|
|
2796
|
+
readonly market: MarketRef;
|
|
2797
|
+
readonly solver?: Pubkey;
|
|
2798
|
+
/** Corridor routes: the hash both covenants commit to. */
|
|
2799
|
+
readonly lock?: {
|
|
2800
|
+
readonly hash: Hex;
|
|
2801
|
+
};
|
|
2802
|
+
/** Corridor routes: when the trader's value comes back. */
|
|
2803
|
+
readonly refundLocktime?: number;
|
|
2804
|
+
readonly artifact?: Artifact;
|
|
2805
|
+
readonly expiresAt: number;
|
|
2806
|
+
/** Absent until the funding is broadcast and its txid written. */
|
|
2807
|
+
readonly fundingTxid?: string;
|
|
2808
|
+
/**
|
|
2809
|
+
* Why the swap `failed`, when it did.
|
|
2810
|
+
*
|
|
2811
|
+
* Carried across from the record rather than derived: the outcome says
|
|
2812
|
+
* WHICH terminal state, and only the record says why.
|
|
2813
|
+
*/
|
|
2814
|
+
readonly failure?: string;
|
|
2815
|
+
/**
|
|
2816
|
+
* Why this wallet will not act, while the outcome is `needs_recovery`.
|
|
2817
|
+
*
|
|
2818
|
+
* Also what makes a suppressed configuration block legible: under
|
|
2819
|
+
* `drive: "manual"` or `"readonly"` the three configuration refusals are
|
|
2820
|
+
* not translated to `needs_recovery`, and this is where the reason is
|
|
2821
|
+
* still read.
|
|
2822
|
+
*/
|
|
2823
|
+
readonly blockedReason?: string;
|
|
2824
|
+
readonly createdAt: number;
|
|
2825
|
+
readonly updatedAt: number;
|
|
2826
|
+
}
|
|
2827
|
+
|
|
1872
2828
|
/** A registry discovery result held for reuse. Refetchable — unlike a swap
|
|
1873
2829
|
* record, losing it costs one network round trip — but it must survive a cold
|
|
1874
2830
|
* boot: serving it stale is what keeps quoting alive while a registry is down. */
|
|
@@ -1878,9 +2834,8 @@ interface MarketsCacheEntry {
|
|
|
1878
2834
|
}
|
|
1879
2835
|
/**
|
|
1880
2836
|
* Everything the package persists, following the monorepo repository
|
|
1881
|
-
* convention
|
|
1882
|
-
*
|
|
1883
|
-
* exactly one of these; there is no second storage seam.
|
|
2837
|
+
* convention: versioned interface, AsyncDisposable, one backend per platform.
|
|
2838
|
+
* Consumers construct exactly one of these; there is no second storage seam.
|
|
1884
2839
|
*
|
|
1885
2840
|
* Durable records (swaps) and rebuildable state (the restore scan's txid
|
|
1886
2841
|
* cursor, the markets cache) live side by side because they share a
|
|
@@ -1888,15 +2843,15 @@ interface MarketsCacheEntry {
|
|
|
1888
2843
|
* that wipes one wants all three gone.
|
|
1889
2844
|
*
|
|
1890
2845
|
* ponytail: no query filters — every consumer reads all swaps and filters
|
|
1891
|
-
* in memory;
|
|
1892
|
-
* subset queries.
|
|
2846
|
+
* in memory; add a filter type when a consumer needs subset queries.
|
|
1893
2847
|
*/
|
|
1894
2848
|
interface AssetSwapRepository extends AsyncDisposable {
|
|
1895
|
-
/**
|
|
1896
|
-
*
|
|
1897
|
-
*
|
|
1898
|
-
*
|
|
1899
|
-
|
|
2849
|
+
/** 5 adds the v2 swap-record store — one row per accepted swap, keyed by
|
|
2850
|
+
* the client-minted quote id. 4 added `getRfqSwap`; 3 added the other RFQ
|
|
2851
|
+
* methods below; 2 was the released shape — swaps, scan cursor, markets,
|
|
2852
|
+
* with `preimageSaltHex` on the swap record — so an implementor built
|
|
2853
|
+
* against any of them cannot satisfy this one silently. */
|
|
2854
|
+
readonly version: 5;
|
|
1900
2855
|
/** Insert or replace a swap by id. Store the record whole: `preimageHex`
|
|
1901
2856
|
* and `preimageSaltHex` both leave the swap unclaimable if a field-mapped
|
|
1902
2857
|
* backend drops them — the first is the only claim secret of a swap whose
|
|
@@ -1928,17 +2883,55 @@ interface AssetSwapRepository extends AsyncDisposable {
|
|
|
1928
2883
|
getAllRfqSwaps(): Promise<RfqSwapRecord[]>;
|
|
1929
2884
|
/** Drop one, once it is past retention — see `shouldRetainRfqSwap`. */
|
|
1930
2885
|
removeRfqSwap(rfqId: string): Promise<void>;
|
|
1931
|
-
/**
|
|
2886
|
+
/**
|
|
2887
|
+
* Insert or replace a v2 swap record by its quote id.
|
|
2888
|
+
*
|
|
2889
|
+
* The store the v2 client's `accept()` writes, and the reason this
|
|
2890
|
+
* interface is at 5. Separate from `swaps` rather than sharing it: the v1
|
|
2891
|
+
* read path drops any row carrying neither `offerHex` nor `paymentHash`
|
|
2892
|
+
* (`getAssetSwapsOrThrow`), silently and as corrupt, so a v2 record in that
|
|
2893
|
+
* store would be pinned by a v1 predicate — and the two histories are meant
|
|
2894
|
+
* to be disjoint for the deprecation window anyway. A v1 reader not seeing
|
|
2895
|
+
* v2 rows is the design, asserted in the conformance suite rather than
|
|
2896
|
+
* tolerated.
|
|
2897
|
+
*
|
|
2898
|
+
* Store the record WHOLE, and note that it is **JSON-safe by declaration**:
|
|
2899
|
+
* every amount on it is a canonical decimal string, precisely so the SQLite
|
|
2900
|
+
* and Realm backends' `JSON.stringify` and IndexedDB's structured clone
|
|
2901
|
+
* agree. A `bigint` reaching here would throw on two backends and
|
|
2902
|
+
* round-trip on the third.
|
|
2903
|
+
*/
|
|
2904
|
+
saveSwapRecord(record: SwapRecord): Promise<void>;
|
|
2905
|
+
/** One record by quote id. `undefined` on a miss — which is the ordinary
|
|
2906
|
+
* answer for a first `accept()`, and what makes it idempotent. */
|
|
2907
|
+
getSwapRecord(id: string): Promise<SwapRecord | undefined>;
|
|
2908
|
+
/** Every stored v2 record, in no particular order. */
|
|
2909
|
+
getAllSwapRecords(): Promise<SwapRecord[]>;
|
|
2910
|
+
/** Drop one, once it is past retention. */
|
|
2911
|
+
removeSwapRecord(id: string): Promise<void>;
|
|
2912
|
+
/**
|
|
2913
|
+
* Sent txids already checked for offer packets (see restore.ts).
|
|
2914
|
+
*
|
|
2915
|
+
* **Shared across both record families, deliberately.** This is not
|
|
2916
|
+
* record-family data — it marks txids of transactions a scan has answered,
|
|
2917
|
+
* whatever family a later record belongs to — and both families walk the
|
|
2918
|
+
* same sent-txid set during the deprecation window. Two cursors would have
|
|
2919
|
+
* each side re-walking deposits the other already answered.
|
|
2920
|
+
*/
|
|
1932
2921
|
getScannedTxids(): Promise<Set<string>>;
|
|
1933
2922
|
markTxidsScanned(txids: Iterable<string>): Promise<void>;
|
|
1934
|
-
/** Cached registry markets, or undefined on a miss.
|
|
2923
|
+
/** Cached registry markets, or undefined on a miss. Shared across both
|
|
2924
|
+
* record families for the reason the cursor is: the key is network-and-
|
|
2925
|
+
* registry, not a store, and two caches would serve two staleness clocks
|
|
2926
|
+
* for one registry. */
|
|
1935
2927
|
getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
|
|
1936
2928
|
saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
|
|
1937
2929
|
clear(): Promise<void>;
|
|
1938
2930
|
}
|
|
1939
2931
|
declare class InMemoryAssetSwapRepository implements AssetSwapRepository {
|
|
1940
|
-
readonly version:
|
|
2932
|
+
readonly version: 5;
|
|
1941
2933
|
private readonly swaps;
|
|
2934
|
+
private readonly records;
|
|
1942
2935
|
private readonly rfqSwaps;
|
|
1943
2936
|
private readonly scanned;
|
|
1944
2937
|
private readonly markets;
|
|
@@ -1948,6 +2941,10 @@ declare class InMemoryAssetSwapRepository implements AssetSwapRepository {
|
|
|
1948
2941
|
getRfqSwap(rfqId: string): Promise<RfqSwapRecord | undefined>;
|
|
1949
2942
|
getAllRfqSwaps(): Promise<RfqSwapRecord[]>;
|
|
1950
2943
|
removeRfqSwap(rfqId: string): Promise<void>;
|
|
2944
|
+
saveSwapRecord(record: SwapRecord): Promise<void>;
|
|
2945
|
+
getSwapRecord(id: string): Promise<SwapRecord | undefined>;
|
|
2946
|
+
getAllSwapRecords(): Promise<SwapRecord[]>;
|
|
2947
|
+
removeSwapRecord(id: string): Promise<void>;
|
|
1951
2948
|
getScannedTxids(): Promise<Set<string>>;
|
|
1952
2949
|
markTxidsScanned(txids: Iterable<string>): Promise<void>;
|
|
1953
2950
|
getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
|
|
@@ -1956,4 +2953,4 @@ declare class InMemoryAssetSwapRepository implements AssetSwapRepository {
|
|
|
1956
2953
|
[Symbol.asyncDispose](): Promise<void>;
|
|
1957
2954
|
}
|
|
1958
2955
|
|
|
1959
|
-
export {
|
|
2956
|
+
export { type RfqSwapRecordStore as $, type AssetSwapRepository as A, BTC_ASSET_ID as B, REFUND_MTP_LAG_SECONDS as C, RFQ_RESOLVED_STATES as D, RFQ_SWAP_RETENTION_SECONDS as E, RFQ_SWAP_TERMINAL_STATES as F, type RefundOutcome as G, type RfqRestoreFailure as H, InMemoryAssetSwapRepository as I, type RfqRestoreOptions as J, type RfqRestoreResult as K, type LockupVtxo as L, type MarketsCacheEntry as M, type RfqSwapActionName as N, type Outcome as O, type PersistableRfqSwap as P, type QuoteInput as Q, type RfqSwapRecord as R, type SwapSecretsProjection as S, type RfqSwapLockup as T, type Unsubscribe as U, type RfqSwapManagerConfig as V, type RfqSwapManagerDeps as W, type RfqSwapManagerEvents as X, type RfqSwapOrigin as Y, RfqSwapOriginRequired as Z, type RfqSwapOutcome as _, type AssetSwap as a, type SwapContractRegistry as a0, addAssetSwap as a1, awaitRfqResolution as a2, createRfqSwapRecord as a3, findLockupVtxos as a4, getAssetSwaps as a5, getAssetSwapsOrThrow as a6, isRfqSwapTerminal as a7, isRfqTerminal as a8, nextOnchainAction as a9, normalizeRfqSwapRecord as aa, preimageForSwapRecord as ab, pushRefundWithoutReceiver as ac, readLockupFate as ad, rebuildRfqSwap as ae, refundIfUnresolved as af, rfqSwapOriginOf as ag, shouldRetainRfqSwap as ah, swapSecretsToRecord as ai, updateAssetSwap as aj, updateAssetSwapBestEffort as ak, updateRfqSwapRecord as al, type SwapRecord as b, type AssetSwapStatus as c, type Swap as d, type RouteResolution as e, type Quote as f, type SwapUpdate as g, type RefundIndexer as h, type SwapOperator as i, type RfqSwap as j, type ArkadeRefundResult as k, type LockupSpendIndexer as l, type RfqSwapState as m, RfqSwapManager as n, type RfqSwapManagerCallbacks as o, type AvailableRfqSwapManagerCallbacks as p, type LightningReceiveSwap as q, type LightningSendSwap as r, type LockupFate as s, LockupNeedsRecoveryError as t, type LockupParams as u, type LockupSpend as v, type OnchainSendAction as w, type OnchainSendSwap as x, type PreimageBlockedReason as y, PreimageNotRecoverableError as z };
|