@hyperbridge/simplex 0.16.5 → 0.16.7

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/dist/index.d.cts CHANGED
@@ -353,46 +353,50 @@ declare class FillerConfigService {
353
353
  * What one quorum call is allowed to spend.
354
354
  *
355
355
  * `deadlineMs` is the ceiling on the call itself: past it, the endpoints still
356
- * outstanding are treated as failures and the call fails with a QuorumError
357
- * naming them, rather than waiting out the slowest provider. It cannot rescue a
358
- * call — early exit already resolves the moment the bar is reachable, over the
359
- * same responses `finalize` would see — so in practice the deadline is where a
360
- * doomed call dies quickly instead of slowly.
356
+ * outstanding are treated as failures, so they stop voting and the call is
357
+ * decided over the endpoints that answered, or fails with a QuorumError naming
358
+ * them. Early exit decides as soon as the endpoints still out could not change
359
+ * the outcome; the deadline decides when one of them never answers.
360
+ *
361
+ * `timeoutMs` bounds one endpoint's attempt, and there is only ever one: the
362
+ * transports are built with `retryCount: 0`, so an endpoint's whole
363
+ * contribution to a call is a single request. That is what keeps the two
364
+ * numbers independent — a task cannot approach `deadlineMs` by retrying inside
365
+ * itself, so a deadline that fires means something outside the budget went
366
+ * wrong rather than an endpoint using the budget as designed.
361
367
  */
362
368
  interface QuorumBudget {
363
369
  timeoutMs: number;
364
- retryCount: number;
365
- retryDelayMs: number;
366
370
  deadlineMs: number;
367
371
  }
368
372
  /**
369
373
  * Wraps multiple viem `PublicClient`s — one per configured RPC URL — and runs
370
374
  * selected read paths (`getLogs`, `getBlockNumber`, `getTransactionConfirmations`)
371
- * as a **BFT quorum**: a result is accepted only when `quorumThreshold(n)` of the
372
- * operator's endpoints agree on it.
375
+ * as a **BFT quorum**: a result is accepted only when `quorumThreshold(m)` of the
376
+ * `m` endpoints that answered agree on it.
373
377
  *
374
378
  * A provider that *answers with divergent data* is never special-cased away — it
375
379
  * simply fails to join the agreeing group, which is what makes a lying or reorged
376
380
  * endpoint detectable rather than authoritative.
377
381
  *
378
- * The pausing state is the bench, and {@link benchFor} decides who goes on it:
379
- * a request-rate limit ({@link isSuspendableRateLimit}), a response that is not
380
- * JSON ({@link isMalformedResponse}), or a head behind the quorum's
381
- * ({@link isStaleHead}). A benched endpoint is DROPPED for its window: not
382
- * queried and not counted, so each call's bar is computed over the endpoints
383
- * actually asked. All three share the property that earns the bench — the
384
- * endpoint will keep failing the same way for a while, so keeping it in the
385
- * denominator would only turn its problem into the solver missing events, and a
386
- * scanner that misses orders loses fills.
382
+ * A provider that *fails* does not vote. A failed request says nothing about the
383
+ * chain, so it must not outvote the endpoints that agree: the threshold guards
384
+ * against endpoints that answer wrongly, and every answer still counts. Six
385
+ * endpoints with two failing decide over the other four, three of which must
386
+ * agree. The price, worth stating plainly, is that a call can be decided by fewer
387
+ * endpoints than the operator configured; at the limit, one endpoint answering
388
+ * while the rest fail decides alone. The voters are still exclusively the
389
+ * operator's own.
387
390
  *
388
- * That is a deliberate widening of what shrinks the voter set, and worth stating
389
- * plainly: 5 endpoints with 2 lagging used to fail the call, and now decide it
390
- * over the remaining 3. The bar still comes from a BFT threshold over the
391
- * endpoints asked, and the voters are still exclusively the operator's own, but
392
- * a call CAN now be decided by fewer of them than the operator configured. When
393
- * every endpoint is benched, all are queried again; there is nobody left to
394
- * prefer. Every other failure mode stays stateless: a chronically slow endpoint
395
- * costs one failed sub-request per call, never a shrunk quorum.
391
+ * The bench is the one state this class keeps, and {@link benchFor} decides who
392
+ * goes on it: a request-rate limit ({@link isSuspendableRateLimit}), a response
393
+ * that is not JSON ({@link isMalformedResponse}), or a head behind the quorum's
394
+ * ({@link isStaleHead}). All three share the property that earns it — the
395
+ * endpoint will keep failing the same way for a while — so a benched endpoint is
396
+ * not asked for its window: asking would spend its quota, or hold the call open
397
+ * for an answer already known to fail. When every endpoint is benched, all are
398
+ * queried again; there is nobody left to prefer. Every other failure is per
399
+ * call, and the endpoint is asked again next time.
396
400
  *
397
401
  * The constructor validates that URLs resolve to distinct hostnames so a "quorum"
398
402
  * isn't secretly the same upstream in disguise.
@@ -402,8 +406,8 @@ declare class QuorumPublicClient {
402
406
  readonly rpcUrls: string[];
403
407
  /**
404
408
  * The quorum bar when every endpoint answers: `quorumThreshold(n)` over the
405
- * full set. Calls made while endpoints are benched use
406
- * `quorumThreshold(queried)` — the bar always matches who was asked.
409
+ * full set. Each call's bar is `quorumThreshold` over the endpoints that answer
410
+ * it, so this is the most it can be.
407
411
  */
408
412
  readonly threshold: number;
409
413
  private readonly logger?;
@@ -424,11 +428,10 @@ declare class QuorumPublicClient {
424
428
  /** Endpoints currently benched, by URL. See {@link benchFor} for the reasons. */
425
429
  suspended(): string[];
426
430
  /**
427
- * The endpoints this call queries — everyone not benched — and the quorum
428
- * bar for exactly that set. A benched endpoint is dropped outright: it
429
- * answers nothing while throttled, so counting it could only fail calls the
430
- * remaining endpoints agree on. With every endpoint benched, everyone is
431
- * queried again; there is nobody left to prefer.
431
+ * The endpoints this call queries: everyone not benched. A benched endpoint is
432
+ * dropped outright: it answers nothing while throttled, so asking it could only
433
+ * cost a request. With every endpoint benched, everyone is queried again; there
434
+ * is nobody left to prefer.
432
435
  */
433
436
  private participants;
434
437
  /**
@@ -453,22 +456,26 @@ declare class QuorumPublicClient {
453
456
  * retries would otherwise stall every confirmation poll). Stragglers settle
454
457
  * out of band and are ignored. `tryDecide` returns undefined to keep
455
458
  * waiting; once every task has settled, `finalize` must decide (it throws
456
- * the QuorumError when the full response set still has no quorum). Early
457
- * exit never weakens the trust model — a decision still requires the same
458
- * quorum, it just doesn't wait for votes it no longer needs.
459
+ * the QuorumError when the full response set still has no quorum).
460
+ *
461
+ * Both are handed the bar: the BFT threshold over the endpoints that have not
462
+ * failed, answered or still out. A failure lowers it rather than counting
463
+ * against it. Counting the pending endpoints keeps an early decision to a bar
464
+ * no lower than the full response set would get, so a vote it did not wait for
465
+ * could never have reversed it.
459
466
  *
460
467
  * `deadlineMs` bounds the other direction: when the bar is NOT reachable
461
468
  * from the fast responders, the call used to wait out every straggler, and
462
469
  * the caller's own retry loop multiplied that into minutes of a chain not
463
470
  * being scanned at all, silently. At the deadline the outstanding endpoints
464
471
  * are treated as failures — named in the QuorumError, so the operator sees
465
- * which ones held the call up — and `finalize` decides on the rest. The
466
- * trust model is untouched: a decision still needs the same quorum.
472
+ * which ones held the call up — and `finalize` decides over the endpoints
473
+ * that answered.
467
474
  */
468
475
  private settleUntilQuorum;
469
476
  /**
470
- * Highest block head backed by a full quorum. Throws {@link QuorumError}
471
- * when fewer than `threshold` endpoints respond.
477
+ * Highest block head backed by a quorum of the endpoints that answer. Throws
478
+ * {@link QuorumError} when none do.
472
479
  */
473
480
  getBlockNumber(): Promise<bigint>;
474
481
  /**
@@ -2652,13 +2659,11 @@ interface PostedOrder {
2652
2659
  * Every code the orderbook's `RejectionCode` enum can send, listed rather than
2653
2660
  * written as a union so a test can hold it against the published schema.
2654
2661
  *
2655
- * Why the orderbook refuses an order. `MIN_ORDER_SIZE` and `TTL_TOO_SHORT` are
2656
- * prevented by validation before posting; `REPLAYED` and `ORDER_EXISTS` are
2657
- * answered by bumping the nonce; the rest are encoding bugs on our side.
2658
- *
2659
- * `UNSUPPORTED_SOURCE_CHAIN` is the exception: a declared source chain has to
2660
- * register the order's input symbol on the server, which is the server's own
2661
- * config and nothing here can check ahead of asking.
2662
+ * Why the orderbook refuses an order. `MIN_ORDER_SIZE`, `TTL_TOO_SHORT` and
2663
+ * `UNSUPPORTED_SOURCE_CHAIN` are prevented by validation before posting, the last
2664
+ * against the chains the server serves and the tokens its registry lists on each;
2665
+ * `REPLAYED` and `ORDER_EXISTS` are answered by bumping the nonce; the rest are
2666
+ * encoding bugs on our side.
2662
2667
  */
2663
2668
  declare const REJECTION_CODES: readonly ["MALFORMED_USER_OP", "NO_FILL_ORDER", "UNSUPPORTED_CHAIN", "UNSUPPORTED_SHAPE", "NOT_PHANTOM", "TTL_TOO_SHORT", "UNSUPPORTED_PAIR", "BAD_SIGNATURE", "BAD_NONCE_BINDING", "REPLAYED", "ORDER_EXISTS", "TOO_MANY_ORDERS", "MIN_ORDER_SIZE", "MISSING_DECLARATION", "EMPTY_DECLARATION", "UNSUPPORTED_SOURCE_CHAIN"];
2664
2669
  type RejectionCode = (typeof REJECTION_CODES)[number];
@@ -2733,9 +2738,10 @@ type CancelOrderResult = {
2733
2738
  * {@link OrderbookRequestError} because it could not get one.
2734
2739
  */
2735
2740
  declare class OrderbookClient {
2736
- private readonly url;
2737
2741
  private readonly requestTimeoutMs;
2738
2742
  private logger;
2743
+ /** The configured endpoint, with `/graphql` if it was left off. */
2744
+ private readonly url;
2739
2745
  constructor(url: string, requestTimeoutMs: number, loggers?: LoggerContext);
2740
2746
  /** The limits to validate against before posting, plus the books on offer. */
2741
2747
  limits(): Promise<OrderbookLimits>;
@@ -2844,6 +2850,13 @@ interface ReconcileReport {
2844
2850
  * orderbook's answer is then written back onto that row: an accepted order
2845
2851
  * carries its commitment, a rejected one carries the reason.
2846
2852
  */
2853
+ /**
2854
+ * The configured ERC-4626 vaults' holdings, as `VaultFundingPlanner` reports them. A fill sources
2855
+ * a payout shortfall from these, so they back a limit order as much as the wallet does.
2856
+ */
2857
+ interface VaultHoldings {
2858
+ getBalanceSnapshot(chain?: string): Promise<VaultBalancePosition[]>;
2859
+ }
2847
2860
  declare class LimitOrderService {
2848
2861
  private readonly store;
2849
2862
  private readonly client;
@@ -2855,12 +2868,16 @@ declare class LimitOrderService {
2855
2868
  private readonly delegationService?;
2856
2869
  /** Where a new order's nonce starts; tests pin it, production draws it at random. */
2857
2870
  private readonly startingNonce;
2871
+ /** The configured ERC-4626 vaults, when the operator runs any. */
2872
+ private readonly vaultBalances?;
2858
2873
  private logger;
2859
2874
  private cachedLimits?;
2860
2875
  private onEvent?;
2861
2876
  constructor(store: LimitOrderStore, client: OrderbookClient, contractService: ContractInteractionService, configService: FillerConfigService, assetRegistry: AssetRegistry, signer: Signer, defaultTtlSecs: number, delegationService?: DelegationService | undefined, loggers?: LoggerContext,
2862
2877
  /** Where a new order's nonce starts; tests pin it, production draws it at random. */
2863
- startingNonce?: () => bigint);
2878
+ startingNonce?: () => bigint,
2879
+ /** The configured ERC-4626 vaults, when the operator runs any. */
2880
+ vaultBalances?: VaultHoldings | undefined);
2864
2881
  /**
2865
2882
  * Attaches the listener fill-driven changes are reported to. `Simplex` does
2866
2883
  * this after boot, the same way the filler is handed the service itself.
@@ -2896,6 +2913,19 @@ declare class LimitOrderService {
2896
2913
  */
2897
2914
  private withdraw;
2898
2915
  private signAndCancel;
2916
+ /**
2917
+ * The pairs an order can be written against, the smallest payout each token may carry, and
2918
+ * the tokens the orderbook registers on each chain.
2919
+ *
2920
+ * The orderbook lists the books; `resolveBook` refuses a request naming anything else, so the
2921
+ * operator is offered exactly these and no combination of symbols they could not post. The
2922
+ * chains do the same for the source chains an order can accept.
2923
+ */
2924
+ books(): Promise<{
2925
+ books: Book[];
2926
+ minOrderSizes: TokenMinSize[];
2927
+ chains: ChainInfo[];
2928
+ }>;
2899
2929
  /**
2900
2930
  * Reads `serverInfo` and `books`, reusing the last read for {@link LIMITS_TTL_MS}.
2901
2931
  *
@@ -2915,15 +2945,30 @@ declare class LimitOrderService {
2915
2945
  * balance on the fill chain, so the operator hears it while they are creating
2916
2946
  * the order.
2917
2947
  *
2948
+ * A fill pays out of the wallet and then withdraws any shortfall from the
2949
+ * configured ERC-4626 vaults in the same batch, so what those vaults hold of
2950
+ * the payout token backs the order too and is counted here. Without it an
2951
+ * operator who sweeps inventory into a vault is refused orders their filler
2952
+ * would have filled.
2953
+ *
2918
2954
  * Each order is checked against the whole balance, not against what is left of
2919
2955
  * it after the others. One balance backs every order resting on it — that is
2920
2956
  * what quoting both sides of a book is — and the orderbook says as much: it
2921
2957
  * advertises each entry at `min(quoted, balance)` rather than dividing the
2922
2958
  * balance between them. Whichever order fills first draws the inventory down,
2923
2959
  * and the rest are cut to what is left. Netting them here would refuse the
2924
- * second side of every book.
2960
+ * second side of every book. Vault positions are counted the same way: the
2961
+ * whole position, not what is left after reservations for fills in flight.
2925
2962
  */
2926
2963
  private assertWalletCanPay;
2964
+ /**
2965
+ * What the configured vaults hold of `token` on `chain`, in scaled units.
2966
+ *
2967
+ * Zero when no vault is configured. A snapshot that cannot be read is also
2968
+ * zero: the wallet alone then has to cover the order, which refuses one the
2969
+ * filler might have managed rather than accepting one it could not.
2970
+ */
2971
+ private vaultHoldings;
2927
2972
  /**
2928
2973
  * Works a limit order down by what a fill delivered, and puts the rest back on
2929
2974
  * the orderbook.
@@ -3457,6 +3502,15 @@ declare class FXFiller implements FillerStrategy {
3457
3502
  * the output token's own decimals.
3458
3503
  */
3459
3504
  private matchLeg;
3505
+ /** Why no limit order serves the order, leg by leg, for the line that says it was passed over. */
3506
+ private explainUnmatched;
3507
+ /**
3508
+ * One leg as the matcher sees it, or null when its input token is not one the asset
3509
+ * registry knows on the source chain.
3510
+ *
3511
+ * Amounts cross into the matcher at 1e18, the unit limit orders are kept in.
3512
+ */
3513
+ private incomingFor;
3460
3514
  /**
3461
3515
  * The order's input in **USD**, or null when no limit order connects its input
3462
3516
  * token to a dollar.
@@ -3794,6 +3848,12 @@ declare class LimitOrderController {
3794
3848
  fills: LimitOrderFill[];
3795
3849
  bids: StoredBid[];
3796
3850
  } | null>;
3851
+ /** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
3852
+ books(): Promise<{
3853
+ books: Book[];
3854
+ minOrderSizes: TokenMinSize[];
3855
+ chains: ChainInfo[];
3856
+ }>;
3797
3857
  /** Creates the order, posts it, and reports what the orderbook made of it. */
3798
3858
  create(request: CreateLimitOrderRequest): Promise<PostedLimitOrder>;
3799
3859
  cancel(id: string): Promise<CancelledLimitOrder>;
package/dist/index.d.ts CHANGED
@@ -353,46 +353,50 @@ declare class FillerConfigService {
353
353
  * What one quorum call is allowed to spend.
354
354
  *
355
355
  * `deadlineMs` is the ceiling on the call itself: past it, the endpoints still
356
- * outstanding are treated as failures and the call fails with a QuorumError
357
- * naming them, rather than waiting out the slowest provider. It cannot rescue a
358
- * call — early exit already resolves the moment the bar is reachable, over the
359
- * same responses `finalize` would see — so in practice the deadline is where a
360
- * doomed call dies quickly instead of slowly.
356
+ * outstanding are treated as failures, so they stop voting and the call is
357
+ * decided over the endpoints that answered, or fails with a QuorumError naming
358
+ * them. Early exit decides as soon as the endpoints still out could not change
359
+ * the outcome; the deadline decides when one of them never answers.
360
+ *
361
+ * `timeoutMs` bounds one endpoint's attempt, and there is only ever one: the
362
+ * transports are built with `retryCount: 0`, so an endpoint's whole
363
+ * contribution to a call is a single request. That is what keeps the two
364
+ * numbers independent — a task cannot approach `deadlineMs` by retrying inside
365
+ * itself, so a deadline that fires means something outside the budget went
366
+ * wrong rather than an endpoint using the budget as designed.
361
367
  */
362
368
  interface QuorumBudget {
363
369
  timeoutMs: number;
364
- retryCount: number;
365
- retryDelayMs: number;
366
370
  deadlineMs: number;
367
371
  }
368
372
  /**
369
373
  * Wraps multiple viem `PublicClient`s — one per configured RPC URL — and runs
370
374
  * selected read paths (`getLogs`, `getBlockNumber`, `getTransactionConfirmations`)
371
- * as a **BFT quorum**: a result is accepted only when `quorumThreshold(n)` of the
372
- * operator's endpoints agree on it.
375
+ * as a **BFT quorum**: a result is accepted only when `quorumThreshold(m)` of the
376
+ * `m` endpoints that answered agree on it.
373
377
  *
374
378
  * A provider that *answers with divergent data* is never special-cased away — it
375
379
  * simply fails to join the agreeing group, which is what makes a lying or reorged
376
380
  * endpoint detectable rather than authoritative.
377
381
  *
378
- * The pausing state is the bench, and {@link benchFor} decides who goes on it:
379
- * a request-rate limit ({@link isSuspendableRateLimit}), a response that is not
380
- * JSON ({@link isMalformedResponse}), or a head behind the quorum's
381
- * ({@link isStaleHead}). A benched endpoint is DROPPED for its window: not
382
- * queried and not counted, so each call's bar is computed over the endpoints
383
- * actually asked. All three share the property that earns the bench — the
384
- * endpoint will keep failing the same way for a while, so keeping it in the
385
- * denominator would only turn its problem into the solver missing events, and a
386
- * scanner that misses orders loses fills.
382
+ * A provider that *fails* does not vote. A failed request says nothing about the
383
+ * chain, so it must not outvote the endpoints that agree: the threshold guards
384
+ * against endpoints that answer wrongly, and every answer still counts. Six
385
+ * endpoints with two failing decide over the other four, three of which must
386
+ * agree. The price, worth stating plainly, is that a call can be decided by fewer
387
+ * endpoints than the operator configured; at the limit, one endpoint answering
388
+ * while the rest fail decides alone. The voters are still exclusively the
389
+ * operator's own.
387
390
  *
388
- * That is a deliberate widening of what shrinks the voter set, and worth stating
389
- * plainly: 5 endpoints with 2 lagging used to fail the call, and now decide it
390
- * over the remaining 3. The bar still comes from a BFT threshold over the
391
- * endpoints asked, and the voters are still exclusively the operator's own, but
392
- * a call CAN now be decided by fewer of them than the operator configured. When
393
- * every endpoint is benched, all are queried again; there is nobody left to
394
- * prefer. Every other failure mode stays stateless: a chronically slow endpoint
395
- * costs one failed sub-request per call, never a shrunk quorum.
391
+ * The bench is the one state this class keeps, and {@link benchFor} decides who
392
+ * goes on it: a request-rate limit ({@link isSuspendableRateLimit}), a response
393
+ * that is not JSON ({@link isMalformedResponse}), or a head behind the quorum's
394
+ * ({@link isStaleHead}). All three share the property that earns it — the
395
+ * endpoint will keep failing the same way for a while — so a benched endpoint is
396
+ * not asked for its window: asking would spend its quota, or hold the call open
397
+ * for an answer already known to fail. When every endpoint is benched, all are
398
+ * queried again; there is nobody left to prefer. Every other failure is per
399
+ * call, and the endpoint is asked again next time.
396
400
  *
397
401
  * The constructor validates that URLs resolve to distinct hostnames so a "quorum"
398
402
  * isn't secretly the same upstream in disguise.
@@ -402,8 +406,8 @@ declare class QuorumPublicClient {
402
406
  readonly rpcUrls: string[];
403
407
  /**
404
408
  * The quorum bar when every endpoint answers: `quorumThreshold(n)` over the
405
- * full set. Calls made while endpoints are benched use
406
- * `quorumThreshold(queried)` — the bar always matches who was asked.
409
+ * full set. Each call's bar is `quorumThreshold` over the endpoints that answer
410
+ * it, so this is the most it can be.
407
411
  */
408
412
  readonly threshold: number;
409
413
  private readonly logger?;
@@ -424,11 +428,10 @@ declare class QuorumPublicClient {
424
428
  /** Endpoints currently benched, by URL. See {@link benchFor} for the reasons. */
425
429
  suspended(): string[];
426
430
  /**
427
- * The endpoints this call queries — everyone not benched — and the quorum
428
- * bar for exactly that set. A benched endpoint is dropped outright: it
429
- * answers nothing while throttled, so counting it could only fail calls the
430
- * remaining endpoints agree on. With every endpoint benched, everyone is
431
- * queried again; there is nobody left to prefer.
431
+ * The endpoints this call queries: everyone not benched. A benched endpoint is
432
+ * dropped outright: it answers nothing while throttled, so asking it could only
433
+ * cost a request. With every endpoint benched, everyone is queried again; there
434
+ * is nobody left to prefer.
432
435
  */
433
436
  private participants;
434
437
  /**
@@ -453,22 +456,26 @@ declare class QuorumPublicClient {
453
456
  * retries would otherwise stall every confirmation poll). Stragglers settle
454
457
  * out of band and are ignored. `tryDecide` returns undefined to keep
455
458
  * waiting; once every task has settled, `finalize` must decide (it throws
456
- * the QuorumError when the full response set still has no quorum). Early
457
- * exit never weakens the trust model — a decision still requires the same
458
- * quorum, it just doesn't wait for votes it no longer needs.
459
+ * the QuorumError when the full response set still has no quorum).
460
+ *
461
+ * Both are handed the bar: the BFT threshold over the endpoints that have not
462
+ * failed, answered or still out. A failure lowers it rather than counting
463
+ * against it. Counting the pending endpoints keeps an early decision to a bar
464
+ * no lower than the full response set would get, so a vote it did not wait for
465
+ * could never have reversed it.
459
466
  *
460
467
  * `deadlineMs` bounds the other direction: when the bar is NOT reachable
461
468
  * from the fast responders, the call used to wait out every straggler, and
462
469
  * the caller's own retry loop multiplied that into minutes of a chain not
463
470
  * being scanned at all, silently. At the deadline the outstanding endpoints
464
471
  * are treated as failures — named in the QuorumError, so the operator sees
465
- * which ones held the call up — and `finalize` decides on the rest. The
466
- * trust model is untouched: a decision still needs the same quorum.
472
+ * which ones held the call up — and `finalize` decides over the endpoints
473
+ * that answered.
467
474
  */
468
475
  private settleUntilQuorum;
469
476
  /**
470
- * Highest block head backed by a full quorum. Throws {@link QuorumError}
471
- * when fewer than `threshold` endpoints respond.
477
+ * Highest block head backed by a quorum of the endpoints that answer. Throws
478
+ * {@link QuorumError} when none do.
472
479
  */
473
480
  getBlockNumber(): Promise<bigint>;
474
481
  /**
@@ -2652,13 +2659,11 @@ interface PostedOrder {
2652
2659
  * Every code the orderbook's `RejectionCode` enum can send, listed rather than
2653
2660
  * written as a union so a test can hold it against the published schema.
2654
2661
  *
2655
- * Why the orderbook refuses an order. `MIN_ORDER_SIZE` and `TTL_TOO_SHORT` are
2656
- * prevented by validation before posting; `REPLAYED` and `ORDER_EXISTS` are
2657
- * answered by bumping the nonce; the rest are encoding bugs on our side.
2658
- *
2659
- * `UNSUPPORTED_SOURCE_CHAIN` is the exception: a declared source chain has to
2660
- * register the order's input symbol on the server, which is the server's own
2661
- * config and nothing here can check ahead of asking.
2662
+ * Why the orderbook refuses an order. `MIN_ORDER_SIZE`, `TTL_TOO_SHORT` and
2663
+ * `UNSUPPORTED_SOURCE_CHAIN` are prevented by validation before posting, the last
2664
+ * against the chains the server serves and the tokens its registry lists on each;
2665
+ * `REPLAYED` and `ORDER_EXISTS` are answered by bumping the nonce; the rest are
2666
+ * encoding bugs on our side.
2662
2667
  */
2663
2668
  declare const REJECTION_CODES: readonly ["MALFORMED_USER_OP", "NO_FILL_ORDER", "UNSUPPORTED_CHAIN", "UNSUPPORTED_SHAPE", "NOT_PHANTOM", "TTL_TOO_SHORT", "UNSUPPORTED_PAIR", "BAD_SIGNATURE", "BAD_NONCE_BINDING", "REPLAYED", "ORDER_EXISTS", "TOO_MANY_ORDERS", "MIN_ORDER_SIZE", "MISSING_DECLARATION", "EMPTY_DECLARATION", "UNSUPPORTED_SOURCE_CHAIN"];
2664
2669
  type RejectionCode = (typeof REJECTION_CODES)[number];
@@ -2733,9 +2738,10 @@ type CancelOrderResult = {
2733
2738
  * {@link OrderbookRequestError} because it could not get one.
2734
2739
  */
2735
2740
  declare class OrderbookClient {
2736
- private readonly url;
2737
2741
  private readonly requestTimeoutMs;
2738
2742
  private logger;
2743
+ /** The configured endpoint, with `/graphql` if it was left off. */
2744
+ private readonly url;
2739
2745
  constructor(url: string, requestTimeoutMs: number, loggers?: LoggerContext);
2740
2746
  /** The limits to validate against before posting, plus the books on offer. */
2741
2747
  limits(): Promise<OrderbookLimits>;
@@ -2844,6 +2850,13 @@ interface ReconcileReport {
2844
2850
  * orderbook's answer is then written back onto that row: an accepted order
2845
2851
  * carries its commitment, a rejected one carries the reason.
2846
2852
  */
2853
+ /**
2854
+ * The configured ERC-4626 vaults' holdings, as `VaultFundingPlanner` reports them. A fill sources
2855
+ * a payout shortfall from these, so they back a limit order as much as the wallet does.
2856
+ */
2857
+ interface VaultHoldings {
2858
+ getBalanceSnapshot(chain?: string): Promise<VaultBalancePosition[]>;
2859
+ }
2847
2860
  declare class LimitOrderService {
2848
2861
  private readonly store;
2849
2862
  private readonly client;
@@ -2855,12 +2868,16 @@ declare class LimitOrderService {
2855
2868
  private readonly delegationService?;
2856
2869
  /** Where a new order's nonce starts; tests pin it, production draws it at random. */
2857
2870
  private readonly startingNonce;
2871
+ /** The configured ERC-4626 vaults, when the operator runs any. */
2872
+ private readonly vaultBalances?;
2858
2873
  private logger;
2859
2874
  private cachedLimits?;
2860
2875
  private onEvent?;
2861
2876
  constructor(store: LimitOrderStore, client: OrderbookClient, contractService: ContractInteractionService, configService: FillerConfigService, assetRegistry: AssetRegistry, signer: Signer, defaultTtlSecs: number, delegationService?: DelegationService | undefined, loggers?: LoggerContext,
2862
2877
  /** Where a new order's nonce starts; tests pin it, production draws it at random. */
2863
- startingNonce?: () => bigint);
2878
+ startingNonce?: () => bigint,
2879
+ /** The configured ERC-4626 vaults, when the operator runs any. */
2880
+ vaultBalances?: VaultHoldings | undefined);
2864
2881
  /**
2865
2882
  * Attaches the listener fill-driven changes are reported to. `Simplex` does
2866
2883
  * this after boot, the same way the filler is handed the service itself.
@@ -2896,6 +2913,19 @@ declare class LimitOrderService {
2896
2913
  */
2897
2914
  private withdraw;
2898
2915
  private signAndCancel;
2916
+ /**
2917
+ * The pairs an order can be written against, the smallest payout each token may carry, and
2918
+ * the tokens the orderbook registers on each chain.
2919
+ *
2920
+ * The orderbook lists the books; `resolveBook` refuses a request naming anything else, so the
2921
+ * operator is offered exactly these and no combination of symbols they could not post. The
2922
+ * chains do the same for the source chains an order can accept.
2923
+ */
2924
+ books(): Promise<{
2925
+ books: Book[];
2926
+ minOrderSizes: TokenMinSize[];
2927
+ chains: ChainInfo[];
2928
+ }>;
2899
2929
  /**
2900
2930
  * Reads `serverInfo` and `books`, reusing the last read for {@link LIMITS_TTL_MS}.
2901
2931
  *
@@ -2915,15 +2945,30 @@ declare class LimitOrderService {
2915
2945
  * balance on the fill chain, so the operator hears it while they are creating
2916
2946
  * the order.
2917
2947
  *
2948
+ * A fill pays out of the wallet and then withdraws any shortfall from the
2949
+ * configured ERC-4626 vaults in the same batch, so what those vaults hold of
2950
+ * the payout token backs the order too and is counted here. Without it an
2951
+ * operator who sweeps inventory into a vault is refused orders their filler
2952
+ * would have filled.
2953
+ *
2918
2954
  * Each order is checked against the whole balance, not against what is left of
2919
2955
  * it after the others. One balance backs every order resting on it — that is
2920
2956
  * what quoting both sides of a book is — and the orderbook says as much: it
2921
2957
  * advertises each entry at `min(quoted, balance)` rather than dividing the
2922
2958
  * balance between them. Whichever order fills first draws the inventory down,
2923
2959
  * and the rest are cut to what is left. Netting them here would refuse the
2924
- * second side of every book.
2960
+ * second side of every book. Vault positions are counted the same way: the
2961
+ * whole position, not what is left after reservations for fills in flight.
2925
2962
  */
2926
2963
  private assertWalletCanPay;
2964
+ /**
2965
+ * What the configured vaults hold of `token` on `chain`, in scaled units.
2966
+ *
2967
+ * Zero when no vault is configured. A snapshot that cannot be read is also
2968
+ * zero: the wallet alone then has to cover the order, which refuses one the
2969
+ * filler might have managed rather than accepting one it could not.
2970
+ */
2971
+ private vaultHoldings;
2927
2972
  /**
2928
2973
  * Works a limit order down by what a fill delivered, and puts the rest back on
2929
2974
  * the orderbook.
@@ -3457,6 +3502,15 @@ declare class FXFiller implements FillerStrategy {
3457
3502
  * the output token's own decimals.
3458
3503
  */
3459
3504
  private matchLeg;
3505
+ /** Why no limit order serves the order, leg by leg, for the line that says it was passed over. */
3506
+ private explainUnmatched;
3507
+ /**
3508
+ * One leg as the matcher sees it, or null when its input token is not one the asset
3509
+ * registry knows on the source chain.
3510
+ *
3511
+ * Amounts cross into the matcher at 1e18, the unit limit orders are kept in.
3512
+ */
3513
+ private incomingFor;
3460
3514
  /**
3461
3515
  * The order's input in **USD**, or null when no limit order connects its input
3462
3516
  * token to a dollar.
@@ -3794,6 +3848,12 @@ declare class LimitOrderController {
3794
3848
  fills: LimitOrderFill[];
3795
3849
  bids: StoredBid[];
3796
3850
  } | null>;
3851
+ /** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
3852
+ books(): Promise<{
3853
+ books: Book[];
3854
+ minOrderSizes: TokenMinSize[];
3855
+ chains: ChainInfo[];
3856
+ }>;
3797
3857
  /** Creates the order, posts it, and reports what the orderbook made of it. */
3798
3858
  create(request: CreateLimitOrderRequest): Promise<PostedLimitOrder>;
3799
3859
  cancel(id: string): Promise<CancelledLimitOrder>;