@hyperbridge/simplex 0.16.11 → 0.16.12

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.
@@ -48802,6 +48802,7 @@ var init_esm2 = __esm({
48802
48802
  init_encodeFunctionData2();
48803
48803
  init_encodePacked2();
48804
48804
  init_parseEventLogs2();
48805
+ init_isAddress2();
48805
48806
  init_defineChain2();
48806
48807
  init_concat2();
48807
48808
  init_isHex2();
@@ -235360,7 +235361,7 @@ function resolveOrderFeeGasPriceBump(sourceStateMachineId, isSameChain) {
235360
235361
  if (isSameChain) {
235361
235362
  return 0n;
235362
235363
  }
235363
- return ORDER_FEE_GAS_PRICE_BUMP_POLICY.bySourceStateMachineId[sourceStateMachineId] ?? ORDER_FEE_GAS_PRICE_BUMP_POLICY.defaultPercent;
235364
+ return sourceStateMachineId === "EVM-1" ? ETHEREUM_ORDER_FEE_GAS_PRICE_BUMP_PERCENT : DEFAULT_ORDER_FEE_GAS_PRICE_BUMP_PERCENT;
235364
235365
  }
235365
235366
  function utf8Encode(text2) {
235366
235367
  const bytes5 = [];
@@ -235421,7 +235422,7 @@ function encodePhantomBidDeclaration(declaration) {
235421
235422
  }
235422
235423
  return u8aToHex(new Uint8Array(bytes5));
235423
235424
  }
235424
- var import_merge_race, __defProp3, __getOwnPropNames2, __esm2, __export2, node_exports, wasm, full_path, __dirname$1, cachedTextDecoder, cachedUint8ArrayMemory0, WASM_VECTOR_LEN, cachedTextEncoder, encodeString5, cachedDataViewMemory0, KeccakMergeFinalization, KeccakMerge, wasmPath, wasmBytes2, bindings, wasmModule, wasmInstance, __wasm, init_node11, RequestStatus, TimeoutStatus, IntentOrderStatus, ABI, evmHost_default, ABI2, handlerV2_default, polkadotAssetHubPaseo, polkadotHubMainnet, pharosMainnet, pharosAtlantic, tronNile, chainConfigs, configsByStateMachineId, getConfigByStateMachineId, getChainId3, getViemChain, hyperbridgeAddress, ABI3, IntentGatewayV2_default, ChainConfigService, ABI4, uniswapRouterV2_default, chains, DEFAULT_ADDRESS, EvmChain, REQUEST_COMMITMENTS_SLOT, RESPONSE_COMMITMENTS_SLOT, REQUEST_RECEIPTS_SLOT, STATE_COMMITMENTS_SLOT, MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, MissingConsensusUpdateTimeError, TokenBucket, BATCHES_NOT_SUPPORTED_CODE, TOO_BIG_BATCH_REQUEST_CODE, DEFAULT_MAX_BATCH_SIZE, _endpoint2, _headers3, _limiter, _maxBatchSize, _batchingSupported, _pending2, _flushTimer, _flushing, _nextId, __BatchingHttpProvider_instances, scheduleFlush_fn, flushNow_fn, flush_fn, post_fn, settleBatch_fn, settle_fn, handleBatchRefusal_fn, requeue_fn, _a5, BatchingHttpProvider, OFFCHAIN_BID_PREFIX, HYPERBRIDGE_TYPES_BUNDLE, BASE_TIP, HTTP_CONNECT_TIMEOUT_MS, INCLUSION_TIMEOUT_MS, DEFAULT_RPC_MAX_RPS, rpcLimiters, BidCodec, PackedUserOperationCodec, IntentsCoprocessor, ReviveContractInfo, ReviveAccountType, ReviveAccountInfo, H256, EvmStateProof, SubstrateHashing, SubstrateStateMachineProof, BasicProof, LeafIndexAndPos, MmrProof, ConsensusStateId, ConsensusMessage, FraudProofMessage, StateMachine, StateMachineId, StateMachineHeight, Proof, PostRequest, PostResponse, GetRequest, StorageValue, GetResponse, Request3, Response2, RequestMessage, RequestResponse, ResponseMessage, TimeoutMessage, GetRequestsWithProof, Message, DEFAULT_CHILD_STORAGE_PREFIX, PharosProofNodeCodec, SiblingLeftmostLeafProofCodec, NonExistenceProofCodec, AccountProofDataCodec, PharosStateProof, DEFAULT_POLL_INTERVAL, ADDRESS_ZERO2, DEFAULT_GRAFFITI, DEFAULT_LOGGER, REQUEST_STATUS_WEIGHTS, TIMEOUT_STATUS_WEIGHTS, COMBINED_STATUS_WEIGHTS, CHAIN_ID_TO_COINGECKO_NATIVE, TESTNET_CHAINS, ORDER_STATUS, loadDriver, detectEnvironment, STORAGE_KEYS, LEGACY_STORAGE_KEYS, ABI5, uniswapV3Quoter_default, UNISWAP_V4_QUOTER_ABI, ABI6, universalRouter_default, PERMIT2_ABI, UniswapQuoteEngine, COMMON_FEE_TIERS, Swap, ABI7, erc7281_default, DEFAULT_GRAFFITI2, ERC7821_BATCH_MODE, BundlerMethod, SELECT_SOLVER_TYPEHASH, PACKED_USEROP_TYPEHASH, DOMAIN_TYPEHASH, CryptoUtils, FEE_TOKEN_CACHE_TTL_MS, OrderPlacer, TOKEN_KEYED_GATEWAY_ABI, keyingByClient, PARTIAL_FILLS_SLOT, USED_USEROPS_STORAGE_KEY, OrderExecutor, _a6, OrderCanceller, FILL_ORDER_SELECTOR, SUPPORTED_INTENTS_VERSION, CONTRACT_VERSION_ABI, CANONICAL_EVM_TOKEN, ENTRY_POINT_EVENT_ABI, BidImpl, BidManager, RELAYER_MESSAGE_GAS, NO_BUNDLER_FILL_GAS_BASE, NO_BUNDLER_FILL_GAS_PER_OUTPUT, NO_BUNDLER_PAYMASTER_VERIFICATION_GAS, NO_BUNDLER_PAYMASTER_POST_OP_GAS, BID_PVG_ESTIMATION_ACCOUNT_CODE, SELECTED_BID_SIGNATURE_BYTES, BID_PVG_HEADROOM_PERCENT, ESCROW_MAPPING_SLOT, GasEstimator, OrderStatusChecker, ORDERBOOK_URLS, ORDERBOOK_DECIMALS, ORDERBOOK_SCALE, OrderbookRequestError, RATE_FIELDS, TOP_OF_BOOK_QUERY, ROUTE_LIQUIDITY_QUERY, QUOTE_QUERY, QUOTE_PESSIMISTIC_QUERY, HyperFxOrderbook, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, OrderbookQuoteNotConvergedError, MAX_EXACT_OUTPUT_ROUNDS, OrderbookMarket, ORDER_FEE_GAS_PRICE_BUMP_POLICY, IntentGateway, DECLARATION_V1, DECLARATION_V2, PAYMASTER_ADDRESS_BYTES, PAYMASTER_GAS_LIMIT_BYTES, PAYMASTER_DATA_OFFSET, PERMIT2_DATA_BYTES, PERMIT2_SPONSORSHIP_BYTES, MAX_DECLARED_ENTRIES, MAX_TOKEN_ID_BYTES, TeleportParams, MultiAccount;
235425
+ var import_merge_race, __defProp3, __getOwnPropNames2, __esm2, __export2, node_exports, wasm, full_path, __dirname$1, cachedTextDecoder, cachedUint8ArrayMemory0, WASM_VECTOR_LEN, cachedTextEncoder, encodeString5, cachedDataViewMemory0, KeccakMergeFinalization, KeccakMerge, wasmPath, wasmBytes2, bindings, wasmModule, wasmInstance, __wasm, init_node11, RequestStatus, TimeoutStatus, IntentOrderStatus, ABI, evmHost_default, ABI2, handlerV2_default, polkadotAssetHubPaseo, polkadotHubMainnet, pharosMainnet, pharosAtlantic, tronNile, chainConfigs, configsByStateMachineId, getConfigByStateMachineId, getChainId3, getViemChain, hyperbridgeAddress, ABI3, IntentGatewayV2_default, ChainConfigService, ABI4, uniswapRouterV2_default, chains, DEFAULT_ADDRESS, EvmChain, REQUEST_COMMITMENTS_SLOT, RESPONSE_COMMITMENTS_SLOT, REQUEST_RECEIPTS_SLOT, STATE_COMMITMENTS_SLOT, MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, MissingConsensusUpdateTimeError, TokenBucket, BATCHES_NOT_SUPPORTED_CODE, TOO_BIG_BATCH_REQUEST_CODE, DEFAULT_MAX_BATCH_SIZE, _endpoint2, _headers3, _limiter, _maxBatchSize, _batchingSupported, _pending2, _flushTimer, _flushing, _nextId, __BatchingHttpProvider_instances, scheduleFlush_fn, flushNow_fn, flush_fn, post_fn, settleBatch_fn, settle_fn, handleBatchRefusal_fn, requeue_fn, _a5, BatchingHttpProvider, OFFCHAIN_BID_PREFIX, HYPERBRIDGE_TYPES_BUNDLE, BASE_TIP, HTTP_CONNECT_TIMEOUT_MS, INCLUSION_TIMEOUT_MS, DEFAULT_RPC_MAX_RPS, rpcLimiters, BidCodec, PackedUserOperationCodec, IntentsCoprocessor, ReviveContractInfo, ReviveAccountType, ReviveAccountInfo, H256, EvmStateProof, SubstrateHashing, SubstrateStateMachineProof, BasicProof, LeafIndexAndPos, MmrProof, ConsensusStateId, ConsensusMessage, FraudProofMessage, StateMachine, StateMachineId, StateMachineHeight, Proof, PostRequest, PostResponse, GetRequest, StorageValue, GetResponse, Request3, Response2, RequestMessage, RequestResponse, ResponseMessage, TimeoutMessage, GetRequestsWithProof, Message, DEFAULT_CHILD_STORAGE_PREFIX, PharosProofNodeCodec, SiblingLeftmostLeafProofCodec, NonExistenceProofCodec, AccountProofDataCodec, PharosStateProof, DEFAULT_POLL_INTERVAL, ADDRESS_ZERO2, DEFAULT_GRAFFITI, DEFAULT_LOGGER, REQUEST_STATUS_WEIGHTS, TIMEOUT_STATUS_WEIGHTS, COMBINED_STATUS_WEIGHTS, CHAIN_ID_TO_COINGECKO_NATIVE, TESTNET_CHAINS, ORDER_STATUS, loadDriver, detectEnvironment, STORAGE_KEYS, LEGACY_STORAGE_KEYS, ABI5, uniswapV3Quoter_default, UNISWAP_V4_QUOTER_ABI, ABI6, universalRouter_default, PERMIT2_ABI, UniswapQuoteEngine, COMMON_FEE_TIERS, Swap, ABI7, erc7281_default, DEFAULT_GRAFFITI2, ERC7821_BATCH_MODE, BundlerMethod, SELECT_SOLVER_TYPEHASH, PACKED_USEROP_TYPEHASH, DOMAIN_TYPEHASH, CryptoUtils, FEE_TOKEN_CACHE_TTL_MS, OrderPlacer, TOKEN_KEYED_GATEWAY_ABI, keyingByClient, PARTIAL_FILLS_SLOT, USED_USEROPS_STORAGE_KEY, OrderExecutor, _a6, OrderCanceller, FILL_ORDER_SELECTOR, SUPPORTED_INTENTS_VERSION, CONTRACT_VERSION_ABI, CANONICAL_EVM_TOKEN, ENTRY_POINT_EVENT_ABI, BidImpl, BidManager, RELAYER_MESSAGE_GAS, NO_BUNDLER_FILL_GAS_BASE, NO_BUNDLER_FILL_GAS_PER_OUTPUT, NO_BUNDLER_PAYMASTER_VERIFICATION_GAS, NO_BUNDLER_PAYMASTER_POST_OP_GAS, BID_PVG_ESTIMATION_ACCOUNT_CODE, SELECTED_BID_SIGNATURE_BYTES, BID_PVG_HEADROOM_PERCENT, ESCROW_MAPPING_SLOT, GasEstimator, OrderStatusChecker, ORDERBOOK_URLS, ORDERBOOK_DECIMALS, ORDERBOOK_SCALE, OrderbookRequestError, RATE_FIELDS, TOP_OF_BOOK_QUERY, ROUTE_LIQUIDITY_QUERY, QUOTE_QUERY, QUOTE_PESSIMISTIC_QUERY, HyperFxOrderbook, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, OrderbookQuoteNotConvergedError, MAX_EXACT_OUTPUT_ROUNDS, OrderbookMarket, ETHEREUM_ORDER_FEE_GAS_PRICE_BUMP_PERCENT, DEFAULT_ORDER_FEE_GAS_PRICE_BUMP_PERCENT, IntentGateway, DECLARATION_V1, DECLARATION_V2, PAYMASTER_ADDRESS_BYTES, PAYMASTER_GAS_LIMIT_BYTES, PAYMASTER_DATA_OFFSET, PERMIT2_DATA_BYTES, PERMIT2_SPONSORSHIP_BYTES, MAX_DECLARED_ENTRIES, MAX_TOKEN_ID_BYTES, TeleportParams, MultiAccount;
235425
235426
  var init_node10 = __esm({
235426
235427
  "../sdk/dist/node/index.js"() {
235427
235428
  "use strict";
@@ -248855,6 +248856,8 @@ query OrderStatus($commitment: String!) {
248855
248856
  constructor(ctx, crypto10) {
248856
248857
  __publicField(this, "ctx");
248857
248858
  __publicField(this, "crypto");
248859
+ /** Call dispatchers read from a gateway, for chains whose config carries none. */
248860
+ __publicField(this, "dispatchers", /* @__PURE__ */ new Map());
248858
248861
  this.ctx = ctx;
248859
248862
  this.crypto = crypto10;
248860
248863
  }
@@ -248876,7 +248879,9 @@ query OrderStatus($commitment: String!) {
248876
248879
  * ephemeral keypair, applies state overrides, and calls
248877
248880
  * `eth_estimateUserOperationGas`. Gas limits are bumped by 5-10% for
248878
248881
  * headroom. If the bundler is Pimlico, gas prices are refined with
248879
- * `pimlico_getUserOperationGasPrice`.
248882
+ * `pimlico_getUserOperationGasPrice`. If the bundler rejects the estimate,
248883
+ * fixed gas limits are returned in its place, or the call throws when
248884
+ * `params.requireBundlerEstimate` is set.
248880
248885
  *
248881
248886
  * **Fallback path (no bundler):** uses a fixed budget
248882
248887
  * ({@link NO_BUNDLER_FILL_GAS_BASE} plus {@link NO_BUNDLER_FILL_GAS_PER_OUTPUT}
@@ -248890,6 +248895,8 @@ query OrderStatus($commitment: String!) {
248890
248895
  * @returns A {@link FillOrderEstimate} containing all gas components,
248891
248896
  * EIP-1559 fee values, total cost in wei, and total cost in the source
248892
248897
  * chain's fee token.
248898
+ * @throws If `params.requireBundlerEstimate` is set and the bundler fails to
248899
+ * estimate the fill.
248893
248900
  */
248894
248901
  async estimateFillOrder(params, pricingOptions = {}) {
248895
248902
  const { order } = params;
@@ -249087,6 +249094,11 @@ query OrderStatus($commitment: String!) {
249087
249094
  maxFeePerGas = bufferedBaseFee + maxPriorityFeePerGas;
249088
249095
  }
249089
249096
  } catch (e4) {
249097
+ if (params.requireBundlerEstimate) {
249098
+ throw new Error(`Bundler gas estimation failed: ${e4 instanceof Error ? e4.message : String(e4)}`, {
249099
+ cause: e4
249100
+ });
249101
+ }
249090
249102
  console.warn("Bundler gas estimation failed, using fallback values:", e4);
249091
249103
  }
249092
249104
  } else {
@@ -249121,6 +249133,27 @@ query OrderStatus($commitment: String!) {
249121
249133
  inputs
249122
249134
  };
249123
249135
  }
249136
+ /**
249137
+ * The call dispatcher the gateway on `chain` uses: the configured address, or the one
249138
+ * the gateway reports when the chain's config carries none.
249139
+ *
249140
+ * {@link buildStateOverride} writes it back into the gateway's params slot. Without an
249141
+ * address that value is 12 bytes instead of 32, and the bundler rejects the whole
249142
+ * estimate as `Invalid params`.
249143
+ */
249144
+ async callDispatcher(chain2, gateway) {
249145
+ const configured = this.ctx.dest.configService.getCalldispatcherAddress(chain2);
249146
+ if (isAddress2(configured)) return configured;
249147
+ const known3 = this.dispatchers.get(chain2);
249148
+ if (known3) return known3;
249149
+ const params = await this.ctx.dest.client.readContract({
249150
+ abi: ABI3,
249151
+ address: gateway,
249152
+ functionName: "params"
249153
+ });
249154
+ this.dispatchers.set(chain2, params.dispatcher);
249155
+ return params.dispatcher;
249156
+ }
249124
249157
  /**
249125
249158
  * Asks the bundler for the `preVerificationGas` of the bid UserOperation that will
249126
249159
  * actually be signed, with every funding call, approval and signature byte in it.
@@ -249234,7 +249267,7 @@ query OrderStatus($commitment: String!) {
249234
249267
  const bundlerOverrides = {};
249235
249268
  if (intentGatewayV2Address) {
249236
249269
  const paramsSlot5 = pad4(toHex3(5n), { size: 32 });
249237
- const dispatcherAddress = this.ctx.dest.configService.getCalldispatcherAddress(chain2);
249270
+ const dispatcherAddress = await this.callDispatcher(chain2, intentGatewayV2Address);
249238
249271
  const newSlot5Value = "0x" + "0".repeat(22) + "00" + dispatcherAddress.slice(2).toLowerCase();
249239
249272
  const gatewayDiffs = [{ slot: paramsSlot5, value: newSlot5Value }];
249240
249273
  for (const [leg, input] of (escrow?.inputs ?? []).entries()) {
@@ -249775,15 +249808,8 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
249775
249808
  return withDecimals(resolvedChain, symbol3, this.configService.getAssetMetadataBySymbol(resolvedChain, symbol3));
249776
249809
  }
249777
249810
  };
249778
- ORDER_FEE_GAS_PRICE_BUMP_POLICY = {
249779
- defaultPercent: 10n,
249780
- bySourceStateMachineId: {
249781
- [
249782
- "EVM-1"
249783
- /* MAINNET */
249784
- ]: 50n
249785
- }
249786
- };
249811
+ ETHEREUM_ORDER_FEE_GAS_PRICE_BUMP_PERCENT = 50n;
249812
+ DEFAULT_ORDER_FEE_GAS_PRICE_BUMP_PERCENT = 100n;
249787
249813
  IntentGateway = class _IntentGateway {
249788
249814
  /**
249789
249815
  * Private constructor — use {@link IntentGateway.create} instead.
@@ -249964,9 +249990,9 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
249964
249990
  * 1. If `order.fees` is unset or zero, prices the fee on an internal copy
249965
249991
  * via {@link quoteOrderFees}: same-chain fees are twice the fill-gas
249966
249992
  * estimate without a gas-price bump; cross-chain order fees originating on
249967
- * Ethereum price gas 50% above the live price, while other source chains use
249968
- * 10%, before attaching (fill gas + the settlement relayer fee)
249969
- * with a further 5% buffer over the whole sum — strictly above the solver's
249993
+ * Ethereum mainnet price gas 50% above the live price, while other source
249994
+ * chains use 100%, before attaching (fill gas + the settlement relayer
249995
+ * fee) with a further 5% buffer over the whole sum — strictly above the solver's
249970
249996
  * unpadded requirement. Direct solver estimates remain unbumped. The wei
249971
249997
  * cost used for the `value` field receives a 2% buffer.
249972
249998
  * 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, nativeFee,
@@ -250356,7 +250382,7 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
250356
250382
  *
250357
250383
  * @param order - The order to quote. `order.fees` is ignored and not mutated.
250358
250384
  * Gas prices used to derive cross-chain `fees` receive 50% SDK-only headroom
250359
- * when the source chain is Ethereum mainnet and 10% for other source chains.
250385
+ * when the source chain is Ethereum mainnet and 100% for other source chains.
250360
250386
  * Same-chain quotes and direct calls to {@link estimateFillOrder}, including
250361
250387
  * Simplex solver estimates, remain unbumped.
250362
250388
  *
@@ -250645,7 +250671,7 @@ async function resolveChainConfigs(chains2, options = {}) {
250645
250671
  })
250646
250672
  );
250647
250673
  }
250648
- var DEFAULT_BLOCK_SCAN_INTERVAL_SECONDS, MIN_BLOCK_SCAN_INTERVAL_SECONDS, FillerConfigService;
250674
+ var DEFAULT_BLOCK_SCAN_INTERVAL_SECONDS, MIN_BLOCK_SCAN_INTERVAL_SECONDS, DEFAULT_MIN_ORDER_SIZE_USD, FillerConfigService;
250649
250675
  var init_FillerConfigService = __esm({
250650
250676
  "src/services/FillerConfigService.ts"() {
250651
250677
  "use strict";
@@ -250654,6 +250680,7 @@ var init_FillerConfigService = __esm({
250654
250680
  init_node10();
250655
250681
  DEFAULT_BLOCK_SCAN_INTERVAL_SECONDS = 3;
250656
250682
  MIN_BLOCK_SCAN_INTERVAL_SECONDS = 0.1;
250683
+ DEFAULT_MIN_ORDER_SIZE_USD = 20;
250657
250684
  FillerConfigService = class {
250658
250685
  constructor(chainConfigs2, fillerConfig, loggers) {
250659
250686
  this.rpcOverrides = /* @__PURE__ */ new Map();
@@ -250994,6 +251021,13 @@ var init_FillerConfigService = __esm({
250994
251021
  getBidValiditySeconds() {
250995
251022
  return this.fillerConfig?.bidValiditySeconds ?? 300;
250996
251023
  }
251024
+ /**
251025
+ * The order size, in USD, below which `order.fees` must cover the execution cost.
251026
+ * Defaults to 20. An order at or above it is filled whatever fees it carries.
251027
+ */
251028
+ getMinOrderSizeUsd() {
251029
+ return this.fillerConfig?.minOrderSizeUsd ?? DEFAULT_MIN_ORDER_SIZE_USD;
251030
+ }
250997
251031
  /** Ceiling bps above user-requested output. Default 500 (5%). */
250998
251032
  getMaxOverfillBps() {
250999
251033
  return BigInt(this.fillerConfig?.overfillProtection?.maxOverfillBps ?? 500);
@@ -251106,6 +251140,12 @@ function validateConfig(config5, cliWatchOnly = false) {
251106
251140
  );
251107
251141
  }
251108
251142
  }
251143
+ const minOrderSize = config5.simplex.minOrderSizeUsd;
251144
+ if (minOrderSize !== void 0) {
251145
+ if (!Number.isFinite(minOrderSize) || minOrderSize < 0) {
251146
+ throw new Error(`simplex.minOrderSizeUsd must be a number >= 0 (USD); got ${minOrderSize}`);
251147
+ }
251148
+ }
251109
251149
  if (config5.allowlist) {
251110
251150
  for (const user of config5.allowlist.users ?? []) {
251111
251151
  if (!isAddress(user)) {
@@ -251591,6 +251631,10 @@ function emitFillerToml(config5, options = {}) {
251591
251631
  push("# Gas units to keep deposited at the ERC-4337 EntryPoint on chains without a paymaster.");
251592
251632
  push(kv("targetGasUnits", config5.simplex.targetGasUnits));
251593
251633
  }
251634
+ if (config5.simplex.minOrderSizeUsd !== void 0) {
251635
+ push("# Order size in USD below which an order's fees must cover gas. Default 20.");
251636
+ push(kv("minOrderSizeUsd", config5.simplex.minOrderSizeUsd));
251637
+ }
251594
251638
  push();
251595
251639
  if (config5.simplex.signer) {
251596
251640
  push("# Wallet that signs fills and holds the stablecoin float on every chain.");
@@ -344731,6 +344775,7 @@ var CacheService = class {
344731
344775
  matchedLimitOrders: {},
344732
344776
  bidPlans: {},
344733
344777
  partialFills: {},
344778
+ feeChecksWaived: {},
344734
344779
  feeTokens: {},
344735
344780
  tokenDecimals: {},
344736
344781
  solverSelection: {}
@@ -344770,6 +344815,10 @@ var CacheService = class {
344770
344815
  stalePartialIds.forEach((orderId) => {
344771
344816
  delete this.cacheData.partialFills[orderId];
344772
344817
  });
344818
+ const staleWaivedIds = Object.entries(this.cacheData.feeChecksWaived).filter(([_5, data4]) => !this.isCacheValid(data4.timestamp)).map(([orderId]) => orderId);
344819
+ staleWaivedIds.forEach((orderId) => {
344820
+ delete this.cacheData.feeChecksWaived[orderId];
344821
+ });
344773
344822
  }
344774
344823
  getGasEstimate(orderId) {
344775
344824
  try {
@@ -345082,6 +345131,24 @@ var CacheService = class {
345082
345131
  clearPartialFill(orderId) {
345083
345132
  delete this.cacheData.partialFills[orderId];
345084
345133
  }
345134
+ /**
345135
+ * Whether the strategy's completed evaluation let a bid through without holding
345136
+ * `order.fees` to the execution cost, because the order is at or above the
345137
+ * operator's minimum size.
345138
+ *
345139
+ * Written and cleared the way {@link isPartialFill} is, for the same reason: the
345140
+ * caller exempts such an order from its profit floor.
345141
+ */
345142
+ isFeeCheckWaived(orderId) {
345143
+ const cache = this.cacheData.feeChecksWaived[orderId];
345144
+ return cache !== void 0 && this.isCacheValid(cache.timestamp) && cache.waived;
345145
+ }
345146
+ setFeeCheckWaived(orderId, waived) {
345147
+ this.cacheData.feeChecksWaived[orderId] = { waived, timestamp: Date.now() };
345148
+ }
345149
+ clearFeeCheckWaived(orderId) {
345150
+ delete this.cacheData.feeChecksWaived[orderId];
345151
+ }
345085
345152
  getFeeTokenWithDecimals(chain2) {
345086
345153
  try {
345087
345154
  const cache = this.cacheData.feeTokens[chain2];
@@ -350396,7 +350463,10 @@ var ContractInteractionService = class {
350396
350463
  order,
350397
350464
  prependCalls: void 0,
350398
350465
  maxPriorityFeePerGasBumpPercent: gasFeeBumpConfig?.maxPriorityFeePerGasBumpPercent,
350399
- maxFeePerGasBumpPercent: gasFeeBumpConfig?.maxFeePerGasBumpPercent
350466
+ maxFeePerGasBumpPercent: gasFeeBumpConfig?.maxFeePerGasBumpPercent,
350467
+ // No bid without the bundler's own figures: an op signed with the SDK's
350468
+ // fixed fallback limits does not execute, so a failed estimate skips the order.
350469
+ requireBundlerEstimate: true
350400
350470
  });
350401
350471
  this.logger.info({ orderId: order.id }, "Caching gas estimate");
350402
350472
  this.logger.info({ estimate }, "Estimate");
@@ -350419,7 +350489,7 @@ var ContractInteractionService = class {
350419
350489
  callGasLimit: estimate.callGasLimit
350420
350490
  };
350421
350491
  } catch (error) {
350422
- this.logger.error({ err: error }, "Error estimating gas, using generous fallback values");
350492
+ this.logger.error({ err: error, orderId: order.id }, "Error estimating gas, not bidding on this order");
350423
350493
  throw new Error(`Failed to estimate gas: ${error instanceof Error ? error.message : "Unknown error"}`);
350424
350494
  }
350425
350495
  }
@@ -384242,6 +384312,23 @@ var IntentFiller = class {
384242
384312
  this.monitor.emit("orderSkipped", { orderId: order.id, reason: "watch-only" });
384243
384313
  return null;
384244
384314
  }
384315
+ const destinationHead = await this.destinationHead(order);
384316
+ if (destinationHead !== null && order.deadline <= destinationHead) {
384317
+ this.logger.info(
384318
+ {
384319
+ orderId: order.id,
384320
+ destChain: order.destination,
384321
+ deadline: order.deadline.toString(),
384322
+ head: destinationHead.toString()
384323
+ },
384324
+ "Skipping order: its deadline has passed on the destination chain"
384325
+ );
384326
+ this.monitor.emit("orderSkipped", {
384327
+ orderId: order.id,
384328
+ reason: "Order deadline has passed on the destination chain"
384329
+ });
384330
+ return null;
384331
+ }
384245
384332
  const evalStartMs = Date.now();
384246
384333
  const eligibleStrategies = await Promise.all(
384247
384334
  this.strategies.map(async (strategy) => {
@@ -384251,7 +384338,10 @@ var IntentFiller = class {
384251
384338
  })
384252
384339
  );
384253
384340
  const fillsPartially = this.fillsPartially(order);
384254
- const validStrategies = eligibleStrategies.filter((s4) => s4 !== null && (s4.profitability > 0 || fillsPartially)).sort((a6, b5) => b5.profitability - a6.profitability);
384341
+ const feeCheckWaived = this.feeCheckWaived(order);
384342
+ const validStrategies = eligibleStrategies.filter(
384343
+ (s4) => s4 !== null && (s4.profitability > 0 || fillsPartially || feeCheckWaived)
384344
+ ).sort((a6, b5) => b5.profitability - a6.profitability);
384255
384345
  const evalDurationSec = (Date.now() - evalStartMs) / 1e3;
384256
384346
  this.monitor.emit("orderTiming", { orderId: order.id, phase: "evaluation", durationSec: evalDurationSec });
384257
384347
  if (validStrategies.length === 0) {
@@ -384276,6 +384366,23 @@ var IntentFiller = class {
384276
384366
  );
384277
384367
  return validStrategies[0];
384278
384368
  }
384369
+ /**
384370
+ * The destination chain's latest block number, the clock `order.deadline` is read on.
384371
+ *
384372
+ * Null when the read fails. That is not an answer about the order, so evaluation goes
384373
+ * on: the gas estimate simulates the same deadline check and refuses an expired order.
384374
+ */
384375
+ async destinationHead(order) {
384376
+ try {
384377
+ return await this.chainClientManager.getPublicClient(order.destination).getBlockNumber();
384378
+ } catch (err) {
384379
+ this.logger.warn(
384380
+ { orderId: order.id, destChain: order.destination, err },
384381
+ "Could not read the destination head to check the order's deadline"
384382
+ );
384383
+ return null;
384384
+ }
384385
+ }
384279
384386
  /**
384280
384387
  * Whether the strategy's evaluation concluded in a deliberate partial fill.
384281
384388
  *
@@ -384289,6 +384396,15 @@ var IntentFiller = class {
384289
384396
  if (!order.id) return false;
384290
384397
  return this.contractService.cacheService.isPartialFill(order.id);
384291
384398
  }
384399
+ /**
384400
+ * Whether the strategy's evaluation let a bid through without the fee check, because
384401
+ * the order is at or above the operator's minimum size. Read from a flag the strategy
384402
+ * sets only once it has an answer, as {@link fillsPartially} is.
384403
+ */
384404
+ feeCheckWaived(order) {
384405
+ if (!order.id) return false;
384406
+ return this.contractService.cacheService.isFeeCheckWaived(order.id);
384407
+ }
384292
384408
  executeOrder(order, bestStrategy, solverSelectionActive, inputUsdValue, profitUsd) {
384293
384409
  const chainQueue = this.chainQueues.get(getChainId3(order.destination));
384294
384410
  if (!chainQueue) {
@@ -384865,11 +384981,13 @@ var FXFiller = class _FXFiller {
384865
384981
  * - Treat a payout below the ask as a partial fill, which only orders
384866
384982
  * without output calldata and without an earlier partial fill allow.
384867
384983
  * - Cap each payout by the filler's balance plus funding-venue withdrawals,
384868
- * then gate it on fees (full fills) and the same-asset spread.
384984
+ * then gate it on fees (full fills of orders below `minOrderSizeUsd`) and the
384985
+ * same-asset spread.
384869
384986
  * - Cache the resulting bids for later use in `executeOrder`.
384870
384987
  */
384871
384988
  async calculateProfitability(order) {
384872
384989
  if (order.id) this.contractService.cacheService.clearPartialFill(order.id);
384990
+ if (order.id) this.contractService.cacheService.clearFeeCheckWaived(order.id);
384873
384991
  if (this.halted) {
384874
384992
  this.logger.warn({ orderId: order.id }, "FXFiller halted \u2014 rejecting order");
384875
384993
  return 0;
@@ -384885,6 +385003,15 @@ var FXFiller = class _FXFiller {
384885
385003
  const plans = [];
384886
385004
  const plannedOn = /* @__PURE__ */ new Map();
384887
385005
  const spentOn = /* @__PURE__ */ new Map();
385006
+ let atOrAboveMinSize;
385007
+ const feeCheckWaived = async () => {
385008
+ if (atOrAboveMinSize === void 0) {
385009
+ const usd = await this.getOrderUsdValue(order);
385010
+ atOrAboveMinSize = usd !== null && usd.inputUsd.gte(this.configService.getMinOrderSizeUsd());
385011
+ }
385012
+ return atOrAboveMinSize;
385013
+ };
385014
+ let feesWaived = false;
384888
385015
  for (let leg = 0; leg < order.inputs.length; leg++) {
384889
385016
  const input = order.inputs[leg];
384890
385017
  const output3 = order.output.assets[leg];
@@ -385128,17 +385255,21 @@ var FXFiller = class _FXFiller {
385128
385255
  const executionCost = totalCostInSourceFeeToken + relayerFeeInSourceFeeToken;
385129
385256
  const partialEdgeUsd = sameAssetEdgeUsd.plus(payoutSurplusUsd);
385130
385257
  if (!partialFill && order.fees < executionCost) {
385131
- this.logger.info(
385132
- {
385133
- orderId: order.id,
385134
- orderFees: formatUnits(order.fees, feeTokenDecimals),
385135
- fillGas: formatUnits(totalCostInSourceFeeToken, feeTokenDecimals),
385136
- relayerFee: formatUnits(relayerFeeInSourceFeeToken, feeTokenDecimals),
385137
- executionCost: formatUnits(executionCost, feeTokenDecimals)
385138
- },
385139
- "Skipping order: attached fees do not cover execution cost (fill gas + relayer fee)"
385140
- );
385141
- continue;
385258
+ if (!await feeCheckWaived()) {
385259
+ this.logger.info(
385260
+ {
385261
+ orderId: order.id,
385262
+ orderFees: formatUnits(order.fees, feeTokenDecimals),
385263
+ fillGas: formatUnits(totalCostInSourceFeeToken, feeTokenDecimals),
385264
+ relayerFee: formatUnits(relayerFeeInSourceFeeToken, feeTokenDecimals),
385265
+ executionCost: formatUnits(executionCost, feeTokenDecimals),
385266
+ minOrderSizeUsd: this.configService.getMinOrderSizeUsd()
385267
+ },
385268
+ "Skipping order: attached fees do not cover execution cost (fill gas + relayer fee)"
385269
+ );
385270
+ continue;
385271
+ }
385272
+ feesWaived = true;
385142
385273
  }
385143
385274
  if (sameAsset && !sameAssetProfitable) {
385144
385275
  this.logger.info(
@@ -385172,7 +385303,8 @@ var FXFiller = class _FXFiller {
385172
385303
  realizedSpreadProfit: formatUnits(realizedSpreadProfit, feeTokenDecimals),
385173
385304
  payoutSurplusUsd: payoutSurplusUsd.toString(),
385174
385305
  totalProfit,
385175
- profitable: totalProfit > 0
385306
+ profitable: totalProfit > 0,
385307
+ feeCheckWaived: !partialFill && order.fees < executionCost
385176
385308
  },
385177
385309
  "FX swap profitability evaluation"
385178
385310
  );
@@ -385204,6 +385336,7 @@ var FXFiller = class _FXFiller {
385204
385336
  plans[0].budget
385205
385337
  );
385206
385338
  this.contractService.cacheService.setPartialFill(order.id, plans[0].partialFill);
385339
+ this.contractService.cacheService.setFeeCheckWaived(order.id, feesWaived);
385207
385340
  this.contractService.cacheService.setMatchedLimitOrder(order.id, [
385208
385341
  { limitOrderId: plans[0].limitOrderId, payout: plans[0].payout }
385209
385342
  ]);
@@ -386470,6 +386603,7 @@ async function bootFiller(config5, options) {
386470
386603
  rebalancing: config5.rebalancing,
386471
386604
  targetGasUnits: config5.simplex.targetGasUnits,
386472
386605
  bidValiditySeconds: config5.simplex.bidValiditySeconds,
386606
+ minOrderSizeUsd: config5.simplex.minOrderSizeUsd,
386473
386607
  blockScanIntervalSeconds: config5.simplex.blockScanIntervalSeconds,
386474
386608
  gasFeeBump: config5.simplex.gasFeeBump,
386475
386609
  overfillProtection: config5.simplex.overfillProtection,