@arkade-os/swap 0.0.4 → 0.0.6

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.
@@ -0,0 +1,236 @@
1
+ import { DiscoveredMarket } from '@arkade-os/solver-discovery';
2
+ import { IWallet, ProvisionedKey, ProvisionedClaimSecret } from '@arkade-os/sdk';
3
+
4
+ type AssetSwapStatus = "pending" | "cancelling" | "fulfilled" | "cancelled" | "recoverable" | "awaiting_fill" | "claimable" | "claimed" | "refunded_l1";
5
+ /** The sentinel asset id for BTC itself, as opposed to a 68-hex asset id.
6
+ * Lives here with the {@link AssetSwap} fields it describes so the market and
7
+ * restore layers share one spelling instead of re-typing the literal. */
8
+ declare const BTC_ASSET_ID = "btc";
9
+ /**
10
+ * The record fields a wallet-provisioned secret becomes — what
11
+ * {@link swapSecretsToRecord} emits, and what every record type carrying swap
12
+ * secrets embeds.
13
+ *
14
+ * A named type rather than four fields restated per record: the mapper and the
15
+ * records it feeds must agree exactly, and a record that silently omits one of
16
+ * these round-trips a swap whose preimage cannot be re-derived. Embedding makes
17
+ * the omission a compile error instead.
18
+ *
19
+ * **Only `preimageHex` is secret.** `signingDescriptor` and `preimageSaltHex`
20
+ * are public derivation inputs — they must survive a field-mapped backend, but
21
+ * they leak nothing without the seed.
22
+ */
23
+ interface SwapSecretsProjection {
24
+ /**
25
+ * The wallet descriptor this swap's sender key comes from — a fresh HD
26
+ * child, or a static wallet's `tr(pubkey)`. Public — the signer
27
+ * re-derives from the wallet, so the record carries no key material.
28
+ */
29
+ signingDescriptor?: string;
30
+ /** P, hex, when it cannot be re-derived from the seed at all: the user
31
+ * supplied it, or the signer cannot sign deterministically. The swap's only
32
+ * claim secret when present. */
33
+ preimageHex?: string;
34
+ /**
35
+ * The salt P derives from, hex, on the salted arm — what a static wallet
36
+ * gets instead of storing P. **Public**, and unlike every other field here
37
+ * it is minted per swap: it is what stops one repeating key from handing
38
+ * every swap the same preimage.
39
+ */
40
+ preimageSaltHex?: string;
41
+ }
42
+ interface AssetSwap extends SwapSecretsProjection {
43
+ /** Funding txid — the swap's identity. */
44
+ id: string;
45
+ /** 'btc' or a 68-hex asset id. */
46
+ fromAsset: string;
47
+ toAsset: string;
48
+ /** Atomic amounts as strings (bigint is not JSON-safe). */
49
+ fromAmount: string;
50
+ /** The covenant wantAmount — a floor, the fill pays >= this. */
51
+ toAmount: string;
52
+ swapAddress: string;
53
+ /** Hex pkScript of the swap contract — the indexer monitoring key. */
54
+ swapPkScript: string;
55
+ /** TLV offer — needed to rebuild the contract for cancel. */
56
+ offerHex: string;
57
+ fundingTxid: string;
58
+ spentTxid?: string;
59
+ status: AssetSwapStatus;
60
+ createdAt: number;
61
+ completedAt?: number;
62
+ /** RFQ pair string, e.g. `arkade:BTC->onchain:BTC`. */
63
+ pair?: string;
64
+ /** `sha256(P)`, hex. Public, and how a restore confirms a candidate
65
+ * derivation is the right one. */
66
+ paymentHash?: string;
67
+ /** The L1 HTLC's pkScript, hex — the chain-watch key. */
68
+ htlcPkScriptHex?: string;
69
+ htlcLocktime?: number;
70
+ /** The L1 funding txid, once observed. */
71
+ l1Txid?: string;
72
+ }
73
+ /** All swaps, newest-first. Insertion order is not chronological — the restore
74
+ * scan rebuilds records in tx-scan order — so sort at read to keep
75
+ * newest-first canonical for every consumer. */
76
+ declare const getAssetSwapsOrThrow: (repository: AssetSwapRepository) => Promise<AssetSwap[]>;
77
+ /** The consumer read: a broken backend reads as no swaps rather than crashing
78
+ * a history view. Mutations must use {@link getAssetSwapsOrThrow} instead —
79
+ * swallowing the read there would let "the backend is gone" masquerade as "no
80
+ * such swap" and skip the write silently. */
81
+ declare const getAssetSwaps: (repository: AssetSwapRepository) => Promise<AssetSwap[]>;
82
+ /** Add a swap; no-op if the id is already stored. Returns the updated list.
83
+ * THROWS on a failed write — nothing irreversible may happen until this record
84
+ * is durable, so the caller must not fund on a failure. */
85
+ declare const addAssetSwap: (repository: AssetSwapRepository, swap: AssetSwap) => Promise<AssetSwap[]>;
86
+ /** Merge changes into a swap by id. Returns the updated list.
87
+ * THROWS on a failed read or write, like {@link addAssetSwap} — use this for a
88
+ * write that gates something irreversible. Transitions written *after* the
89
+ * irreversible act belong on {@link updateAssetSwapBestEffort}. */
90
+ declare const updateAssetSwap: (repository: AssetSwapRepository, id: string, changes: Partial<Omit<AssetSwap, "id">>) => Promise<AssetSwap[]>;
91
+ /**
92
+ * {@link updateAssetSwap} for transitions that follow an irreversible action (a
93
+ * broadcast claim, a spent lockup): failing the caller there would report as
94
+ * failed a swap whose funds already moved, and a stale status is recoverable —
95
+ * crash recovery re-derives the true state from the chain
96
+ * (`classifyOnchainHtlc`).
97
+ *
98
+ * `persisted` is the part that must not be hidden: a caller that notifies on a
99
+ * change, or treats one as terminal, has to know the store did not agree.
100
+ */
101
+ declare const updateAssetSwapBestEffort: (repository: AssetSwapRepository, id: string, changes: Partial<Omit<AssetSwap, "id">>) => Promise<{
102
+ swaps: AssetSwap[];
103
+ persisted: boolean;
104
+ }>;
105
+ /**
106
+ * The record fields a wallet-provisioned secret becomes.
107
+ *
108
+ * `signingDescriptor` is public and always stored — it is what recovers the
109
+ * signer. Then at most one of: `preimageHex`, when the wallet says it cannot
110
+ * re-derive P and it becomes the swap's only claim secret; or
111
+ * `preimageSaltHex`, the public input a derivable-but-repeating key needs.
112
+ */
113
+ declare const swapSecretsToRecord: (secrets: ProvisionedKey | ProvisionedClaimSecret) => SwapSecretsProjection & {
114
+ signingDescriptor: string;
115
+ };
116
+ /** Why a wallet cannot produce a swap's preimage. */
117
+ type PreimageBlockedReason =
118
+ /** The record carries no `signingDescriptor`. */
119
+ "no-secrets"
120
+ /** `preimageHex` or `preimageSaltHex` is present but not 32 bytes of hex. */
121
+ | "malformed-record"
122
+ /**
123
+ * Nothing to derive from: a descriptor that repeats across swaps, with
124
+ * neither a stored preimage nor a salt — or one this wallet holds no key
125
+ * for. Merged deliberately: `contractSigner` reports a key it does not
126
+ * hold as a plain `Error` for static wallets and a `ForeignDescriptorError`
127
+ * for HD ones, so splitting the two here would mean matching on message
128
+ * text, which is the thing this type exists to avoid. The `cause` carries
129
+ * whichever it was.
130
+ */
131
+ | "not-derivable"
132
+ /** Derived, but it does not hash to the record's `paymentHash`. */
133
+ | "hash-mismatch";
134
+ /**
135
+ * The wallet cannot produce this swap's preimage, and which of the four ways
136
+ * is `reason`.
137
+ *
138
+ * Deliberately **not** {@link RefundNotLocallyPossibleError}: that one means
139
+ * "no local refund is possible", and `RfqSwapManager` acts on it by reporting
140
+ * `needs_counterparty`. A claim-path read failure is a different verdict, and
141
+ * borrowing the refund error would have the manager announce one for the
142
+ * other.
143
+ */
144
+ declare class PreimageNotRecoverableError extends Error {
145
+ readonly reason: PreimageBlockedReason;
146
+ readonly name = "PreimageNotRecoverableError";
147
+ constructor(reason: PreimageBlockedReason, message: string, options?: {
148
+ cause?: unknown;
149
+ });
150
+ }
151
+ /**
152
+ * The preimage a swap record claims with — stored, or re-derived from the
153
+ * wallet.
154
+ *
155
+ * The record-shaped inverse of {@link swapSecretsToRecord}, and the one place
156
+ * that knows which of a record's fields `contractPreimage` needs. Wire claim
157
+ * paths here rather than composing it by hand: a caller that forgets to pass
158
+ * `preimageSaltHex` gets a *wrong* preimage from a wallet that can derive,
159
+ * not an error.
160
+ *
161
+ * Verifies the result against `paymentHash` when the record carries one. The
162
+ * salted arm has two inputs that can be wrong — the key and the salt — where
163
+ * the HD arm had one, and a wrong P otherwise surfaces as an opaque script
164
+ * failure at claim time, long after the mistake.
165
+ *
166
+ * Every refusal is a {@link PreimageNotRecoverableError} carrying a `reason`,
167
+ * so a caller can tell "this record predates the descriptor" from "the salt is
168
+ * corrupt" without reading message text.
169
+ */
170
+ declare const preimageForSwapRecord: (wallet: IWallet, record: SwapSecretsProjection & {
171
+ paymentHash?: string;
172
+ }) => Promise<Uint8Array>;
173
+
174
+ /** A registry discovery result held for reuse. Refetchable — unlike a swap
175
+ * record, losing it costs one network round trip — but it must survive a cold
176
+ * boot: serving it stale is what keeps quoting alive while a registry is down. */
177
+ interface MarketsCacheEntry {
178
+ markets: DiscoveredMarket[];
179
+ fetchedAt: number;
180
+ }
181
+ /**
182
+ * Everything the package persists, following the monorepo repository
183
+ * convention (versioned interface, AsyncDisposable, one backend per
184
+ * platform — see the Boltz plugin's SwapRepository). Consumers construct
185
+ * exactly one of these; there is no second storage seam.
186
+ *
187
+ * Durable records (swaps) and rebuildable state (the restore scan's txid
188
+ * cursor, the markets cache) live side by side because they share a
189
+ * lifetime: all three belong to one wallet on one device, and a consumer
190
+ * that wipes one wants all three gone.
191
+ *
192
+ * ponytail: no query filters — every consumer reads all swaps and filters
193
+ * in memory; mirror the Boltz plugin's GetSwapsFilter when a consumer needs
194
+ * subset queries.
195
+ */
196
+ interface AssetSwapRepository extends AsyncDisposable {
197
+ readonly version: 2;
198
+ /** Insert or replace a swap by id. Store the record whole: `preimageHex`
199
+ * and `preimageSaltHex` both leave the swap unclaimable if a field-mapped
200
+ * backend drops them — the first is the only claim secret of a swap whose
201
+ * signer cannot derive, the second the public input every other static
202
+ * wallet's preimage derives from.
203
+ *
204
+ * Records must be **JSON-safe**: the SQLite and Realm backends serialize
205
+ * the record to JSON, so a `Date` in a consumer-added field comes back a
206
+ * string, a `Set`/`Map` comes back empty, and a `bigint` throws here —
207
+ * none of which happens on IndexedDB's structured clone. `AssetSwap` as
208
+ * declared is JSON-safe; keep added fields that way. */
209
+ saveSwap(swap: AssetSwap): Promise<void>;
210
+ /** All stored swaps, in no particular order — `getAssetSwaps` is the
211
+ * canonical newest-first read. */
212
+ getAllSwaps(): Promise<AssetSwap[]>;
213
+ /** Sent txids already checked for offer packets (see restore.ts). */
214
+ getScannedTxids(): Promise<Set<string>>;
215
+ markTxidsScanned(txids: Iterable<string>): Promise<void>;
216
+ /** Cached registry markets, or undefined on a miss. */
217
+ getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
218
+ saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
219
+ clear(): Promise<void>;
220
+ }
221
+ declare class InMemoryAssetSwapRepository implements AssetSwapRepository {
222
+ readonly version: 2;
223
+ private readonly swaps;
224
+ private readonly scanned;
225
+ private readonly markets;
226
+ saveSwap(swap: AssetSwap): Promise<void>;
227
+ getAllSwaps(): Promise<AssetSwap[]>;
228
+ getScannedTxids(): Promise<Set<string>>;
229
+ markTxidsScanned(txids: Iterable<string>): Promise<void>;
230
+ getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
231
+ saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
232
+ clear(): Promise<void>;
233
+ [Symbol.asyncDispose](): Promise<void>;
234
+ }
235
+
236
+ export { type AssetSwapRepository as A, BTC_ASSET_ID as B, InMemoryAssetSwapRepository as I, type MarketsCacheEntry as M, type PreimageBlockedReason as P, type SwapSecretsProjection as S, type AssetSwap as a, type AssetSwapStatus as b, PreimageNotRecoverableError as c, addAssetSwap as d, getAssetSwapsOrThrow as e, updateAssetSwapBestEffort as f, getAssetSwaps as g, preimageForSwapRecord as p, swapSecretsToRecord as s, updateAssetSwap as u };
@@ -0,0 +1,236 @@
1
+ import { DiscoveredMarket } from '@arkade-os/solver-discovery';
2
+ import { IWallet, ProvisionedKey, ProvisionedClaimSecret } from '@arkade-os/sdk';
3
+
4
+ type AssetSwapStatus = "pending" | "cancelling" | "fulfilled" | "cancelled" | "recoverable" | "awaiting_fill" | "claimable" | "claimed" | "refunded_l1";
5
+ /** The sentinel asset id for BTC itself, as opposed to a 68-hex asset id.
6
+ * Lives here with the {@link AssetSwap} fields it describes so the market and
7
+ * restore layers share one spelling instead of re-typing the literal. */
8
+ declare const BTC_ASSET_ID = "btc";
9
+ /**
10
+ * The record fields a wallet-provisioned secret becomes — what
11
+ * {@link swapSecretsToRecord} emits, and what every record type carrying swap
12
+ * secrets embeds.
13
+ *
14
+ * A named type rather than four fields restated per record: the mapper and the
15
+ * records it feeds must agree exactly, and a record that silently omits one of
16
+ * these round-trips a swap whose preimage cannot be re-derived. Embedding makes
17
+ * the omission a compile error instead.
18
+ *
19
+ * **Only `preimageHex` is secret.** `signingDescriptor` and `preimageSaltHex`
20
+ * are public derivation inputs — they must survive a field-mapped backend, but
21
+ * they leak nothing without the seed.
22
+ */
23
+ interface SwapSecretsProjection {
24
+ /**
25
+ * The wallet descriptor this swap's sender key comes from — a fresh HD
26
+ * child, or a static wallet's `tr(pubkey)`. Public — the signer
27
+ * re-derives from the wallet, so the record carries no key material.
28
+ */
29
+ signingDescriptor?: string;
30
+ /** P, hex, when it cannot be re-derived from the seed at all: the user
31
+ * supplied it, or the signer cannot sign deterministically. The swap's only
32
+ * claim secret when present. */
33
+ preimageHex?: string;
34
+ /**
35
+ * The salt P derives from, hex, on the salted arm — what a static wallet
36
+ * gets instead of storing P. **Public**, and unlike every other field here
37
+ * it is minted per swap: it is what stops one repeating key from handing
38
+ * every swap the same preimage.
39
+ */
40
+ preimageSaltHex?: string;
41
+ }
42
+ interface AssetSwap extends SwapSecretsProjection {
43
+ /** Funding txid — the swap's identity. */
44
+ id: string;
45
+ /** 'btc' or a 68-hex asset id. */
46
+ fromAsset: string;
47
+ toAsset: string;
48
+ /** Atomic amounts as strings (bigint is not JSON-safe). */
49
+ fromAmount: string;
50
+ /** The covenant wantAmount — a floor, the fill pays >= this. */
51
+ toAmount: string;
52
+ swapAddress: string;
53
+ /** Hex pkScript of the swap contract — the indexer monitoring key. */
54
+ swapPkScript: string;
55
+ /** TLV offer — needed to rebuild the contract for cancel. */
56
+ offerHex: string;
57
+ fundingTxid: string;
58
+ spentTxid?: string;
59
+ status: AssetSwapStatus;
60
+ createdAt: number;
61
+ completedAt?: number;
62
+ /** RFQ pair string, e.g. `arkade:BTC->onchain:BTC`. */
63
+ pair?: string;
64
+ /** `sha256(P)`, hex. Public, and how a restore confirms a candidate
65
+ * derivation is the right one. */
66
+ paymentHash?: string;
67
+ /** The L1 HTLC's pkScript, hex — the chain-watch key. */
68
+ htlcPkScriptHex?: string;
69
+ htlcLocktime?: number;
70
+ /** The L1 funding txid, once observed. */
71
+ l1Txid?: string;
72
+ }
73
+ /** All swaps, newest-first. Insertion order is not chronological — the restore
74
+ * scan rebuilds records in tx-scan order — so sort at read to keep
75
+ * newest-first canonical for every consumer. */
76
+ declare const getAssetSwapsOrThrow: (repository: AssetSwapRepository) => Promise<AssetSwap[]>;
77
+ /** The consumer read: a broken backend reads as no swaps rather than crashing
78
+ * a history view. Mutations must use {@link getAssetSwapsOrThrow} instead —
79
+ * swallowing the read there would let "the backend is gone" masquerade as "no
80
+ * such swap" and skip the write silently. */
81
+ declare const getAssetSwaps: (repository: AssetSwapRepository) => Promise<AssetSwap[]>;
82
+ /** Add a swap; no-op if the id is already stored. Returns the updated list.
83
+ * THROWS on a failed write — nothing irreversible may happen until this record
84
+ * is durable, so the caller must not fund on a failure. */
85
+ declare const addAssetSwap: (repository: AssetSwapRepository, swap: AssetSwap) => Promise<AssetSwap[]>;
86
+ /** Merge changes into a swap by id. Returns the updated list.
87
+ * THROWS on a failed read or write, like {@link addAssetSwap} — use this for a
88
+ * write that gates something irreversible. Transitions written *after* the
89
+ * irreversible act belong on {@link updateAssetSwapBestEffort}. */
90
+ declare const updateAssetSwap: (repository: AssetSwapRepository, id: string, changes: Partial<Omit<AssetSwap, "id">>) => Promise<AssetSwap[]>;
91
+ /**
92
+ * {@link updateAssetSwap} for transitions that follow an irreversible action (a
93
+ * broadcast claim, a spent lockup): failing the caller there would report as
94
+ * failed a swap whose funds already moved, and a stale status is recoverable —
95
+ * crash recovery re-derives the true state from the chain
96
+ * (`classifyOnchainHtlc`).
97
+ *
98
+ * `persisted` is the part that must not be hidden: a caller that notifies on a
99
+ * change, or treats one as terminal, has to know the store did not agree.
100
+ */
101
+ declare const updateAssetSwapBestEffort: (repository: AssetSwapRepository, id: string, changes: Partial<Omit<AssetSwap, "id">>) => Promise<{
102
+ swaps: AssetSwap[];
103
+ persisted: boolean;
104
+ }>;
105
+ /**
106
+ * The record fields a wallet-provisioned secret becomes.
107
+ *
108
+ * `signingDescriptor` is public and always stored — it is what recovers the
109
+ * signer. Then at most one of: `preimageHex`, when the wallet says it cannot
110
+ * re-derive P and it becomes the swap's only claim secret; or
111
+ * `preimageSaltHex`, the public input a derivable-but-repeating key needs.
112
+ */
113
+ declare const swapSecretsToRecord: (secrets: ProvisionedKey | ProvisionedClaimSecret) => SwapSecretsProjection & {
114
+ signingDescriptor: string;
115
+ };
116
+ /** Why a wallet cannot produce a swap's preimage. */
117
+ type PreimageBlockedReason =
118
+ /** The record carries no `signingDescriptor`. */
119
+ "no-secrets"
120
+ /** `preimageHex` or `preimageSaltHex` is present but not 32 bytes of hex. */
121
+ | "malformed-record"
122
+ /**
123
+ * Nothing to derive from: a descriptor that repeats across swaps, with
124
+ * neither a stored preimage nor a salt — or one this wallet holds no key
125
+ * for. Merged deliberately: `contractSigner` reports a key it does not
126
+ * hold as a plain `Error` for static wallets and a `ForeignDescriptorError`
127
+ * for HD ones, so splitting the two here would mean matching on message
128
+ * text, which is the thing this type exists to avoid. The `cause` carries
129
+ * whichever it was.
130
+ */
131
+ | "not-derivable"
132
+ /** Derived, but it does not hash to the record's `paymentHash`. */
133
+ | "hash-mismatch";
134
+ /**
135
+ * The wallet cannot produce this swap's preimage, and which of the four ways
136
+ * is `reason`.
137
+ *
138
+ * Deliberately **not** {@link RefundNotLocallyPossibleError}: that one means
139
+ * "no local refund is possible", and `RfqSwapManager` acts on it by reporting
140
+ * `needs_counterparty`. A claim-path read failure is a different verdict, and
141
+ * borrowing the refund error would have the manager announce one for the
142
+ * other.
143
+ */
144
+ declare class PreimageNotRecoverableError extends Error {
145
+ readonly reason: PreimageBlockedReason;
146
+ readonly name = "PreimageNotRecoverableError";
147
+ constructor(reason: PreimageBlockedReason, message: string, options?: {
148
+ cause?: unknown;
149
+ });
150
+ }
151
+ /**
152
+ * The preimage a swap record claims with — stored, or re-derived from the
153
+ * wallet.
154
+ *
155
+ * The record-shaped inverse of {@link swapSecretsToRecord}, and the one place
156
+ * that knows which of a record's fields `contractPreimage` needs. Wire claim
157
+ * paths here rather than composing it by hand: a caller that forgets to pass
158
+ * `preimageSaltHex` gets a *wrong* preimage from a wallet that can derive,
159
+ * not an error.
160
+ *
161
+ * Verifies the result against `paymentHash` when the record carries one. The
162
+ * salted arm has two inputs that can be wrong — the key and the salt — where
163
+ * the HD arm had one, and a wrong P otherwise surfaces as an opaque script
164
+ * failure at claim time, long after the mistake.
165
+ *
166
+ * Every refusal is a {@link PreimageNotRecoverableError} carrying a `reason`,
167
+ * so a caller can tell "this record predates the descriptor" from "the salt is
168
+ * corrupt" without reading message text.
169
+ */
170
+ declare const preimageForSwapRecord: (wallet: IWallet, record: SwapSecretsProjection & {
171
+ paymentHash?: string;
172
+ }) => Promise<Uint8Array>;
173
+
174
+ /** A registry discovery result held for reuse. Refetchable — unlike a swap
175
+ * record, losing it costs one network round trip — but it must survive a cold
176
+ * boot: serving it stale is what keeps quoting alive while a registry is down. */
177
+ interface MarketsCacheEntry {
178
+ markets: DiscoveredMarket[];
179
+ fetchedAt: number;
180
+ }
181
+ /**
182
+ * Everything the package persists, following the monorepo repository
183
+ * convention (versioned interface, AsyncDisposable, one backend per
184
+ * platform — see the Boltz plugin's SwapRepository). Consumers construct
185
+ * exactly one of these; there is no second storage seam.
186
+ *
187
+ * Durable records (swaps) and rebuildable state (the restore scan's txid
188
+ * cursor, the markets cache) live side by side because they share a
189
+ * lifetime: all three belong to one wallet on one device, and a consumer
190
+ * that wipes one wants all three gone.
191
+ *
192
+ * ponytail: no query filters — every consumer reads all swaps and filters
193
+ * in memory; mirror the Boltz plugin's GetSwapsFilter when a consumer needs
194
+ * subset queries.
195
+ */
196
+ interface AssetSwapRepository extends AsyncDisposable {
197
+ readonly version: 2;
198
+ /** Insert or replace a swap by id. Store the record whole: `preimageHex`
199
+ * and `preimageSaltHex` both leave the swap unclaimable if a field-mapped
200
+ * backend drops them — the first is the only claim secret of a swap whose
201
+ * signer cannot derive, the second the public input every other static
202
+ * wallet's preimage derives from.
203
+ *
204
+ * Records must be **JSON-safe**: the SQLite and Realm backends serialize
205
+ * the record to JSON, so a `Date` in a consumer-added field comes back a
206
+ * string, a `Set`/`Map` comes back empty, and a `bigint` throws here —
207
+ * none of which happens on IndexedDB's structured clone. `AssetSwap` as
208
+ * declared is JSON-safe; keep added fields that way. */
209
+ saveSwap(swap: AssetSwap): Promise<void>;
210
+ /** All stored swaps, in no particular order — `getAssetSwaps` is the
211
+ * canonical newest-first read. */
212
+ getAllSwaps(): Promise<AssetSwap[]>;
213
+ /** Sent txids already checked for offer packets (see restore.ts). */
214
+ getScannedTxids(): Promise<Set<string>>;
215
+ markTxidsScanned(txids: Iterable<string>): Promise<void>;
216
+ /** Cached registry markets, or undefined on a miss. */
217
+ getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
218
+ saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
219
+ clear(): Promise<void>;
220
+ }
221
+ declare class InMemoryAssetSwapRepository implements AssetSwapRepository {
222
+ readonly version: 2;
223
+ private readonly swaps;
224
+ private readonly scanned;
225
+ private readonly markets;
226
+ saveSwap(swap: AssetSwap): Promise<void>;
227
+ getAllSwaps(): Promise<AssetSwap[]>;
228
+ getScannedTxids(): Promise<Set<string>>;
229
+ markTxidsScanned(txids: Iterable<string>): Promise<void>;
230
+ getCachedMarkets(network: string, registry: string): Promise<MarketsCacheEntry | undefined>;
231
+ saveCachedMarkets(network: string, registry: string, entry: MarketsCacheEntry): Promise<void>;
232
+ clear(): Promise<void>;
233
+ [Symbol.asyncDispose](): Promise<void>;
234
+ }
235
+
236
+ export { type AssetSwapRepository as A, BTC_ASSET_ID as B, InMemoryAssetSwapRepository as I, type MarketsCacheEntry as M, type PreimageBlockedReason as P, type SwapSecretsProjection as S, type AssetSwap as a, type AssetSwapStatus as b, PreimageNotRecoverableError as c, addAssetSwap as d, getAssetSwapsOrThrow as e, updateAssetSwapBestEffort as f, getAssetSwaps as g, preimageForSwapRecord as p, swapSecretsToRecord as s, updateAssetSwap as u };
@@ -276,9 +276,10 @@ interface RfqStatus {
276
276
  * `senderPubkey` is the trader's own key for the VHTLC's sender-side leaves
277
277
  * (see {@link lightningSendVtxoScript}) — required, never sent anywhere else,
278
278
  * never trusted by the solver as anything but a pubkey to bind into the
279
- * script. On the wire it's `client_refund_pubkey` (docs/rfq-protocol.md
280
- * the solver's schema is `.strict()`, so both the wrong name AND the missing
281
- * required field would refuse every request). */
279
+ * script. On the wire it's `client_refund_pubkey` (the payload schemas are
280
+ * public at https://docs.arkadeos.com/intents/reference/rfq — the solver's schema
281
+ * is `.strict()`, so both the wrong name AND the missing required field would
282
+ * refuse every request). */
282
283
  declare const lightningSendRequest: (input: {
283
284
  rfqId: string;
284
285
  invoice: string;
@@ -355,20 +356,38 @@ declare const relayTransport: (relayUrl: string, options: {
355
356
  WebSocketCtor?: new (url: string) => RelaySocket;
356
357
  timeoutMs?: number;
357
358
  }) => RfqTransport;
359
+ /**
360
+ * How long the sender's SOLO refund opens after the receiver's claim, seconds.
361
+ *
362
+ * This is the window in which a claimant holding the preimage must be able to
363
+ * finish taking their money before the funder could take it back. On a live
364
+ * Arkade server that is one collaborative spend; with the server gone it is a
365
+ * full unilateral exit — an unroll broadcast per chain step, each waiting on a
366
+ * confirmation, then the CSV spend.
367
+ *
368
+ * 4096s (eight 512s units, ~68 minutes) is sized for that worst case. It is
369
+ * REASONED, not measured, and it mirrors `SOLO_REFUND_HEADROOM_SECONDS` in the
370
+ * reference solver's `src/core/timelocks.ts` — the two must move together or a
371
+ * trader derives an address the solver never quoted.
372
+ *
373
+ * A multiple of the granularity on purpose: BIP68 would round anything else,
374
+ * making the encoded timelock differ from the number written here.
375
+ */
376
+ declare const SOLO_REFUND_HEADROOM_SECONDS: number;
358
377
  /** The solver's unilateral-claim delay, derived from the Ark server's reported
359
378
  * exit delay exactly as the reference solver derives it — both sides read the
360
379
  * SAME server, so the derivation (not a quote field) is what keeps the two
361
380
  * scripts identical. */
362
381
  declare const unilateralClaimDelay: (serverExitDelaySeconds: number) => number;
363
- /** VHTLC's `unilateralRefund` tier: sender + solver, no server, one 512s step
364
- * past `claimDelay` the middle rung between the fully-collaborative paths
365
- * and the sender's last-resort `unilateralRefundWithoutReceiver`. Same
366
- * already-rounded `claimDelay` input as {@link unilateralClaimDelay}
367
- * produces — one rounding, shared across all three tiers. */
382
+ /** VHTLC's `unilateralRefund` tier: sender + receiver, no server LEVEL with
383
+ * `claimDelay`, not above it. Neither party can spend a two-signature leaf
384
+ * alone, so separating it buys no safety, and every second spent separating it
385
+ * is a second taken off the headroom that does matter. */
368
386
  declare const unilateralRefundDelay: (claimDelay: number) => number;
369
- /** VHTLC's `unilateralRefundWithoutReceiver` tier: sender alone, needs
370
- * nobody two 512s steps past `claimDelay`, past {@link
371
- * unilateralRefundDelay}. */
387
+ /** VHTLC's `unilateralRefundWithoutReceiver` tier: sender alone, needing
388
+ * nobody. The only leaf whose timing can steal — a funder able to refund
389
+ * before the claimant can claim takes money from someone holding the preimage
390
+ * — so it opens last, by {@link SOLO_REFUND_HEADROOM_SECONDS}. */
372
391
  declare const unilateralRefundWithoutReceiverDelay: (claimDelay: number) => number;
373
392
  /** Compile the lightning-send VHTLC from the quote's binding fields plus the
374
393
  * trader's own data. `paymentHash` is the BOLT11 payment hash (`sha256(P)`,
@@ -631,8 +650,9 @@ declare function requestOnchainSend(wallet: IWallet, arkServerUrl: string, trans
631
650
  htlc: OnchainHtlc;
632
651
  /** The VHTLC `sender` x-only key, bound into the covenant. Public. */
633
652
  senderPubkey: Uint8Array;
634
- /** How the preimage and the `sender` key are recovered later. Persist it
635
- * with the record BEFORE funding. */
653
+ /** How the preimage and the `sender` key are recovered later map it
654
+ * through `swapSecretsToRecord` and persist BEFORE funding. Public unless
655
+ * `mustPersistPreimage` says the wallet could not derive P. */
636
656
  secrets: ProvisionedClaimSecret;
637
657
  }>;
638
658
  /** Default floor for the window between the last moment the hold invoice can
@@ -820,8 +840,9 @@ declare function requestLightningReceive(wallet: IWallet, arkServerUrl: string,
820
840
  payoutAddress: string;
821
841
  /** The trader's covenant `receiver` key, bound into the tree. Public. */
822
842
  payoutPubkey: Uint8Array;
823
- /** How the preimage and the payout key are recovered later. Persist it
824
- * with the record BEFORE paying the invoice. */
843
+ /** How the preimage and the payout key are recovered later map it
844
+ * through `swapSecretsToRecord` and persist BEFORE paying the invoice.
845
+ * Public unless `mustPersistPreimage` says the wallet could not derive P. */
825
846
  secrets: ProvisionedClaimSecret;
826
847
  }>;
