@hyperbridge/simplex 0.16.6 → 0.16.8

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.
@@ -70714,13 +70714,13 @@ var require_ponyfill_es2018 = __commonJS({
70714
70714
  }
70715
70715
  function convertReadableWritablePair(pair, context) {
70716
70716
  assertDictionary(pair, context);
70717
- const readable = pair === null || pair === void 0 ? void 0 : pair.readable;
70718
- assertRequiredField(readable, "readable", "ReadableWritablePair");
70719
- assertReadableStream(readable, `${context} has member 'readable' that`);
70717
+ const readable2 = pair === null || pair === void 0 ? void 0 : pair.readable;
70718
+ assertRequiredField(readable2, "readable", "ReadableWritablePair");
70719
+ assertReadableStream(readable2, `${context} has member 'readable' that`);
70720
70720
  const writable = pair === null || pair === void 0 ? void 0 : pair.writable;
70721
70721
  assertRequiredField(writable, "writable", "ReadableWritablePair");
70722
70722
  assertWritableStream(writable, `${context} has member 'writable' that`);
70723
- return { readable, writable };
70723
+ return { readable: readable2, writable };
70724
70724
  }
70725
70725
  class ReadableStream2 {
70726
70726
  constructor(rawUnderlyingSource = {}, rawStrategy = {}) {
@@ -71399,7 +71399,7 @@ var require_ponyfill_es2018 = __commonJS({
71399
71399
  if (controller._finishPromise !== void 0) {
71400
71400
  return controller._finishPromise;
71401
71401
  }
71402
- const readable = stream4._readable;
71402
+ const readable2 = stream4._readable;
71403
71403
  controller._finishPromise = newPromise((resolve11, reject) => {
71404
71404
  controller._finishPromise_resolve = resolve11;
71405
71405
  controller._finishPromise_reject = reject;
@@ -71407,15 +71407,15 @@ var require_ponyfill_es2018 = __commonJS({
71407
71407
  const cancelPromise = controller._cancelAlgorithm(reason);
71408
71408
  TransformStreamDefaultControllerClearAlgorithms(controller);
71409
71409
  uponPromise(cancelPromise, () => {
71410
- if (readable._state === "errored") {
71411
- defaultControllerFinishPromiseReject(controller, readable._storedError);
71410
+ if (readable2._state === "errored") {
71411
+ defaultControllerFinishPromiseReject(controller, readable2._storedError);
71412
71412
  } else {
71413
- ReadableStreamDefaultControllerError(readable._readableStreamController, reason);
71413
+ ReadableStreamDefaultControllerError(readable2._readableStreamController, reason);
71414
71414
  defaultControllerFinishPromiseResolve(controller);
71415
71415
  }
71416
71416
  return null;
71417
71417
  }, (r7) => {
71418
- ReadableStreamDefaultControllerError(readable._readableStreamController, r7);
71418
+ ReadableStreamDefaultControllerError(readable2._readableStreamController, r7);
71419
71419
  defaultControllerFinishPromiseReject(controller, r7);
71420
71420
  return null;
71421
71421
  });
@@ -71426,7 +71426,7 @@ var require_ponyfill_es2018 = __commonJS({
71426
71426
  if (controller._finishPromise !== void 0) {
71427
71427
  return controller._finishPromise;
71428
71428
  }
71429
- const readable = stream4._readable;
71429
+ const readable2 = stream4._readable;
71430
71430
  controller._finishPromise = newPromise((resolve11, reject) => {
71431
71431
  controller._finishPromise_resolve = resolve11;
71432
71432
  controller._finishPromise_reject = reject;
@@ -71434,15 +71434,15 @@ var require_ponyfill_es2018 = __commonJS({
71434
71434
  const flushPromise = controller._flushAlgorithm();
71435
71435
  TransformStreamDefaultControllerClearAlgorithms(controller);
71436
71436
  uponPromise(flushPromise, () => {
71437
- if (readable._state === "errored") {
71438
- defaultControllerFinishPromiseReject(controller, readable._storedError);
71437
+ if (readable2._state === "errored") {
71438
+ defaultControllerFinishPromiseReject(controller, readable2._storedError);
71439
71439
  } else {
71440
- ReadableStreamDefaultControllerClose(readable._readableStreamController);
71440
+ ReadableStreamDefaultControllerClose(readable2._readableStreamController);
71441
71441
  defaultControllerFinishPromiseResolve(controller);
71442
71442
  }
71443
71443
  return null;
71444
71444
  }, (r7) => {
71445
- ReadableStreamDefaultControllerError(readable._readableStreamController, r7);
71445
+ ReadableStreamDefaultControllerError(readable2._readableStreamController, r7);
71446
71446
  defaultControllerFinishPromiseReject(controller, r7);
71447
71447
  return null;
71448
71448
  });
@@ -214220,6 +214220,12 @@ function userOperationLogs(logs, entryPoint, userOpHash) {
214220
214220
  while (start >= 0 && !isBoundary(logs[start])) start--;
214221
214221
  return logs.slice(start + 1, end);
214222
214222
  }
214223
+ function escrowSlot(commitment, leg) {
214224
+ const byCommitment = keccak2563(
214225
+ encodeAbiParameters2([{ type: "bytes32" }, { type: "uint256" }], [commitment, ESCROW_MAPPING_SLOT])
214226
+ );
214227
+ return keccak2563(encodeAbiParameters2([{ type: "uint256" }, { type: "bytes32" }], [BigInt(leg), byCommitment]));
214228
+ }
214223
214229
  function destinationStateMachine(order) {
214224
214230
  return isHex3(order.destination) ? hexToString2(order.destination) : order.destination;
214225
214231
  }
@@ -214231,13 +214237,19 @@ function describeError(error) {
214231
214237
  if (graphqlErrors?.length) return graphqlErrors.map((e4) => e4.message).join("; ");
214232
214238
  return error instanceof Error ? error.message : String(error);
214233
214239
  }
214240
+ function sameSymbol(a6, b5) {
214241
+ return a6.toLowerCase() === b5.toLowerCase();
214242
+ }
214234
214243
  function findBook(books, tokenA, tokenB) {
214235
214244
  const book = books.find(
214236
- (b5) => b5.base === tokenA && b5.quote === tokenB || b5.base === tokenB && b5.quote === tokenA
214245
+ (b5) => sameSymbol(b5.base, tokenA) && sameSymbol(b5.quote, tokenB) || sameSymbol(b5.base, tokenB) && sameSymbol(b5.quote, tokenA)
214237
214246
  );
214238
214247
  if (!book) throw new OrderbookRequestError(`no book trades ${tokenA} for ${tokenB}`);
214239
214248
  return book;
214240
214249
  }
214250
+ function optionalBigInt(value) {
214251
+ return value === null ? null : BigInt(value);
214252
+ }
214241
214253
  function parseRate(raw) {
214242
214254
  return {
214243
214255
  side: raw.side,
@@ -214282,24 +214294,32 @@ function toRoute(tokenIn, tokenOut) {
214282
214294
  function requiredInput(side, amountOut, rate) {
214283
214295
  return side === "BID" ? divCeil(amountOut * ORDERBOOK_SCALE, rate) : divCeil(amountOut * rate, ORDERBOOK_SCALE);
214284
214296
  }
214285
- function buildQuote(tradeType, amountIn, amountOut, quote, tokenIn, tokenOut) {
214286
- if (quote.rate === null) throw new OrderbookRequestError("the orderbook served a fillable quote with no rate");
214287
- const [base32, quoteToken] = quote.side === "BID" ? [tokenIn, tokenOut] : [tokenOut, tokenIn];
214297
+ function swapQuoteTotals(quote) {
214298
+ const worstFill = quote.fills.at(-1);
214299
+ if (quote.fillable && !worstFill)
214300
+ throw new OrderbookRequestError("the orderbook served a fillable quote with no fills");
214288
214301
  return {
214289
- tradeType,
214290
- amountIn,
214291
- amountOut,
214292
- quoteMetadata: {
214293
- sourceChain: tokenIn.chain,
214294
- destinationChain: tokenOut.chain,
214295
- route: quote.route,
214296
- side: quote.side,
214297
- baseTokenSymbol: base32.symbol,
214298
- quoteTokenSymbol: quoteToken.symbol,
214299
- rate: format(quote.rate),
214300
- maxFillableIn: fromOrderbookAmount(quote.maxFillableIn, tokenIn.decimals),
214301
- orderCount: quote.fills.length
214302
- }
214302
+ side: quote.side,
214303
+ amountIn: quote.amountIn,
214304
+ amountOut: quote.fills.reduce((total, fill) => total + fill.amountOut, 0n),
214305
+ fillable: quote.fillable,
214306
+ maxFillableIn: quote.maxFillableIn,
214307
+ worstIn: worstFill?.amountIn ?? 0n,
214308
+ worstOut: worstFill?.amountOut ?? 0n
214309
+ };
214310
+ }
214311
+ function levelQuoteTotals(quote) {
214312
+ if (quote.fillable && quote.rate === null) {
214313
+ throw new OrderbookRequestError("the orderbook served a fillable quote with no rate");
214314
+ }
214315
+ return {
214316
+ side: quote.side,
214317
+ amountIn: quote.amountIn,
214318
+ amountOut: quote.amountOut,
214319
+ fillable: quote.fillable,
214320
+ maxFillableIn: quote.maxFillableIn,
214321
+ worstIn: quote.amountIn,
214322
+ worstOut: quote.amountOut
214303
214323
  };
214304
214324
  }
214305
214325
  function toOrderbookAmount(amount, decimals) {
@@ -214385,7 +214405,7 @@ function encodePhantomBidDeclaration(declaration) {
214385
214405
  }
214386
214406
  return u8aToHex(new Uint8Array(bytes4));
214387
214407
  }
214388
- 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, GasEstimator, OrderStatusChecker, ORDERBOOK_URLS, ORDERBOOK_DECIMALS, ORDERBOOK_SCALE, OrderbookRequestError, RATE_FIELDS, TOP_OF_BOOK_QUERY, ROUTE_LIQUIDITY_QUERY, QUOTE_QUERY, HyperFxOrderbook, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, OrderbookQuoteNotConvergedError, InsufficientOrderbookLiquidityError, 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;
214408
+ 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;
214389
214409
  var init_node10 = __esm({
214390
214410
  "../sdk/dist/node/index.js"() {
214391
214411
  "use strict";
@@ -227805,6 +227825,10 @@ query OrderStatus($commitment: String!) {
227805
227825
  NO_BUNDLER_FILL_GAS_PER_OUTPUT = 150000n;
227806
227826
  NO_BUNDLER_PAYMASTER_VERIFICATION_GAS = 250000n;
227807
227827
  NO_BUNDLER_PAYMASTER_POST_OP_GAS = 100000n;
227828
+ BID_PVG_ESTIMATION_ACCOUNT_CODE = "0x60003560e01c631626ba7e1460145760206000f35b631626ba7e60e01b60005260206000f3";
227829
+ SELECTED_BID_SIGNATURE_BYTES = 32 + 65 + 65;
227830
+ BID_PVG_HEADROOM_PERCENT = 10n;
227831
+ ESCROW_MAPPING_SLOT = 9n;
227808
227832
  GasEstimator = class {
227809
227833
  /**
227810
227834
  * @param ctx - Shared IntentsV2 context providing the source and destination
@@ -227883,6 +227907,7 @@ query OrderStatus($commitment: String!) {
227883
227907
  assetsForOverrides.push({ token: feeTokenAsBytes32, amount: 0n });
227884
227908
  }
227885
227909
  const isSameChain = souceStateMachineId === destStateMachineId;
227910
+ const commitment = orderCommitment(order);
227886
227911
  const [stateOverridesResult, crossChainFees] = await Promise.all([
227887
227912
  this.ctx.bundlerUrl ? this.buildStateOverride({
227888
227913
  accountAddress: solverAccountAddress,
@@ -227890,7 +227915,16 @@ query OrderStatus($commitment: String!) {
227890
227915
  outputAssets: assetsForOverrides,
227891
227916
  spenderAddress: intentGatewayV2Address,
227892
227917
  intentGatewayV2Address,
227893
- entryPointAddress
227918
+ entryPointAddress,
227919
+ // A fee quote runs before the order is placed, so nothing is escrowed under
227920
+ // its commitment yet. Cross-chain fills release escrow on the source chain.
227921
+ escrow: isSameChain ? {
227922
+ commitment,
227923
+ inputs: order.inputs.map((input) => ({
227924
+ token: normalizeAddressForEvmBytes32(input.token),
227925
+ amount: input.amount
227926
+ }))
227927
+ } : void 0
227894
227928
  }) : Promise.resolve({ viem: [], bundler: {} }),
227895
227929
  isSameChain ? Promise.resolve({ postRequestFee: 0n, relayerFeeInSourceFeeToken: 0n }) : this.estimateCrossChainFees(
227896
227930
  sourceFeeToken,
@@ -227919,10 +227953,8 @@ query OrderStatus($commitment: String!) {
227919
227953
  const maxFeeBumpPercent = params.maxFeePerGasBumpPercent ?? 10;
227920
227954
  let maxPriorityFeePerGas = gasPrice + gasPrice * BigInt(priorityFeeBumpPercent) / 100n;
227921
227955
  let maxFeePerGas = gasPrice + gasPrice * BigInt(maxFeeBumpPercent) / 100n;
227922
- const orderForEstimation = { ...order, session: solverAccountAddress };
227923
- const commitment = orderCommitment(orderForEstimation);
227924
227956
  await assertGatewayRelease(this.ctx.dest.client, intentGatewayV2Address);
227925
- const fillOrderCalldata = encodeFillOrder(transformOrderForContract(orderForEstimation), fillOptions);
227957
+ const fillOrderCalldata = encodeFillOrder(transformOrderForContract(order), fillOptions);
227926
227958
  let callGasLimit = 500000n;
227927
227959
  let verificationGasLimit = 100000n;
227928
227960
  let preVerificationGas = 100000n;
@@ -227934,7 +227966,7 @@ query OrderStatus($commitment: String!) {
227934
227966
  ...params.prependCalls ?? [],
227935
227967
  { target: intentGatewayV2Address, value: totalNativeValue, data: fillOrderCalldata }
227936
227968
  ]);
227937
- const accountGasLimits = CryptoUtils.packGasLimits(100000n, callGasLimit);
227969
+ const accountGasLimits = CryptoUtils.packGasLimits(0n, 0n);
227938
227970
  const gasFees = CryptoUtils.packGasFees(maxPriorityFeePerGas, maxFeePerGas);
227939
227971
  const nonce = 0n;
227940
227972
  const preliminaryUserOp = {
@@ -227943,7 +227975,7 @@ query OrderStatus($commitment: String!) {
227943
227975
  initCode: "0x",
227944
227976
  callData,
227945
227977
  accountGasLimits,
227946
- preVerificationGas: 100000n,
227978
+ preVerificationGas: 0n,
227947
227979
  gasFees,
227948
227980
  paymasterAndData: "0x",
227949
227981
  signature: "0x"
@@ -228073,6 +228105,53 @@ query OrderStatus($commitment: String!) {
228073
228105
  inputs
228074
228106
  };
228075
228107
  }
228108
+ /**
228109
+ * Asks the bundler for the `preVerificationGas` of the bid UserOperation that will
228110
+ * actually be signed, with every funding call, approval and signature byte in it.
228111
+ *
228112
+ * On an L2, `preVerificationGas` also pays the L1 data fee for the op's bytes, a fixed
228113
+ * amount of wei that the bundler converts to gas at the price the op pays. A bundler
228114
+ * admits an op priced at its `maxFeePerGas`, but bundles it at the base fee plus its
228115
+ * priority fee, where the same wei costs more gas. An op sized only for admission is
228116
+ * accepted and then skipped in every bundle until it expires. The estimate is taken
228117
+ * at the bundle's price: the latest base fee plus the op's priority fee, capped at its
228118
+ * `maxFeePerGas`.
228119
+ *
228120
+ * The solver account's code is overridden with {@link BID_PVG_ESTIMATION_ACCOUNT_CODE}
228121
+ * for the call, so only the bytes are priced and the fill itself is not simulated.
228122
+ *
228123
+ * @param params - The bid's fields as they will be signed.
228124
+ * @returns The `preVerificationGas` to sign, with {@link BID_PVG_HEADROOM_PERCENT} added.
228125
+ * @throws If no bundler is configured or the bundler rejects the estimate.
228126
+ */
228127
+ async estimateBidPreVerificationGas(params) {
228128
+ const { solverAccount, maxFeePerGas, maxPriorityFeePerGas } = params;
228129
+ const latestBlock = await this.ctx.dest.client.getBlock({ blockTag: "latest" });
228130
+ const baseFeePerGas = latestBlock.baseFeePerGas;
228131
+ const bundlePrice = baseFeePerGas == null || baseFeePerGas + maxPriorityFeePerGas > maxFeePerGas ? maxFeePerGas : baseFeePerGas + maxPriorityFeePerGas;
228132
+ const priorityFee = maxPriorityFeePerGas < bundlePrice ? maxPriorityFeePerGas : bundlePrice;
228133
+ const userOp = {
228134
+ sender: solverAccount,
228135
+ nonce: params.nonce,
228136
+ initCode: "0x",
228137
+ callData: params.callData,
228138
+ accountGasLimits: CryptoUtils.packGasLimits(params.verificationGasLimit, params.callGasLimit),
228139
+ // Bundlers return a non-zero preVerificationGas as given rather than estimating it.
228140
+ preVerificationGas: 0n,
228141
+ gasFees: CryptoUtils.packGasFees(priorityFee, bundlePrice),
228142
+ paymasterAndData: params.paymasterAndData ?? "0x",
228143
+ signature: `0x${"ff".repeat(SELECTED_BID_SIGNATURE_BYTES)}`
228144
+ };
228145
+ const estimate = await this.crypto.sendBundler(
228146
+ BundlerMethod.ETH_ESTIMATE_USER_OPERATION_GAS,
228147
+ [
228148
+ CryptoUtils.prepareBundlerCall(userOp),
228149
+ params.entryPointAddress,
228150
+ { [solverAccount]: { code: BID_PVG_ESTIMATION_ACCOUNT_CODE } }
228151
+ ]
228152
+ );
228153
+ return BigInt(estimate.preVerificationGas) * (100n + BID_PVG_HEADROOM_PERCENT) / 100n;
228154
+ }
228076
228155
  /**
228077
228156
  * Estimates the cross-chain ISMP POST request fee (the relayer fee for the
228078
228157
  * RedeemEscrow message), in both the source and destination fee tokens.
@@ -228125,20 +228204,38 @@ query OrderStatus($commitment: String!) {
228125
228204
  * @returns An object with `viem` and `bundler` state-override collections.
228126
228205
  */
228127
228206
  async buildStateOverride(params) {
228128
- const { accountAddress, chain: chain2, outputAssets, spenderAddress, intentGatewayV2Address, entryPointAddress } = params;
228129
- const testValue = toHex3(maxUint2563 / 2n, { size: 32 });
228207
+ const {
228208
+ accountAddress,
228209
+ chain: chain2,
228210
+ outputAssets,
228211
+ spenderAddress,
228212
+ intentGatewayV2Address,
228213
+ entryPointAddress,
228214
+ escrow
228215
+ } = params;
228216
+ const testValue = toHex3(2n ** 128n, { size: 32 });
228130
228217
  const viemOverrides = [];
228131
228218
  const bundlerOverrides = {};
228132
228219
  if (intentGatewayV2Address) {
228133
228220
  const paramsSlot5 = pad4(toHex3(5n), { size: 32 });
228134
228221
  const dispatcherAddress = this.ctx.dest.configService.getCalldispatcherAddress(chain2);
228135
228222
  const newSlot5Value = "0x" + "0".repeat(22) + "00" + dispatcherAddress.slice(2).toLowerCase();
228223
+ const gatewayDiffs = [{ slot: paramsSlot5, value: newSlot5Value }];
228224
+ for (const [leg, input] of (escrow?.inputs ?? []).entries()) {
228225
+ gatewayDiffs.push({
228226
+ slot: escrowSlot(escrow.commitment, leg),
228227
+ value: toHex3(input.amount, { size: 32 })
228228
+ });
228229
+ }
228230
+ const releasesNative = escrow?.inputs.some((input) => bytes32ToBytes20(input.token) === ADDRESS_ZERO2);
228136
228231
  viemOverrides.push({
228137
228232
  address: intentGatewayV2Address,
228138
- stateDiff: [{ slot: paramsSlot5, value: newSlot5Value }]
228233
+ ...releasesNative ? { balance: 2n ** 128n } : {},
228234
+ stateDiff: gatewayDiffs
228139
228235
  });
228140
228236
  bundlerOverrides[intentGatewayV2Address] = {
228141
- stateDiff: { [paramsSlot5]: newSlot5Value }
228237
+ ...releasesNative ? { balance: testValue } : {},
228238
+ stateDiff: Object.fromEntries(gatewayDiffs.map(({ slot, value }) => [slot, value]))
228142
228239
  };
228143
228240
  }
228144
228241
  if (entryPointAddress) {
@@ -228174,6 +228271,25 @@ query OrderStatus($commitment: String!) {
228174
228271
  bundlerOverrides[result.address] = { stateDiff: result.bundlerStateDiffs };
228175
228272
  }
228176
228273
  }
228274
+ if (intentGatewayV2Address && escrow) {
228275
+ const gatewayBalances = await Promise.all(
228276
+ escrow.inputs.map((input) => this.tokenBalanceSlot(input.token, intentGatewayV2Address, chain2))
228277
+ );
228278
+ for (const override of gatewayBalances) {
228279
+ if (!override) continue;
228280
+ const existing = viemOverrides.find((entry) => entry.address === override.address && entry.stateDiff);
228281
+ if (existing) existing.stateDiff.push({ slot: override.slot, value: testValue });
228282
+ else
228283
+ viemOverrides.push({
228284
+ address: override.address,
228285
+ stateDiff: [{ slot: override.slot, value: testValue }]
228286
+ });
228287
+ bundlerOverrides[override.address] = {
228288
+ ...bundlerOverrides[override.address],
228289
+ stateDiff: { ...bundlerOverrides[override.address]?.stateDiff, [override.slot]: testValue }
228290
+ };
228291
+ }
228292
+ }
228177
228293
  const solverAccountContract = this.ctx.dest.configService.getSolverAccountAddress(chain2);
228178
228294
  if (solverAccountContract) {
228179
228295
  try {
@@ -228196,6 +228312,21 @@ query OrderStatus($commitment: String!) {
228196
228312
  }
228197
228313
  return { viem: viemOverrides, bundler: bundlerOverrides };
228198
228314
  }
228315
+ /**
228316
+ * The balance slot of `holder` in an ERC-20 token, for overriding. `null` for the native
228317
+ * token or when the slot cannot be discovered.
228318
+ */
228319
+ async tokenBalanceSlot(tokenHex, holder, chain2) {
228320
+ const tokenAddress = bytes32ToBytes20(tokenHex);
228321
+ if (tokenAddress === ADDRESS_ZERO2) return null;
228322
+ try {
228323
+ const balanceData = "0x70a08231" + bytes20ToBytes32(holder).slice(2);
228324
+ const slot = await getOrFetchStorageSlot(this.ctx.dest.client, chain2, tokenAddress, balanceData);
228325
+ return slot ? { address: tokenAddress, slot } : null;
228326
+ } catch {
228327
+ return null;
228328
+ }
228329
+ }
228199
228330
  /**
228200
228331
  * Resolves the balance and allowance storage slot overrides for a single
228201
228332
  * ERC-20 token in parallel. Returns `null` for native-ETH tokens or when
@@ -228328,14 +228459,20 @@ query TopOfBook($tokenA: String!, $tokenB: String!, $fillChain: String!, $source
228328
228459
  query RouteLiquidity($route: RouteInput!) {
228329
228460
  books { id base quote }
228330
228461
  routeLiquidity(route: $route) {
228331
- route bestRate depthIn depthOut availableLiquidity maxFillableIn orderCount solverCount
228462
+ route bestRate slippageBps depthIn depthOut availableLiquidity maxFillableIn orderCount solverCount
228332
228463
  }
228333
228464
  }`;
228334
228465
  QUOTE_QUERY = `
228335
228466
  query Quote($route: RouteInput!, $amountIn: BigInt!) {
228336
228467
  quote(route: $route, amountIn: $amountIn) {
228337
- route side amountIn amountOut rate fillable depth maxFillableIn
228338
- fills { orderRate amountOut advertisedSize }
228468
+ route side amountIn slippageBps fillable maxFillableIn
228469
+ fills { advertisedSize orderRate amountIn amountOut }
228470
+ }
228471
+ }`;
228472
+ QUOTE_PESSIMISTIC_QUERY = `
228473
+ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
228474
+ quotePessimistic(route: $route, amountIn: $amountIn) {
228475
+ route side amountIn amountOut rate priceBucket slippageBps fillable maxFillableIn
228339
228476
  }
228340
228477
  }`;
228341
228478
  HyperFxOrderbook = class {
@@ -228365,8 +228502,9 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228365
228502
  return {
228366
228503
  route: raw.route,
228367
228504
  book,
228368
- side: book.base === route.tokenIn ? "BID" : "ASK",
228369
- bestRate: raw.bestRate === null ? null : BigInt(raw.bestRate),
228505
+ side: sameSymbol(book.base, route.tokenIn) ? "BID" : "ASK",
228506
+ bestRate: optionalBigInt(raw.bestRate),
228507
+ slippageBps: raw.slippageBps,
228370
228508
  depthIn: BigInt(raw.depthIn),
228371
228509
  depthOut: BigInt(raw.depthOut),
228372
228510
  availableLiquidity: BigInt(raw.availableLiquidity),
@@ -228375,7 +228513,10 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228375
228513
  solverCount: raw.solverCount
228376
228514
  };
228377
228515
  }
228378
- /** The clearing price for `amountIn` (1e18 tokenIn) on a route, which may combine several solvers' orders. */
228516
+ /**
228517
+ * The optimistic quote for `amountIn` (1e18 tokenIn) on a route: the route's orders, best price
228518
+ * first, each taking what it can at its own price. The fills' `amountOut`s sum to the output.
228519
+ */
228379
228520
  async quote(route, amountIn) {
228380
228521
  const { quote: raw } = await this.request(QUOTE_QUERY, {
228381
228522
  route,
@@ -228385,18 +228526,39 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228385
228526
  route: raw.route,
228386
228527
  side: raw.side,
228387
228528
  amountIn: BigInt(raw.amountIn),
228388
- amountOut: BigInt(raw.amountOut),
228389
- rate: raw.rate === null ? null : BigInt(raw.rate),
228529
+ slippageBps: raw.slippageBps,
228390
228530
  fillable: raw.fillable,
228391
- depth: BigInt(raw.depth),
228392
228531
  maxFillableIn: BigInt(raw.maxFillableIn),
228393
228532
  fills: raw.fills.map((fill) => ({
228533
+ advertisedSize: BigInt(fill.advertisedSize),
228394
228534
  orderRate: BigInt(fill.orderRate),
228395
- amountOut: BigInt(fill.amountOut),
228396
- advertisedSize: BigInt(fill.advertisedSize)
228535
+ amountIn: BigInt(fill.amountIn),
228536
+ amountOut: BigInt(fill.amountOut)
228397
228537
  }))
228398
228538
  };
228399
228539
  }
228540
+ /**
228541
+ * The pessimistic quote for `amountIn` (1e18 tokenIn) on a route: one price, from the first level
228542
+ * deep enough to fill the whole trade by itself, at which every order in it fills; the route's
228543
+ * worst price when no one level can but its levels together can.
228544
+ */
228545
+ async quotePessimistic(route, amountIn) {
228546
+ const { quotePessimistic: raw } = await this.request(
228547
+ QUOTE_PESSIMISTIC_QUERY,
228548
+ { route, amountIn: amountIn.toString() }
228549
+ );
228550
+ return {
228551
+ route: raw.route,
228552
+ side: raw.side,
228553
+ amountIn: BigInt(raw.amountIn),
228554
+ amountOut: BigInt(raw.amountOut),
228555
+ rate: optionalBigInt(raw.rate),
228556
+ priceBucket: optionalBigInt(raw.priceBucket),
228557
+ slippageBps: raw.slippageBps,
228558
+ fillable: raw.fillable,
228559
+ maxFillableIn: BigInt(raw.maxFillableIn)
228560
+ };
228561
+ }
228400
228562
  async request(query3, variables) {
228401
228563
  try {
228402
228564
  return await this.client.request(query3, variables);
@@ -228431,18 +228593,6 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228431
228593
  this.name = "OrderbookQuoteNotConvergedError";
228432
228594
  }
228433
228595
  };
228434
- InsufficientOrderbookLiquidityError = class extends Error {
228435
- constructor(route, maxFillableIn) {
228436
- super(
228437
- `The HyperFX orderbook cannot fill ${route.tokenIn} -> ${route.tokenOut} on ${route.sourceChain} -> ${route.destinationChain}; max fillable input is ${maxFillableIn} raw units`
228438
- );
228439
- __publicField(this, "route");
228440
- __publicField(this, "maxFillableIn");
228441
- this.route = route;
228442
- this.maxFillableIn = maxFillableIn;
228443
- this.name = "InsufficientOrderbookLiquidityError";
228444
- }
228445
- };
228446
228596
  MAX_EXACT_OUTPUT_ROUNDS = 16;
228447
228597
  OrderbookMarket = class {
228448
228598
  constructor(configService, orderbook) {
@@ -228452,26 +228602,67 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228452
228602
  this.orderbook = orderbook;
228453
228603
  }
228454
228604
  async quoteIntent(params, sourceChain, destinationChain) {
228605
+ const orderbook = this.orderbook();
228606
+ if (!params.optimistic) {
228607
+ const { quote: quote2, tokenIn: tokenIn2, tokenOut: tokenOut2 } = await this.priceIntent(
228608
+ params,
228609
+ sourceChain,
228610
+ destinationChain,
228611
+ (route, amountIn) => orderbook.quotePessimistic(route, amountIn),
228612
+ levelQuoteTotals
228613
+ );
228614
+ return {
228615
+ route: quote2.route,
228616
+ side: quote2.side,
228617
+ amountIn: fromOrderbookAmount(quote2.amountIn, tokenIn2.decimals),
228618
+ amountOut: fromOrderbookAmount(quote2.amountOut, tokenOut2.decimals),
228619
+ rate: quote2.rate,
228620
+ priceBucket: quote2.priceBucket,
228621
+ slippageBps: quote2.slippageBps,
228622
+ fillable: quote2.fillable,
228623
+ maxFillableIn: fromOrderbookAmount(quote2.maxFillableIn, tokenIn2.decimals)
228624
+ };
228625
+ }
228626
+ const { quote, tokenIn, tokenOut } = await this.priceIntent(
228627
+ params,
228628
+ sourceChain,
228629
+ destinationChain,
228630
+ (route, amountIn) => orderbook.quote(route, amountIn),
228631
+ swapQuoteTotals
228632
+ );
228633
+ return {
228634
+ route: quote.route,
228635
+ side: quote.side,
228636
+ amountIn: fromOrderbookAmount(quote.amountIn, tokenIn.decimals),
228637
+ slippageBps: quote.slippageBps,
228638
+ fillable: quote.fillable,
228639
+ maxFillableIn: fromOrderbookAmount(quote.maxFillableIn, tokenIn.decimals),
228640
+ legs: quote.fills.map((fill) => ({
228641
+ advertisedSize: fromOrderbookAmount(fill.advertisedSize, tokenOut.decimals),
228642
+ orderRate: fill.orderRate,
228643
+ amountIn: fromOrderbookAmount(fill.amountIn, tokenIn.decimals),
228644
+ amountOut: fromOrderbookAmount(fill.amountOut, tokenOut.decimals)
228645
+ }))
228646
+ };
228647
+ }
228648
+ /**
228649
+ * The orderbook's quote for an exact input, or for the input found to deliver
228650
+ * an exact output, with the assets it trades. A route that cannot fill the
228651
+ * trade answers with an unfillable quote rather than an error.
228652
+ */
228653
+ async priceIntent(params, sourceChain, destinationChain, price, totals) {
228455
228654
  validateQuoteParams(params);
228456
228655
  const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
228457
228656
  const tokenOut = this.assetByAddress(destinationChain, params.tokenOut);
228458
228657
  const route = toRoute(tokenIn, tokenOut);
228459
228658
  if (params.amountIn !== void 0) {
228460
- const quote2 = await this.orderbook().quote(route, toOrderbookAmount(params.amountIn, tokenIn.decimals));
228461
- const amountOut2 = quote2.fillable ? fromOrderbookAmount(quote2.amountOut, tokenOut.decimals) : 0n;
228462
- if (amountOut2 === 0n) throw this.insufficient(route, quote2.maxFillableIn, tokenIn);
228463
- return buildQuote("EXACT_INPUT", params.amountIn, amountOut2, quote2, tokenIn, tokenOut);
228659
+ const quote2 = await price(route, toOrderbookAmount(params.amountIn, tokenIn.decimals));
228660
+ totals(quote2);
228661
+ return { quote: quote2, tokenIn, tokenOut };
228464
228662
  }
228465
- const amountOut = params.amountOut;
228466
- const quote = await this.quoteExactOutput(route, toOrderbookAmount(amountOut, tokenOut.decimals), tokenIn);
228467
- return buildQuote(
228468
- "EXACT_OUTPUT",
228469
- fromOrderbookAmount(quote.amountIn, tokenIn.decimals),
228470
- amountOut,
228471
- quote,
228472
- tokenIn,
228473
- tokenOut
228474
- );
228663
+ const targetOut = toOrderbookAmount(params.amountOut, tokenOut.decimals);
228664
+ const quote = await this.quoteExactOutput(route, targetOut, tokenIn, price, totals);
228665
+ return { quote, tokenIn, tokenOut };
228475
228666
  }
228476
228667
  async availableLiquidity(params, sourceChain, destinationChain) {
228477
228668
  const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
@@ -228524,22 +228715,29 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228524
228715
  }
228525
228716
  /**
228526
228717
  * The orderbook only quotes an input amount, so an output is priced by
228527
- * guessing the input from the best rate and raising it at each clearing
228528
- * price until the quote delivers `targetOut`.
228718
+ * guessing the input from the best rate, then keeping the part of each quote
228719
+ * filled better than its worst rate and re-pricing the rest of `targetOut` at
228720
+ * the input-to-output ratio that part filled at, until a quote delivers it.
228721
+ * That ratio is the orderbook's own, with the protocol fee already taken off.
228722
+ *
228723
+ * An output the route cannot deliver is answered by quoting an input past its
228724
+ * `maxFillableIn`, which the orderbook serves as unfillable: the smallest such
228725
+ * input guessed from the best rate, or one raw unit when no order serves it.
228529
228726
  */
228530
- async quoteExactOutput(route, targetOut, tokenIn) {
228531
- const orderbook = this.orderbook();
228532
- const liquidity = await orderbook.routeLiquidity(route);
228533
- if (liquidity.bestRate === null) throw this.insufficient(route, 0n, tokenIn);
228727
+ async quoteExactOutput(route, targetOut, tokenIn, price, totals) {
228728
+ const liquidity = await this.orderbook().routeLiquidity(route);
228534
228729
  const inputUnit = toOrderbookAmount(1n, tokenIn.decimals);
228535
- let amountIn = roundUpTo(requiredInput(liquidity.side, targetOut, liquidity.bestRate), inputUnit);
228730
+ let amountIn = liquidity.bestRate === null ? inputUnit : roundUpTo(requiredInput(liquidity.side, targetOut, liquidity.bestRate), inputUnit);
228536
228731
  let lastAmountIn = amountIn;
228537
228732
  for (let round2 = 0; round2 < MAX_EXACT_OUTPUT_ROUNDS; round2++) {
228538
- if (amountIn > liquidity.maxFillableIn) throw this.insufficient(route, liquidity.maxFillableIn, tokenIn);
228539
- const quote = await orderbook.quote(route, amountIn);
228540
- if (quote.fillable && quote.amountOut >= targetOut) return quote;
228733
+ const served = await price(route, amountIn);
228734
+ const quote = totals(served);
228735
+ if (quote.fillable && quote.amountOut >= targetOut) return served;
228736
+ if (!quote.fillable && amountIn > liquidity.maxFillableIn) return served;
228541
228737
  lastAmountIn = amountIn;
228542
- const next = quote.rate === null ? amountIn : roundUpTo(requiredInput(quote.side, targetOut, quote.rate), inputUnit);
228738
+ const betterIn = quote.amountIn - quote.worstIn;
228739
+ const betterOut = quote.amountOut - quote.worstOut;
228740
+ const next = quote.fillable && quote.worstOut > 0n ? betterIn + roundUpTo(divCeil((targetOut - betterOut) * quote.worstIn, quote.worstOut), inputUnit) : amountIn;
228543
228741
  amountIn = next > amountIn ? next : amountIn + inputUnit;
228544
228742
  }
228545
228743
  throw new OrderbookQuoteNotConvergedError(
@@ -228548,9 +228746,6 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228548
228746
  fromOrderbookAmount(lastAmountIn, tokenIn.decimals)
228549
228747
  );
228550
228748
  }
228551
- insufficient(route, maxFillableIn, tokenIn) {
228552
- return new InsufficientOrderbookLiquidityError(route, fromOrderbookAmount(maxFillableIn, tokenIn.decimals));
228553
- }
228554
228749
  assetByAddress(chain2, address) {
228555
228750
  const resolvedChain = resolveChain(chain2);
228556
228751
  return withDecimals(
@@ -228709,22 +228904,6 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
228709
228904
  this.orderbook = typeof orderbook === "string" ? new HyperFxOrderbook(orderbook) : orderbook;
228710
228905
  return this;
228711
228906
  }
228712
- /**
228713
- * Quotes an intent between this gateway's source and destination chains from
228714
- * the HyperFX orderbook. Provide exactly one of `amountIn` or `amountOut`, in
228715
- * raw token units.
228716
- *
228717
- * An exact-input quote is the orderbook's clearing price for `amountIn`,
228718
- * which may combine several solvers' orders. An exact-output quote finds the
228719
- * smallest input whose clearing price delivers `amountOut`. A cross-chain
228720
- * route only counts orders whose solvers accept the source chain.
228721
- *
228722
- * The orderbook's rates already carry the gateway protocol fee, so the
228723
- * returned amounts can be placed as the order's inputs and outputs directly.
228724
- *
228725
- * @throws {InsufficientOrderbookLiquidityError} When the route cannot fill the amount.
228726
- * @throws {OrderbookRequestError} When the orderbook is unreachable or trades no book for the pair.
228727
- */
228728
228907
  async quoteIntent(params) {
228729
228908
  return this.market.quoteIntent(params, this.source.config.stateMachineId, this.dest.config.stateMachineId);
228730
228909
  }
@@ -229139,6 +229318,16 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
229139
229318
  async estimateFillOrder(params) {
229140
229319
  return this.gasEstimator.estimateFillOrder(params);
229141
229320
  }
229321
+ /**
229322
+ * Asks the bundler for the `preVerificationGas` of a bid as it will be signed, priced at
229323
+ * the gas price its bundle pays. Call it with the final calldata and `paymasterAndData`,
229324
+ * before {@link prepareSubmitBid} signs the op.
229325
+ *
229326
+ * Delegates to {@link GasEstimator.estimateBidPreVerificationGas}.
229327
+ */
229328
+ async estimateBidPreVerificationGas(params) {
229329
+ return this.gasEstimator.estimateBidPreVerificationGas(params);
229330
+ }
229142
229331
  /**
229143
229332
  * Quotes the solver fee for an order using the same policy {@link execute} /
229144
229333
  * {@link executeBest} apply when `order.fees` is `0n`.
@@ -229954,6 +230143,7 @@ var init_external_links = __esm({
229954
230143
  "src/config/external-links.json"() {
229955
230144
  external_links_default = {
229956
230145
  hyperfxApp: "https://app.hyperfx.finance",
230146
+ alchemyDashboard: "https://dashboard.alchemy.com/",
229957
230147
  hyperbridgeExplorers: {
229958
230148
  mainnet: "https://nexus.statescan.io",
229959
230149
  testnet: "https://gargantua.statescan.io"
@@ -229987,12 +230177,58 @@ function chainByChainId(chainId) {
229987
230177
  function chainByAlchemySubdomain(subdomain) {
229988
230178
  return INIT_CHAINS.find((chain2) => chain2.alchemySubdomain === subdomain);
229989
230179
  }
229990
- var CHAIN_NATIVE_SYMBOLS, INIT_CHAINS, HYPERBRIDGE_WS_DEFAULTS;
230180
+ var FREE_RPC_URLS, CHAIN_NATIVE_SYMBOLS, INIT_CHAINS, HYPERBRIDGE_WS_DEFAULTS;
229991
230181
  var init_chains2 = __esm({
229992
230182
  "src/cli/init/chains.ts"() {
229993
230183
  "use strict";
229994
230184
  init_esm_shims();
229995
230185
  init_external_links();
230186
+ FREE_RPC_URLS = {
230187
+ 1: [
230188
+ "https://mainnet.gateway.tenderly.co",
230189
+ "https://rpc.mevblocker.io",
230190
+ "https://ethereum-rpc.publicnode.com",
230191
+ "https://ethereum.public.blockpi.network/v1/rpc/public",
230192
+ "https://eth.blockrazor.xyz",
230193
+ "https://eth.api.pocket.network",
230194
+ "https://ethereum-public.nodies.app",
230195
+ "https://eth-mainnet.public.blastapi.io",
230196
+ "https://public-eth.nownodes.io"
230197
+ ],
230198
+ 56: [
230199
+ "https://bsc-rpc.publicnode.com",
230200
+ "https://public-bsc-mainnet.fastnode.io",
230201
+ "https://public-bsc.nownodes.io",
230202
+ "https://bsc.blockrazor.xyz",
230203
+ "https://rpc-bsc.48.club",
230204
+ "https://bsc.api.pocket.network"
230205
+ ],
230206
+ 137: [
230207
+ "https://polygon-bor-rpc.publicnode.com",
230208
+ "https://polygon.drpc.org",
230209
+ "https://polygon.gateway.tenderly.co",
230210
+ "https://rpc.private.mev-x.com/polygon",
230211
+ "https://poly.api.pocket.network"
230212
+ ],
230213
+ 8453: [
230214
+ "https://base-rpc.publicnode.com",
230215
+ "https://base.gateway.tenderly.co",
230216
+ "https://base.public.blockpi.network/v1/rpc/public",
230217
+ "https://base.rpc.blxrbdn.com",
230218
+ "https://rpc.baseazul.dev",
230219
+ "https://mainnet.base.org",
230220
+ "https://base-public.nodies.app"
230221
+ ],
230222
+ 42161: [
230223
+ "https://arbitrum-one-rpc.publicnode.com",
230224
+ "https://arbitrum.gateway.tenderly.co",
230225
+ "https://arbitrum.drpc.org",
230226
+ "https://public-arb-mainnet.fastnode.io",
230227
+ "https://arb-one.api.pocket.network",
230228
+ "https://arbitrum-one-public.nodies.app",
230229
+ "https://arb1.arbitrum.io/rpc"
230230
+ ]
230231
+ };
229996
230232
  CHAIN_NATIVE_SYMBOLS = {
229997
230233
  1: "ETH",
229998
230234
  56: "BNB",
@@ -230017,7 +230253,8 @@ var init_chains2 = __esm({
230017
230253
  label: "Ethereum",
230018
230254
  network: "mainnet",
230019
230255
  alchemySubdomain: "eth-mainnet",
230020
- explorerUrl: external_links_default.chainExplorers.ethereum
230256
+ explorerUrl: external_links_default.chainExplorers.ethereum,
230257
+ defaultRpcUrls: FREE_RPC_URLS[1]
230021
230258
  },
230022
230259
  {
230023
230260
  chainId: 42161,
@@ -230025,7 +230262,8 @@ var init_chains2 = __esm({
230025
230262
  label: "Arbitrum",
230026
230263
  network: "mainnet",
230027
230264
  alchemySubdomain: "arb-mainnet",
230028
- explorerUrl: external_links_default.chainExplorers.arbitrum
230265
+ explorerUrl: external_links_default.chainExplorers.arbitrum,
230266
+ defaultRpcUrls: FREE_RPC_URLS[42161]
230029
230267
  },
230030
230268
  {
230031
230269
  chainId: 8453,
@@ -230033,7 +230271,8 @@ var init_chains2 = __esm({
230033
230271
  label: "Base",
230034
230272
  network: "mainnet",
230035
230273
  alchemySubdomain: "base-mainnet",
230036
- explorerUrl: external_links_default.chainExplorers.base
230274
+ explorerUrl: external_links_default.chainExplorers.base,
230275
+ defaultRpcUrls: FREE_RPC_URLS[8453]
230037
230276
  },
230038
230277
  {
230039
230278
  chainId: 137,
@@ -230041,7 +230280,8 @@ var init_chains2 = __esm({
230041
230280
  label: "Polygon",
230042
230281
  network: "mainnet",
230043
230282
  alchemySubdomain: "polygon-mainnet",
230044
- explorerUrl: external_links_default.chainExplorers.polygon
230283
+ explorerUrl: external_links_default.chainExplorers.polygon,
230284
+ defaultRpcUrls: FREE_RPC_URLS[137]
230045
230285
  },
230046
230286
  {
230047
230287
  chainId: 56,
@@ -230049,7 +230289,8 @@ var init_chains2 = __esm({
230049
230289
  label: "BNB Chain",
230050
230290
  network: "mainnet",
230051
230291
  alchemySubdomain: "bnb-mainnet",
230052
- explorerUrl: external_links_default.chainExplorers.bnb
230292
+ explorerUrl: external_links_default.chainExplorers.bnb,
230293
+ defaultRpcUrls: FREE_RPC_URLS[56]
230053
230294
  },
230054
230295
  {
230055
230296
  chainId: 11155111,
@@ -295054,12 +295295,12 @@ var init_lib3 = __esm({
295054
295295
  return Promise.resolve(ab);
295055
295296
  }
295056
295297
  stream() {
295057
- const readable = new Readable3();
295058
- readable._read = function() {
295298
+ const readable2 = new Readable3();
295299
+ readable2._read = function() {
295059
295300
  };
295060
- readable.push(this[BUFFER]);
295061
- readable.push(null);
295062
- return readable;
295301
+ readable2.push(this[BUFFER]);
295302
+ readable2.push(null);
295303
+ return readable2;
295063
295304
  }
295064
295305
  toString() {
295065
295306
  return "[object Blob]";
@@ -316113,6 +316354,19 @@ async function collectRpcUrls(state, prefill) {
316113
316354
  for (const chain2 of state.chains) {
316114
316355
  const existing = prefillRpcFor(chain2.meta.chainId, prefill);
316115
316356
  const derived2 = alchemy.candidate(chain2.meta.chainId);
316357
+ const bundled = chain2.meta.defaultRpcUrls;
316358
+ if (!existing?.length && !derived2 && bundled?.length) {
316359
+ const usePublic = guard(
316360
+ await confirm({
316361
+ message: `Use ${bundled.length} public RPC endpoints for ${chain2.meta.label}?`,
316362
+ initialValue: true
316363
+ })
316364
+ );
316365
+ if (usePublic) {
316366
+ chain2.rpcUrls = [...bundled];
316367
+ continue;
316368
+ }
316369
+ }
316116
316370
  let url2;
316117
316371
  if (existing?.length) {
316118
316372
  url2 = await askRpcUrl(chain2.meta, existing[0]);
@@ -316290,6 +316544,22 @@ async function stepBundlers(state, prefill) {
316290
316544
  for (const chain2 of alchemyChains) chain2.bundlerUrl = chain2.rpcUrls[0];
316291
316545
  }
316292
316546
  }
316547
+ const alchemyCapable = state.chains.filter((chain2) => !chain2.bundlerUrl && chain2.meta.alchemySubdomain);
316548
+ if (alchemyCapable.length > 0) {
316549
+ const useKey = guard(
316550
+ await confirm({
316551
+ message: `Configure the bundler for ${alchemyCapable.map((c6) => c6.meta.label).join(", ")} from one Alchemy API key?`,
316552
+ initialValue: true
316553
+ })
316554
+ );
316555
+ if (useKey) {
316556
+ const apiKey = await askSecret("Alchemy API key");
316557
+ for (const chain2 of alchemyCapable) {
316558
+ const url2 = deriveAlchemyRpc(apiKey, chain2.meta.chainId);
316559
+ if (url2) chain2.bundlerUrl = url2;
316560
+ }
316561
+ }
316562
+ }
316293
316563
  const pimlico = new ProviderDerivation({
316294
316564
  detect: (url2) => parsePimlicoUrl(url2)?.apiKey ?? null,
316295
316565
  derive: derivePimlicoBundler,
@@ -319025,14 +319295,16 @@ var LimitOrderService = class {
319025
319295
  }
319026
319296
  }
319027
319297
  /**
319028
- * The pairs an order can be written against, and the smallest payout each token may carry.
319298
+ * The pairs an order can be written against, the smallest payout each token may carry, and
319299
+ * the tokens the orderbook registers on each chain.
319029
319300
  *
319030
319301
  * The orderbook lists the books; `resolveBook` refuses a request naming anything else, so the
319031
- * operator is offered exactly these and no combination of symbols they could not post.
319302
+ * operator is offered exactly these and no combination of symbols they could not post. The
319303
+ * chains do the same for the source chains an order can accept.
319032
319304
  */
319033
319305
  async books() {
319034
319306
  const limits = await this.limits();
319035
- return { books: limits.books, minOrderSizes: limits.serverInfo.minOrderSizes };
319307
+ return { books: limits.books, minOrderSizes: limits.serverInfo.minOrderSizes, chains: limits.chains ?? [] };
319036
319308
  }
319037
319309
  /**
319038
319310
  * Reads `serverInfo` and `books`, reusing the last read for {@link LIMITS_TTL_MS}.
@@ -319114,6 +319386,18 @@ var LimitOrderService = class {
319114
319386
  );
319115
319387
  }
319116
319388
  }
319389
+ if (book.base !== book.quote) {
319390
+ const input = normalizeSymbol(request3.tokenIn);
319391
+ const lacking = sources.filter((source) => {
319392
+ const listed = limits.chains?.find((chain2) => chain2.id === source);
319393
+ return listed && !listed.tokens.some((token) => normalizeSymbol(token.symbol) === input);
319394
+ });
319395
+ if (lacking.length > 0) {
319396
+ throw new LimitOrderValidationError(
319397
+ `The orderbook has no ${request3.tokenIn} on ${lacking.join(", ")}, so swaps from there cannot pay this order`
319398
+ );
319399
+ }
319400
+ }
319117
319401
  if (ttlSecs < limits.serverInfo.minOrderTtlSecs) {
319118
319402
  throw new LimitOrderValidationError(
319119
319403
  `ttlSecs must be at least the orderbook's minimum of ${limits.serverInfo.minOrderTtlSecs}; got ${ttlSecs}`
@@ -320776,7 +321060,7 @@ var NotificationService = class extends EventEmitter6 {
320776
321060
  title: "Swap filled",
320777
321061
  body: swapBody(event),
320778
321062
  tag: `simplex-swap-${id2}`,
320779
- url: "./orders"
321063
+ url: "./history"
320780
321064
  }).catch((error) => this.logger.error({ err: error }, "Could not publish swap notification"));
320781
321065
  }
320782
321066
  };
@@ -320969,6 +321253,7 @@ init_chains2();
320969
321253
  init_alchemy();
320970
321254
  init_prompt_utils();
320971
321255
  init_state();
321256
+ init_defaults8();
320972
321257
  init_Logger();
320973
321258
  var logger2 = getLogger("setup");
320974
321259
  async function defaultRpcRequest(url2, method, params) {
@@ -321015,6 +321300,18 @@ async function handleSetupRequest(server, setup, req, res, path2, method) {
321015
321300
  };
321016
321301
  return sendJson(res, 200, defaults3);
321017
321302
  }
321303
+ if (endpoint === "orderbook") {
321304
+ if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
321305
+ const url2 = DEFAULT_ORDERBOOK_URLS.mainnet;
321306
+ try {
321307
+ const { books } = await new OrderbookClient(url2, DEFAULT_ORDERBOOK_TIMEOUT_MS).limits();
321308
+ const orderbook = { url: url2, books };
321309
+ return sendJson(res, 200, orderbook);
321310
+ } catch (err) {
321311
+ const reason = err instanceof Error ? err.message : String(err);
321312
+ return sendJson(res, 502, { error: `Could not read the orderbook's markets: ${reason}` });
321313
+ }
321314
+ }
321018
321315
  if (method !== "POST") return sendJson(res, 405, { error: "Method not allowed" });
321019
321316
  let body;
321020
321317
  try {
@@ -323326,14 +323623,10 @@ var RATE_LIMIT_RPC_CODES = /* @__PURE__ */ new Set([-32005, -32097, -32016, 429]
323326
323623
  var RATE_LIMIT_SUSPENSION_MS = 5 * 6e4;
323327
323624
  var SCAN_BUDGET = {
323328
323625
  timeoutMs: 5e3,
323329
- retryCount: 1,
323330
- retryDelayMs: 500,
323331
323626
  deadlineMs: 12e3
323332
323627
  };
323333
323628
  var CONFIRMATION_BUDGET = {
323334
323629
  timeoutMs: 1e4,
323335
- retryCount: 1,
323336
- retryDelayMs: 500,
323337
323630
  deadlineMs: 3e4
323338
323631
  };
323339
323632
  var MALFORMED_RESPONSE_SUSPENSION_MS = 3e4;
@@ -323425,8 +323718,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323425
323718
  chain: chain2,
323426
323719
  transport: http(url2, {
323427
323720
  timeout: budget.timeoutMs,
323428
- retryCount: budget.retryCount,
323429
- retryDelay: budget.retryDelayMs
323721
+ // No transport-level retry. viem's retry sleeps for the provider's
323722
+ // `Retry-After` verbatim and uncapped, which reaches the socket
323723
+ // before {@link suspensionMsFor} can apply its clamp — and even
323724
+ // without that header, retrying inside the task is what makes a
323725
+ // task's worst case (timeout, backoff, timeout) approach the
323726
+ // deadline that is supposed to bound it. One attempt per endpoint
323727
+ // per call; `retryPromise` above the quorum owns the retry policy.
323728
+ retryCount: 0
323430
323729
  })
323431
323730
  })
323432
323731
  );
@@ -323440,11 +323739,10 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323440
323739
  return this.rpcUrls.filter((url2) => (_QuorumPublicClient.benchedUntil.get(url2) ?? 0) > now);
323441
323740
  }
323442
323741
  /**
323443
- * The endpoints this call queries — everyone not benched — and the quorum
323444
- * bar for exactly that set. A benched endpoint is dropped outright: it
323445
- * answers nothing while throttled, so counting it could only fail calls the
323446
- * remaining endpoints agree on. With every endpoint benched, everyone is
323447
- * queried again; there is nobody left to prefer.
323742
+ * The endpoints this call queries: everyone not benched. A benched endpoint is
323743
+ * dropped outright: it answers nothing while throttled, so asking it could only
323744
+ * cost a request. With every endpoint benched, everyone is queried again; there
323745
+ * is nobody left to prefer.
323448
323746
  */
323449
323747
  participants() {
323450
323748
  const now = Date.now();
@@ -323452,12 +323750,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323452
323750
  const active = all7.filter(({ idx }) => (_QuorumPublicClient.benchedUntil.get(this.rpcUrls[idx]) ?? 0) <= now);
323453
323751
  const allBenched = active.length === 0;
323454
323752
  const queried = allBenched ? all7 : active;
323455
- return {
323456
- queried,
323457
- skipped: all7.length - queried.length,
323458
- threshold: quorumThreshold(queried.length),
323459
- allBenched
323460
- };
323753
+ return { queried, skipped: all7.length - queried.length, allBenched };
323461
323754
  }
323462
323755
  /**
323463
323756
  * Records a task failure against its endpoint. Only the failures
@@ -323502,17 +323795,21 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323502
323795
  * retries would otherwise stall every confirmation poll). Stragglers settle
323503
323796
  * out of band and are ignored. `tryDecide` returns undefined to keep
323504
323797
  * waiting; once every task has settled, `finalize` must decide (it throws
323505
- * the QuorumError when the full response set still has no quorum). Early
323506
- * exit never weakens the trust model — a decision still requires the same
323507
- * quorum, it just doesn't wait for votes it no longer needs.
323798
+ * the QuorumError when the full response set still has no quorum).
323799
+ *
323800
+ * Both are handed the bar: the BFT threshold over the endpoints that have not
323801
+ * failed, answered or still out. A failure lowers it rather than counting
323802
+ * against it. Counting the pending endpoints keeps an early decision to a bar
323803
+ * no lower than the full response set would get, so a vote it did not wait for
323804
+ * could never have reversed it.
323508
323805
  *
323509
323806
  * `deadlineMs` bounds the other direction: when the bar is NOT reachable
323510
323807
  * from the fast responders, the call used to wait out every straggler, and
323511
323808
  * the caller's own retry loop multiplied that into minutes of a chain not
323512
323809
  * being scanned at all, silently. At the deadline the outstanding endpoints
323513
323810
  * are treated as failures — named in the QuorumError, so the operator sees
323514
- * which ones held the call up — and `finalize` decides on the rest. The
323515
- * trust model is untouched: a decision still needs the same quorum.
323811
+ * which ones held the call up — and `finalize` decides over the endpoints
323812
+ * that answered.
323516
323813
  */
323517
323814
  settleUntilQuorum(tasks, tryDecide, finalize) {
323518
323815
  return new Promise((resolve11, reject) => {
@@ -323530,13 +323827,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323530
323827
  const evaluate = () => {
323531
323828
  if (done) return;
323532
323829
  try {
323533
- const early = tryDecide(fulfilled);
323830
+ const threshold = quorumThreshold(tasks.length - failures.length);
323831
+ const early = tryDecide(fulfilled, threshold);
323534
323832
  if (early !== void 0) {
323535
323833
  finish(() => resolve11(early));
323536
323834
  return;
323537
323835
  }
323538
323836
  if (outstanding === 0) {
323539
- const value = finalize(fulfilled, failures);
323837
+ const value = finalize(fulfilled, failures, threshold);
323540
323838
  finish(() => resolve11(value));
323541
323839
  }
323542
323840
  } catch (error) {
@@ -323554,7 +323852,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323554
323852
  error: new Error(`no answer within the ${this.deadlineMs}ms quorum deadline`)
323555
323853
  }));
323556
323854
  try {
323557
- const value = finalize(fulfilled, [...failures, ...stragglers]);
323855
+ const value = finalize(fulfilled, [...failures, ...stragglers], quorumThreshold(fulfilled.length));
323558
323856
  finish(() => resolve11(value));
323559
323857
  } catch (error) {
323560
323858
  finish(() => reject(error));
@@ -323581,22 +323879,22 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323581
323879
  });
323582
323880
  }
323583
323881
  /**
323584
- * Highest block head backed by a full quorum. Throws {@link QuorumError}
323585
- * when fewer than `threshold` endpoints respond.
323882
+ * Highest block head backed by a quorum of the endpoints that answer. Throws
323883
+ * {@link QuorumError} when none do.
323586
323884
  */
323587
323885
  async getBlockNumber() {
323588
- const { queried, skipped, threshold, allBenched } = this.participants();
323886
+ const { queried, skipped, allBenched } = this.participants();
323589
323887
  return this.settleUntilQuorum(
323590
323888
  queried.map(({ idx, client }) => ({ idx, task: client.getBlockNumber() })),
323591
323889
  // Early exit as soon as a quorum is satisfiable. Late voters could
323592
323890
  // only raise the reported head; the earlier (lower) head is
323593
323891
  // conservative for every consumer (fewer confirmations counted,
323594
323892
  // smaller scan windows).
323595
- (fulfilled) => this.quorumHead(
323893
+ (fulfilled, threshold) => this.quorumHead(
323596
323894
  fulfilled.map((f7) => f7.value),
323597
323895
  threshold
323598
323896
  ) ?? void 0,
323599
- (fulfilled, failures) => {
323897
+ (fulfilled, failures, threshold) => {
323600
323898
  const head = this.quorumHead(
323601
323899
  fulfilled.map((f7) => f7.value),
323602
323900
  threshold
@@ -323637,7 +323935,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323637
323935
  }
323638
323936
  return views;
323639
323937
  };
323640
- const { queried, skipped, threshold, allBenched } = this.participants();
323938
+ const { queried, skipped, allBenched } = this.participants();
323641
323939
  return this.settleUntilQuorum(
323642
323940
  queried.map(({ idx, client }) => ({
323643
323941
  idx,
@@ -323656,8 +323954,8 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323656
323954
  // Early exit only on a POSITIVE quorum: an agreeing inclusion group can
323657
323955
  // only grow, so the first satisfiable quorum is final. Not-found votes
323658
323956
  // never decide early — "not confirmed" needs the full response set.
323659
- (fulfilled) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
323660
- (fulfilled, failures) => {
323957
+ (fulfilled, threshold) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
323958
+ (fulfilled, failures, threshold) => {
323661
323959
  const confirmations = aggregateConfirmations(toViews(fulfilled), threshold);
323662
323960
  if (confirmations === null) {
323663
323961
  throw new QuorumError(
@@ -323686,7 +323984,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323686
323984
  }
323687
323985
  return groups;
323688
323986
  };
323689
- const { queried, skipped, threshold, allBenched } = this.participants();
323987
+ const { queried, skipped, allBenched } = this.participants();
323690
323988
  return this.settleUntilQuorum(
323691
323989
  queried.map(({ idx, client }) => ({
323692
323990
  idx,
@@ -323698,13 +323996,13 @@ var _QuorumPublicClient = class _QuorumPublicClient {
323698
323996
  // two disjoint groups can't both hold one), so the first satisfiable
323699
323997
  // agreement is final — no need to wait out stragglers that could
323700
323998
  // only join or lose.
323701
- (fulfilled) => {
323999
+ (fulfilled, threshold) => {
323702
324000
  for (const group of groupOf(fulfilled).values()) {
323703
324001
  if (group.providerIdxs.length >= threshold) return group.result;
323704
324002
  }
323705
324003
  return void 0;
323706
324004
  },
323707
- (fulfilled, failures) => {
324005
+ (fulfilled, failures, threshold) => {
323708
324006
  const groups = groupOf(fulfilled);
323709
324007
  for (const group of groups.values()) {
323710
324008
  if (group.providerIdxs.length >= threshold) return group.result;
@@ -328936,20 +329234,32 @@ var ContractInteractionService = class {
328936
329234
  } else {
328937
329235
  this.logger.warn({ reason: pmResult.reason }, "No paymaster for bid UserOp; relying on EntryPoint deposit");
328938
329236
  }
328939
- const userOp = await sdkHelper.prepareSubmitBid({
328940
- order,
328941
- fillOptions,
329237
+ const bidGas = {
328942
329238
  solverAccount: solverAccountAddress,
328943
- solverSigner: sdkSigningAccount(this.signer),
328944
329239
  nonce,
328945
329240
  entryPointAddress,
328946
329241
  callGasLimit,
328947
329242
  verificationGasLimit: cachedEstimate.verificationGasLimit,
328948
- preVerificationGas: cachedEstimate.preVerificationGas,
328949
329243
  maxFeePerGas: cachedEstimate.maxFeePerGas,
328950
329244
  maxPriorityFeePerGas: cachedEstimate.maxPriorityFeePerGas,
328951
329245
  callData,
328952
329246
  paymasterAndData
329247
+ };
329248
+ let preVerificationGas = cachedEstimate.preVerificationGas;
329249
+ try {
329250
+ preVerificationGas = await sdkHelper.estimateBidPreVerificationGas(bidGas);
329251
+ } catch (err) {
329252
+ this.logger.warn(
329253
+ { orderId: order.id, err, preVerificationGas: preVerificationGas.toString() },
329254
+ "Could not estimate the bid's preVerificationGas; using the fill estimate's"
329255
+ );
329256
+ }
329257
+ const userOp = await sdkHelper.prepareSubmitBid({
329258
+ ...bidGas,
329259
+ order,
329260
+ fillOptions,
329261
+ solverSigner: sdkSigningAccount(this.signer),
329262
+ preVerificationGas
328953
329263
  });
328954
329264
  const encodedUserOp = encodeUserOpScale(userOp);
328955
329265
  this.logger.info(
@@ -328958,6 +329268,7 @@ var ContractInteractionService = class {
328958
329268
  solverAccount: solverAccountAddress,
328959
329269
  nonce: nonce.toString(),
328960
329270
  callGasLimit: callGasLimit.toString(),
329271
+ preVerificationGas: preVerificationGas.toString(),
328961
329272
  maxFeePerGas: cachedEstimate.maxFeePerGas.toString()
328962
329273
  },
328963
329274
  "Prepared bid UserOp"
@@ -362304,8 +362615,15 @@ var IntentFiller = class {
362304
362615
  const evalDurationSec = (Date.now() - evalStartMs) / 1e3;
362305
362616
  this.monitor.emit("orderTiming", { orderId: order.id, phase: "evaluation", durationSec: evalDurationSec });
362306
362617
  if (validStrategies.length === 0) {
362307
- this.logger.warn({ orderId: order.id }, "No profitable strategy found for order");
362308
- this.monitor.emit("orderSkipped", { orderId: order.id, reason: "No profitable strategy" });
362618
+ const priced = eligibleStrategies.some((s4) => s4 !== null);
362619
+ this.logger.warn(
362620
+ { orderId: order.id },
362621
+ priced ? "No profitable strategy found for order" : "No strategy can fill this order"
362622
+ );
362623
+ this.monitor.emit("orderSkipped", {
362624
+ orderId: order.id,
362625
+ reason: priced ? "No profitable strategy" : "No strategy can fill this order"
362626
+ });
362309
362627
  return null;
362310
362628
  }
362311
362629
  this.logger.info(
@@ -362732,10 +363050,6 @@ init_asset_registry();
362732
363050
  function limitOrderLegs(order) {
362733
363051
  return order.side === "BID" ? { input: order.base, output: order.quote } : { input: order.quote, output: order.base };
362734
363052
  }
362735
- function availableOn(order) {
362736
- const available = BigInt(order.remaining) - BigInt(order.reserved);
362737
- return available > 0n ? available : 0n;
362738
- }
362739
363053
  function matchLimitOrders(orders, incoming, resolve11, now = /* @__PURE__ */ new Date()) {
362740
363054
  const qualifying = orders.filter((order) => serves(order, incoming, resolve11, now)).map((order) => {
362741
363055
  const offer = offerFor({
@@ -362744,7 +363058,7 @@ function matchLimitOrders(orders, incoming, resolve11, now = /* @__PURE__ */ new
362744
363058
  price: BigInt(order.price),
362745
363059
  outputDecimals: incoming.outputDecimals
362746
363060
  });
362747
- const available = availableOn(order);
363061
+ const available = BigInt(order.remaining);
362748
363062
  return { order, offer, available, payout: offer < available ? offer : available };
362749
363063
  }).filter((candidate) => candidate.offer >= incoming.requestedOutput && candidate.payout > 0n).sort(byBestOfferFirst);
362750
363064
  return qualifying;
@@ -362754,16 +363068,63 @@ function byBestOfferFirst(a6, b5) {
362754
363068
  if (a6.available !== b5.available) return a6.available > b5.available ? -1 : 1;
362755
363069
  return a6.order.id < b5.order.id ? -1 : 1;
362756
363070
  }
363071
+ function whyUnmatched(orders, incoming, resolve11, now = /* @__PURE__ */ new Date()) {
363072
+ let furthest = -1;
363073
+ for (const order of orders) {
363074
+ const failed = firstFailed(order, incoming, resolve11, now);
363075
+ furthest = Math.max(furthest, failed === null ? CHECKS.length : CHECKS.indexOf(failed));
363076
+ }
363077
+ const { source, destination, inputSymbol } = incoming;
363078
+ if (furthest < CHECKS.length) {
363079
+ switch (CHECKS[Math.max(furthest, 0)]) {
363080
+ case "open":
363081
+ return "no limit order is open";
363082
+ case "chain":
363083
+ return `no open limit order fills on ${destination}`;
363084
+ case "input":
363085
+ return `no limit order on ${destination} takes ${inputSymbol} in`;
363086
+ case "output":
363087
+ return `no limit order on ${destination} taking ${inputSymbol} in pays out the token this order asks for`;
363088
+ case "source":
363089
+ return `no limit order for this pair on ${destination} accepts swaps from ${source}`;
363090
+ }
363091
+ }
363092
+ const priced = orders.filter((order) => firstFailed(order, incoming, resolve11, now) === null).map((order) => ({
363093
+ order,
363094
+ offer: offerFor({
363095
+ side: order.side,
363096
+ inputAmount: incoming.inputNet,
363097
+ price: BigInt(order.price),
363098
+ outputDecimals: incoming.outputDecimals
363099
+ })
363100
+ }));
363101
+ if (priced.some(({ offer }) => offer >= incoming.requestedOutput)) {
363102
+ return "the limit orders that meet its rate have nothing left to pay out";
363103
+ }
363104
+ const best = priced.reduce((a6, b5) => b5.offer > a6.offer ? b5 : a6);
363105
+ const symbol3 = limitOrderLegs(best.order).output;
363106
+ return `it asks for ${readable(incoming.requestedOutput)} ${symbol3}; the best limit order offers ${readable(best.offer)} ${symbol3}`;
363107
+ }
363108
+ var CHECKS = ["open", "chain", "input", "output", "source"];
362757
363109
  function serves(order, incoming, resolve11, now) {
362758
- if (order.status !== "open") return false;
362759
- if (order.expiresAt !== null && new Date(order.expiresAt) <= now) return false;
362760
- if (order.fillChain !== incoming.destination) return false;
363110
+ return firstFailed(order, incoming, resolve11, now) === null;
363111
+ }
363112
+ function firstFailed(order, incoming, resolve11, now) {
363113
+ if (order.status !== "open") return "open";
363114
+ if (order.expiresAt !== null && new Date(order.expiresAt) <= now) return "open";
363115
+ if (order.fillChain !== incoming.destination) return "chain";
362761
363116
  const legs = limitOrderLegs(order);
362762
- if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return false;
363117
+ if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return "input";
362763
363118
  const outputToken = resolve11(legs.output, order.fillChain);
362764
- if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return false;
362765
- if (incoming.source === incoming.destination) return true;
362766
- return order.acceptedSources.includes(incoming.source);
363119
+ if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return "output";
363120
+ if (incoming.source === incoming.destination) return null;
363121
+ return order.acceptedSources.includes(incoming.source) ? null : "source";
363122
+ }
363123
+ function readable(amount) {
363124
+ const [whole, fraction] = toHuman(amount).split(".");
363125
+ const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
363126
+ const cut = fraction?.slice(0, 6).replace(/0+$/, "");
363127
+ return cut ? `${grouped}.${cut}` : grouped;
362767
363128
  }
362768
363129
 
362769
363130
  // src/orderbook/usd.ts
@@ -362907,15 +363268,20 @@ var FXFiller = class _FXFiller {
362907
363268
  }
362908
363269
  try {
362909
363270
  if (order.inputs.length !== order.output.assets.length) {
362910
- this.logger.debug(
362911
- { inputs: order.inputs.length, outputs: order.output.assets.length },
363271
+ this.logger.info(
363272
+ { orderId: order.id, inputs: order.inputs.length, outputs: order.output.assets.length },
362912
363273
  "Order input/output length mismatch or empty"
362913
363274
  );
362914
363275
  return false;
362915
363276
  }
362916
363277
  if ((await this.matchOrder(order)).length === 0) {
362917
- this.logger.debug(
362918
- { orderId: order.id, sourceChain: order.source, destChain: order.destination },
363278
+ this.logger.info(
363279
+ {
363280
+ orderId: order.id,
363281
+ sourceChain: order.source,
363282
+ destChain: order.destination,
363283
+ reason: await this.explainUnmatched(order)
363284
+ },
362919
363285
  "No limit order matches this order"
362920
363286
  );
362921
363287
  return false;
@@ -363457,30 +363823,53 @@ var FXFiller = class _FXFiller {
363457
363823
  */
363458
363824
  async matchLeg(order, leg) {
363459
363825
  if (!this.limitOrders) return [];
363826
+ const incoming = await this.incomingFor(order, leg);
363827
+ if (!incoming) return [];
363828
+ return matchLimitOrders(
363829
+ await this.limitOrders.open(),
363830
+ incoming,
363831
+ (symbol3, chain2) => this.registry.getAddress(symbol3, chain2)
363832
+ );
363833
+ }
363834
+ /** Why no limit order serves the order, leg by leg, for the line that says it was passed over. */
363835
+ async explainUnmatched(order) {
363836
+ if (!this.limitOrders) return "limit orders are not loaded";
363837
+ const open2 = await this.limitOrders.open();
363838
+ const reasons = [];
363839
+ for (let leg = 0; leg < order.inputs.length; leg++) {
363840
+ const incoming = await this.incomingFor(order, leg);
363841
+ const reason = incoming ? whyUnmatched(open2, incoming, (symbol3, chain2) => this.registry.getAddress(symbol3, chain2)) : `its input token is not in the asset registry on ${order.source}`;
363842
+ reasons.push(order.inputs.length > 1 ? `leg ${leg}: ${reason}` : reason);
363843
+ }
363844
+ return reasons.join("; ");
363845
+ }
363846
+ /**
363847
+ * One leg as the matcher sees it, or null when its input token is not one the asset
363848
+ * registry knows on the source chain.
363849
+ *
363850
+ * Amounts cross into the matcher at 1e18, the unit limit orders are kept in.
363851
+ */
363852
+ async incomingFor(order, leg) {
363460
363853
  const input = order.inputs[leg];
363461
363854
  const output3 = order.output.assets[leg];
363462
- if (!input || !output3) return [];
363855
+ if (!input || !output3) return null;
363463
363856
  const inputToken = bytes32ToBytes20(input.token);
363464
363857
  const outputToken = bytes32ToBytes20(output3.token);
363465
363858
  const inputSymbol = this.registry.symbolFor(inputToken, order.source);
363466
- if (!inputSymbol) return [];
363859
+ if (!inputSymbol) return null;
363467
363860
  const [inputDecimals, outputDecimals] = await Promise.all([
363468
363861
  this.contractService.getTokenDecimals(inputToken, order.source),
363469
363862
  this.contractService.getTokenDecimals(outputToken, order.destination)
363470
363863
  ]);
363471
- return matchLimitOrders(
363472
- await this.limitOrders.open(),
363473
- {
363474
- source: order.source,
363475
- destination: order.destination,
363476
- inputSymbol,
363477
- outputToken,
363478
- inputNet: toScaled(input.amount, inputDecimals),
363479
- requestedOutput: toScaled(output3.amount, outputDecimals),
363480
- outputDecimals
363481
- },
363482
- (symbol3, chain2) => this.registry.getAddress(symbol3, chain2)
363483
- );
363864
+ return {
363865
+ source: order.source,
363866
+ destination: order.destination,
363867
+ inputSymbol,
363868
+ outputToken,
363869
+ inputNet: toScaled(input.amount, inputDecimals),
363870
+ requestedOutput: toScaled(output3.amount, outputDecimals),
363871
+ outputDecimals
363872
+ };
363484
363873
  }
363485
363874
  /**
363486
363875
  * The order's input in **USD**, or null when no limit order connects its input
@@ -364583,48 +364972,42 @@ async function bootFiller(config5, options) {
364583
364972
  assertPairSymbolsResolve(config5.pairs, assetRegistry, configuredChainNames);
364584
364973
  }
364585
364974
  const adminStrategies = [];
364586
- const strategies = [];
364587
- let confirmationPolicy;
364588
- let tradingPairs;
364589
- let engine;
364590
- if (config5.pairs?.length) {
364591
- tradingPairs = config5.pairs.map((pair) => ({ token0: pair.token0, token1: pair.token1 }));
364592
- tradingPairs.forEach((pair, pairIndex) => {
364593
- adminStrategies.push({
364594
- index: pairIndex,
364595
- pairIndex,
364596
- exotic: `${pair.token0}/${pair.token1}`,
364597
- token0: pair.token0,
364598
- token1: pair.token1,
364599
- sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
364600
- });
364975
+ const tradingPairs = (config5.pairs ?? []).map((pair) => ({
364976
+ token0: pair.token0,
364977
+ token1: pair.token1
364978
+ }));
364979
+ tradingPairs.forEach((pair, pairIndex) => {
364980
+ adminStrategies.push({
364981
+ index: pairIndex,
364982
+ pairIndex,
364983
+ exotic: `${pair.token0}/${pair.token1}`,
364984
+ token0: pair.token0,
364985
+ token1: pair.token1,
364986
+ sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
364601
364987
  });
364602
- confirmationPolicy = assertConfirmationCoverage(
364603
- config5.confirmationPolicies,
364604
- resolvedChains.map((c6) => c6.chainId)
364605
- );
364606
- const fundingVenues = vaultVenue ? [vaultVenue] : [];
364607
- engine = new FXFiller(
364608
- runtimeSigner,
364609
- configService,
364610
- chainClientManager,
364611
- contractService,
364612
- tradingPairs,
364613
- assetRegistry,
364614
- { confirmationPolicy, fundingVenues, limitOrders: options.data.limitOrders }
364615
- );
364616
- logger8.info("Hydrating funding venue state...");
364617
- await engine.initialise();
364618
- strategies.push(engine);
364619
- }
364988
+ });
364989
+ const confirmationPolicy = assertConfirmationCoverage(
364990
+ config5.confirmationPolicies,
364991
+ resolvedChains.map((c6) => c6.chainId)
364992
+ );
364993
+ const fundingVenues = vaultVenue ? [vaultVenue] : [];
364994
+ const engine = new FXFiller(
364995
+ runtimeSigner,
364996
+ configService,
364997
+ chainClientManager,
364998
+ contractService,
364999
+ tradingPairs,
365000
+ assetRegistry,
365001
+ { confirmationPolicy, fundingVenues, limitOrders: options.data.limitOrders }
365002
+ );
365003
+ logger8.info("Hydrating funding venue state...");
365004
+ await engine.initialise();
365005
+ const strategies = [engine];
364620
365006
  const haltControls = strategies.map((engine2, index3) => ({
364621
365007
  index: index3,
364622
365008
  isHalted: () => engine2.isHalted(),
364623
365009
  resetHalt: () => engine2.resetHalt()
364624
365010
  }));
364625
- if (vaultVenue) {
364626
- await vaultVenue.initialise(runtimeSigner.address);
364627
- }
364628
365011
  let rebalancingService;
364629
365012
  const rebalancingConfig = configService.getRebalancingConfig();
364630
365013
  if (rebalancingConfig?.triggerPercentage !== void 0 && rebalancingConfig?.baseBalances) {
@@ -365150,8 +365533,8 @@ var MemoryLimitOrderStore = class {
365150
365533
  async reserve(id2, amount) {
365151
365534
  const order = this.orders.get(id2);
365152
365535
  if (!order || order.status !== "open") return false;
365536
+ if (BigInt(amount) > BigInt(order.remaining)) return false;
365153
365537
  const reserved = BigInt(order.reserved) + BigInt(amount);
365154
- if (reserved > BigInt(order.remaining)) return false;
365155
365538
  this.patch(id2, { reserved: reserved.toString() });
365156
365539
  return true;
365157
365540
  }
@@ -365232,6 +365615,8 @@ var MAX_BLOCK_RANGE = 1000n;
365232
365615
  var PUBLISH_CHUNK = 250;
365233
365616
  var yieldToConsumers = () => new Promise((resolve11) => setImmediate(resolve11));
365234
365617
  var STOP_DRAIN_TIMEOUT_MS = 5e3;
365618
+ var STALL_THRESHOLD_MS = 45e3;
365619
+ var STALL_REPORT_INTERVAL_MS = 6e4;
365235
365620
  var ChainScanner = class {
365236
365621
  constructor(target, loggers = defaultLoggerContext(), scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS) {
365237
365622
  this.target = target;
@@ -365245,6 +365630,18 @@ var ChainScanner = class {
365245
365630
  this.errorHandler = () => {
365246
365631
  };
365247
365632
  this.stopped = false;
365633
+ /**
365634
+ * How far past the cursor the next pass reads, as `toBlock - fromBlock`:
365635
+ * halved after a failed read, doubled after a good one, between 0 (one block)
365636
+ * and {@link MAX_BLOCK_RANGE}.
365637
+ *
365638
+ * Free endpoints cap `eth_getLogs` ranges far below the full span — on
365639
+ * Arbitrum three of seven refused 100 blocks — and the cursor never moves
365640
+ * past a range it failed to read. At a fixed span a catch-up that outgrew
365641
+ * those caps was stuck for good: the range only grew, so the capped endpoints
365642
+ * never rejoined and the quorum never formed.
365643
+ */
365644
+ this.span = MAX_BLOCK_RANGE;
365248
365645
  this.logger = loggers.get("chain-scanner");
365249
365646
  this.quorumClient = new QuorumPublicClient(target.chainId, target.rpcUrls, loggers);
365250
365647
  if (this.quorumClient.size > 1) {
@@ -365287,8 +365684,9 @@ var ChainScanner = class {
365287
365684
  if (this.timer || this.stopped) return;
365288
365685
  this.logger.info({ chainId: this.target.chainId }, "Block scanner started");
365289
365686
  this.timer = setInterval(() => {
365290
- if (this.mutex.isLocked()) return;
365687
+ if (this.mutex.isLocked()) return this.reportStall();
365291
365688
  void this.mutex.runExclusive(async () => {
365689
+ this.pass = { phase: "head", startedAt: Date.now(), reportedAt: 0 };
365292
365690
  try {
365293
365691
  await this.scan();
365294
365692
  } catch (error) {
@@ -365302,10 +365700,66 @@ var ChainScanner = class {
365302
365700
  "Error in block scanner"
365303
365701
  );
365304
365702
  this.errorHandler(error);
365703
+ } finally {
365704
+ this.notePassEnd();
365305
365705
  }
365306
365706
  });
365307
365707
  }, this.scanIntervalMs);
365308
365708
  }
365709
+ /**
365710
+ * Records which await the current pass is sitting on, for the watchdog.
365711
+ *
365712
+ * A no-op when no pass is registered — `scan()` is driven directly by tests
365713
+ * and by nothing else, so there is no watchdog to inform.
365714
+ */
365715
+ enter(phase) {
365716
+ if (this.pass) this.pass.phase = phase;
365717
+ }
365718
+ /**
365719
+ * Says that a pass has outlived the quorum budgets and names the phase it is
365720
+ * stuck in, then repeats every {@link STALL_REPORT_INTERVAL_MS} until it ends.
365721
+ *
365722
+ * Observation only: the pass is left alone. Aborting it would need a cancel
365723
+ * path through the quorum client that does not exist, and the cursor is safe
365724
+ * either way — a pass that never completes never advances it.
365725
+ */
365726
+ reportStall() {
365727
+ const pass = this.pass;
365728
+ if (!pass) return;
365729
+ const now = Date.now();
365730
+ const heldMs = now - pass.startedAt;
365731
+ if (heldMs < STALL_THRESHOLD_MS) return;
365732
+ if (pass.reportedAt !== 0 && now - pass.reportedAt < STALL_REPORT_INTERVAL_MS) return;
365733
+ pass.reportedAt = now;
365734
+ this.logger.warn(
365735
+ {
365736
+ chainId: this.target.chainId,
365737
+ phase: pass.phase,
365738
+ heldMs,
365739
+ cursor: this.cursor?.toString(),
365740
+ rpcUrls: this.target.rpcUrls
365741
+ },
365742
+ "Block scan pass has outlived its budget \u2014 this chain is not being scanned"
365743
+ );
365744
+ }
365745
+ /**
365746
+ * Closes out a pass, and says so if the watchdog complained about it — without
365747
+ * this the log shows a chain going quiet and never shows it coming back.
365748
+ */
365749
+ notePassEnd() {
365750
+ const pass = this.pass;
365751
+ this.pass = void 0;
365752
+ if (!pass || pass.reportedAt === 0) return;
365753
+ this.logger.warn(
365754
+ {
365755
+ chainId: this.target.chainId,
365756
+ phase: pass.phase,
365757
+ heldMs: Date.now() - pass.startedAt,
365758
+ cursor: this.cursor?.toString()
365759
+ },
365760
+ "Block scan pass finished after outliving its budget \u2014 scanning has resumed"
365761
+ );
365762
+ }
365309
365763
  /**
365310
365764
  * Points this loop at different endpoints, keeping the cursor.
365311
365765
  *
@@ -365347,11 +365801,12 @@ var ChainScanner = class {
365347
365801
  }
365348
365802
  if (currentBlock <= this.cursor) return;
365349
365803
  const fromBlock = this.cursor + 1n;
365350
- const toBlock = fromBlock + MAX_BLOCK_RANGE > currentBlock ? currentBlock : fromBlock + MAX_BLOCK_RANGE;
365804
+ const toBlock = fromBlock + this.span > currentBlock ? currentBlock : fromBlock + this.span;
365351
365805
  this.logger.debug(
365352
365806
  { chainId: this.target.chainId, fromBlock, toBlock, gap: Number(toBlock - fromBlock) },
365353
365807
  "Scanning blocks"
365354
365808
  );
365809
+ this.enter("logs");
365355
365810
  let logs;
365356
365811
  try {
365357
365812
  logs = await retryPromise(
@@ -365373,6 +365828,7 @@ var ChainScanner = class {
365373
365828
  );
365374
365829
  } catch (error) {
365375
365830
  if (isBlockRangeError(error)) return;
365831
+ this.narrow(toBlock - fromBlock);
365376
365832
  throw error;
365377
365833
  }
365378
365834
  if (this.stopped) return;
@@ -365383,6 +365839,7 @@ var ChainScanner = class {
365383
365839
  { chainId: this.target.chainId, fromBlock, toBlock, eventCount: placed.length },
365384
365840
  "Found OrderPlaced events in block scan"
365385
365841
  );
365842
+ this.enter("publish-orders");
365386
365843
  await this.publishOrders(placed);
365387
365844
  }
365388
365845
  if (filled.length > 0) {
@@ -365390,10 +365847,36 @@ var ChainScanner = class {
365390
365847
  { chainId: this.target.chainId, fromBlock, toBlock, eventCount: filled.length },
365391
365848
  "Found OrderFilled events in block scan"
365392
365849
  );
365850
+ this.enter("publish-fills");
365393
365851
  await this.publishFills(filled);
365394
365852
  }
365395
365853
  if (this.stopped) return;
365396
365854
  this.cursor = toBlock;
365855
+ this.widen();
365856
+ }
365857
+ /**
365858
+ * Halves the span below the range that just failed, so the next pass asks
365859
+ * for something a range-capped endpoint will answer. Halving the attempted
365860
+ * range rather than the span matters near the head, where a pass reads fewer
365861
+ * blocks than the span allows.
365862
+ */
365863
+ narrow(attempted) {
365864
+ const next = attempted / 2n;
365865
+ if (next >= this.span) return;
365866
+ this.span = next;
365867
+ this.logger.warn(
365868
+ { chainId: this.target.chainId, span: Number(next) },
365869
+ "Narrowing the block scan range after a failed read"
365870
+ );
365871
+ }
365872
+ /** Doubles the span back toward {@link MAX_BLOCK_RANGE} after a good read. */
365873
+ widen() {
365874
+ if (this.span >= MAX_BLOCK_RANGE) return;
365875
+ const next = this.span === 0n ? 1n : this.span * 2n;
365876
+ this.span = next > MAX_BLOCK_RANGE ? MAX_BLOCK_RANGE : next;
365877
+ if (this.span === MAX_BLOCK_RANGE) {
365878
+ this.logger.info({ chainId: this.target.chainId }, "Block scan range back to full");
365879
+ }
365397
365880
  }
365398
365881
  async publishOrders(logs) {
365399
365882
  const rebuilt = reconstructOrdersFromLogs(logs, {
@@ -365762,7 +366245,7 @@ var LimitOrderController = class {
365762
366245
  ]);
365763
366246
  return { order, fills, bids };
365764
366247
  }
365765
- /** The pairs the orderbook lists, and the smallest payout each token may carry. */
366248
+ /** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
365766
366249
  books() {
365767
366250
  return this.service.books();
365768
366251
  }
@@ -365810,10 +366293,7 @@ var PairController = class {
365810
366293
  * the pair's symbols have to resolve before the market can be hydrated.
365811
366294
  */
365812
366295
  async add(pair, options) {
365813
- const { engine, tradingPairs, assetRegistry, adminStrategies, balanceTokens, config: config5 } = this.runtime;
365814
- if (!engine || !tradingPairs) {
365815
- throw new Error("No trading engine is running \u2014 this filler was started without pairs");
365816
- }
366296
+ const { engine, assetRegistry, adminStrategies, balanceTokens, config: config5 } = this.runtime;
365817
366297
  const assets = options?.assets;
365818
366298
  const nextAssets = { ...config5.assets ?? {}, ...assets ?? {} };
365819
366299
  const nextPairs = [...this.pairs, pair];
@@ -365852,7 +366332,6 @@ var PairController = class {
365852
366332
  /** Closes a market. Orders already in flight against it still complete. */
365853
366333
  async remove(index3) {
365854
366334
  const { engine, tradingPairs, adminStrategies, config: config5 } = this.runtime;
365855
- if (!engine || !tradingPairs) throw new Error("No trading engine is running");
365856
366335
  if (index3 < 0 || index3 >= this.pairs.length) throw new Error(`Unknown pair ${index3}`);
365857
366336
  engine.removePair(tradingPairs[index3]);
365858
366337
  config5.pairs = this.pairs.filter((_5, i6) => i6 !== index3);
@@ -365864,7 +366343,7 @@ var PairController = class {
365864
366343
  await this.persist();
365865
366344
  }
365866
366345
  livePair(index3) {
365867
- const pair = this.runtime.tradingPairs?.[index3];
366346
+ const pair = this.runtime.tradingPairs[index3];
365868
366347
  if (!pair) throw new Error(`Unknown pair ${index3}`);
365869
366348
  return pair;
365870
366349
  }
@@ -365956,9 +366435,9 @@ var ChainController = class {
365956
366435
  throw error;
365957
366436
  }
365958
366437
  const curve = confirmationPolicies[String(chainId)];
365959
- if (curve) this.runtime.confirmationPolicy?.add(chainId, curve);
365960
- else if (!this.runtime.confirmationPolicy?.has(chainId)) {
365961
- this.runtime.confirmationPolicy?.adopt(chainId, resolvedPolicy);
366438
+ if (curve) this.runtime.confirmationPolicy.add(chainId, curve);
366439
+ else if (!this.runtime.confirmationPolicy.has(chainId)) {
366440
+ this.runtime.confirmationPolicy.adopt(chainId, resolvedPolicy);
365962
366441
  }
365963
366442
  this.runtime.resolvedChains.push(resolved);
365964
366443
  config5.chains = [...config5.chains, { rpcUrls: chain2.rpcUrls, bundlerUrl: chain2.bundlerUrl }];
@@ -365986,7 +366465,7 @@ var ChainController = class {
365986
366465
  await intentFiller.removeChain(chainId);
365987
366466
  if (this.ownsScanner) await this.scanner.removeChain(chainId);
365988
366467
  configService.removeChain(chainId);
365989
- this.runtime.confirmationPolicy?.remove(chainId);
366468
+ this.runtime.confirmationPolicy.remove(chainId);
365990
366469
  this.runtime.chainClientManager.invalidate(chainKey);
365991
366470
  this.runtime.resolvedChains.splice(index3, 1);
365992
366471
  config5.chains = config5.chains.filter((_5, i6) => i6 !== index3);
@@ -367022,8 +367501,8 @@ var SqliteLimitOrderStore = class {
367022
367501
  async reserve(id2, amount) {
367023
367502
  const order = this.read(id2);
367024
367503
  if (!order || order.status !== "open") return false;
367504
+ if (BigInt(amount) > BigInt(order.remaining)) return false;
367025
367505
  const reserved = BigInt(order.reserved) + BigInt(amount);
367026
- if (reserved > BigInt(order.remaining)) return false;
367027
367506
  const result = this.db.prepare(`
367028
367507
  UPDATE limit_orders SET reserved = ?, updated_at = datetime('now')
367029
367508
  WHERE id = ? AND reserved = ? AND status = 'open'