@hyperbridge/simplex 0.18.2 → 0.18.3

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.cjs CHANGED
@@ -17093,8 +17093,10 @@ function budgetFromCache(budget) {
17093
17093
  }
17094
17094
  var CacheService = class {
17095
17095
  constructor(loggers = defaultLoggerContext()) {
17096
- this.CACHE_EXPIRY_MS = 1 * 60 * 1e3;
17097
- // 1 minute
17096
+ this.CACHE_EXPIRY_MS = 5 * 60 * 1e3;
17097
+ // 5 minutes
17098
+ /** How long each order's source confirmations take, for the orders still waiting for them. */
17099
+ this.confirmationWaits = /* @__PURE__ */ new Map();
17098
17100
  /** When each order's bid loop began, for the orders whose loop is still running. */
17099
17101
  this.pins = /* @__PURE__ */ new Map();
17100
17102
  this.logger = loggers.get("cache-service");
@@ -17114,13 +17116,32 @@ var CacheService = class {
17114
17116
  };
17115
17117
  }
17116
17118
  /**
17117
- * Whether an entry is still fresh. A pinned order's entries are aged as of the pin,
17118
- * so what was fresh when its bid loop began stays fresh until the loop ends, and
17119
+ * Whether an entry is still live. A pinned order's entries are judged as of the pin,
17120
+ * so what was live when its bid loop began stays live until the loop ends, and
17119
17121
  * what had already expired stays expired.
17120
17122
  */
17121
- isCacheValid(timestamp, orderId) {
17122
- const now = (orderId !== void 0 ? this.pins.get(orderId) : void 0) ?? Date.now();
17123
- return now - timestamp < this.CACHE_EXPIRY_MS;
17123
+ isCacheValid(expiresAt, orderId) {
17124
+ return (this.pins.get(orderId) ?? Date.now()) < expiresAt;
17125
+ }
17126
+ /**
17127
+ * When an entry written now for the order expires: `CACHE_EXPIRY_MS` from now, plus the
17128
+ * time the order's source confirmations take while it waits for them.
17129
+ */
17130
+ expiresAt(orderId) {
17131
+ return Date.now() + this.CACHE_EXPIRY_MS + (this.confirmationWaits.get(orderId) ?? 0);
17132
+ }
17133
+ /**
17134
+ * Lets what is cached for the order outlive `CACHE_EXPIRY_MS` by `waitMs`, the time its
17135
+ * source confirmations take, and returns the release.
17136
+ *
17137
+ * An order is evaluated while it waits for its confirmations, and its bids are signed after
17138
+ * them from what the evaluation cached, so that has to outlast the wait. Each entry takes
17139
+ * its expiry when it is written: the release does not shorten what is already cached, it
17140
+ * only stops later entries from getting the extra time.
17141
+ */
17142
+ extendForConfirmations(orderId, waitMs) {
17143
+ this.confirmationWaits.set(orderId, waitMs);
17144
+ return () => this.confirmationWaits.delete(orderId);
17124
17145
  }
17125
17146
  /**
17126
17147
  * Keeps the order's evaluation alive while its bids go out, and returns the release.
@@ -17137,37 +17158,37 @@ var CacheService = class {
17137
17158
  return () => this.pins.delete(orderId);
17138
17159
  }
17139
17160
  cleanupStaleData() {
17140
- const staleGasEstimateIds = Object.entries(this.cacheData.gasEstimates).filter(([orderId, data]) => !this.isCacheValid(data.timestamp, orderId)).map(([orderId]) => orderId);
17161
+ const staleGasEstimateIds = Object.entries(this.cacheData.gasEstimates).filter(([orderId, data]) => !this.isCacheValid(data.expiresAt, orderId)).map(([orderId]) => orderId);
17141
17162
  staleGasEstimateIds.forEach((orderId) => {
17142
17163
  delete this.cacheData.gasEstimates[orderId];
17143
17164
  });
17144
17165
  for (const [orderId, data] of Object.entries(this.cacheData.bidPlans)) {
17145
- if (!this.isCacheValid(data.timestamp, orderId)) delete this.cacheData.bidPlans[orderId];
17166
+ if (!this.isCacheValid(data.expiresAt, orderId)) delete this.cacheData.bidPlans[orderId];
17146
17167
  }
17147
17168
  for (const [orderId, data] of Object.entries(this.cacheData.matchedLimitOrders)) {
17148
- if (!this.isCacheValid(data.timestamp, orderId)) delete this.cacheData.matchedLimitOrders[orderId];
17169
+ if (!this.isCacheValid(data.expiresAt, orderId)) delete this.cacheData.matchedLimitOrders[orderId];
17149
17170
  }
17150
- const staleSwapOperationIds = Object.entries(this.cacheData.swapOperations).filter(([orderId, data]) => !this.isCacheValid(data.timestamp, orderId)).map(([orderId]) => orderId);
17171
+ const staleSwapOperationIds = Object.entries(this.cacheData.swapOperations).filter(([orderId, data]) => !this.isCacheValid(data.expiresAt, orderId)).map(([orderId]) => orderId);
17151
17172
  staleSwapOperationIds.forEach((orderId) => {
17152
17173
  delete this.cacheData.swapOperations[orderId];
17153
17174
  });
17154
- const staleFillerOutputIds = Object.entries(this.cacheData.fillerOutputs).filter(([orderId, data]) => !this.isCacheValid(data.timestamp, orderId)).map(([orderId]) => orderId);
17175
+ const staleFillerOutputIds = Object.entries(this.cacheData.fillerOutputs).filter(([orderId, data]) => !this.isCacheValid(data.expiresAt, orderId)).map(([orderId]) => orderId);
17155
17176
  staleFillerOutputIds.forEach((orderId) => {
17156
17177
  delete this.cacheData.fillerOutputs[orderId];
17157
17178
  });
17158
- const stalePairIds = Object.entries(this.cacheData.pairClassifications).filter(([orderId, data]) => !this.isCacheValid(data.timestamp, orderId)).map(([orderId]) => orderId);
17179
+ const stalePairIds = Object.entries(this.cacheData.pairClassifications).filter(([orderId, data]) => !this.isCacheValid(data.expiresAt, orderId)).map(([orderId]) => orderId);
17159
17180
  stalePairIds.forEach((orderId) => {
17160
17181
  delete this.cacheData.pairClassifications[orderId];
17161
17182
  });
17162
- const staleFundingIds = Object.entries(this.cacheData.fundingPrepends).filter(([orderId, data]) => !this.isCacheValid(data.timestamp, orderId)).map(([orderId]) => orderId);
17183
+ const staleFundingIds = Object.entries(this.cacheData.fundingPrepends).filter(([orderId, data]) => !this.isCacheValid(data.expiresAt, orderId)).map(([orderId]) => orderId);
17163
17184
  staleFundingIds.forEach((orderId) => {
17164
17185
  delete this.cacheData.fundingPrepends[orderId];
17165
17186
  });
17166
- const stalePartialIds = Object.entries(this.cacheData.partialFills).filter(([orderId, data]) => !this.isCacheValid(data.timestamp, orderId)).map(([orderId]) => orderId);
17187
+ const stalePartialIds = Object.entries(this.cacheData.partialFills).filter(([orderId, data]) => !this.isCacheValid(data.expiresAt, orderId)).map(([orderId]) => orderId);
17167
17188
  stalePartialIds.forEach((orderId) => {
17168
17189
  delete this.cacheData.partialFills[orderId];
17169
17190
  });
17170
- const staleWaivedIds = Object.entries(this.cacheData.feeChecksWaived).filter(([orderId, data]) => !this.isCacheValid(data.timestamp, orderId)).map(([orderId]) => orderId);
17191
+ const staleWaivedIds = Object.entries(this.cacheData.feeChecksWaived).filter(([orderId, data]) => !this.isCacheValid(data.expiresAt, orderId)).map(([orderId]) => orderId);
17171
17192
  staleWaivedIds.forEach((orderId) => {
17172
17193
  delete this.cacheData.feeChecksWaived[orderId];
17173
17194
  });
@@ -17175,7 +17196,7 @@ var CacheService = class {
17175
17196
  getGasEstimate(orderId) {
17176
17197
  try {
17177
17198
  const cache = this.cacheData.gasEstimates[orderId];
17178
- if (cache && this.isCacheValid(cache.timestamp, orderId)) {
17199
+ if (cache && this.isCacheValid(cache.expiresAt, orderId)) {
17179
17200
  return {
17180
17201
  totalCostInSourceFeeToken: BigInt(cache.totalCostInSourceFeeToken),
17181
17202
  relayerFeeInSourceFeeToken: BigInt(cache.relayerFeeInSourceFeeToken ?? "0"),
@@ -17210,7 +17231,7 @@ var CacheService = class {
17210
17231
  maxFeePerGas: maxFeePerGas.toString(),
17211
17232
  maxPriorityFeePerGas: maxPriorityFeePerGas.toString(),
17212
17233
  totalGasCostWei: totalGasCostWei.toString(),
17213
- timestamp: Date.now()
17234
+ expiresAt: this.expiresAt(orderId)
17214
17235
  };
17215
17236
  } catch (error) {
17216
17237
  this.logger.error({ err: error }, "Error setting gas estimate");
@@ -17220,7 +17241,7 @@ var CacheService = class {
17220
17241
  getSwapOperations(orderId) {
17221
17242
  try {
17222
17243
  const cache = this.cacheData.swapOperations[orderId];
17223
- if (cache && this.isCacheValid(cache.timestamp, orderId)) {
17244
+ if (cache && this.isCacheValid(cache.expiresAt, orderId)) {
17224
17245
  return {
17225
17246
  calls: cache.calls,
17226
17247
  totalGasEstimate: BigInt(cache.totalGasEstimate)
@@ -17238,7 +17259,7 @@ var CacheService = class {
17238
17259
  this.cacheData.swapOperations[orderId] = {
17239
17260
  calls,
17240
17261
  totalGasEstimate: totalGasEstimate.toString(),
17241
- timestamp: Date.now()
17262
+ expiresAt: this.expiresAt(orderId)
17242
17263
  };
17243
17264
  } catch (error) {
17244
17265
  this.logger.error({ err: error }, "Error setting swap operations");
@@ -17248,7 +17269,7 @@ var CacheService = class {
17248
17269
  getFillerOutputs(orderId) {
17249
17270
  try {
17250
17271
  const cache = this.cacheData.fillerOutputs[orderId];
17251
- if (cache && this.isCacheValid(cache.timestamp, orderId)) {
17272
+ if (cache && this.isCacheValid(cache.expiresAt, orderId)) {
17252
17273
  return cache.outputs.map((o) => ({
17253
17274
  token: o.token,
17254
17275
  amount: BigInt(o.amount)
@@ -17264,7 +17285,7 @@ var CacheService = class {
17264
17285
  getFillerInputs(orderId) {
17265
17286
  try {
17266
17287
  const cache = this.cacheData.fillerOutputs[orderId];
17267
- if (cache?.inputs && this.isCacheValid(cache.timestamp, orderId)) {
17288
+ if (cache?.inputs && this.isCacheValid(cache.expiresAt, orderId)) {
17268
17289
  return cache.inputs.map((o) => ({
17269
17290
  token: o.token,
17270
17291
  amount: BigInt(o.amount)
@@ -17286,7 +17307,7 @@ var CacheService = class {
17286
17307
  getBidBudget(orderId) {
17287
17308
  try {
17288
17309
  const cache = this.cacheData.fillerOutputs[orderId];
17289
- if (cache?.budget && this.isCacheValid(cache.timestamp, orderId)) return budgetFromCache(cache.budget);
17310
+ if (cache?.budget && this.isCacheValid(cache.expiresAt, orderId)) return budgetFromCache(cache.budget);
17290
17311
  return null;
17291
17312
  } catch (error) {
17292
17313
  this.logger.error({ err: error }, "Error getting bid budget");
@@ -17306,7 +17327,7 @@ var CacheService = class {
17306
17327
  amount: o.amount.toString()
17307
17328
  })),
17308
17329
  ...budget ? { budget: budgetToCache(budget) } : {},
17309
- timestamp: Date.now()
17330
+ expiresAt: this.expiresAt(orderId)
17310
17331
  };
17311
17332
  } catch (error) {
17312
17333
  this.logger.error({ err: error }, "Error setting filler outputs");
@@ -17323,7 +17344,7 @@ var CacheService = class {
17323
17344
  getMatchedLimitOrder(orderId) {
17324
17345
  try {
17325
17346
  const cache = this.cacheData.matchedLimitOrders[orderId];
17326
- if (cache && this.isCacheValid(cache.timestamp, orderId)) {
17347
+ if (cache && this.isCacheValid(cache.expiresAt, orderId)) {
17327
17348
  return cache.holds.map((hold) => ({ limitOrderId: hold.limitOrderId, payout: BigInt(hold.payout) }));
17328
17349
  }
17329
17350
  return [];
@@ -17335,7 +17356,7 @@ var CacheService = class {
17335
17356
  getBidPlans(orderId) {
17336
17357
  try {
17337
17358
  const cache = this.cacheData.bidPlans[orderId];
17338
- if (!cache || !this.isCacheValid(cache.timestamp, orderId)) return [];
17359
+ if (!cache || !this.isCacheValid(cache.expiresAt, orderId)) return [];
17339
17360
  return cache.plans.map((plan) => ({
17340
17361
  limitOrderId: plan.limitOrderId,
17341
17362
  leg: plan.leg ?? 0,
@@ -17386,7 +17407,7 @@ var CacheService = class {
17386
17407
  ...plan.inputDecimals !== void 0 ? { inputDecimals: plan.inputDecimals } : {},
17387
17408
  ...plan.budget ? { budget: budgetToCache(plan.budget) } : {}
17388
17409
  })),
17389
- timestamp: Date.now()
17410
+ expiresAt: this.expiresAt(orderId)
17390
17411
  };
17391
17412
  } catch (error) {
17392
17413
  this.logger.error({ err: error }, "Error setting bid plans");
@@ -17401,7 +17422,7 @@ var CacheService = class {
17401
17422
  this.cleanupStaleData();
17402
17423
  this.cacheData.matchedLimitOrders[orderId] = {
17403
17424
  holds: holds.map((hold) => ({ limitOrderId: hold.limitOrderId, payout: hold.payout.toString() })),
17404
- timestamp: Date.now()
17425
+ expiresAt: this.expiresAt(orderId)
17405
17426
  };
17406
17427
  } catch (error) {
17407
17428
  this.logger.error({ err: error }, "Error setting matched limit order");
@@ -17411,7 +17432,7 @@ var CacheService = class {
17411
17432
  getPairClassifications(orderId) {
17412
17433
  try {
17413
17434
  const cache = this.cacheData.pairClassifications[orderId];
17414
- if (cache && this.isCacheValid(cache.timestamp, orderId)) {
17435
+ if (cache && this.isCacheValid(cache.expiresAt, orderId)) {
17415
17436
  return cache.pairs;
17416
17437
  }
17417
17438
  return null;
@@ -17424,7 +17445,7 @@ var CacheService = class {
17424
17445
  try {
17425
17446
  this.cacheData.pairClassifications[orderId] = {
17426
17447
  pairs,
17427
- timestamp: Date.now()
17448
+ expiresAt: this.expiresAt(orderId)
17428
17449
  };
17429
17450
  } catch (error) {
17430
17451
  this.logger.error({ err: error }, "Error setting pair classifications");
@@ -17434,7 +17455,7 @@ var CacheService = class {
17434
17455
  getFundingPrepends(orderId) {
17435
17456
  try {
17436
17457
  const cache = this.cacheData.fundingPrepends[orderId];
17437
- if (cache && this.isCacheValid(cache.timestamp, orderId)) {
17458
+ if (cache && this.isCacheValid(cache.expiresAt, orderId)) {
17438
17459
  return {
17439
17460
  calls: cache.calls.map((c) => ({
17440
17461
  target: c.target,
@@ -17458,7 +17479,7 @@ var CacheService = class {
17458
17479
  value: c.value.toString(),
17459
17480
  data: c.data
17460
17481
  })),
17461
- timestamp: Date.now()
17482
+ expiresAt: this.expiresAt(orderId)
17462
17483
  };
17463
17484
  } catch (error) {
17464
17485
  this.logger.error({ err: error }, "Error setting funding prepends");
@@ -17477,10 +17498,10 @@ var CacheService = class {
17477
17498
  */
17478
17499
  isPartialFill(orderId) {
17479
17500
  const cache = this.cacheData.partialFills[orderId];
17480
- return cache !== void 0 && this.isCacheValid(cache.timestamp, orderId) && cache.partial;
17501
+ return cache !== void 0 && this.isCacheValid(cache.expiresAt, orderId) && cache.partial;
17481
17502
  }
17482
17503
  setPartialFill(orderId, partial) {
17483
- this.cacheData.partialFills[orderId] = { partial, timestamp: Date.now() };
17504
+ this.cacheData.partialFills[orderId] = { partial, expiresAt: this.expiresAt(orderId) };
17484
17505
  }
17485
17506
  clearPartialFill(orderId) {
17486
17507
  delete this.cacheData.partialFills[orderId];
@@ -17495,10 +17516,10 @@ var CacheService = class {
17495
17516
  */
17496
17517
  isFeeCheckWaived(orderId) {
17497
17518
  const cache = this.cacheData.feeChecksWaived[orderId];
17498
- return cache !== void 0 && this.isCacheValid(cache.timestamp, orderId) && cache.waived;
17519
+ return cache !== void 0 && this.isCacheValid(cache.expiresAt, orderId) && cache.waived;
17499
17520
  }
17500
17521
  setFeeCheckWaived(orderId, waived) {
17501
- this.cacheData.feeChecksWaived[orderId] = { waived, timestamp: Date.now() };
17522
+ this.cacheData.feeChecksWaived[orderId] = { waived, expiresAt: this.expiresAt(orderId) };
17502
17523
  }
17503
17524
  clearFeeCheckWaived(orderId) {
17504
17525
  delete this.cacheData.feeChecksWaived[orderId];
@@ -23942,6 +23963,10 @@ var IntentFiller = class {
23942
23963
  this.monitor.emit("orderSkipped", { orderId: order.id, reason: "Placement block unknown" });
23943
23964
  return;
23944
23965
  }
23966
+ const releaseWait = order.id && requiredConfirmations > 0 ? this.contractService.cacheService.extendForConfirmations(
23967
+ order.id,
23968
+ await this.confirmationWaitMs(order.source, requiredConfirmations)
23969
+ ) : void 0;
23945
23970
  const abortController = new AbortController();
23946
23971
  const confirmStartMs = Date.now();
23947
23972
  const confirmationPollMs = sourceQuorumClient.size > 1 ? 1e3 : 300;
@@ -24005,7 +24030,7 @@ var IntentFiller = class {
24005
24030
  if (!result) abortController.abort();
24006
24031
  return result;
24007
24032
  })
24008
- ]);
24033
+ ]).finally(() => releaseWait?.());
24009
24034
  const confirmDurationSec = (Date.now() - confirmStartMs) / 1e3;