827
848
  /**
@@ -893,7 +914,10 @@ declare function requestOnchainReceive(wallet: IWallet, arkServerUrl: string, tr
893
914
  htlc: OnchainHtlc;
894
915
  payoutAddress: string;
895
916
  payoutPubkey: Uint8Array;
917
+ /** How the preimage and the payout key are recovered later — map it
918
+ * through `swapSecretsToRecord` and persist BEFORE funding. Public unless
919
+ * `mustPersistPreimage` says the wallet could not derive P. */
896
920
  secrets: ProvisionedClaimSecret;
897
921
  }>;
898
922
 
899
- export { paymentHashOf as $, ARKADE_ASSET as A, buildHtlcClaim as B, type ChainSource as C, buildHtlcRefund as D, claimOnchainFill as E, classifyOnchainHtlc as F, deriveLightningReceive as G, type HtlcUtxo as H, type InvoiceFacts as I, deriveOnchainReceive as J, deriveOnchainSend as K, LIGHTNING_BTC as L, MAX_MIN_CONFIRMATIONS as M, extractPreimage as N, type OnchainHtlc as O, httpTransport as P, lightningReceiveRequest as Q, type RfqStatus as R, SwapRefusal as S, lightningSendRequest as T, lightningSendVtxoScript as U, newPreimage as V, newRfqId as W, offerTermsFromQuote as X, onchainHtlcScript as Y, onchainReceiveRequest as Z, onchainSendRequest as _, type RfqTransport as a, receiveVtxoScript as a0, relayTransport as a1, requestLightningReceive as a2, requestLightningSend as a3, requestOnchainReceive as a4, requestOnchainSend as a5, rfqPair as a6, unilateralClaimDelay as a7, unilateralRefundDelay as a8, unilateralRefundWithoutReceiverDelay as a9, verifyLockupAddress as aa, verifyReceiveInvoice as ab, type ChainUtxo as b, type OnchainHtlcPhase as c, ARKADE_BTC as d, AddressMismatch as e, LIGHTNING_RECEIVE_PAIR as f, LIGHTNING_SEND_PAIR as g, MIN_CLAIM_WINDOW_SECONDS as h, MIN_HEADROOM_SECONDS as i, ONCHAIN_BTC as j, ONCHAIN_CLAIM_MARGIN_SECONDS as k, ONCHAIN_DUST_SATS as l, ONCHAIN_ORDER_MARGIN_SECONDS as m, ONCHAIN_RECEIVE_PAIR as n, ONCHAIN_SECONDS_PER_BLOCK as o, ONCHAIN_SEND_PAIR as p, type OnchainHtlcParams as q, type OnchainNetwork as r, RFQ_TERMINAL_STATES as s, type RelaySocket as t, type RfqQuote as u, type RfqRefusalReason as v, arkadeSwapRequest as w, assertFundable as x, assertReceivable as y, awaitOnchainFill as z };
923
+ export { onchainSendRequest as $, ARKADE_ASSET as A, awaitOnchainFill as B, type ChainSource as C, buildHtlcClaim as D, buildHtlcRefund as E, claimOnchainFill as F, classifyOnchainHtlc as G, type HtlcUtxo as H, type InvoiceFacts as I, deriveLightningReceive as J, deriveOnchainReceive as K, LIGHTNING_BTC as L, MAX_MIN_CONFIRMATIONS as M, deriveOnchainSend as N, type OnchainHtlc as O, extractPreimage as P, httpTransport as Q, type RfqStatus as R, SOLO_REFUND_HEADROOM_SECONDS as S, lightningReceiveRequest as T, lightningSendRequest as U, lightningSendVtxoScript as V, newPreimage as W, newRfqId as X, offerTermsFromQuote as Y, onchainHtlcScript as Z, onchainReceiveRequest as _, type RfqTransport as a, paymentHashOf as a0, receiveVtxoScript as a1, relayTransport as a2, requestLightningReceive as a3, requestLightningSend as a4, requestOnchainReceive as a5, requestOnchainSend as a6, rfqPair as a7, unilateralClaimDelay as a8, unilateralRefundDelay as a9, unilateralRefundWithoutReceiverDelay as aa, verifyLockupAddress as ab, verifyReceiveInvoice as ac, type ChainUtxo as b, type OnchainHtlcPhase as c, ARKADE_BTC as d, AddressMismatch as e, LIGHTNING_RECEIVE_PAIR as f, LIGHTNING_SEND_PAIR as g, MIN_CLAIM_WINDOW_SECONDS as h, MIN_HEADROOM_SECONDS as i, ONCHAIN_BTC as j, ONCHAIN_CLAIM_MARGIN_SECONDS as k, ONCHAIN_DUST_SATS as l, ONCHAIN_ORDER_MARGIN_SECONDS as m, ONCHAIN_RECEIVE_PAIR as n, ONCHAIN_SECONDS_PER_BLOCK as o, ONCHAIN_SEND_PAIR as p, type OnchainHtlcParams as q, type OnchainNetwork as r, RFQ_TERMINAL_STATES as s, type RelaySocket as t, type RfqQuote as u, type RfqRefusalReason as v, SwapRefusal as w, arkadeSwapRequest as x, assertFundable as y, assertReceivable as z };