@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.
- package/README.md +85 -151
- package/dist/bin/simplex.js +7769 -1943
- package/dist/bin/simplex.js.map +1 -1
- package/dist/error-reporting.cjs +123 -0
- package/dist/error-reporting.cjs.map +1 -0
- package/dist/error-reporting.d.cts +46 -0
- package/dist/error-reporting.d.ts +46 -0
- package/dist/error-reporting.js +95 -0
- package/dist/error-reporting.js.map +1 -0
- package/dist/index.cjs +1364 -557
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +311 -1896
- package/dist/index.d.ts +311 -1896
- package/dist/index.js +1362 -523
- package/dist/index.js.map +1 -1
- package/dist/sqlite.cjs +1 -0
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +7 -1
- package/dist/sqlite.d.ts +7 -1
- package/dist/sqlite.js +1 -0
- package/dist/sqlite.js.map +1 -1
- package/dist/types-6MEltdkX.d.cts +2297 -0
- package/dist/types-6MEltdkX.d.ts +2297 -0
- package/dist/ui/assets/bridge-BB9cnjYo.png +0 -0
- package/dist/ui/assets/hyperbridge-B8MVl-Oj.png +0 -0
- package/dist/ui/assets/index-DWJcHNgO.css +1 -0
- package/dist/ui/assets/index-Dy5dPCr_.js +2168 -0
- package/dist/ui/assets/usdr-gm4f6KeA.png +0 -0
- package/dist/ui/index.html +2 -2
- package/package.json +19 -4
- package/dist/types-BPyUX8qu.d.cts +0 -663
- package/dist/types-BPyUX8qu.d.ts +0 -663
- package/dist/ui/assets/index-CK8E6aPx.css +0 -1
- package/dist/ui/assets/index-D8DLeEH0.js +0 -106
|
@@ -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 };
|