24010
24035
  this.monitor.emit("orderTiming", {
24011
24036
  orderId: order.id,
@@ -24034,7 +24059,8 @@ var IntentFiller = class {
24034
24059
  evaluationResult.strategy,
24035
24060
  solverSelectionActive,
24036
24061
  inputUsdValue,
24037
- evaluationResult.profitability
24062
+ evaluationResult.profitability,
24063
+ () => this.evaluateOrder(order, canFillCache)
24038
24064
  );
24039
24065
  } catch (error) {
24040
24066
  this.logger.error({ orderId: order.id, err: error }, "Error processing order");
@@ -24115,6 +24141,19 @@ var IntentFiller = class {
24115
24141
  );
24116
24142
  return validStrategies[0];
24117
24143
  }
24144
+ /**
24145
+ * How long `confirmations` take on `chain`, at the block time measured when simplex started.
24146
+ * Zero when there is no block time to be had: the order then keeps the cache's own lifetime,
24147
+ * and an evaluation that expires before its bids is redone.
24148
+ */
24149
+ async confirmationWaitMs(chain, confirmations) {
24150
+ try {
24151
+ return Math.ceil(confirmations * await this.contractService.blockTimeSeconds(chain) * 1e3);
24152
+ } catch (err) {
24153
+ this.logger.warn({ chain, err }, "Could not read the block time to size the order's cache lifetime");
24154
+ return 0;
24155
+ }
24156
+ }
24118
24157
  /**
24119
24158
  * The destination chain's latest block number, the clock `order.deadline` is read on.
24120
24159
  *
@@ -24154,7 +24193,7 @@ var IntentFiller = class {
24154
24193
  if (!order.id) return false;
24155
24194
  return this.contractService.cacheService.isFeeCheckWaived(order.id);
24156
24195
  }
24157
- executeOrder(order, bestStrategy, solverSelectionActive, inputUsdValue, profitUsd) {
24196
+ executeOrder(order, bestStrategy, solverSelectionActive, inputUsdValue, profitUsd, reevaluate) {
24158
24197
  const chainQueue = this.chainQueues.get((0, import_sdk14.getChainId)(order.destination));
24159
24198
  if (!chainQueue) {
24160
24199
  this.logger.error({ chain: order.destination }, "No queue configured for chain");
@@ -24165,12 +24204,19 @@ var IntentFiller = class {
24165
24204
  chainQueue.add(async () => {
24166
24205
  const queueDurationSec = (Date.now() - queuedAtMs) / 1e3;
24167
24206
  this.monitor.emit("orderTiming", { orderId: order.id, phase: "queue_wait", durationSec: queueDurationSec });
24207
+ const cache = this.contractService.cacheService;
24208
+ unpin = order.id ? cache.pinOrder(order.id) : void 0;
24209
+ if (order.id && reevaluate && !cache.getGasEstimate(order.id)) {
24210
+ this.logger.info({ orderId: order.id }, "Order evaluation expired before bidding, evaluating it again");
24211
+ const evaluation = await reevaluate();
24212
+ if (!evaluation) return;
24213
+ bestStrategy = evaluation.strategy;
24214
+ profitUsd = evaluation.profitability;
24215
+ }
24168
24216
  this.logger.info(
24169
24217
  { orderId: order.id, strategy: bestStrategy.name, chain: order.destination },
24170
24218
  "Executing order"
24171
24219
  );
24172
- const cache = this.contractService.cacheService;
24173
- unpin = order.id ? cache.pinOrder(order.id) : void 0;
24174
24220
  const plans = this.limitOrders && order.id ? cache.getBidPlans(order.id) : [];
24175
24221
  let lastResult;
24176
24222
  for (const plan of plans.length > 0 ? plans : [void 0]) {