@hyperbridge/simplex 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,663 +0,0 @@
1
- import pino from 'pino';
2
-
3
- declare const LOG_LEVELS: readonly ["trace", "debug", "info", "warn", "error"];
4
- type LogLevel = (typeof LOG_LEVELS)[number];
5
- /**
6
- * Anything that accepts a line of text: `process.stdout`, a `fs` write stream, a
7
- * socket, a `pino-pretty` transform, or an object literal that forwards into
8
- * your own logger. Node's `Writable` satisfies it as-is.
9
- */
10
- interface LogSink {
11
- write(line: string): void;
12
- }
13
- type LogFn = {
14
- (obj: unknown, msg?: string, ...args: unknown[]): void;
15
- (msg: string, ...args: unknown[]): void;
16
- };
17
- interface Logger {
18
- trace: LogFn;
19
- debug: LogFn;
20
- info: LogFn;
21
- warn: LogFn;
22
- error: LogFn;
23
- fatal: LogFn;
24
- }
25
- type LoggerOptions = {
26
- module?: string;
27
- };
28
- /**
29
- * An isolated logging destination: its own sinks, its own level, its own pino
30
- * instance.
31
- *
32
- * Each `Simplex` owns one, so two fillers in a process log to their own sinks at
33
- * their own verbosity and never cross-contaminate. Services receive the context
34
- * their filler was started with and resolve module loggers from it.
35
- */
36
- declare class LoggerContext {
37
- private level;
38
- private readonly sinks;
39
- private base;
40
- private readonly children;
41
- constructor(options?: {
42
- level?: LogLevel;
43
- sink?: LogSink;
44
- });
45
- /**
46
- * Drops the memoized loggers so the next call rebuilds at the current level
47
- * and sink set. Module loggers are wrappers rather than pino instances
48
- * precisely so this reaches loggers that already exist — replacing the base
49
- * logger would leave every service that had captured a child logging at the
50
- * old level forever.
51
- */
52
- private invalidate;
53
- private baseLogger;
54
- private childFor;
55
- /** Registers a destination and returns a function that removes it. */
56
- addSink(sink: LogSink): () => void;
57
- /** Sets verbosity, including for loggers already handed out. */
58
- setLevel(level: LogLevel): void;
59
- /** A logger tagged with `module`. Safe to hold; it resolves the current state per call. */
60
- get(module?: string): Logger;
61
- /** @internal — used by {@link ModuleLogger} to resolve per call. */
62
- resolve(module?: string): pino.Logger;
63
- }
64
- /**
65
- * Registers a destination on the process-wide context and returns a function
66
- * that removes it.
67
- *
68
- * This is for the application layer. To capture a specific filler's output, pass
69
- * `SimplexOptions.logger` instead — a filler's records go to its own context,
70
- * not here.
71
- */
72
- declare function addLogSink(sink: LogSink): () => void;
73
- /** Sets the level of the process-wide context. Does not affect running fillers. */
74
- declare function configureLogger(level: LogLevel): void;
75
- /** A logger on the process-wide context. Instance-scoped code resolves from its own {@link LoggerContext}. */
76
- declare function getLogger(moduleOrOptions?: string | LoggerOptions): Logger;
77
-
78
- /**
79
- * The persistence contract for a running filler.
80
- *
81
- * Everything simplex must remember across a restart lives behind these four
82
- * stores: submitted bids (so deposits can be reclaimed), the order-activity
83
- * feed, the operator's limit orders, and a scrap of operator state. Nothing
84
- * else in the filler touches a database, a file, or a data directory.
85
- *
86
- * Every method is async. The bundled SQLite adapter is synchronous underneath
87
- * and simply returns resolved promises — the async signature exists so a
88
- * consumer can back these with Postgres, Redis, or a hosted API. Making the
89
- * interface synchronous would have limited "bring your own backend" to backends
90
- * that are local and synchronous, which is nearly the same as not having the
91
- * interface at all.
92
- */
93
- interface SimplexDataStore {
94
- bids: BidStore;
95
- activity: ActivityStore;
96
- state: StateStore;
97
- limitOrders: LimitOrderStore;
98
- inventory: InventoryStore;
99
- /** Releases any underlying handles. Called by `Simplex.stop()`. */
100
- close?(): Promise<void>;
101
- }
102
- /** A bid submitted to Hyperbridge's solver-selection pallet. */
103
- interface StoredBid {
104
- id: number;
105
- commitment: string;
106
- /** The identifier Hyperbridge files the bid under; see {@link BidInsert.bid}. */
107
- bid: string | null;
108
- extrinsicHash: string | null;
109
- blockHash: string | null;
110
- success: boolean;
111
- /**
112
- * The submission timed out with the extrinsic still in Hyperbridge's pool. It
113
- * may yet land and reserve a deposit, so the sweep treats it as reclaimable
114
- * even though `success` is false.
115
- */
116
- pending: boolean;
117
- error: string | null;
118
- /** SQLite-style "YYYY-MM-DD HH:MM:SS" in UTC. Sorts lexicographically. */
119
- createdAt: string;
120
- retracted: boolean;
121
- retractedAt: string | null;
122
- retractExtrinsicHash: string | null;
123
- /** The order was seen filled on-chain, so this bid can never win — reclaim its deposit now. */
124
- dead: boolean;
125
- /**
126
- * What this bid holds against the operator's limit orders, at 1e18, in the
127
- * order the payout draws on them. Empty for a bid placed before limit orders
128
- * priced anything, and empty again once the hold has been settled, whether it
129
- * was released because the bid lost or converted because the bid filled.
130
- *
131
- * More than one when a swap drew on several levels: the orderbook quotes a
132
- * same-chain swapper across every level that can fill the trade together, so a
133
- * bid may be meeting several of the operator's own orders at once.
134
- */
135
- reservations: LimitOrderHold[];
136
- }
137
- /** One limit order and what a bid holds against it, at 1e18. */
138
- interface LimitOrderHold {
139
- limitOrderId: string;
140
- amount: string;
141
- /**
142
- * The take the bid signed beside `amount`, in the input token's raw units. With it, settlement
143
- * works out what the gateway charged: the escrow it released at `amount / take`. Holds recorded
144
- * before bids carried their own rate have none.
145
- */
146
- take?: string;
147
- /**
148
- * The input token's decimals on the order's source chain, which is where `take` and the escrow
149
- * a fill releases are counted. With it, settlement records what a fill took in at 1e18. Holds
150
- * recorded before fills kept their input have none.
151
- */
152
- takeDecimals?: number;
153
- }
154
- interface BidInsert {
155
- commitment: string;
156
- /**
157
- * The identifier Hyperbridge files the bid under, `keccak256` of its calldata. Bids on one
158
- * incoming order share a commitment, and this is what tells them apart. It is also what
159
- * retracting the bid names, so it is recorded here at placement rather than looked up later:
160
- * a bid still in Hyperbridge's pool is not in its storage yet, but its retraction, sent after it
161
- * from the same account, lands after it and reclaims its deposit all the same.
162
- */
163
- bid?: string;
164
- /** What this bid holds against the limit orders that priced it, best first. */
165
- reservations?: LimitOrderHold[];
166
- extrinsicHash?: string;
167
- blockHash?: string;
168
- success: boolean;
169
- /** Submission is still in the pool; see {@link StoredBid.pending}. */
170
- pending?: boolean;
171
- error?: string;
172
- }
173
- interface BidStats {
174
- total: number;
175
- successful: number;
176
- failed: number;
177
- retracted: number;
178
- /** Deposits still locked: successful or pending, and not yet retracted. */
179
- pendingRetraction: number;
180
- }
181
- /**
182
- * Persistent record of every bid submitted to Hyperbridge.
183
- *
184
- * This is the money-critical store: a successful bid locks a deposit that is
185
- * only reclaimable by retracting it, and the retraction sweep finds what to
186
- * reclaim by querying here. An implementation that loses writes leaks deposits,
187
- * so `store` must be durable before it resolves — do not buffer it in memory
188
- * and flush later.
189
- */
190
- interface BidStore {
191
- store(bid: BidInsert): Promise<void>;
192
- /** The most recent bid for a commitment, or null. */
193
- byCommitment(commitment: string): Promise<StoredBid | null>;
194
- /** Every bid for any of `commitments`, newest first. Empty input resolves empty. */
195
- byCommitments(commitments: string[]): Promise<StoredBid[]>;
196
- /** Unretracted bids whose deposit may still be locked: confirmed or pooled. */
197
- unretractedReclaimable(): Promise<StoredBid[]>;
198
- /**
199
- * Bids due for retraction: successful *or still pending*, unretracted, and
200
- * either older than `maxAgeMs` or flagged dead. Pending counts because a
201
- * pooled extrinsic that later lands reserves a deposit exactly like a
202
- * confirmed one. Dead bids ignore the age cut — their deposit is
203
- * reclaimable immediately, so a failed attempt retries on the next sweep
204
- * rather than waiting out the TTL.
205
- */
206
- expiredUnretracted(maxAgeMs: number): Promise<StoredBid[]>;
207
- /**
208
- * Marks a bid retracted. Also called when the chain reports `BidNotFound`:
209
- * bids only leave the pallet by retraction, so "no bid" means there is
210
- * nothing left to reclaim and no hash is available.
211
- *
212
- * Resolves false when no unretracted bid matched (already retracted, or never
213
- * stored) — the caller treats that as success, not an error.
214
- */
215
- markRetracted(commitment: string, retractExtrinsicHash: string | null): Promise<boolean>;
216
- /** Flags a bid dead (its order was filled on-chain). False when nothing matched. */
217
- markDead(commitment: string): Promise<boolean>;
218
- /**
219
- * Takes what this bid holds, exactly once, and returns it.
220
- *
221
- * A losing bid gives its holds back and a winning one converts them into
222
- * draw-downs, and both routes end at the same bid row — a bid that won is still
223
- * retracted eventually, by the stale sweep, so an unguarded release would undo
224
- * a conversion that already happened. Whichever settles first claims them here;
225
- * the other gets an empty list and does nothing.
226
- */
227
- /**
228
- * Takes the holds off a bid, exactly once, and hands them to the caller.
229
- *
230
- * With a `bid` it claims that one bid; without, every outstanding hold on the
231
- * commitment, which is what a filled or dead order needs so no bid's hold is
232
- * left behind.
233
- */
234
- claimReservation(commitment: string, bid?: string): Promise<LimitOrderHold[]>;
235
- /**
236
- * Every bid that drew on a limit order, newest first. What makes a `remaining`
237
- * explicable to the operator: which bids took the difference.
238
- */
239
- byLimitOrder(limitOrderId: string, limit?: number): Promise<StoredBid[]>;
240
- /** Newest first. Implementations should cap `limit` at a few hundred. */
241
- recent(limit?: number): Promise<StoredBid[]>;
242
- /** Failed bids, newest first — for debugging. */
243
- failed(limit?: number): Promise<StoredBid[]>;
244
- byDateRange(from: Date, to: Date): Promise<StoredBid[]>;
245
- stats(): Promise<BidStats>;
246
- }
247
- /**
248
- * `bid`: this filler's bid for the order was accepted by Hyperbridge (txHash is
249
- * the extrinsic hash). `filled`: this filler filled the order on chain. `lost`:
250
- * another filler did (reason carries its address). `executed`: a direct fill
251
- * attempt or a failed bid.
252
- */
253
- type ActivityType = "detected" | "bid" | "filled" | "lost" | "executed" | "skipped" | "rebalance";
254
- /** One leg of an order as the activity feed records it. */
255
- interface OrderLeg {
256
- /** 20-byte token address; the zero address is the chain's native asset. */
257
- token: string;
258
- /** Raw on-chain amount as a decimal string (bigint-safe). */
259
- amount: string;
260
- /** Registry symbol when the token is known to this filler, else null. */
261
- symbol: string | null;
262
- /** ERC-20 decimals when they could be read, else null (the UI then shows the raw amount). */
263
- decimals: number | null;
264
- }
265
- /**
266
- * What the activity feed knows about an order, captured when it was detected
267
- * and attached to every later event for that order so each row stands alone.
268
- */
269
- interface OrderSummary {
270
- user: string;
271
- /** Source chain state machine id, e.g. "EVM-8453". */
272
- source: string;
273
- destination: string;
274
- /** Hash of the transaction that placed the order on the source chain. */
275
- placedTxHash: string | null;
276
- /** 20-byte referrer from the order's graffiti tag; null when unattributed. */
277
- referrer: string | null;
278
- inputs: OrderLeg[];
279
- outputs: OrderLeg[];
280
- /** Order deadline (block number) as a decimal string. */
281
- deadline: string;
282
- }
283
- interface ActivityEvent {
284
- id: number;
285
- ts: number;
286
- type: ActivityType;
287
- orderId: string | null;
288
- chainId: number | null;
289
- strategy: string | null;
290
- success: boolean | null;
291
- /** Skip reason or execution error. */
292
- reason: string | null;
293
- volumeUsd: number | null;
294
- profitUsd: number | null;
295
- txHash: string | null;
296
- /** The order this event concerns, when known; null for rebalances and legacy rows. */
297
- order: OrderSummary | null;
298
- }
299
- type ActivityInsert = Partial<Omit<ActivityEvent, "id" | "ts" | "type">> & {
300
- type: ActivityType;
301
- };
302
- type WalletTxKind = "send" | "sweep" | "redeem";
303
- /** One outbound transaction from the filler wallet (operator send, vault sweep/redeem). */
304
- interface WalletTx {
305
- id: number;
306
- ts: number;
307
- kind: WalletTxKind;
308
- chainId: number | null;
309
- /** What left the wallet: symbol and decimal amount (sends, sweeps, redeems). */
310
- token: string | null;
311
- amount: string | null;
312
- to: string | null;
313
- txHash: string;
314
- sponsored: boolean | null;
315
- /** What came back: vault shares for a sweep, the underlying for a redeem. */
316
- tokenIn?: string | null;
317
- amountIn?: string | null;
318
- }
319
- /**
320
- * Append-only feed of what the filler did, backing the operator dashboard.
321
- *
322
- * Unlike {@link BidStore} this is observability, not correctness: dropping a
323
- * row costs a line in the activity view and nothing else, so an implementation
324
- * may prune, sample, or cap history freely.
325
- */
326
- interface ActivityStore {
327
- /** Appends an event and resolves the stored row (its `id` and `ts` assigned). */
328
- record(event: ActivityInsert): Promise<ActivityEvent>;
329
- /** Newest first; pass `beforeId` to page backwards. */
330
- recent(limit?: number, beforeId?: number): Promise<ActivityEvent[]>;
331
- /** Fill events carrying both a tx hash and a chain, newest first. */
332
- fills(limit?: number): Promise<ActivityEvent[]>;
333
- recordWalletTx(tx: Omit<WalletTx, "id" | "ts">): Promise<void>;
334
- walletTxs(limit?: number): Promise<WalletTx[]>;
335
- /** Sweep and redeem rows recorded without amounts (before the ledger carried them). Newest first. */
336
- walletTxsWithoutAmounts(limit?: number): Promise<WalletTx[]>;
337
- /** Fills in the amount fields of one ledger row. */
338
- updateWalletTx(id: number, patch: Pick<WalletTx, "token" | "amount" | "to" | "tokenIn" | "amountIn">): Promise<void>;
339
- /**
340
- * Distinct order ids among the newest rows that carry no order summary —
341
- * rows written before summaries existed. Newest first, at most `limit`.
342
- */
343
- orderIdsMissingSummary(limit?: number): Promise<string[]>;
344
- /** Sets the summary on every row for `orderId` that lacks one; resolves the rows it changed. */
345
- attachOrder(orderId: string, order: OrderSummary): Promise<ActivityEvent[]>;
346
- /**
347
- * Orders (rows sharing an order id) newest-activity first, one page at a
348
- * time. `page` is 1-based; `total` counts every distinct order.
349
- */
350
- orderHistory(page: number, pageSize: number): Promise<OrderHistoryPage>;
351
- /** Whether any row exists for `orderId` — i.e. this filler has seen the order. */
352
- knowsOrder(orderId: string): Promise<boolean>;
353
- /**
354
- * Orders this filler bid on that have no on-chain outcome recorded yet: a
355
- * `bid` row, or a legacy bid-time `filled` row (those carry `volumeUsd`), and
356
- * neither a `lost` row nor an observed `filled` row (no `volumeUsd`). Newest first.
357
- */
358
- unsettledOrders(limit?: number): Promise<string[]>;
359
- /** Retypes an order's legacy bid-time `filled` rows to `bid`; resolves the rows it changed. */
360
- retypeLegacyBid(orderId: string): Promise<ActivityEvent[]>;
361
- }
362
- /** One order's rows, newest first. */
363
- interface OrderHistoryEntry {
364
- orderId: string;
365
- events: ActivityEvent[];
366
- }
367
- interface OrderHistoryPage {
368
- page: number;
369
- pageSize: number;
370
- total: number;
371
- orders: OrderHistoryEntry[];
372
- }
373
- /** Which way round a limit order trades its book's pair. */
374
- type LimitOrderSide = "BID" | "ASK";
375
- /**
376
- * `open`: live, and bids may draw on it. `resizing`: a repost is in flight after
377
- * a fill, so the orderbook entry may be missing until it lands. `filled`: worked
378
- * down past the dust floor. `cancelled`: withdrawn by the operator. `expired`:
379
- * past the operator's own `expiresAt` and swept off the book. `rejected`: the
380
- * orderbook refused it and `lastError` says why.
381
- */
382
- type LimitOrderStatus = "open" | "resizing" | "filled" | "cancelled" | "expired" | "rejected";
383
- /**
384
- * One of the operator's limit orders.
385
- *
386
- * This record is what simplex prices against and draws down; the orderbook entry
387
- * is a derived copy that expires and is reposted. Amounts and prices are decimal
388
- * strings at 1e18, the unit the orderbook takes and returns, whatever decimals
389
- * the tokens use on their own chains.
390
- */
391
- interface LimitOrder {
392
- /** Stable across every repost, unlike `commitment`. */
393
- id: string;
394
- /** Orderbook book id, with the symbols it resolved to. */
395
- book: string;
396
- base: string;
397
- quote: string;
398
- side: LimitOrderSide;
399
- /** Where simplex fills, as a state machine id. */
400
- fillChain: string;
401
- /** Quote per 1 base: the rate simplex signs, before the orderbook's fee haircut. */
402
- price: string;
403
- /** The output simplex offered to pay when the order was created. */
404
- size: string;
405
- /** Output not yet delivered. */
406
- remaining: string;
407
- /** Output promised to bids that have neither filled nor been retracted. */
408
- reserved: string;
409
- /** Source chains this order accepts swaps from. Never empty. */
410
- acceptedSources: string[];
411
- /** TTL written into each posting. */
412
- ttlSecs: number;
413
- /** Operator expiry for the limit order itself, independent of the posting's. */
414
- expiresAt: string | null;
415
- status: LimitOrderStatus;
416
- /** The current posting's commitment, absent while nothing is live. */
417
- commitment: string | null;
418
- /** Bumped on every repost, so each posting hashes differently. */
419
- orderNonce: string;
420
- /**
421
- * The EntryPoint the current posting's op was signed for. Null while nothing is
422
- * posted, and on a posting stored before this was recorded, which was signed
423
- * for v0.8.
424
- */
425
- entryPoint: string | null;
426
- /** When the current posting expires, as the orderbook reported it. */
427
- bookExpiresAt: string | null;
428
- /** `Order.price` from the orderbook, which shades `price` by the protocol fee. */
429
- bookPrice: string | null;
430
- /** The last rejection, as "CODE: message". */
431
- lastError: string | null;
432
- /** SQLite-style "YYYY-MM-DD HH:MM:SS" in UTC. Sorts lexicographically. */
433
- createdAt: string;
434
- updatedAt: string;
435
- }
436
- /** What the operator supplies; everything else is derived or defaulted. */
437
- interface LimitOrderInsert {
438
- id: string;
439
- book: string;
440
- base: string;
441
- quote: string;
442
- side: LimitOrderSide;
443
- fillChain: string;
444
- price: string;
445
- size: string;
446
- acceptedSources: string[];
447
- ttlSecs: number;
448
- expiresAt?: string | null;
449
- /** Where the order's posted-op nonce starts. Defaults to "0". */
450
- orderNonce?: string;
451
- }
452
- interface LimitOrderFilter {
453
- status?: LimitOrderStatus;
454
- fillChain?: string;
455
- book?: string;
456
- }
457
- /** The fields a posting writes back, applied together so a half-posted row is never visible. */
458
- interface LimitOrderPosting {
459
- commitment: string | null;
460
- bookExpiresAt: string | null;
461
- bookPrice: string | null;
462
- orderNonce: string;
463
- entryPoint: string | null;
464
- status: LimitOrderStatus;
465
- lastError: string | null;
466
- }
467
- /**
468
- * Persistent record of the operator's limit orders.
469
- *
470
- * This is inventory, not a cache: `remaining` and `reserved` are what stop two
471
- * chains from paying out the same liability twice, so a store that loses writes
472
- * overcommits real money.
473
- */
474
- /**
475
- * One fill that drew a limit order down: kept apart from the bid that priced it, whose
476
- * holds are cleared on settlement, so an order keeps its history across every resize.
477
- */
478
- interface LimitOrderFill {
479
- id: number;
480
- limitOrderId: string;
481
- /** The swap order the fill settled. */
482
- commitment: string;
483
- /** The identifier Hyperbridge filed the executed bid under, when known. */
484
- bid: string | null;
485
- /** What the fill drew the limit order down by, at 1e18 in the token it pays. */
486
- amount: string;
487
- /**
488
- * The escrow the fill released to the solver, at 1e18 in the token the order takes in. Null
489
- * for a fill recorded before this was kept, and for one whose event carried no inputs.
490
- */
491
- amountIn: string | null;
492
- /** The fill's transaction on the order's fill chain, when the event carried it. */
493
- transactionHash: string | null;
494
- /** SQLite-style "YYYY-MM-DD HH:MM:SS" in UTC. */
495
- filledAt: string;
496
- }
497
- interface LimitOrderFillInsert {
498
- limitOrderId: string;
499
- commitment: string;
500
- bid?: string | null;
501
- amount: string;
502
- amountIn?: string | null;
503
- transactionHash?: string | null;
504
- }
505
- /**
506
- * A fill with the terms of the order it drew on: what profitability is worked out from. The
507
- * order's `price` stands in for the rate of a fill that kept no `amountIn`.
508
- */
509
- interface LimitOrderFillRecord {
510
- id: number;
511
- limitOrderId: string;
512
- book: string;
513
- base: string;
514
- quote: string;
515
- side: LimitOrderSide;
516
- price: string;
517
- amount: string;
518
- amountIn: string | null;
519
- filledAt: string;
520
- }
521
- interface LimitOrderStore {
522
- create(order: LimitOrderInsert): Promise<LimitOrder>;
523
- get(id: string): Promise<LimitOrder | null>;
524
- list(filter?: LimitOrderFilter): Promise<LimitOrder[]>;
525
- /** Every `open` order, which is what the matcher prices against. */
526
- open(): Promise<LimitOrder[]>;
527
- /**
528
- * Records what the orderbook did with the current posting.
529
- *
530
- * `only` guards the write on the status the row still holds, and answers null
531
- * when it has moved on. A posting is a slow round trip, and an operator's
532
- * cancel or an expiry sweep that lands first must not be undone by an answer
533
- * that was already in flight.
534
- */
535
- setPosting(id: string, posting: LimitOrderPosting, only?: readonly LimitOrderStatus[]): Promise<LimitOrder | null>;
536
- setStatus(id: string, status: LimitOrderStatus, lastError?: string | null, only?: readonly LimitOrderStatus[]): Promise<LimitOrder | null>;
537
- /**
538
- * Adds `amount` to `reserved`, but only while the order is `open` and
539
- * `remaining` covers this one hold. Resolves false when it does not.
540
- *
541
- * Other bids' holds do not count against it: a pending bid must not stop the
542
- * next one going out, so the limit order bounds each bid and what fills draw
543
- * down, not the sum of bids still waiting. The hold is bookkeeping, turned into
544
- * a draw-down if the bid wins and given back if it loses. The write is still
545
- * guarded on the `reserved` it read, so two holds landing together both count.
546
- */
547
- reserve(id: string, amount: string): Promise<boolean>;
548
- /** Gives a reservation back, after a bid was retracted, lost or found dead. */
549
- release(id: string, amount: string): Promise<void>;
550
- /**
551
- * Works the order down by output that has actually been delivered, floored at
552
- * zero. Returns the order as it now stands, or null when there is none.
553
- */
554
- drawDown(id: string, amount: string): Promise<LimitOrder | null>;
555
- /**
556
- * Lowers an `open` order's `remaining` to `room + reserved`, where `room` is
557
- * what the solver's account on chain will still pay for this order, at 1e18.
558
- * Never raises it. Returns the order when it was lowered, null otherwise.
559
- *
560
- * `reserved` is added back because a fill the chain has already counted, but
561
- * that has not been drawn down here yet, still holds its amount there until it
562
- * settles. Clamping to `room` alone would take that fill off twice.
563
- */
564
- clampRemaining(id: string, room: string): Promise<LimitOrder | null>;
565
- /** Records a fill against its limit order. Called alongside the draw-down it explains. */
566
- recordFill(fill: LimitOrderFillInsert): Promise<void>;
567
- /** Every fill of every limit order, oldest first, each with its order's terms. */
568
- fillHistory(): Promise<LimitOrderFillRecord[]>;
569
- /** A limit order's fills, newest first. */
570
- fills(limitOrderId: string, limit?: number): Promise<LimitOrderFill[]>;
571
- /**
572
- * Runs `settle` as one unit where the backend can.
573
- *
574
- * A fill claims what its bid held, works the orders down by what went out and
575
- * gives the rest back, and a crash between those leaves the hold released
576
- * against an order that was never drawn down. The bid rows live in the same
577
- * database as the limit orders, which is what lets one transaction cover both.
578
- *
579
- * Only store calls belong inside: they are synchronous underneath, so nothing
580
- * else interleaves on the connection, which would not hold for a network call.
581
- */
582
- transaction<T>(settle: () => Promise<T>): Promise<T>;
583
- }
584
- /** Operator state that must survive a restart. */
585
- interface RuntimeState {
586
- /** A pause set by the operator stays set across restarts. */
587
- paused?: boolean;
588
- /** Notification rules, browser subscriptions, and the per-installation VAPID identity. */
589
- notifications?: NotificationRuntimeState;
590
- }
591
- interface NotificationSettings {
592
- /** Alert once when aggregate spendable USD-stable liquidity crosses below this value. */
593
- lowLiquidityThresholdUsd: number | null;
594
- /** Alert after this filler completes a swap. */
595
- swaps: boolean;
596
- }
597
- /** Transport-safe alert payload shared by Web Push and the native desktop shell. */
598
- interface OperatorNotification {
599
- title: string;
600
- body: string;
601
- tag: string;
602
- /** Relative dashboard destination to open when the operator clicks the alert. */
603
- url: string;
604
- /** Opaque, short-lived receipt used only to confirm native desktop test delivery. */
605
- receiptId?: string;
606
- }
607
- interface StoredPushSubscription {
608
- endpoint: string;
609
- expirationTime?: number | null;
610
- keys: {
611
- p256dh: string;
612
- auth: string;
613
- };
614
- }
615
- interface NotificationRuntimeState {
616
- settings: NotificationSettings;
617
- vapid: {
618
- publicKey: string;
619
- privateKey: string;
620
- };
621
- subscriptions: StoredPushSubscription[];
622
- /** Prevents restart/poll spam while liquidity remains below the configured threshold. */
623
- lowLiquidityActive?: boolean;
624
- }
625
- interface StateStore {
626
- get(): Promise<RuntimeState>;
627
- /** Replaces the whole record. Keys absent from `state` are dropped. */
628
- set(state: RuntimeState): Promise<void>;
629
- /**
630
- * Merges `patch` into the stored record atomically, leaving keys it does not
631
- * name untouched. Optional: `patchRuntimeState` falls back to a read and a
632
- * `set` for stores that cannot do better, which loses a key written
633
- * concurrently. The bundled SQLite store implements it.
634
- */
635
- patch?(patch: Partial<RuntimeState>): Promise<RuntimeState>;
636
- }
637
- /**
638
- * What the solver held at one moment: whole tokens per symbol, counting the wallet and the
639
- * vaults on every chain.
640
- */
641
- interface InventorySnapshot {
642
- id: number;
643
- /** SQLite-style "YYYY-MM-DD HH:MM:SS" in UTC. */
644
- takenAt: string;
645
- balances: Record<string, number>;
646
- }
647
- /**
648
- * A record of what the solver held, taken about once a day.
649
- *
650
- * It is what profit is measured against: the inventory a period began with. The chain can say
651
- * what an account holds now but not what it held last week, so a snapshot that was never taken
652
- * cannot be recovered. Like {@link ActivityStore} it is observability rather than correctness:
653
- * a lost row costs a figure on the analytics page and nothing else.
654
- */
655
- interface InventoryStore {
656
- record(balances: Record<string, number>): Promise<void>;
657
- /** The most recent snapshot, or null when none has been taken. */
658
- latest(): Promise<InventorySnapshot | null>;
659
- /** Every snapshot taken at or after `from`, oldest first. */
660
- since(from: Date): Promise<InventorySnapshot[]>;
661
- }
662
-
663
- export { type ActivityStore as A, type BidStore as B, getLogger as C, type OrderSummary as D, type InventoryStore as I, LoggerContext as L, type OrderHistoryPage as O, type RuntimeState as R, type SimplexDataStore as S, type WalletTx as W, type LogLevel as a, type Logger as b, type ActivityInsert as c, type ActivityEvent as d, type LimitOrderStore as e, type LimitOrder as f, type LimitOrderFilter as g, type LimitOrderFill as h, type StoredBid as i, type LogSink as j, type BidStats as k, type StateStore as l, type ActivityType as m, type BidInsert as n, type InventorySnapshot as o, type LimitOrderFillInsert as p, type LimitOrderFillRecord as q, type LimitOrderHold as r, type LimitOrderInsert as s, type LimitOrderPosting as t, type LimitOrderSide as u, type LimitOrderStatus as v, type OperatorNotification as w, type WalletTxKind as x, addLogSink as y, configureLogger as z };