@hyperbridge/simplex 0.16.11 → 0.16.13

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.
@@ -29007,17 +29007,17 @@ function cosh(x4) {
29007
29007
  }
29008
29008
  function clone(obj) {
29009
29009
  var i6, p3, ps;
29010
- function Decimal2(v7) {
29010
+ function Decimal3(v7) {
29011
29011
  var e4, i7, t6, x4 = this;
29012
- if (!(x4 instanceof Decimal2)) return new Decimal2(v7);
29013
- x4.constructor = Decimal2;
29012
+ if (!(x4 instanceof Decimal3)) return new Decimal3(v7);
29013
+ x4.constructor = Decimal3;
29014
29014
  if (isDecimalInstance(v7)) {
29015
29015
  x4.s = v7.s;
29016
29016
  if (external) {
29017
- if (!v7.d || v7.e > Decimal2.maxE) {
29017
+ if (!v7.d || v7.e > Decimal3.maxE) {
29018
29018
  x4.e = NaN;
29019
29019
  x4.d = null;
29020
- } else if (v7.e < Decimal2.minE) {
29020
+ } else if (v7.e < Decimal3.minE) {
29021
29021
  x4.e = 0;
29022
29022
  x4.d = [0];
29023
29023
  } else {
@@ -29047,10 +29047,10 @@ function clone(obj) {
29047
29047
  if (v7 === ~~v7 && v7 < 1e7) {
29048
29048
  for (e4 = 0, i7 = v7; i7 >= 10; i7 /= 10) e4++;
29049
29049
  if (external) {
29050
- if (e4 > Decimal2.maxE) {
29050
+ if (e4 > Decimal3.maxE) {
29051
29051
  x4.e = NaN;
29052
29052
  x4.d = null;
29053
- } else if (e4 < Decimal2.minE) {
29053
+ } else if (e4 < Decimal3.minE) {
29054
29054
  x4.e = 0;
29055
29055
  x4.d = [0];
29056
29056
  } else {
@@ -29092,58 +29092,58 @@ function clone(obj) {
29092
29092
  }
29093
29093
  throw Error(invalidArgument + v7);
29094
29094
  }
29095
- Decimal2.prototype = P;
29096
- Decimal2.ROUND_UP = 0;
29097
- Decimal2.ROUND_DOWN = 1;
29098
- Decimal2.ROUND_CEIL = 2;
29099
- Decimal2.ROUND_FLOOR = 3;
29100
- Decimal2.ROUND_HALF_UP = 4;
29101
- Decimal2.ROUND_HALF_DOWN = 5;
29102
- Decimal2.ROUND_HALF_EVEN = 6;
29103
- Decimal2.ROUND_HALF_CEIL = 7;
29104
- Decimal2.ROUND_HALF_FLOOR = 8;
29105
- Decimal2.EUCLID = 9;
29106
- Decimal2.config = Decimal2.set = config;
29107
- Decimal2.clone = clone;
29108
- Decimal2.isDecimal = isDecimalInstance;
29109
- Decimal2.abs = abs;
29110
- Decimal2.acos = acos;
29111
- Decimal2.acosh = acosh;
29112
- Decimal2.add = add2;
29113
- Decimal2.asin = asin;
29114
- Decimal2.asinh = asinh;
29115
- Decimal2.atan = atan;
29116
- Decimal2.atanh = atanh;
29117
- Decimal2.atan2 = atan2;
29118
- Decimal2.cbrt = cbrt;
29119
- Decimal2.ceil = ceil;
29120
- Decimal2.clamp = clamp;
29121
- Decimal2.cos = cos;
29122
- Decimal2.cosh = cosh;
29123
- Decimal2.div = div;
29124
- Decimal2.exp = exp;
29125
- Decimal2.floor = floor;
29126
- Decimal2.hypot = hypot;
29127
- Decimal2.ln = ln;
29128
- Decimal2.log = log;
29129
- Decimal2.log10 = log10;
29130
- Decimal2.log2 = log2;
29131
- Decimal2.max = max;
29132
- Decimal2.min = min;
29133
- Decimal2.mod = mod3;
29134
- Decimal2.mul = mul;
29135
- Decimal2.pow = pow;
29136
- Decimal2.random = random;
29137
- Decimal2.round = round;
29138
- Decimal2.sign = sign;
29139
- Decimal2.sin = sin;
29140
- Decimal2.sinh = sinh;
29141
- Decimal2.sqrt = sqrt;
29142
- Decimal2.sub = sub;
29143
- Decimal2.sum = sum;
29144
- Decimal2.tan = tan;
29145
- Decimal2.tanh = tanh;
29146
- Decimal2.trunc = trunc;
29095
+ Decimal3.prototype = P;
29096
+ Decimal3.ROUND_UP = 0;
29097
+ Decimal3.ROUND_DOWN = 1;
29098
+ Decimal3.ROUND_CEIL = 2;
29099
+ Decimal3.ROUND_FLOOR = 3;
29100
+ Decimal3.ROUND_HALF_UP = 4;
29101
+ Decimal3.ROUND_HALF_DOWN = 5;
29102
+ Decimal3.ROUND_HALF_EVEN = 6;
29103
+ Decimal3.ROUND_HALF_CEIL = 7;
29104
+ Decimal3.ROUND_HALF_FLOOR = 8;
29105
+ Decimal3.EUCLID = 9;
29106
+ Decimal3.config = Decimal3.set = config;
29107
+ Decimal3.clone = clone;
29108
+ Decimal3.isDecimal = isDecimalInstance;
29109
+ Decimal3.abs = abs;
29110
+ Decimal3.acos = acos;
29111
+ Decimal3.acosh = acosh;
29112
+ Decimal3.add = add2;
29113
+ Decimal3.asin = asin;
29114
+ Decimal3.asinh = asinh;
29115
+ Decimal3.atan = atan;
29116
+ Decimal3.atanh = atanh;
29117
+ Decimal3.atan2 = atan2;
29118
+ Decimal3.cbrt = cbrt;
29119
+ Decimal3.ceil = ceil;
29120
+ Decimal3.clamp = clamp;
29121
+ Decimal3.cos = cos;
29122
+ Decimal3.cosh = cosh;
29123
+ Decimal3.div = div;
29124
+ Decimal3.exp = exp;
29125
+ Decimal3.floor = floor;
29126
+ Decimal3.hypot = hypot;
29127
+ Decimal3.ln = ln;
29128
+ Decimal3.log = log;
29129
+ Decimal3.log10 = log10;
29130
+ Decimal3.log2 = log2;
29131
+ Decimal3.max = max;
29132
+ Decimal3.min = min;
29133
+ Decimal3.mod = mod3;
29134
+ Decimal3.mul = mul;
29135
+ Decimal3.pow = pow;
29136
+ Decimal3.random = random;
29137
+ Decimal3.round = round;
29138
+ Decimal3.sign = sign;
29139
+ Decimal3.sin = sin;
29140
+ Decimal3.sinh = sinh;
29141
+ Decimal3.sqrt = sqrt;
29142
+ Decimal3.sub = sub;
29143
+ Decimal3.sum = sum;
29144
+ Decimal3.tan = tan;
29145
+ Decimal3.tanh = tanh;
29146
+ Decimal3.trunc = trunc;
29147
29147
  if (obj === void 0) obj = {};
29148
29148
  if (obj) {
29149
29149
  if (obj.defaults !== true) {
@@ -29151,8 +29151,8 @@ function clone(obj) {
29151
29151
  for (i6 = 0; i6 < ps.length; ) if (!obj.hasOwnProperty(p3 = ps[i6++])) obj[p3] = this[p3];
29152
29152
  }
29153
29153
  }
29154
- Decimal2.config(obj);
29155
- return Decimal2;
29154
+ Decimal3.config(obj);
29155
+ return Decimal3;
29156
29156
  }
29157
29157
  function div(x4, y5) {
29158
29158
  return new this(x4).div(y5);
@@ -48802,6 +48802,7 @@ var init_esm2 = __esm({
48802
48802
  init_encodeFunctionData2();
48803
48803
  init_encodePacked2();
48804
48804
  init_parseEventLogs2();
48805
+ init_isAddress2();
48805
48806
  init_defineChain2();
48806
48807
  init_concat2();
48807
48808
  init_isHex2();
@@ -51068,7 +51069,7 @@ var require_bn = __commonJS({
51068
51069
  }
51069
51070
  return this;
51070
51071
  };
51071
- BN3.prototype.add = function add3(num2) {
51072
+ BN3.prototype.add = function add4(num2) {
51072
51073
  var res;
51073
51074
  if (num2.negative !== 0 && this.negative === 0) {
51074
51075
  num2.negative = 0;
@@ -52905,7 +52906,7 @@ var require_bn = __commonJS({
52905
52906
  }
52906
52907
  return this.m.sub(a6)._forceRed(this);
52907
52908
  };
52908
- Red.prototype.add = function add3(a6, b5) {
52909
+ Red.prototype.add = function add4(a6, b5) {
52909
52910
  this._verify2(a6, b5);
52910
52911
  var res = a6.add(b5);
52911
52912
  if (res.cmp(this.m) >= 0) {
@@ -55785,10 +55786,10 @@ var init_fflate = __esm({
55785
55786
  lm = void 0;
55786
55787
  break;
55787
55788
  } else {
55788
- let add3 = sym - 254;
55789
+ let add4 = sym - 254;
55789
55790
  if (sym > 264) {
55790
55791
  const i6 = sym - 257, b5 = fleb[i6];
55791
- add3 = bits(dat, pos, (1 << b5) - 1) + fl[i6];
55792
+ add4 = bits(dat, pos, (1 << b5) - 1) + fl[i6];
55792
55793
  pos += b5;
55793
55794
  }
55794
55795
  const d5 = dm[bits16(dat, pos) & dms], dsym = d5 >>> 4;
@@ -55804,7 +55805,7 @@ var init_fflate = __esm({
55804
55805
  throw "unexpected EOF";
55805
55806
  if (noBuf)
55806
55807
  cbuf(bt + 131072);
55807
- const end = bt + add3;
55808
+ const end = bt + add4;
55808
55809
  for (; bt < end; bt += 4) {
55809
55810
  buf[bt] = buf[bt - dt];
55810
55811
  buf[bt + 1] = buf[bt + 1 - dt];
@@ -83508,15 +83509,15 @@ var require_fromEvent = __commonJS({
83508
83509
  return function(handler) {
83509
83510
  return target[methodName](eventName, handler, options);
83510
83511
  };
83511
- }) : isNodeStyleEventEmitter(target) ? nodeEventEmitterMethods.map(toCommonHandlerRegistry(target, eventName)) : isJQueryStyleEventEmitter(target) ? jqueryMethods.map(toCommonHandlerRegistry(target, eventName)) : [], 2), add3 = _a10[0], remove = _a10[1];
83512
- if (!add3) {
83512
+ }) : isNodeStyleEventEmitter(target) ? nodeEventEmitterMethods.map(toCommonHandlerRegistry(target, eventName)) : isJQueryStyleEventEmitter(target) ? jqueryMethods.map(toCommonHandlerRegistry(target, eventName)) : [], 2), add4 = _a10[0], remove = _a10[1];
83513
+ if (!add4) {
83513
83514
  if (isArrayLike_1.isArrayLike(target)) {
83514
83515
  return mergeMap_1.mergeMap(function(subTarget) {
83515
83516
  return fromEvent(subTarget, eventName, options);
83516
83517
  })(innerFrom_1.innerFrom(target));
83517
83518
  }
83518
83519
  }
83519
- if (!add3) {
83520
+ if (!add4) {
83520
83521
  throw new TypeError("Invalid event target");
83521
83522
  }
83522
83523
  return new Observable_1.Observable(function(subscriber) {
@@ -83527,7 +83528,7 @@ var require_fromEvent = __commonJS({
83527
83528
  }
83528
83529
  return subscriber.next(1 < args.length ? args : args[0]);
83529
83530
  };
83530
- add3(handler);
83531
+ add4(handler);
83531
83532
  return function() {
83532
83533
  return remove(handler);
83533
83534
  };
@@ -233929,8 +233930,8 @@ function addToExternrefTable0(obj) {
233929
233930
  function passArrayJsValueToWasm0(array, malloc) {
233930
233931
  const ptr = malloc(array.length * 4, 4) >>> 0;
233931
233932
  for (let i6 = 0; i6 < array.length; i6++) {
233932
- const add3 = addToExternrefTable0(array[i6]);
233933
- getDataViewMemory0().setUint32(ptr + 4 * i6, add3, true);
233933
+ const add4 = addToExternrefTable0(array[i6]);
233934
+ getDataViewMemory0().setUint32(ptr + 4 * i6, add4, true);
233934
233935
  }
233935
233936
  WASM_VECTOR_LEN = array.length;
233936
233937
  return ptr;
@@ -235360,7 +235361,7 @@ function resolveOrderFeeGasPriceBump(sourceStateMachineId, isSameChain) {
235360
235361
  if (isSameChain) {
235361
235362
  return 0n;
235362
235363
  }
235363
- return ORDER_FEE_GAS_PRICE_BUMP_POLICY.bySourceStateMachineId[sourceStateMachineId] ?? ORDER_FEE_GAS_PRICE_BUMP_POLICY.defaultPercent;
235364
+ return sourceStateMachineId === "EVM-1" ? ETHEREUM_ORDER_FEE_GAS_PRICE_BUMP_PERCENT : DEFAULT_ORDER_FEE_GAS_PRICE_BUMP_PERCENT;
235364
235365
  }
235365
235366
  function utf8Encode(text2) {
235366
235367
  const bytes5 = [];
@@ -235421,7 +235422,7 @@ function encodePhantomBidDeclaration(declaration) {
235421
235422
  }
235422
235423
  return u8aToHex(new Uint8Array(bytes5));
235423
235424
  }
235424
- var import_merge_race, __defProp3, __getOwnPropNames2, __esm2, __export2, node_exports, wasm, full_path, __dirname$1, cachedTextDecoder, cachedUint8ArrayMemory0, WASM_VECTOR_LEN, cachedTextEncoder, encodeString5, cachedDataViewMemory0, KeccakMergeFinalization, KeccakMerge, wasmPath, wasmBytes2, bindings, wasmModule, wasmInstance, __wasm, init_node11, RequestStatus, TimeoutStatus, IntentOrderStatus, ABI, evmHost_default, ABI2, handlerV2_default, polkadotAssetHubPaseo, polkadotHubMainnet, pharosMainnet, pharosAtlantic, tronNile, chainConfigs, configsByStateMachineId, getConfigByStateMachineId, getChainId3, getViemChain, hyperbridgeAddress, ABI3, IntentGatewayV2_default, ChainConfigService, ABI4, uniswapRouterV2_default, chains, DEFAULT_ADDRESS, EvmChain, REQUEST_COMMITMENTS_SLOT, RESPONSE_COMMITMENTS_SLOT, REQUEST_RECEIPTS_SLOT, STATE_COMMITMENTS_SLOT, MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, MissingConsensusUpdateTimeError, TokenBucket, BATCHES_NOT_SUPPORTED_CODE, TOO_BIG_BATCH_REQUEST_CODE, DEFAULT_MAX_BATCH_SIZE, _endpoint2, _headers3, _limiter, _maxBatchSize, _batchingSupported, _pending2, _flushTimer, _flushing, _nextId, __BatchingHttpProvider_instances, scheduleFlush_fn, flushNow_fn, flush_fn, post_fn, settleBatch_fn, settle_fn, handleBatchRefusal_fn, requeue_fn, _a5, BatchingHttpProvider, OFFCHAIN_BID_PREFIX, HYPERBRIDGE_TYPES_BUNDLE, BASE_TIP, HTTP_CONNECT_TIMEOUT_MS, INCLUSION_TIMEOUT_MS, DEFAULT_RPC_MAX_RPS, rpcLimiters, BidCodec, PackedUserOperationCodec, IntentsCoprocessor, ReviveContractInfo, ReviveAccountType, ReviveAccountInfo, H256, EvmStateProof, SubstrateHashing, SubstrateStateMachineProof, BasicProof, LeafIndexAndPos, MmrProof, ConsensusStateId, ConsensusMessage, FraudProofMessage, StateMachine, StateMachineId, StateMachineHeight, Proof, PostRequest, PostResponse, GetRequest, StorageValue, GetResponse, Request3, Response2, RequestMessage, RequestResponse, ResponseMessage, TimeoutMessage, GetRequestsWithProof, Message, DEFAULT_CHILD_STORAGE_PREFIX, PharosProofNodeCodec, SiblingLeftmostLeafProofCodec, NonExistenceProofCodec, AccountProofDataCodec, PharosStateProof, DEFAULT_POLL_INTERVAL, ADDRESS_ZERO2, DEFAULT_GRAFFITI, DEFAULT_LOGGER, REQUEST_STATUS_WEIGHTS, TIMEOUT_STATUS_WEIGHTS, COMBINED_STATUS_WEIGHTS, CHAIN_ID_TO_COINGECKO_NATIVE, TESTNET_CHAINS, ORDER_STATUS, loadDriver, detectEnvironment, STORAGE_KEYS, LEGACY_STORAGE_KEYS, ABI5, uniswapV3Quoter_default, UNISWAP_V4_QUOTER_ABI, ABI6, universalRouter_default, PERMIT2_ABI, UniswapQuoteEngine, COMMON_FEE_TIERS, Swap, ABI7, erc7281_default, DEFAULT_GRAFFITI2, ERC7821_BATCH_MODE, BundlerMethod, SELECT_SOLVER_TYPEHASH, PACKED_USEROP_TYPEHASH, DOMAIN_TYPEHASH, CryptoUtils, FEE_TOKEN_CACHE_TTL_MS, OrderPlacer, TOKEN_KEYED_GATEWAY_ABI, keyingByClient, PARTIAL_FILLS_SLOT, USED_USEROPS_STORAGE_KEY, OrderExecutor, _a6, OrderCanceller, FILL_ORDER_SELECTOR, SUPPORTED_INTENTS_VERSION, CONTRACT_VERSION_ABI, CANONICAL_EVM_TOKEN, ENTRY_POINT_EVENT_ABI, BidImpl, BidManager, RELAYER_MESSAGE_GAS, NO_BUNDLER_FILL_GAS_BASE, NO_BUNDLER_FILL_GAS_PER_OUTPUT, NO_BUNDLER_PAYMASTER_VERIFICATION_GAS, NO_BUNDLER_PAYMASTER_POST_OP_GAS, BID_PVG_ESTIMATION_ACCOUNT_CODE, SELECTED_BID_SIGNATURE_BYTES, BID_PVG_HEADROOM_PERCENT, ESCROW_MAPPING_SLOT, GasEstimator, OrderStatusChecker, ORDERBOOK_URLS, ORDERBOOK_DECIMALS, ORDERBOOK_SCALE, OrderbookRequestError, RATE_FIELDS, TOP_OF_BOOK_QUERY, ROUTE_LIQUIDITY_QUERY, QUOTE_QUERY, QUOTE_PESSIMISTIC_QUERY, HyperFxOrderbook, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, OrderbookQuoteNotConvergedError, MAX_EXACT_OUTPUT_ROUNDS, OrderbookMarket, ORDER_FEE_GAS_PRICE_BUMP_POLICY, IntentGateway, DECLARATION_V1, DECLARATION_V2, PAYMASTER_ADDRESS_BYTES, PAYMASTER_GAS_LIMIT_BYTES, PAYMASTER_DATA_OFFSET, PERMIT2_DATA_BYTES, PERMIT2_SPONSORSHIP_BYTES, MAX_DECLARED_ENTRIES, MAX_TOKEN_ID_BYTES, TeleportParams, MultiAccount;
235425
+ var import_merge_race, __defProp3, __getOwnPropNames2, __esm2, __export2, node_exports, wasm, full_path, __dirname$1, cachedTextDecoder, cachedUint8ArrayMemory0, WASM_VECTOR_LEN, cachedTextEncoder, encodeString5, cachedDataViewMemory0, KeccakMergeFinalization, KeccakMerge, wasmPath, wasmBytes2, bindings, wasmModule, wasmInstance, __wasm, init_node11, RequestStatus, TimeoutStatus, IntentOrderStatus, ABI, evmHost_default, ABI2, handlerV2_default, polkadotAssetHubPaseo, polkadotHubMainnet, pharosMainnet, pharosAtlantic, tronNile, chainConfigs, configsByStateMachineId, getConfigByStateMachineId, getChainId3, getViemChain, hyperbridgeAddress, ABI3, IntentGatewayV2_default, ChainConfigService, ABI4, uniswapRouterV2_default, chains, DEFAULT_ADDRESS, EvmChain, REQUEST_COMMITMENTS_SLOT, RESPONSE_COMMITMENTS_SLOT, REQUEST_RECEIPTS_SLOT, STATE_COMMITMENTS_SLOT, MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, MissingConsensusUpdateTimeError, TokenBucket, BATCHES_NOT_SUPPORTED_CODE, TOO_BIG_BATCH_REQUEST_CODE, DEFAULT_MAX_BATCH_SIZE, _endpoint2, _headers3, _limiter, _maxBatchSize, _batchingSupported, _pending2, _flushTimer, _flushing, _nextId, __BatchingHttpProvider_instances, scheduleFlush_fn, flushNow_fn, flush_fn, post_fn, settleBatch_fn, settle_fn, handleBatchRefusal_fn, requeue_fn, _a5, BatchingHttpProvider, OFFCHAIN_BID_PREFIX, HYPERBRIDGE_TYPES_BUNDLE, BASE_TIP, HTTP_CONNECT_TIMEOUT_MS, INCLUSION_TIMEOUT_MS, DEFAULT_RPC_MAX_RPS, rpcLimiters, BidCodec, PackedUserOperationCodec, IntentsCoprocessor, ReviveContractInfo, ReviveAccountType, ReviveAccountInfo, H256, EvmStateProof, SubstrateHashing, SubstrateStateMachineProof, BasicProof, LeafIndexAndPos, MmrProof, ConsensusStateId, ConsensusMessage, FraudProofMessage, StateMachine, StateMachineId, StateMachineHeight, Proof, PostRequest, PostResponse, GetRequest, StorageValue, GetResponse, Request3, Response2, RequestMessage, RequestResponse, ResponseMessage, TimeoutMessage, GetRequestsWithProof, Message, DEFAULT_CHILD_STORAGE_PREFIX, PharosProofNodeCodec, SiblingLeftmostLeafProofCodec, NonExistenceProofCodec, AccountProofDataCodec, PharosStateProof, DEFAULT_POLL_INTERVAL, ADDRESS_ZERO2, DEFAULT_GRAFFITI, DEFAULT_LOGGER, REQUEST_STATUS_WEIGHTS, TIMEOUT_STATUS_WEIGHTS, COMBINED_STATUS_WEIGHTS, CHAIN_ID_TO_COINGECKO_NATIVE, TESTNET_CHAINS, ORDER_STATUS, loadDriver, detectEnvironment, STORAGE_KEYS, LEGACY_STORAGE_KEYS, ABI5, uniswapV3Quoter_default, UNISWAP_V4_QUOTER_ABI, ABI6, universalRouter_default, PERMIT2_ABI, UniswapQuoteEngine, COMMON_FEE_TIERS, Swap, ABI7, erc7281_default, DEFAULT_GRAFFITI2, ERC7821_BATCH_MODE, BundlerMethod, SELECT_SOLVER_TYPEHASH, PACKED_USEROP_TYPEHASH, DOMAIN_TYPEHASH, CryptoUtils, FEE_TOKEN_CACHE_TTL_MS, OrderPlacer, TOKEN_KEYED_GATEWAY_ABI, keyingByClient, PARTIAL_FILLS_SLOT, USED_USEROPS_STORAGE_KEY, OrderExecutor, _a6, OrderCanceller, FILL_ORDER_SELECTOR, SUPPORTED_INTENTS_VERSION, CONTRACT_VERSION_ABI, CANONICAL_EVM_TOKEN, ENTRY_POINT_EVENT_ABI, BidImpl, BidManager, RELAYER_MESSAGE_GAS, NO_BUNDLER_FILL_GAS_BASE, NO_BUNDLER_FILL_GAS_PER_OUTPUT, NO_BUNDLER_PAYMASTER_VERIFICATION_GAS, NO_BUNDLER_PAYMASTER_POST_OP_GAS, BID_PVG_ESTIMATION_ACCOUNT_CODE, SELECTED_BID_SIGNATURE_BYTES, BID_PVG_HEADROOM_PERCENT, ESCROW_MAPPING_SLOT, GasEstimator, OrderStatusChecker, ORDERBOOK_URLS, ORDERBOOK_DECIMALS, ORDERBOOK_SCALE, OrderbookRequestError, RATE_FIELDS, TOP_OF_BOOK_QUERY, ROUTE_LIQUIDITY_QUERY, QUOTE_QUERY, QUOTE_PESSIMISTIC_QUERY, HyperFxOrderbook, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, OrderbookQuoteNotConvergedError, MAX_EXACT_OUTPUT_ROUNDS, OrderbookMarket, ETHEREUM_ORDER_FEE_GAS_PRICE_BUMP_PERCENT, DEFAULT_ORDER_FEE_GAS_PRICE_BUMP_PERCENT, IntentGateway, DECLARATION_V1, DECLARATION_V2, PAYMASTER_ADDRESS_BYTES, PAYMASTER_GAS_LIMIT_BYTES, PAYMASTER_DATA_OFFSET, PERMIT2_DATA_BYTES, PERMIT2_SPONSORSHIP_BYTES, MAX_DECLARED_ENTRIES, MAX_TOKEN_ID_BYTES, TeleportParams, MultiAccount;
235425
235426
  var init_node10 = __esm({
235426
235427
  "../sdk/dist/node/index.js"() {
235427
235428
  "use strict";
@@ -247567,9 +247568,17 @@ query OrderStatus($commitment: String!) {
247567
247568
  * order. Frontends can use `relayerFee` to approve the ERC-20 spend before
247568
247569
  * submitting the cancel transaction.
247569
247570
  *
247571
+ * A cross-chain cancel is paid on the chain that receives the cancel
247572
+ * transaction: the source chain for the source route, the destination chain
247573
+ * for the destination route. When that chain is a testnet, the quote has no
247574
+ * native value, since testnet hosts have no router to swap it into the fee
247575
+ * token. The caller must first approve `relayerFee` of that host's fee token
247576
+ * to that chain's IntentGateway.
247577
+ *
247570
247578
  * @param order - The order to quote.
247571
247579
  * @param options - Choose the initiation side. Defaults to source-side cancellation.
247572
- * @returns `{ nativeValue }` — native token amount (wei) to send as `value`;
247580
+ * @returns `{ nativeValue }` — native token amount (wei) to send as `value`,
247581
+ * or `0n` when the relayer fee must be paid in the fee token;
247573
247582
  * `{ relayerFee }` — relayer incentive denominated in the chain's fee token.
247574
247583
  */
247575
247584
  async quoteCancelOrder(order, options = {}) {
@@ -247583,11 +247592,12 @@ query OrderStatus($commitment: String!) {
247583
247592
  *
247584
247593
  * Constructs the ISMP GET request the source gateway will dispatch, one
247585
247594
  * `_partialFills` key per leg, and calls `quoteNative` on the source host to
247586
- * obtain the dispatch fee.
247595
+ * obtain the dispatch fee. Returns a zero `nativeValue` when the source is a
247596
+ * testnet chain, leaving the relayer fee to be paid in its fee token.
247587
247597
  * Returns 0 for same-chain orders (no ISMP call needed).
247588
247598
  *
247589
247599
  * @param order - The order to quote.
247590
- * @returns The native token dispatch fee in wei.
247600
+ * @returns The native token dispatch fee in wei and the relayer fee.
247591
247601
  */
247592
247602
  async quoteCancelFromSource(order) {
247593
247603
  const sourceStateMachine = normalizeStateMachineId(order.source);
@@ -247595,6 +247605,14 @@ query OrderStatus($commitment: String!) {
247595
247605
  if (sourceStateMachine === destStateMachine) {
247596
247606
  return { nativeValue: 0n, relayerFee: 0n };
247597
247607
  }
247608
+ const feeInSourceFeeToken = await convertGasToFeeToken(
247609
+ this.ctx,
247610
+ _a6.SOURCE_GET_RESPONSE_GAS,
247611
+ "source",
247612
+ sourceStateMachine
247613
+ );
247614
+ const relayerFee = feeInSourceFeeToken * 1005n / 1000n;
247615
+ if (TESTNET_CHAINS.has(sourceStateMachine)) return { nativeValue: 0n, relayerFee };
247598
247616
  const height = order.deadline + 1n;
247599
247617
  const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
247600
247618
  const commitment = this.orderId(order);
@@ -247603,20 +247621,13 @@ query OrderStatus($commitment: String!) {
247603
247621
  const getRequest = {
247604
247622
  source: sourceStateMachine,
247605
247623
  dest: destStateMachine,
247606
- from: this.ctx.source.configService.getIntentGatewayAddress(destStateMachine),
247624
+ from: this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachine),
247607
247625
  nonce: await this.ctx.source.getHostNonce(),
247608
247626
  height,
247609
247627
  keys: keys3,
247610
247628
  timeoutTimestamp: 0n,
247611
247629
  context
247612
247630
  };
247613
- const feeInSourceFeeToken = await convertGasToFeeToken(
247614
- this.ctx,
247615
- _a6.SOURCE_GET_RESPONSE_GAS,
247616
- "source",
247617
- sourceStateMachine
247618
- );
247619
- const relayerFee = feeInSourceFeeToken * 1005n / 1000n;
247620
247631
  const nativeValue = await this.ctx.source.quoteNative(getRequest, relayerFee) * 101n / 100n;
247621
247632
  return { nativeValue, relayerFee };
247622
247633
  }
@@ -247637,7 +247648,10 @@ query OrderStatus($commitment: String!) {
247637
247648
  * cancellation. Same-chain orders always use the direct local route.
247638
247649
  * @yields {@link CancelEvent} objects describing each stage of the
247639
247650
  * cancellation lifecycle. The caller signs or broadcasts the yielded
247640
- * transaction, so its signer may differ from `order.user` after expiry.
247651
+ * transaction, and the gateway decides who may sign it. A same-chain cancel
247652
+ * or a cross-chain cancel from the destination accepts only `order.user`
247653
+ * through the deadline and any signer strictly after it. A cross-chain
247654
+ * cancel from the source accepts only `order.user`, before or after expiry.
247641
247655
  */
247642
247656
  async *cancelOrder(order, indexerClient, options = {}) {
247643
247657
  const sourceStateMachine = normalizeStateMachineId(order.source);
@@ -247857,11 +247871,13 @@ query OrderStatus($commitment: String!) {
247857
247871
  *
247858
247872
  * Estimates the relayer fee for delivering the refund POST request from the
247859
247873
  * destination chain back to the source chain, converts it to the destination
247860
- * fee token, and calls `quoteNative` on the destination host.
247874
+ * fee token, and calls `quoteNative` on the destination host. Returns a zero
247875
+ * `nativeValue` when the destination is a testnet chain, leaving the relayer
247876
+ * fee to be paid in its fee token.
247861
247877
  * Returns 0 for same-chain orders.
247862
247878
  *
247863
247879
  * @param order - The order to quote.
247864
- * @returns The native token dispatch fee in wei.
247880
+ * @returns The native token dispatch fee in wei and the relayer fee.
247865
247881
  */
247866
247882
  async quoteCancelFromDest(order) {
247867
247883
  const sourceStateMachine = normalizeStateMachineId(order.source);
@@ -247872,6 +247888,7 @@ query OrderStatus($commitment: String!) {
247872
247888
  const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
247873
247889
  const sourceIntentGateway = this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachine);
247874
247890
  const relayerFee = await this.estimateRelayerFee(sourceStateMachine, destStateMachine);
247891
+ if (TESTNET_CHAINS.has(destStateMachine)) return { nativeValue: 0n, relayerFee };
247875
247892
  const body = constructRefundEscrowRequestBody(order, order.user);
247876
247893
  const postRequest = {
247877
247894
  source: destStateMachine,
@@ -248855,6 +248872,8 @@ query OrderStatus($commitment: String!) {
248855
248872
  constructor(ctx, crypto10) {
248856
248873
  __publicField(this, "ctx");
248857
248874
  __publicField(this, "crypto");
248875
+ /** Call dispatchers read from a gateway, for chains whose config carries none. */
248876
+ __publicField(this, "dispatchers", /* @__PURE__ */ new Map());
248858
248877
  this.ctx = ctx;
248859
248878
  this.crypto = crypto10;
248860
248879
  }
@@ -248876,7 +248895,9 @@ query OrderStatus($commitment: String!) {
248876
248895
  * ephemeral keypair, applies state overrides, and calls
248877
248896
  * `eth_estimateUserOperationGas`. Gas limits are bumped by 5-10% for
248878
248897
  * headroom. If the bundler is Pimlico, gas prices are refined with
248879
- * `pimlico_getUserOperationGasPrice`.
248898
+ * `pimlico_getUserOperationGasPrice`. If the bundler rejects the estimate,
248899
+ * fixed gas limits are returned in its place, or the call throws when
248900
+ * `params.requireBundlerEstimate` is set.
248880
248901
  *
248881
248902
  * **Fallback path (no bundler):** uses a fixed budget
248882
248903
  * ({@link NO_BUNDLER_FILL_GAS_BASE} plus {@link NO_BUNDLER_FILL_GAS_PER_OUTPUT}
@@ -248890,6 +248911,8 @@ query OrderStatus($commitment: String!) {
248890
248911
  * @returns A {@link FillOrderEstimate} containing all gas components,
248891
248912
  * EIP-1559 fee values, total cost in wei, and total cost in the source
248892
248913
  * chain's fee token.
248914
+ * @throws If `params.requireBundlerEstimate` is set and the bundler fails to
248915
+ * estimate the fill.
248893
248916
  */
248894
248917
  async estimateFillOrder(params, pricingOptions = {}) {
248895
248918
  const { order } = params;
@@ -249087,6 +249110,11 @@ query OrderStatus($commitment: String!) {
249087
249110
  maxFeePerGas = bufferedBaseFee + maxPriorityFeePerGas;
249088
249111
  }
249089
249112
  } catch (e4) {
249113
+ if (params.requireBundlerEstimate) {
249114
+ throw new Error(`Bundler gas estimation failed: ${e4 instanceof Error ? e4.message : String(e4)}`, {
249115
+ cause: e4
249116
+ });
249117
+ }
249090
249118
  console.warn("Bundler gas estimation failed, using fallback values:", e4);
249091
249119
  }
249092
249120
  } else {
@@ -249121,6 +249149,27 @@ query OrderStatus($commitment: String!) {
249121
249149
  inputs
249122
249150
  };
249123
249151
  }
249152
+ /**
249153
+ * The call dispatcher the gateway on `chain` uses: the configured address, or the one
249154
+ * the gateway reports when the chain's config carries none.
249155
+ *
249156
+ * {@link buildStateOverride} writes it back into the gateway's params slot. Without an
249157
+ * address that value is 12 bytes instead of 32, and the bundler rejects the whole
249158
+ * estimate as `Invalid params`.
249159
+ */
249160
+ async callDispatcher(chain2, gateway) {
249161
+ const configured = this.ctx.dest.configService.getCalldispatcherAddress(chain2);
249162
+ if (isAddress2(configured)) return configured;
249163
+ const known3 = this.dispatchers.get(chain2);
249164
+ if (known3) return known3;
249165
+ const params = await this.ctx.dest.client.readContract({
249166
+ abi: ABI3,
249167
+ address: gateway,
249168
+ functionName: "params"
249169
+ });
249170
+ this.dispatchers.set(chain2, params.dispatcher);
249171
+ return params.dispatcher;
249172
+ }
249124
249173
  /**
249125
249174
  * Asks the bundler for the `preVerificationGas` of the bid UserOperation that will
249126
249175
  * actually be signed, with every funding call, approval and signature byte in it.
@@ -249234,7 +249283,7 @@ query OrderStatus($commitment: String!) {
249234
249283
  const bundlerOverrides = {};
249235
249284
  if (intentGatewayV2Address) {
249236
249285
  const paramsSlot5 = pad4(toHex3(5n), { size: 32 });
249237
- const dispatcherAddress = this.ctx.dest.configService.getCalldispatcherAddress(chain2);
249286
+ const dispatcherAddress = await this.callDispatcher(chain2, intentGatewayV2Address);
249238
249287
  const newSlot5Value = "0x" + "0".repeat(22) + "00" + dispatcherAddress.slice(2).toLowerCase();
249239
249288
  const gatewayDiffs = [{ slot: paramsSlot5, value: newSlot5Value }];
249240
249289
  for (const [leg, input] of (escrow?.inputs ?? []).entries()) {
@@ -249775,15 +249824,8 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
249775
249824
  return withDecimals(resolvedChain, symbol3, this.configService.getAssetMetadataBySymbol(resolvedChain, symbol3));
249776
249825
  }
249777
249826
  };
249778
- ORDER_FEE_GAS_PRICE_BUMP_POLICY = {
249779
- defaultPercent: 10n,
249780
- bySourceStateMachineId: {
249781
- [
249782
- "EVM-1"
249783
- /* MAINNET */
249784
- ]: 50n
249785
- }
249786
- };
249827
+ ETHEREUM_ORDER_FEE_GAS_PRICE_BUMP_PERCENT = 50n;
249828
+ DEFAULT_ORDER_FEE_GAS_PRICE_BUMP_PERCENT = 100n;
249787
249829
  IntentGateway = class _IntentGateway {
249788
249830
  /**
249789
249831
  * Private constructor — use {@link IntentGateway.create} instead.
@@ -249964,9 +250006,9 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
249964
250006
  * 1. If `order.fees` is unset or zero, prices the fee on an internal copy
249965
250007
  * via {@link quoteOrderFees}: same-chain fees are twice the fill-gas
249966
250008
  * estimate without a gas-price bump; cross-chain order fees originating on
249967
- * Ethereum price gas 50% above the live price, while other source chains use
249968
- * 10%, before attaching (fill gas + the settlement relayer fee)
249969
- * with a further 5% buffer over the whole sum — strictly above the solver's
250009
+ * Ethereum mainnet price gas 50% above the live price, while other source
250010
+ * chains use 100%, before attaching (fill gas + the settlement relayer
250011
+ * fee) with a further 5% buffer over the whole sum — strictly above the solver's
249970
250012
  * unpadded requirement. Direct solver estimates remain unbumped. The wei
249971
250013
  * cost used for the `value` field receives a 2% buffer.
249972
250014
  * 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, nativeFee,
@@ -250206,6 +250248,8 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
250206
250248
  * order. Use `relayerFee` to approve the ERC-20 spend before submitting.
250207
250249
  * Same-chain orders use the direct local route for either `from` option and
250208
250250
  * return `{ nativeValue: 0n, relayerFee: 0n }`.
250251
+ * When the chain receiving the cancel transaction is a testnet, `nativeValue` is `0n` and the caller
250252
+ * approves `relayerFee` of that host's fee token to that chain's gateway.
250209
250253
  *
250210
250254
  * Delegates to {@link OrderCanceller.quoteCancelOrder}.
250211
250255
  *
@@ -250356,7 +250400,7 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
250356
250400
  *
250357
250401
  * @param order - The order to quote. `order.fees` is ignored and not mutated.
250358
250402
  * Gas prices used to derive cross-chain `fees` receive 50% SDK-only headroom
250359
- * when the source chain is Ethereum mainnet and 10% for other source chains.
250403
+ * when the source chain is Ethereum mainnet and 100% for other source chains.
250360
250404
  * Same-chain quotes and direct calls to {@link estimateFillOrder}, including
250361
250405
  * Simplex solver estimates, remain unbumped.
250362
250406
  *
@@ -250645,7 +250689,7 @@ async function resolveChainConfigs(chains2, options = {}) {
250645
250689
  })
250646
250690
  );
250647
250691
  }
250648
- var DEFAULT_BLOCK_SCAN_INTERVAL_SECONDS, MIN_BLOCK_SCAN_INTERVAL_SECONDS, FillerConfigService;
250692
+ var DEFAULT_BLOCK_SCAN_INTERVAL_SECONDS, MIN_BLOCK_SCAN_INTERVAL_SECONDS, DEFAULT_MIN_ORDER_SIZE_USD, FillerConfigService;
250649
250693
  var init_FillerConfigService = __esm({
250650
250694
  "src/services/FillerConfigService.ts"() {
250651
250695
  "use strict";
@@ -250654,6 +250698,7 @@ var init_FillerConfigService = __esm({
250654
250698
  init_node10();
250655
250699
  DEFAULT_BLOCK_SCAN_INTERVAL_SECONDS = 3;
250656
250700
  MIN_BLOCK_SCAN_INTERVAL_SECONDS = 0.1;
250701
+ DEFAULT_MIN_ORDER_SIZE_USD = 20;
250657
250702
  FillerConfigService = class {
250658
250703
  constructor(chainConfigs2, fillerConfig, loggers) {
250659
250704
  this.rpcOverrides = /* @__PURE__ */ new Map();
@@ -250994,6 +251039,13 @@ var init_FillerConfigService = __esm({
250994
251039
  getBidValiditySeconds() {
250995
251040
  return this.fillerConfig?.bidValiditySeconds ?? 300;
250996
251041
  }
251042
+ /**
251043
+ * The order size, in USD, below which `order.fees` must cover the execution cost.
251044
+ * Defaults to 20. An order at or above it is filled whatever fees it carries.
251045
+ */
251046
+ getMinOrderSizeUsd() {
251047
+ return this.fillerConfig?.minOrderSizeUsd ?? DEFAULT_MIN_ORDER_SIZE_USD;
251048
+ }
250997
251049
  /** Ceiling bps above user-requested output. Default 500 (5%). */
250998
251050
  getMaxOverfillBps() {
250999
251051
  return BigInt(this.fillerConfig?.overfillProtection?.maxOverfillBps ?? 500);
@@ -251106,6 +251158,12 @@ function validateConfig(config5, cliWatchOnly = false) {
251106
251158
  );
251107
251159
  }
251108
251160
  }
251161
+ const minOrderSize = config5.simplex.minOrderSizeUsd;
251162
+ if (minOrderSize !== void 0) {
251163
+ if (!Number.isFinite(minOrderSize) || minOrderSize < 0) {
251164
+ throw new Error(`simplex.minOrderSizeUsd must be a number >= 0 (USD); got ${minOrderSize}`);
251165
+ }
251166
+ }
251109
251167
  if (config5.allowlist) {
251110
251168
  for (const user of config5.allowlist.users ?? []) {
251111
251169
  if (!isAddress(user)) {
@@ -251591,6 +251649,10 @@ function emitFillerToml(config5, options = {}) {
251591
251649
  push("# Gas units to keep deposited at the ERC-4337 EntryPoint on chains without a paymaster.");
251592
251650
  push(kv("targetGasUnits", config5.simplex.targetGasUnits));
251593
251651
  }
251652
+ if (config5.simplex.minOrderSizeUsd !== void 0) {
251653
+ push("# Order size in USD below which an order's fees must cover gas. Default 20.");
251654
+ push(kv("minOrderSizeUsd", config5.simplex.minOrderSizeUsd));
251655
+ }
251594
251656
  push();
251595
251657
  if (config5.simplex.signer) {
251596
251658
  push("# Wallet that signs fills and holds the stablecoin float on every chain.");
@@ -254188,7 +254250,7 @@ var require_bn2 = __commonJS({
254188
254250
  }
254189
254251
  return this;
254190
254252
  };
254191
- BN3.prototype.add = function add3(num2) {
254253
+ BN3.prototype.add = function add4(num2) {
254192
254254
  var res;
254193
254255
  if (num2.negative !== 0 && this.negative === 0) {
254194
254256
  num2.negative = 0;
@@ -256015,7 +256077,7 @@ var require_bn2 = __commonJS({
256015
256077
  }
256016
256078
  return this.m.sub(a6)._forceRed(this);
256017
256079
  };
256018
- Red.prototype.add = function add3(a6, b5) {
256080
+ Red.prototype.add = function add4(a6, b5) {
256019
256081
  this._verify2(a6, b5);
256020
256082
  var res = a6.add(b5);
256021
256083
  if (res.cmp(this.m) >= 0) {
@@ -267026,7 +267088,7 @@ var require_namespace = __commonJS({
267026
267088
  return this.nested[name3].values;
267027
267089
  throw Error("no such enum: " + name3);
267028
267090
  };
267029
- Namespace.prototype.add = function add3(object) {
267091
+ Namespace.prototype.add = function add4(object) {
267030
267092
  if (!(object instanceof Field6 && object.extend !== void 0 || object instanceof Type2 || object instanceof OneOf || object instanceof Enum3 || object instanceof Service || object instanceof Namespace))
267031
267093
  throw TypeError("object must be a valid nested object");
267032
267094
  if (!this.nested)
@@ -267406,7 +267468,7 @@ var require_service2 = __commonJS({
267406
267468
  });
267407
267469
  return this;
267408
267470
  };
267409
- Service.prototype.add = function add3(object) {
267471
+ Service.prototype.add = function add4(object) {
267410
267472
  if (this.get(object.name))
267411
267473
  throw Error("duplicate name '" + object.name + "' in " + this);
267412
267474
  if (object instanceof Method2) {
@@ -268111,7 +268173,7 @@ var require_type2 = __commonJS({
268111
268173
  Type2.prototype.get = function get4(name3) {
268112
268174
  return this.fields[name3] || this.oneofs && this.oneofs[name3] || this.nested && this.nested[name3] || null;
268113
268175
  };
268114
- Type2.prototype.add = function add3(object) {
268176
+ Type2.prototype.add = function add4(object) {
268115
268177
  if (this.get(object.name))
268116
268178
  throw Error("duplicate name '" + object.name + "' in " + this);
268117
268179
  if (object instanceof Field6 && object.extend === void 0) {
@@ -269022,7 +269084,7 @@ var require_oneof = __commonJS({
269022
269084
  oneof.parent.add(oneof.fieldsArray[i6]);
269023
269085
  }
269024
269086
  }
269025
- OneOf.prototype.add = function add3(field) {
269087
+ OneOf.prototype.add = function add4(field) {
269026
269088
  if (!(field instanceof Field6))
269027
269089
  throw TypeError("field must be a Field");
269028
269090
  if (field.parent && field.parent !== this.parent)
@@ -269351,7 +269413,7 @@ var require_enum = __commonJS({
269351
269413
  keepComments ? this.comments : void 0
269352
269414
  ]);
269353
269415
  };
269354
- Enum3.prototype.add = function add3(name3, id2, comment, options) {
269416
+ Enum3.prototype.add = function add4(name3, id2, comment, options) {
269355
269417
  if (!util7.isString(name3))
269356
269418
  throw TypeError("name must be a string");
269357
269419
  if (!util7.isInteger(id2))
@@ -273828,7 +273890,7 @@ var require_umd = __commonJS({
273828
273890
  return this.not().add(ONE2);
273829
273891
  };
273830
273892
  LongPrototype.neg = LongPrototype.negate;
273831
- LongPrototype.add = function add3(addend) {
273893
+ LongPrototype.add = function add4(addend) {
273832
273894
  if (!isLong(addend)) addend = fromValue(addend);
273833
273895
  var a48 = this.high >>> 16;
273834
273896
  var a32 = this.high & 65535;
@@ -340188,6 +340250,35 @@ var LIMITS_QUERY = `
340188
340250
  chains { id name tokens { symbol decimals } }
340189
340251
  }
340190
340252
  `;
340253
+ var LEVEL_FIELDS = "fillChain priceBucket price worstPrice baseSize quoteSize orderCount solverCount";
340254
+ var SNAPSHOT_QUERY = `
340255
+ query OrderbookSnapshot($id: ID!, $sourceChain: String, $fillChain: String) {
340256
+ book(id: $id) {
340257
+ id base quote
340258
+ bids: levels(side: BID, sourceChain: $sourceChain, fillChain: $fillChain) { ${LEVEL_FIELDS} }
340259
+ asks: levels(side: ASK, sourceChain: $sourceChain, fillChain: $fillChain) { ${LEVEL_FIELDS} }
340260
+ bidLiquidity: availableLiquidity(side: BID, sourceChain: $sourceChain, fillChain: $fillChain)
340261
+ askLiquidity: availableLiquidity(side: ASK, sourceChain: $sourceChain, fillChain: $fillChain)
340262
+ }
340263
+ serverInfo { priceGranularities { book granularity } }
340264
+ }
340265
+ `;
340266
+ var LEVEL_ORDERS_QUERY = `
340267
+ query OrderbookLevelOrders($id: ID!, $side: Side!, $fillChain: String, $sourceChain: String, $after: String) {
340268
+ book(id: $id) {
340269
+ orders(side: $side, fillChain: $fillChain, sourceChain: $sourceChain, first: 50, after: $after) {
340270
+ edges {
340271
+ node {
340272
+ solver { address }
340273
+ commitment fillChain price priceBucket advertisedSize quotedAmount resized expiresAt acceptedSources
340274
+ }
340275
+ }
340276
+ pageInfo { hasNextPage endCursor }
340277
+ }
340278
+ }
340279
+ }
340280
+ `;
340281
+ var LEVEL_ORDER_PAGES = 20;
340191
340282
  var POSTED_ORDER_FIELDS = "commitment side status price quotedAmount advertisedSize expiresAt acceptedSources";
340192
340283
  var SUBMIT_ORDER_MUTATION = `
340193
340284
  mutation SubmitOrder($userOp: Bytes!) {
@@ -340252,6 +340343,44 @@ var OrderbookClient = class {
340252
340343
  async limits() {
340253
340344
  return this.request(LIMITS_QUERY, {});
340254
340345
  }
340346
+ /** The public book across all solvers, restricted to the selected route. */
340347
+ async snapshot(id2, filters = {}) {
340348
+ const { book, serverInfo } = await this.request(SNAPSHOT_QUERY, {
340349
+ id: id2,
340350
+ sourceChain: filters.sourceChain || null,
340351
+ fillChain: filters.fillChain || null
340352
+ });
340353
+ if (!book) return null;
340354
+ const granularity = serverInfo.priceGranularities.find((entry) => entry.book === book.id)?.granularity ?? null;
340355
+ return { ...book, granularity };
340356
+ }
340357
+ /**
340358
+ * The orders behind one price level. The orderbook filters orders by side and route but not by
340359
+ * price bucket, so this walks the side's orders on the level's fill chain and keeps the bucket.
340360
+ * Null when the book does not exist.
340361
+ */
340362
+ async levelOrders(id2, level) {
340363
+ const found = [];
340364
+ let after = null;
340365
+ for (let page = 0; page < LEVEL_ORDER_PAGES; page++) {
340366
+ const { book } = await this.request(LEVEL_ORDERS_QUERY, {
340367
+ id: id2,
340368
+ side: level.side,
340369
+ fillChain: level.fillChain,
340370
+ sourceChain: level.sourceChain || null,
340371
+ after
340372
+ });
340373
+ if (!book) return null;
340374
+ for (const { node } of book.orders.edges) {
340375
+ if (node.priceBucket !== level.priceBucket) continue;
340376
+ const { solver, priceBucket: _bucket, ...order } = node;
340377
+ found.push({ ...order, solver: solver.address });
340378
+ }
340379
+ if (!book.orders.pageInfo.hasNextPage || !book.orders.pageInfo.endCursor) break;
340380
+ after = book.orders.pageInfo.endCursor;
340381
+ }
340382
+ return found;
340383
+ }
340255
340384
  async submitOrder(userOp) {
340256
340385
  const { submitOrder } = await this.request(SUBMIT_ORDER_MUTATION, { userOp });
340257
340386
  switch (submitOrder.__typename) {
@@ -340440,16 +340569,16 @@ function signedAmounts(params) {
340440
340569
  function offerFor(params) {
340441
340570
  const { side, inputAmount, price, outputDecimals } = params;
340442
340571
  if (price <= 0n) throw new Error("A limit order's price must be greater than zero");
340443
- const scaled = side === "BID" ? inputAmount * price / ORDERBOOK_SCALE2 : inputAmount * ORDERBOOK_SCALE2 / price;
340572
+ const scaled2 = side === "BID" ? inputAmount * price / ORDERBOOK_SCALE2 : inputAmount * ORDERBOOK_SCALE2 / price;
340444
340573
  const unit = outputDecimals >= 18 ? 1n : 10n ** BigInt(18 - outputDecimals);
340445
- return scaled / unit * unit;
340574
+ return scaled2 / unit * unit;
340446
340575
  }
340447
340576
  function inputFor(params) {
340448
340577
  const { side, outputAmount, price, inputDecimals } = params;
340449
340578
  if (price <= 0n) throw new Error("A limit order's price must be greater than zero");
340450
- const scaled = side === "BID" ? divCeil2(outputAmount * ORDERBOOK_SCALE2, price) : divCeil2(outputAmount * price, ORDERBOOK_SCALE2);
340579
+ const scaled2 = side === "BID" ? divCeil2(outputAmount * ORDERBOOK_SCALE2, price) : divCeil2(outputAmount * price, ORDERBOOK_SCALE2);
340451
340580
  const unit = inputDecimals >= 18 ? 1n : 10n ** BigInt(18 - inputDecimals);
340452
- return divCeil2(scaled, unit) * unit;
340581
+ return divCeil2(scaled2, unit) * unit;
340453
340582
  }
340454
340583
 
340455
340584
  // src/orderbook/matching.ts
@@ -340707,6 +340836,14 @@ var LimitOrderService = class {
340707
340836
  throw err;
340708
340837
  }
340709
340838
  }
340839
+ /** Public market levels read through the configured orderbook client. */
340840
+ orderbookSnapshot(book, filters = {}) {
340841
+ return this.client.snapshot(book, filters);
340842
+ }
340843
+ /** Every solver's order behind one public price level. */
340844
+ orderbookLevelOrders(book, level) {
340845
+ return this.client.levelOrders(book, level);
340846
+ }
340710
340847
  /**
340711
340848
  * The pairs an order can be written against, the smallest payout each token may carry, and
340712
340849
  * the tokens the orderbook registers on each chain.
@@ -340761,16 +340898,16 @@ var LimitOrderService = class {
340761
340898
  if (!this.configService.getConfiguredChainIds().includes(getChainId3(request3.fillChain) ?? -1)) {
340762
340899
  throw new LimitOrderValidationError(`'${request3.fillChain}' is not a chain this filler is configured for`);
340763
340900
  }
340764
- const scaled = { amountIn: 0n, amountOut: 0n };
340901
+ const scaled2 = { amountIn: 0n, amountOut: 0n };
340765
340902
  for (const name3 of ["amountIn", "amountOut"]) {
340766
340903
  try {
340767
- scaled[name3] = fromHuman(request3[name3] ?? "");
340904
+ scaled2[name3] = fromHuman(request3[name3] ?? "");
340768
340905
  } catch (err) {
340769
340906
  throw new LimitOrderValidationError(
340770
340907
  `${name3} must be an amount in whole tokens, like "1000" or "1500.25"; ${err.message}`
340771
340908
  );
340772
340909
  }
340773
- if (scaled[name3] <= 0n) throw new LimitOrderValidationError(`${name3} must be greater than zero`);
340910
+ if (scaled2[name3] <= 0n) throw new LimitOrderValidationError(`${name3} must be greater than zero`);
340774
340911
  }
340775
340912
  const sources = request3.acceptedSources ?? [];
340776
340913
  if (sources.length === 0) {
@@ -340817,7 +340954,7 @@ var LimitOrderService = class {
340817
340954
  );
340818
340955
  }
340819
340956
  const floor2 = limits.serverInfo.minOrderSizes.find((entry) => entry.symbol === request3.tokenOut);
340820
- if (floor2 && scaled.amountOut < BigInt(floor2.size)) {
340957
+ if (floor2 && scaled2.amountOut < BigInt(floor2.size)) {
340821
340958
  throw new LimitOrderValidationError(
340822
340959
  `amountOut is below the orderbook's dust floor for ${request3.tokenOut} (${toHuman(BigInt(floor2.size))})`
340823
340960
  );
@@ -340827,7 +340964,7 @@ var LimitOrderService = class {
340827
340964
  throw new LimitOrderValidationError(`'${symbol3}' does not resolve to a token address on ${request3.fillChain}`);
340828
340965
  }
340829
340966
  }
340830
- return { amountIn: scaled.amountIn, amountOut: scaled.amountOut };
340967
+ return { amountIn: scaled2.amountIn, amountOut: scaled2.amountOut };
340831
340968
  }
340832
340969
  /**
340833
340970
  * Refuses an order the wallet cannot pay out.
@@ -341353,6 +341490,459 @@ function underFunded(entry) {
341353
341490
  return entry.resized ? `UNDER_FUNDED: the orderbook is advertising ${entry.advertisedSize} of the ${entry.quotedAmount} quoted` : "UNDER_FUNDED: the solver's balance does not cover this posting";
341354
341491
  }
341355
341492
 
341493
+ // src/orderbook/profitability.ts
341494
+ init_esm_shims();
341495
+ init_decimal();
341496
+ init_asset_registry();
341497
+
341498
+ // src/data/inventory.ts
341499
+ init_esm_shims();
341500
+ var INVENTORY_INTERVAL_MS = 24 * 60 * 60 * 1e3;
341501
+ function inventoryOf(snapshot) {
341502
+ if (snapshot.status !== "fresh") return null;
341503
+ const held = {};
341504
+ for (const chain2 of snapshot.chains) {
341505
+ for (const asset of chain2.assets) {
341506
+ if (asset.total === null) return null;
341507
+ held[asset.symbol] = (held[asset.symbol] ?? 0) + asset.total;
341508
+ }
341509
+ }
341510
+ return Object.keys(held).length > 0 ? held : null;
341511
+ }
341512
+ function parseUtc(value) {
341513
+ return Date.parse(`${value.replace(" ", "T")}Z`);
341514
+ }
341515
+ var InventoryRecorder = class {
341516
+ constructor(store, balances, logger8, now = Date.now) {
341517
+ this.store = store;
341518
+ this.balances = balances;
341519
+ this.logger = logger8;
341520
+ this.now = now;
341521
+ this.recording = false;
341522
+ this.onSnapshot = (snapshot) => void this.consider(snapshot);
341523
+ }
341524
+ start() {
341525
+ this.balances.on("snapshot", this.onSnapshot);
341526
+ void this.consider(this.balances.getSnapshot());
341527
+ }
341528
+ stop() {
341529
+ this.balances.off("snapshot", this.onSnapshot);
341530
+ }
341531
+ /** Takes a snapshot from `snapshot` if one is due and the read is complete. Never rejects. */
341532
+ async consider(snapshot) {
341533
+ if (this.recording) return;
341534
+ const held = inventoryOf(snapshot);
341535
+ if (!held) return;
341536
+ this.recording = true;
341537
+ try {
341538
+ if (this.lastTakenAt === void 0) {
341539
+ const latest2 = await this.store.latest();
341540
+ this.lastTakenAt = latest2 ? parseUtc(latest2.takenAt) : 0;
341541
+ }
341542
+ if (this.now() - this.lastTakenAt < INVENTORY_INTERVAL_MS) return;
341543
+ await this.store.record(held);
341544
+ this.lastTakenAt = this.now();
341545
+ this.logger.info({ tokens: Object.keys(held).length }, "Recorded an inventory snapshot");
341546
+ } catch (err) {
341547
+ this.logger.warn({ err }, "Could not record an inventory snapshot");
341548
+ } finally {
341549
+ this.recording = false;
341550
+ }
341551
+ }
341552
+ };
341553
+
341554
+ // src/orderbook/usd.ts
341555
+ init_esm_shims();
341556
+ init_decimal();
341557
+ init_asset_registry();
341558
+ function limitOrderUsdEdges(orders) {
341559
+ return orders.filter((order) => normalizeSymbol(order.base) !== normalizeSymbol(order.quote)).map((order) => ({
341560
+ base: normalizeSymbol(order.base),
341561
+ quote: normalizeSymbol(order.quote),
341562
+ rate: new Decimal(order.price).div(ORDERBOOK_SCALE2.toString())
341563
+ })).filter((edge) => edge.rate.isFinite() && edge.rate.gt(0));
341564
+ }
341565
+ function usdFactorsFrom(edges) {
341566
+ const factors = /* @__PURE__ */ new Map();
341567
+ for (const symbol3 of USD_STABLE_SYMBOLS) factors.set(symbol3, new Decimal(1));
341568
+ const ordered = [...edges].sort((a6, b5) => a6.base.localeCompare(b5.base) || a6.quote.localeCompare(b5.quote));
341569
+ let grew = true;
341570
+ while (grew) {
341571
+ grew = false;
341572
+ for (const edge of ordered) {
341573
+ const usdBase = factors.get(edge.base);
341574
+ const usdQuote = factors.get(edge.quote);
341575
+ if (usdBase && !usdQuote) {
341576
+ factors.set(edge.quote, usdBase.div(edge.rate));
341577
+ grew = true;
341578
+ } else if (usdQuote && !usdBase) {
341579
+ factors.set(edge.base, usdQuote.mul(edge.rate));
341580
+ grew = true;
341581
+ }
341582
+ }
341583
+ }
341584
+ return factors;
341585
+ }
341586
+ function usdValueOf(factors, symbol3, amount) {
341587
+ const factor = factors.get(normalizeSymbol(symbol3));
341588
+ return factor ? amount.mul(factor) : null;
341589
+ }
341590
+
341591
+ // src/orderbook/profitability.ts
341592
+ var Decimal2 = Decimal.clone({ precision: 40 });
341593
+ var PROFIT_PERIODS = {
341594
+ "7d": { bucket: "day", count: 7 },
341595
+ "30d": { bucket: "day", count: 30 },
341596
+ "12w": { bucket: "week", count: 12 },
341597
+ "12m": { bucket: "month", count: 12 },
341598
+ all: { bucket: "year", count: null }
341599
+ };
341600
+ function isProfitPeriod(value) {
341601
+ return value in PROFIT_PERIODS;
341602
+ }
341603
+ var MINUTE = 6e4;
341604
+ var DAY = 864e5;
341605
+ var BALANCES_REACH_MS = 7 * DAY;
341606
+ function bucketStart(local, bucket) {
341607
+ const date = new Date(local);
341608
+ const year = date.getUTCFullYear();
341609
+ const month = date.getUTCMonth();
341610
+ if (bucket === "year") return Date.UTC(year, 0, 1);
341611
+ if (bucket === "month") return Date.UTC(year, month, 1);
341612
+ const day = Date.UTC(year, month, date.getUTCDate());
341613
+ return bucket === "day" ? day : day - (date.getUTCDay() + 6) % 7 * DAY;
341614
+ }
341615
+ function shiftBucket(start, bucket, steps) {
341616
+ if (bucket === "day") return start + steps * DAY;
341617
+ if (bucket === "week") return start + steps * 7 * DAY;
341618
+ const date = new Date(start);
341619
+ return bucket === "month" ? Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + steps, 1) : Date.UTC(date.getUTCFullYear() + steps, 0, 1);
341620
+ }
341621
+ function scaled(value) {
341622
+ return new Decimal2(value).div(ORDERBOOK_SCALE2.toString());
341623
+ }
341624
+ function tradeOf(fill, at) {
341625
+ try {
341626
+ const paid = scaled(fill.amount);
341627
+ const taken = fill.amountIn === null ? null : scaled(fill.amountIn);
341628
+ const price = scaled(fill.price);
341629
+ if (!paid.gt(0) || !price.gt(0) || taken !== null && !taken.gt(0)) return null;
341630
+ const buying = fill.side === "BID";
341631
+ const [baseAmount, quoteAmount] = buying ? [taken ?? paid.div(price), paid] : [paid, taken ?? paid.mul(price)];
341632
+ return {
341633
+ at,
341634
+ book: fill.book,
341635
+ base: normalizeSymbol(fill.base),
341636
+ quote: normalizeSymbol(fill.quote),
341637
+ buying,
341638
+ baseAmount,
341639
+ quoteAmount,
341640
+ estimated: taken === null
341641
+ };
341642
+ } catch {
341643
+ return null;
341644
+ }
341645
+ }
341646
+ function add3(holdings, symbol3, amount) {
341647
+ holdings.set(symbol3, (holdings.get(symbol3) ?? new Decimal2(0)).add(amount));
341648
+ }
341649
+ function apply3(holdings, trade, sign6) {
341650
+ const base4 = trade.buying ? trade.baseAmount : trade.baseAmount.neg();
341651
+ const quote = trade.buying ? trade.quoteAmount.neg() : trade.quoteAmount;
341652
+ add3(holdings, trade.base, base4.mul(sign6));
341653
+ add3(holdings, trade.quote, quote.mul(sign6));
341654
+ }
341655
+ var Sums = class {
341656
+ constructor() {
341657
+ this.change = /* @__PURE__ */ new Map();
341658
+ this.boughtUsd = new Decimal2(0);
341659
+ this.soldUsd = new Decimal2(0);
341660
+ this.buys = 0;
341661
+ this.sells = 0;
341662
+ }
341663
+ };
341664
+ var BookLedger = class {
341665
+ constructor(id2, base4, quote) {
341666
+ this.id = id2;
341667
+ this.base = base4;
341668
+ this.quote = quote;
341669
+ this.bought = new Decimal2(0);
341670
+ this.boughtQuote = new Decimal2(0);
341671
+ this.sold = new Decimal2(0);
341672
+ this.soldQuote = new Decimal2(0);
341673
+ /** The rates of the book's latest buy and latest sell, over all of its history. */
341674
+ this.latestBuy = null;
341675
+ this.latestSell = null;
341676
+ this.inPeriod = false;
341677
+ }
341678
+ /**
341679
+ * The rate the book is valued at. Midway between the latest buy and the latest sell, because
341680
+ * either alone sits half a spread to one side and would price open volume against whichever
341681
+ * way the book happened to trade last.
341682
+ */
341683
+ rate() {
341684
+ if (this.latestBuy && this.latestSell) return this.latestBuy.add(this.latestSell).div(2);
341685
+ return this.latestBuy ?? this.latestSell;
341686
+ }
341687
+ };
341688
+ function readingOf(reading) {
341689
+ const held = /* @__PURE__ */ new Map();
341690
+ const cover = /* @__PURE__ */ new Map();
341691
+ for (const [symbol3, amount] of Object.entries(reading.balances)) {
341692
+ const chains2 = reading.chains[symbol3];
341693
+ if (!Number.isFinite(amount) || !chains2?.length) continue;
341694
+ add3(held, normalizeSymbol(symbol3), new Decimal2(amount));
341695
+ cover.set(normalizeSymbol(symbol3), [...chains2].sort().join(","));
341696
+ }
341697
+ return { held, cover };
341698
+ }
341699
+ function movedBetween(earlier, later) {
341700
+ if (!earlier || !later) return null;
341701
+ const moved = /* @__PURE__ */ new Map();
341702
+ for (const [symbol3, cover] of later.cover) {
341703
+ if (earlier.cover.get(symbol3) !== cover) continue;
341704
+ const before = earlier.held.get(symbol3) ?? new Decimal2(0);
341705
+ moved.set(symbol3, (later.held.get(symbol3) ?? new Decimal2(0)).sub(before));
341706
+ }
341707
+ return moved;
341708
+ }
341709
+ function nearestRecord(instant, records) {
341710
+ let record;
341711
+ let gap = Number.POSITIVE_INFINITY;
341712
+ for (const candidate of records) {
341713
+ const distance = Math.abs(candidate.at - instant);
341714
+ if (candidate.live && distance > BALANCES_REACH_MS) continue;
341715
+ if (distance < gap || distance === gap && record?.live && !candidate.live) {
341716
+ record = candidate;
341717
+ gap = distance;
341718
+ }
341719
+ }
341720
+ return record;
341721
+ }
341722
+ function inventoryAt(instant, records, trades, outflows, readings) {
341723
+ const record = nearestRecord(instant, records);
341724
+ if (!record) return null;
341725
+ const recorded = /* @__PURE__ */ new Map();
341726
+ for (const [symbol3, amount] of Object.entries(record.balances)) {
341727
+ if (Number.isFinite(amount)) add3(recorded, normalizeSymbol(symbol3), new Decimal2(amount));
341728
+ }
341729
+ const held = new Map(recorded);
341730
+ const after = record.at >= instant;
341731
+ const [from32, to] = after ? [instant, record.at] : [record.at, instant];
341732
+ const sign6 = after ? -1 : 1;
341733
+ for (const trade of trades) {
341734
+ if (trade.at >= from32 && trade.at < to) apply3(held, trade, sign6);
341735
+ }
341736
+ for (const outflow of outflows) {
341737
+ if (outflow.at >= from32 && outflow.at < to) {
341738
+ add3(held, normalizeSymbol(outflow.symbol), new Decimal2(outflow.amount).mul(-sign6));
341739
+ }
341740
+ }
341741
+ const moved = movedBetween(readings.get(from32), readings.get(to));
341742
+ if (moved) {
341743
+ for (const [symbol3, amount] of moved) {
341744
+ held.set(symbol3, (recorded.get(symbol3) ?? new Decimal2(0)).add(amount.mul(sign6)));
341745
+ }
341746
+ }
341747
+ for (const [symbol3, amount] of held) if (amount.isNegative()) held.set(symbol3, new Decimal2(0));
341748
+ return { held, record };
341749
+ }
341750
+ function tradesOf(fills, now) {
341751
+ const trades = [];
341752
+ for (const fill of fills) {
341753
+ const at = parseUtc(fill.filledAt);
341754
+ if (!Number.isFinite(at) || at > now) continue;
341755
+ const trade = tradeOf(fill, at);
341756
+ if (trade) trades.push(trade);
341757
+ }
341758
+ return trades.sort((a6, b5) => a6.at - b5.at);
341759
+ }
341760
+ function bucketStarts(period, now, offset, firstAt) {
341761
+ const { bucket, count } = PROFIT_PERIODS[period];
341762
+ const last = bucketStart(now - offset, bucket);
341763
+ const first3 = count === null ? bucketStart((firstAt ?? now) - offset, bucket) : shiftBucket(last, bucket, 1 - count);
341764
+ const starts = [];
341765
+ for (let start = first3; start <= last; start = shiftBucket(start, bucket, 1)) starts.push(start);
341766
+ return starts;
341767
+ }
341768
+ function inventoryInstants(fills, options) {
341769
+ const { period, now } = options;
341770
+ const offset = (options.tzOffsetMinutes ?? 0) * MINUTE;
341771
+ const records = (options.inventory ?? []).filter((record) => record.at <= now);
341772
+ const instants = /* @__PURE__ */ new Set([now]);
341773
+ for (const start of bucketStarts(period, now, offset, tradesOf(fills, now)[0]?.at)) {
341774
+ instants.add(start + offset);
341775
+ const record = nearestRecord(start + offset, records);
341776
+ if (record) instants.add(record.at);
341777
+ }
341778
+ return [...instants].sort((a6, b5) => a6 - b5);
341779
+ }
341780
+ function summarizeProfit(fills, options) {
341781
+ const { period, now } = options;
341782
+ const offset = (options.tzOffsetMinutes ?? 0) * MINUTE;
341783
+ const { bucket } = PROFIT_PERIODS[period];
341784
+ const records = (options.inventory ?? []).filter((record) => record.at <= now);
341785
+ const outflows = options.outflows ?? [];
341786
+ const readings = new Map((options.readings ?? []).map((reading) => [reading.at, readingOf(reading)]));
341787
+ const trades = tradesOf(fills, now);
341788
+ const starts = bucketStarts(period, now, offset, trades[0]?.at);
341789
+ const first3 = starts[0];
341790
+ const books = /* @__PURE__ */ new Map();
341791
+ const spelling2 = /* @__PURE__ */ new Map();
341792
+ for (const record of records) {
341793
+ for (const symbol3 of Object.keys(record.balances)) {
341794
+ if (!spelling2.has(normalizeSymbol(symbol3))) spelling2.set(normalizeSymbol(symbol3), symbol3);
341795
+ }
341796
+ }
341797
+ for (const fill of fills) {
341798
+ for (const symbol3 of [fill.base, fill.quote]) {
341799
+ if (!spelling2.has(normalizeSymbol(symbol3))) spelling2.set(normalizeSymbol(symbol3), symbol3);
341800
+ }
341801
+ }
341802
+ for (const trade of trades) {
341803
+ let book = books.get(trade.book);
341804
+ if (!book) {
341805
+ book = new BookLedger(trade.book, trade.base, trade.quote);
341806
+ books.set(trade.book, book);
341807
+ }
341808
+ const rate = trade.quoteAmount.div(trade.baseAmount);
341809
+ if (trade.buying) book.latestBuy = rate;
341810
+ else book.latestSell = rate;
341811
+ }
341812
+ const edges = [];
341813
+ for (const book of books.values()) {
341814
+ const rate = book.rate();
341815
+ if (rate && book.base !== book.quote) {
341816
+ edges.push({ base: book.base, quote: book.quote, rate: new Decimal(rate.toString()) });
341817
+ }
341818
+ }
341819
+ const factors = usdFactorsFrom(edges);
341820
+ const priceOf = (symbol3) => {
341821
+ const factor = factors.get(symbol3);
341822
+ return factor ? new Decimal2(factor.toString()) : null;
341823
+ };
341824
+ const unpriced = /* @__PURE__ */ new Set();
341825
+ const valueOf = (holdings) => {
341826
+ let total = new Decimal2(0);
341827
+ for (const [symbol3, amount] of holdings) {
341828
+ if (amount.isZero()) continue;
341829
+ const price = priceOf(symbol3);
341830
+ if (price) total = total.add(amount.mul(price));
341831
+ else unpriced.add(symbol3);
341832
+ }
341833
+ return total;
341834
+ };
341835
+ const totals = new Sums();
341836
+ const buckets = new Map(starts.map((start) => [start, new Sums()]));
341837
+ let estimatedFills = 0;
341838
+ for (const trade of trades) {
341839
+ const sums = buckets.get(bucketStart(trade.at - offset, bucket));
341840
+ if (!sums) continue;
341841
+ const book = books.get(trade.book);
341842
+ if (!book) continue;
341843
+ book.inPeriod = true;
341844
+ if (trade.estimated) estimatedFills += 1;
341845
+ if (trade.buying) {
341846
+ book.bought = book.bought.add(trade.baseAmount);
341847
+ book.boughtQuote = book.boughtQuote.add(trade.quoteAmount);
341848
+ } else {
341849
+ book.sold = book.sold.add(trade.baseAmount);
341850
+ book.soldQuote = book.soldQuote.add(trade.quoteAmount);
341851
+ }
341852
+ const basePrice = priceOf(trade.base);
341853
+ const quotePrice = priceOf(trade.quote);
341854
+ const volume = basePrice ? trade.baseAmount.mul(basePrice) : quotePrice ? trade.quoteAmount.mul(quotePrice) : new Decimal2(0);
341855
+ for (const sum2 of [totals, sums]) {
341856
+ apply3(sum2.change, trade, 1);
341857
+ if (trade.buying) {
341858
+ sum2.buys += 1;
341859
+ sum2.boughtUsd = sum2.boughtUsd.add(volume);
341860
+ } else {
341861
+ sum2.sells += 1;
341862
+ sum2.soldUsd = sum2.soldUsd.add(volume);
341863
+ }
341864
+ }
341865
+ }
341866
+ const figures = (sums, startsAt, endsAt) => {
341867
+ const profit = valueOf(sums.change);
341868
+ const began2 = inventoryAt(startsAt, records, trades, outflows, readings);
341869
+ const inventory = began2 ? valueOf(began2.held) : null;
341870
+ const moved = movedBetween(readings.get(startsAt), readings.get(endsAt));
341871
+ if (moved) {
341872
+ for (const [symbol3, amount] of sums.change) {
341873
+ if (moved.has(symbol3)) moved.set(symbol3, (moved.get(symbol3) ?? new Decimal2(0)).sub(amount));
341874
+ }
341875
+ }
341876
+ return {
341877
+ profitUsd: profit.toNumber(),
341878
+ startInventoryUsd: inventory ? inventory.toNumber() : null,
341879
+ returnPct: inventory?.gt(0) ? profit.div(inventory).mul(100).toNumber() : null,
341880
+ transfersUsd: moved && moved.size > 0 ? valueOf(moved).toNumber() : null,
341881
+ boughtUsd: sums.boughtUsd.toNumber(),
341882
+ soldUsd: sums.soldUsd.toNumber(),
341883
+ buys: sums.buys,
341884
+ sells: sums.sells
341885
+ };
341886
+ };
341887
+ const from32 = first3 + offset;
341888
+ const began = inventoryAt(from32, records, trades, outflows, readings);
341889
+ const startInventory = began && {
341890
+ at: from32,
341891
+ source: began.record.live ? "balances" : "snapshot",
341892
+ recordedAt: began.record.at,
341893
+ usd: valueOf(began.held).toNumber(),
341894
+ tokens: [...began.held].filter(([, amount]) => amount.gt(0)).map(([symbol3, amount]) => {
341895
+ const price = priceOf(symbol3);
341896
+ return {
341897
+ symbol: spelling2.get(symbol3) ?? symbol3,
341898
+ amount: amount.toNumber(),
341899
+ usd: price ? amount.mul(price).toNumber() : null
341900
+ };
341901
+ }).sort((a6, b5) => (b5.usd ?? 0) - (a6.usd ?? 0) || a6.symbol.localeCompare(b5.symbol))
341902
+ };
341903
+ const rows = [];
341904
+ for (const book of books.values()) {
341905
+ if (!book.inPeriod) continue;
341906
+ const averageBuy = book.bought.gt(0) ? book.boughtQuote.div(book.bought) : null;
341907
+ const averageSell = book.sold.gt(0) ? book.soldQuote.div(book.sold) : null;
341908
+ const baseChange = book.bought.sub(book.sold);
341909
+ const quoteChange = book.soldQuote.sub(book.boughtQuote);
341910
+ const basePrice = priceOf(book.base);
341911
+ const quotePrice = priceOf(book.quote);
341912
+ rows.push({
341913
+ book: book.id,
341914
+ base: spelling2.get(book.base) ?? book.base,
341915
+ quote: spelling2.get(book.quote) ?? book.quote,
341916
+ bought: book.bought.toNumber(),
341917
+ sold: book.sold.toNumber(),
341918
+ averageBuy: averageBuy ? averageBuy.toNumber() : null,
341919
+ averageSell: averageSell ? averageSell.toNumber() : null,
341920
+ spreadPct: averageBuy && averageSell ? averageSell.div(averageBuy).sub(1).mul(100).toNumber() : null,
341921
+ baseChange: baseChange.toNumber(),
341922
+ quoteChange: quoteChange.toNumber(),
341923
+ rate: book.rate()?.toNumber() ?? null,
341924
+ profitUsd: basePrice && quotePrice ? baseChange.mul(basePrice).add(quoteChange.mul(quotePrice)).toNumber() : null
341925
+ });
341926
+ }
341927
+ rows.sort((a6, b5) => (b5.profitUsd ?? 0) - (a6.profitUsd ?? 0) || a6.book.localeCompare(b5.book));
341928
+ return {
341929
+ period,
341930
+ bucket,
341931
+ from: from32,
341932
+ to: now,
341933
+ totals: figures(totals, from32, now),
341934
+ series: starts.map((start, index3) => {
341935
+ const sums = buckets.get(start) ?? new Sums();
341936
+ const endsAt = index3 + 1 < starts.length ? starts[index3 + 1] + offset : now;
341937
+ return { start: start + offset, ...figures(sums, start + offset, endsAt) };
341938
+ }),
341939
+ books: rows,
341940
+ startInventory,
341941
+ estimatedFills,
341942
+ unpricedTokens: [...unpriced].map((symbol3) => spelling2.get(symbol3) ?? symbol3).sort()
341943
+ };
341944
+ }
341945
+
341356
341946
  // src/services/server/UiServer.ts
341357
341947
  init_Logger();
341358
341948
 
@@ -343595,6 +344185,55 @@ var UiServer = class {
343595
344185
  if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
343596
344186
  return this.handleLimitOrders(res, () => this.operator.limitOrders.books());
343597
344187
  }
344188
+ if (path2 === "/api/analytics/profitability") {
344189
+ if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
344190
+ if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
344191
+ const params = new URL(req.url ?? "/", "http://localhost").searchParams;
344192
+ const period = params.get("period") ?? "7d";
344193
+ if (!isProfitPeriod(period)) return sendJson(res, 400, { error: "period must be 7d, 30d, 12w, 12m or all" });
344194
+ const tz = Number(params.get("tz") ?? 0);
344195
+ if (!Number.isInteger(tz) || Math.abs(tz) > 14 * 60) {
344196
+ return sendJson(res, 400, { error: "tz must be a whole number of minutes" });
344197
+ }
344198
+ return this.handleLimitOrders(res, () => this.operator.limitOrders.profitability(period, tz));
344199
+ }
344200
+ if (path2 === "/api/orderbook/snapshot") {
344201
+ if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
344202
+ if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
344203
+ const params = new URL(req.url ?? "/", "http://localhost").searchParams;
344204
+ const book = params.get("book")?.trim();
344205
+ if (!book) return sendJson(res, 400, { error: "A book is required" });
344206
+ return this.handleLimitOrders(
344207
+ res,
344208
+ () => this.operator.limitOrders.orderbookSnapshot(book, {
344209
+ sourceChain: params.get("sourceChain") || void 0,
344210
+ fillChain: params.get("fillChain") || void 0
344211
+ })
344212
+ );
344213
+ }
344214
+ if (path2 === "/api/orderbook/level-orders") {
344215
+ if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
344216
+ if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
344217
+ const params = new URL(req.url ?? "/", "http://localhost").searchParams;
344218
+ const book = params.get("book")?.trim();
344219
+ const side = params.get("side");
344220
+ const fillChain = params.get("fillChain")?.trim();
344221
+ const priceBucket = params.get("priceBucket")?.trim();
344222
+ if (!book) return sendJson(res, 400, { error: "A book is required" });
344223
+ if (side !== "BID" && side !== "ASK") return sendJson(res, 400, { error: "side must be BID or ASK" });
344224
+ if (!fillChain) return sendJson(res, 400, { error: "A fill chain is required" });
344225
+ if (!priceBucket || !/^\d+$/.test(priceBucket))
344226
+ return sendJson(res, 400, { error: "priceBucket must be an integer" });
344227
+ return this.handleLimitOrders(
344228
+ res,
344229
+ () => this.operator.limitOrders.orderbookLevelOrders(book, {
344230
+ side,
344231
+ fillChain,
344232
+ priceBucket,
344233
+ sourceChain: params.get("sourceChain") || void 0
344234
+ })
344235
+ );
344236
+ }
343598
344237
  const limitOrderMatch = path2.match(/^\/api\/limit-orders\/([\w-]+)$/);
343599
344238
  if (limitOrderMatch) {
343600
344239
  if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
@@ -344731,6 +345370,7 @@ var CacheService = class {
344731
345370
  matchedLimitOrders: {},
344732
345371
  bidPlans: {},
344733
345372
  partialFills: {},
345373
+ feeChecksWaived: {},
344734
345374
  feeTokens: {},
344735
345375
  tokenDecimals: {},
344736
345376
  solverSelection: {}
@@ -344770,6 +345410,10 @@ var CacheService = class {
344770
345410
  stalePartialIds.forEach((orderId) => {
344771
345411
  delete this.cacheData.partialFills[orderId];
344772
345412
  });
345413
+ const staleWaivedIds = Object.entries(this.cacheData.feeChecksWaived).filter(([_5, data4]) => !this.isCacheValid(data4.timestamp)).map(([orderId]) => orderId);
345414
+ staleWaivedIds.forEach((orderId) => {
345415
+ delete this.cacheData.feeChecksWaived[orderId];
345416
+ });
344773
345417
  }
344774
345418
  getGasEstimate(orderId) {
344775
345419
  try {
@@ -344951,6 +345595,7 @@ var CacheService = class {
344951
345595
  })),
344952
345596
  partialFill: plan.partialFill,
344953
345597
  profit: plan.profit,
345598
+ ...plan.inputDecimals !== void 0 ? { inputDecimals: plan.inputDecimals } : {},
344954
345599
  ...plan.budget ? { budget: budgetFromCache(plan.budget) } : {}
344955
345600
  }));
344956
345601
  } catch (error) {
@@ -344981,6 +345626,7 @@ var CacheService = class {
344981
345626
  })),
344982
345627
  partialFill: plan.partialFill,
344983
345628
  profit: plan.profit,
345629
+ ...plan.inputDecimals !== void 0 ? { inputDecimals: plan.inputDecimals } : {},
344984
345630
  ...plan.budget ? { budget: budgetToCache(plan.budget) } : {}
344985
345631
  })),
344986
345632
  timestamp: Date.now()
@@ -345082,6 +345728,24 @@ var CacheService = class {
345082
345728
  clearPartialFill(orderId) {
345083
345729
  delete this.cacheData.partialFills[orderId];
345084
345730
  }
345731
+ /**
345732
+ * Whether the strategy's completed evaluation let a bid through without holding
345733
+ * `order.fees` to the execution cost, because the order is at or above the
345734
+ * operator's minimum size.
345735
+ *
345736
+ * Written and cleared the way {@link isPartialFill} is, for the same reason: the
345737
+ * caller exempts such an order from its profit floor.
345738
+ */
345739
+ isFeeCheckWaived(orderId) {
345740
+ const cache = this.cacheData.feeChecksWaived[orderId];
345741
+ return cache !== void 0 && this.isCacheValid(cache.timestamp) && cache.waived;
345742
+ }
345743
+ setFeeCheckWaived(orderId, waived) {
345744
+ this.cacheData.feeChecksWaived[orderId] = { waived, timestamp: Date.now() };
345745
+ }
345746
+ clearFeeCheckWaived(orderId) {
345747
+ delete this.cacheData.feeChecksWaived[orderId];
345748
+ }
345085
345749
  getFeeTokenWithDecimals(chain2) {
345086
345750
  try {
345087
345751
  const cache = this.cacheData.feeTokens[chain2];
@@ -350396,7 +351060,10 @@ var ContractInteractionService = class {
350396
351060
  order,
350397
351061
  prependCalls: void 0,
350398
351062
  maxPriorityFeePerGasBumpPercent: gasFeeBumpConfig?.maxPriorityFeePerGasBumpPercent,
350399
- maxFeePerGasBumpPercent: gasFeeBumpConfig?.maxFeePerGasBumpPercent
351063
+ maxFeePerGasBumpPercent: gasFeeBumpConfig?.maxFeePerGasBumpPercent,
351064
+ // No bid without the bundler's own figures: an op signed with the SDK's
351065
+ // fixed fallback limits does not execute, so a failed estimate skips the order.
351066
+ requireBundlerEstimate: true
350400
351067
  });
350401
351068
  this.logger.info({ orderId: order.id }, "Caching gas estimate");
350402
351069
  this.logger.info({ estimate }, "Estimate");
@@ -350419,7 +351086,7 @@ var ContractInteractionService = class {
350419
351086
  callGasLimit: estimate.callGasLimit
350420
351087
  };
350421
351088
  } catch (error) {
350422
- this.logger.error({ err: error }, "Error estimating gas, using generous fallback values");
351089
+ this.logger.error({ err: error, orderId: order.id }, "Error estimating gas, not bidding on this order");
350423
351090
  throw new Error(`Failed to estimate gas: ${error instanceof Error ? error.message : "Unknown error"}`);
350424
351091
  }
350425
351092
  }
@@ -384242,6 +384909,23 @@ var IntentFiller = class {
384242
384909
  this.monitor.emit("orderSkipped", { orderId: order.id, reason: "watch-only" });
384243
384910
  return null;
384244
384911
  }
384912
+ const destinationHead = await this.destinationHead(order);
384913
+ if (destinationHead !== null && order.deadline <= destinationHead) {
384914
+ this.logger.info(
384915
+ {
384916
+ orderId: order.id,
384917
+ destChain: order.destination,
384918
+ deadline: order.deadline.toString(),
384919
+ head: destinationHead.toString()
384920
+ },
384921
+ "Skipping order: its deadline has passed on the destination chain"
384922
+ );
384923
+ this.monitor.emit("orderSkipped", {
384924
+ orderId: order.id,
384925
+ reason: "Order deadline has passed on the destination chain"
384926
+ });
384927
+ return null;
384928
+ }
384245
384929
  const evalStartMs = Date.now();
384246
384930
  const eligibleStrategies = await Promise.all(
384247
384931
  this.strategies.map(async (strategy) => {
@@ -384251,7 +384935,10 @@ var IntentFiller = class {
384251
384935
  })
384252
384936
  );
384253
384937
  const fillsPartially = this.fillsPartially(order);
384254
- const validStrategies = eligibleStrategies.filter((s4) => s4 !== null && (s4.profitability > 0 || fillsPartially)).sort((a6, b5) => b5.profitability - a6.profitability);
384938
+ const feeCheckWaived = this.feeCheckWaived(order);
384939
+ const validStrategies = eligibleStrategies.filter(
384940
+ (s4) => s4 !== null && (s4.profitability > 0 || fillsPartially || feeCheckWaived)
384941
+ ).sort((a6, b5) => b5.profitability - a6.profitability);
384255
384942
  const evalDurationSec = (Date.now() - evalStartMs) / 1e3;
384256
384943
  this.monitor.emit("orderTiming", { orderId: order.id, phase: "evaluation", durationSec: evalDurationSec });
384257
384944
  if (validStrategies.length === 0) {
@@ -384276,6 +384963,23 @@ var IntentFiller = class {
384276
384963
  );
384277
384964
  return validStrategies[0];
384278
384965
  }
384966
+ /**
384967
+ * The destination chain's latest block number, the clock `order.deadline` is read on.
384968
+ *
384969
+ * Null when the read fails. That is not an answer about the order, so evaluation goes
384970
+ * on: the gas estimate simulates the same deadline check and refuses an expired order.
384971
+ */
384972
+ async destinationHead(order) {
384973
+ try {
384974
+ return await this.chainClientManager.getPublicClient(order.destination).getBlockNumber();
384975
+ } catch (err) {
384976
+ this.logger.warn(
384977
+ { orderId: order.id, destChain: order.destination, err },
384978
+ "Could not read the destination head to check the order's deadline"
384979
+ );
384980
+ return null;
384981
+ }
384982
+ }
384279
384983
  /**
384280
384984
  * Whether the strategy's evaluation concluded in a deliberate partial fill.
384281
384985
  *
@@ -384289,6 +384993,15 @@ var IntentFiller = class {
384289
384993
  if (!order.id) return false;
384290
384994
  return this.contractService.cacheService.isPartialFill(order.id);
384291
384995
  }
384996
+ /**
384997
+ * Whether the strategy's evaluation let a bid through without the fee check, because
384998
+ * the order is at or above the operator's minimum size. Read from a flag the strategy
384999
+ * sets only once it has an answer, as {@link fillsPartially} is.
385000
+ */
385001
+ feeCheckWaived(order) {
385002
+ if (!order.id) return false;
385003
+ return this.contractService.cacheService.isFeeCheckWaived(order.id);
385004
+ }
384292
385005
  executeOrder(order, bestStrategy, solverSelectionActive, inputUsdValue, profitUsd) {
384293
385006
  const chainQueue = this.chainQueues.get(getChainId3(order.destination));
384294
385007
  if (!chainQueue) {
@@ -384316,7 +385029,12 @@ var IntentFiller = class {
384316
385029
  else cache.clearFundingPrepends(order.id);
384317
385030
  }
384318
385031
  reservation = await this.holdAll([
384319
- { limitOrderId: plan.limitOrderId, payout: plan.payout, take: plan.fillerInputs[plan.leg]?.amount }
385032
+ {
385033
+ limitOrderId: plan.limitOrderId,
385034
+ payout: plan.payout,
385035
+ take: plan.fillerInputs[plan.leg]?.amount,
385036
+ takeDecimals: plan.inputDecimals
385037
+ }
384320
385038
  ]);
384321
385039
  if (reservation.length === 0) {
384322
385040
  this.logger.info(
@@ -384486,7 +385204,8 @@ var IntentFiller = class {
384486
385204
  taken.push({
384487
385205
  limitOrderId: hold.limitOrderId,
384488
385206
  amount,
384489
- ...hold.take !== void 0 ? { take: hold.take.toString() } : {}
385207
+ ...hold.take !== void 0 ? { take: hold.take.toString() } : {},
385208
+ ...hold.takeDecimals !== void 0 ? { takeDecimals: hold.takeDecimals } : {}
384490
385209
  });
384491
385210
  }
384492
385211
  return taken;
@@ -384513,6 +385232,7 @@ var IntentFiller = class {
384513
385232
  }
384514
385233
  const settled = executedHold(claimed, delivered.scaled, released);
384515
385234
  const charged = chargedFor(settled, delivered, released);
385235
+ const takenIn = released > 0n && settled.takeDecimals !== void 0 ? toScaled(released, settled.takeDecimals) : null;
384516
385236
  const settledBid = rows.find(
384517
385237
  (row) => row.reservations.some(
384518
385238
  (hold) => hold.limitOrderId === settled.limitOrderId && hold.amount === settled.amount && hold.take === settled.take
@@ -384531,6 +385251,7 @@ var IntentFiller = class {
384531
385251
  commitment,
384532
385252
  bid: settledBid,
384533
385253
  amount: share.toString(),
385254
+ amountIn: takenIn?.toString() ?? null,
384534
385255
  transactionHash: transactionHash ?? null
384535
385256
  });
384536
385257
  }
@@ -384683,45 +385404,6 @@ init_esm();
384683
385404
  init_Logger();
384684
385405
  init_asset_registry();
384685
385406
  init_decimal();
384686
-
384687
- // src/orderbook/usd.ts
384688
- init_esm_shims();
384689
- init_decimal();
384690
- init_asset_registry();
384691
- function limitOrderUsdEdges(orders) {
384692
- return orders.filter((order) => normalizeSymbol(order.base) !== normalizeSymbol(order.quote)).map((order) => ({
384693
- base: normalizeSymbol(order.base),
384694
- quote: normalizeSymbol(order.quote),
384695
- rate: new Decimal(order.price).div(ORDERBOOK_SCALE2.toString())
384696
- })).filter((edge) => edge.rate.isFinite() && edge.rate.gt(0));
384697
- }
384698
- function usdFactorsFrom(edges) {
384699
- const factors = /* @__PURE__ */ new Map();
384700
- for (const symbol3 of USD_STABLE_SYMBOLS) factors.set(symbol3, new Decimal(1));
384701
- const ordered = [...edges].sort((a6, b5) => a6.base.localeCompare(b5.base) || a6.quote.localeCompare(b5.quote));
384702
- let grew = true;
384703
- while (grew) {
384704
- grew = false;
384705
- for (const edge of ordered) {
384706
- const usdBase = factors.get(edge.base);
384707
- const usdQuote = factors.get(edge.quote);
384708
- if (usdBase && !usdQuote) {
384709
- factors.set(edge.quote, usdBase.div(edge.rate));
384710
- grew = true;
384711
- } else if (usdQuote && !usdBase) {
384712
- factors.set(edge.base, usdQuote.mul(edge.rate));
384713
- grew = true;
384714
- }
384715
- }
384716
- }
384717
- return factors;
384718
- }
384719
- function usdValueOf(factors, symbol3, amount) {
384720
- const factor = factors.get(normalizeSymbol(symbol3));
384721
- return factor ? amount.mul(factor) : null;
384722
- }
384723
-
384724
- // src/strategies/fx.ts
384725
385407
  function adjustDecimalsFloor(amount, fromDecimals, toDecimals) {
384726
385408
  if (fromDecimals === toDecimals) return amount;
384727
385409
  if (fromDecimals < toDecimals) return amount * BigInt(10 ** (toDecimals - fromDecimals));
@@ -384865,11 +385547,13 @@ var FXFiller = class _FXFiller {
384865
385547
  * - Treat a payout below the ask as a partial fill, which only orders
384866
385548
  * without output calldata and without an earlier partial fill allow.
384867
385549
  * - Cap each payout by the filler's balance plus funding-venue withdrawals,
384868
- * then gate it on fees (full fills) and the same-asset spread.
385550
+ * then gate it on fees (full fills of orders below `minOrderSizeUsd`) and the
385551
+ * same-asset spread.
384869
385552
  * - Cache the resulting bids for later use in `executeOrder`.
384870
385553
  */
384871
385554
  async calculateProfitability(order) {
384872
385555
  if (order.id) this.contractService.cacheService.clearPartialFill(order.id);
385556
+ if (order.id) this.contractService.cacheService.clearFeeCheckWaived(order.id);
384873
385557
  if (this.halted) {
384874
385558
  this.logger.warn({ orderId: order.id }, "FXFiller halted \u2014 rejecting order");
384875
385559
  return 0;
@@ -384885,6 +385569,15 @@ var FXFiller = class _FXFiller {
384885
385569
  const plans = [];
384886
385570
  const plannedOn = /* @__PURE__ */ new Map();
384887
385571
  const spentOn = /* @__PURE__ */ new Map();
385572
+ let atOrAboveMinSize;
385573
+ const feeCheckWaived = async () => {
385574
+ if (atOrAboveMinSize === void 0) {
385575
+ const usd = await this.getOrderUsdValue(order);
385576
+ atOrAboveMinSize = usd !== null && usd.inputUsd.gte(this.configService.getMinOrderSizeUsd());
385577
+ }
385578
+ return atOrAboveMinSize;
385579
+ };
385580
+ let feesWaived = false;
384888
385581
  for (let leg = 0; leg < order.inputs.length; leg++) {
384889
385582
  const input = order.inputs[leg];
384890
385583
  const output3 = order.output.assets[leg];
@@ -385128,17 +385821,21 @@ var FXFiller = class _FXFiller {
385128
385821
  const executionCost = totalCostInSourceFeeToken + relayerFeeInSourceFeeToken;
385129
385822
  const partialEdgeUsd = sameAssetEdgeUsd.plus(payoutSurplusUsd);
385130
385823
  if (!partialFill && order.fees < executionCost) {
385131
- this.logger.info(
385132
- {
385133
- orderId: order.id,
385134
- orderFees: formatUnits(order.fees, feeTokenDecimals),
385135
- fillGas: formatUnits(totalCostInSourceFeeToken, feeTokenDecimals),
385136
- relayerFee: formatUnits(relayerFeeInSourceFeeToken, feeTokenDecimals),
385137
- executionCost: formatUnits(executionCost, feeTokenDecimals)
385138
- },
385139
- "Skipping order: attached fees do not cover execution cost (fill gas + relayer fee)"
385140
- );
385141
- continue;
385824
+ if (!await feeCheckWaived()) {
385825
+ this.logger.info(
385826
+ {
385827
+ orderId: order.id,
385828
+ orderFees: formatUnits(order.fees, feeTokenDecimals),
385829
+ fillGas: formatUnits(totalCostInSourceFeeToken, feeTokenDecimals),
385830
+ relayerFee: formatUnits(relayerFeeInSourceFeeToken, feeTokenDecimals),
385831
+ executionCost: formatUnits(executionCost, feeTokenDecimals),
385832
+ minOrderSizeUsd: this.configService.getMinOrderSizeUsd()
385833
+ },
385834
+ "Skipping order: attached fees do not cover execution cost (fill gas + relayer fee)"
385835
+ );
385836
+ continue;
385837
+ }
385838
+ feesWaived = true;
385142
385839
  }
385143
385840
  if (sameAsset && !sameAssetProfitable) {
385144
385841
  this.logger.info(
@@ -385172,7 +385869,8 @@ var FXFiller = class _FXFiller {
385172
385869
  realizedSpreadProfit: formatUnits(realizedSpreadProfit, feeTokenDecimals),
385173
385870
  payoutSurplusUsd: payoutSurplusUsd.toString(),
385174
385871
  totalProfit,
385175
- profitable: totalProfit > 0
385872
+ profitable: totalProfit > 0,
385873
+ feeCheckWaived: !partialFill && order.fees < executionCost
385176
385874
  },
385177
385875
  "FX swap profitability evaluation"
385178
385876
  );
@@ -385186,6 +385884,7 @@ var FXFiller = class _FXFiller {
385186
385884
  payout: toScaled(finalOutputAmount, outputDecimals),
385187
385885
  fillerOutputs,
385188
385886
  fillerInputs,
385887
+ inputDecimals,
385189
385888
  fundingCalls: [...fundingCalls],
385190
385889
  partialFill,
385191
385890
  profit: totalProfit,
@@ -385204,6 +385903,7 @@ var FXFiller = class _FXFiller {
385204
385903
  plans[0].budget
385205
385904
  );
385206
385905
  this.contractService.cacheService.setPartialFill(order.id, plans[0].partialFill);
385906
+ this.contractService.cacheService.setFeeCheckWaived(order.id, feesWaived);
385207
385907
  this.contractService.cacheService.setMatchedLimitOrder(order.id, [
385208
385908
  { limitOrderId: plans[0].limitOrderId, payout: plans[0].payout }
385209
385909
  ]);
@@ -386470,6 +387170,7 @@ async function bootFiller(config5, options) {
386470
387170
  rebalancing: config5.rebalancing,
386471
387171
  targetGasUnits: config5.simplex.targetGasUnits,
386472
387172
  bidValiditySeconds: config5.simplex.bidValiditySeconds,
387173
+ minOrderSizeUsd: config5.simplex.minOrderSizeUsd,
386473
387174
  blockScanIntervalSeconds: config5.simplex.blockScanIntervalSeconds,
386474
387175
  gasFeeBump: config5.simplex.gasFeeBump,
386475
387176
  overfillProtection: config5.simplex.overfillProtection,
@@ -386678,9 +387379,10 @@ async function bootFiller(config5, options) {
386678
387379
  const activity = new ActivityRecorder(options.data.activity, options.loggers, { describeToken });
386679
387380
  activity.attach(intentFiller.monitor);
386680
387381
  const network = resolvedChains.some((chain2) => chainByChainId(chain2.chainId)?.network === "testnet") ? "testnet" : "mainnet";
387382
+ const indexerUrl = config5.simplex.indexerUrl ?? DEFAULT_INDEXER_URLS[network];
386681
387383
  void backfillOrderSummaries({
386682
387384
  store: options.data.activity,
386683
- indexerUrl: config5.simplex.indexerUrl ?? DEFAULT_INDEXER_URLS[network],
387385
+ indexerUrl,
386684
387386
  fillerAddress: runtimeSigner.address,
386685
387387
  describeToken,
386686
387388
  onUpdated: (rows) => {
@@ -386746,6 +387448,15 @@ async function bootFiller(config5, options) {
386746
387448
  vaultVenue?.startSweeping();
386747
387449
  started.push(() => balanceProvider.stop());
386748
387450
  await balanceProvider.start();
387451
+ if (options.signer) {
387452
+ const inventoryRecorder = new InventoryRecorder(
387453
+ options.data.inventory,
387454
+ balanceProvider,
387455
+ moduleLogger(options.loggers, "inventory")
387456
+ );
387457
+ started.push(() => inventoryRecorder.stop());
387458
+ inventoryRecorder.start();
387459
+ }
386749
387460
  const watchOnlyChains = watchOnlyConfig ? Object.entries(watchOnlyConfig).filter(([, value]) => value === true).map(([chainId]) => Number.parseInt(chainId, 10)) : [];
386750
387461
  logger8.info(
386751
387462
  {
@@ -386791,6 +387502,8 @@ async function bootFiller(config5, options) {
386791
387502
  rebalancingService,
386792
387503
  resolvedChains,
386793
387504
  fillerAddress: runtimeSigner.address,
387505
+ indexerUrl,
387506
+ describeToken,
386794
387507
  watchOnly: watchOnlyConfig,
386795
387508
  config: config5,
386796
387509
  configPath: options.configPath,
@@ -387074,10 +387787,20 @@ var MemoryLimitOrderStore = class {
387074
387787
  commitment: fill.commitment,
387075
387788
  bid: fill.bid ?? null,
387076
387789
  amount: fill.amount,
387790
+ amountIn: fill.amountIn ?? null,
387077
387791
  transactionHash: fill.transactionHash ?? null,
387078
387792
  filledAt: sqliteDatetime(/* @__PURE__ */ new Date())
387079
387793
  });
387080
387794
  }
387795
+ async fillHistory() {
387796
+ return this.fillRows.flatMap((fill) => {
387797
+ const order = this.orders.get(fill.limitOrderId);
387798
+ if (!order) return [];
387799
+ const { id: id2, limitOrderId, amount, amountIn, filledAt } = fill;
387800
+ const { book, base: base4, quote, side, price } = order;
387801
+ return [{ id: id2, limitOrderId, book, base: base4, quote, side, price, amount, amountIn, filledAt }];
387802
+ });
387803
+ }
387081
387804
  async fills(limitOrderId, limit = 100) {
387082
387805
  return this.fillRows.filter((fill) => fill.limitOrderId === limitOrderId).slice(-capLimit(limit)).reverse().map((fill) => ({ ...fill }));
387083
387806
  }
@@ -387184,12 +387907,29 @@ var MemoryStateStore = class {
387184
387907
  this.state = { ...state };
387185
387908
  }
387186
387909
  };
387910
+ var MemoryInventoryStore = class {
387911
+ constructor() {
387912
+ this.rows = [];
387913
+ }
387914
+ async record(balances) {
387915
+ this.rows.push({ id: this.rows.length + 1, takenAt: sqliteDatetime(/* @__PURE__ */ new Date()), balances: { ...balances } });
387916
+ }
387917
+ async latest() {
387918
+ const last = this.rows[this.rows.length - 1];
387919
+ return last ? { ...last, balances: { ...last.balances } } : null;
387920
+ }
387921
+ async since(from32) {
387922
+ const cutoff = sqliteDatetime(from32);
387923
+ return this.rows.filter((row) => row.takenAt >= cutoff).map((row) => ({ ...row, balances: { ...row.balances } }));
387924
+ }
387925
+ };
387187
387926
  var MemoryDataStore = class {
387188
387927
  constructor() {
387189
387928
  this.bids = new MemoryBidStore();
387190
387929
  this.activity = new MemoryActivityStore();
387191
387930
  this.state = new MemoryStateStore();
387192
387931
  this.limitOrders = new MemoryLimitOrderStore();
387932
+ this.inventory = new MemoryInventoryStore();
387193
387933
  }
387194
387934
  };
387195
387935
 
@@ -387801,6 +388541,107 @@ var OrderScanner = class _OrderScanner {
387801
388541
  }
387802
388542
  };
387803
388543
 
388544
+ // src/data/indexer-inventory.ts
388545
+ init_esm_shims();
388546
+ init_esm();
388547
+ var READ_TIMEOUT_MS = 1e4;
388548
+ var SETTLED_AFTER_MS = 10 * 6e4;
388549
+ var BATCH = 40;
388550
+ var MAX_KEPT = 2e3;
388551
+ var IndexerInventory = class {
388552
+ constructor(options) {
388553
+ this.options = options;
388554
+ this.kept = /* @__PURE__ */ new Map();
388555
+ this.described = /* @__PURE__ */ new Map();
388556
+ this.fetch = options.fetch ?? fetch;
388557
+ }
388558
+ /**
388559
+ * A reading for each of `instants` the indexer could answer for. Never rejects: an instant it
388560
+ * could not be asked about is simply missing from the answer.
388561
+ */
388562
+ async at(instants, now) {
388563
+ const readings = [];
388564
+ const wanted = [];
388565
+ for (const instant of new Set(instants)) {
388566
+ const kept = this.kept.get(instant);
388567
+ if (kept) readings.push(kept);
388568
+ else wanted.push(instant);
388569
+ }
388570
+ try {
388571
+ for (let index3 = 0; index3 < wanted.length; index3 += BATCH) {
388572
+ for (const reading of await this.read(wanted.slice(index3, index3 + BATCH))) {
388573
+ readings.push(reading);
388574
+ if (reading.at <= now - SETTLED_AFTER_MS) this.keep(reading);
388575
+ }
388576
+ }
388577
+ } catch (err) {
388578
+ this.options.logger.warn(
388579
+ { err: err instanceof Error ? err.message : String(err) },
388580
+ "Could not read inventory from the indexer; carrying records by fills and sends alone"
388581
+ );
388582
+ }
388583
+ return readings;
388584
+ }
388585
+ keep(reading) {
388586
+ if (this.kept.size >= MAX_KEPT) this.kept.clear();
388587
+ this.kept.set(reading.at, reading);
388588
+ }
388589
+ /** One request for several instants, each its own aliased query at that moment in history. */
388590
+ async read(instants) {
388591
+ const queries = instants.map(
388592
+ (instant, index3) => `r${index3}: solverInventories(filter: { solver: { equalTo: $solver } }, blockHeight: "${Math.floor(instant)}", first: 1000) { nodes { chain tokenAddress balance } }`
388593
+ );
388594
+ const response = await this.fetch(this.options.indexerUrl, {
388595
+ method: "POST",
388596
+ headers: { "content-type": "application/json" },
388597
+ body: JSON.stringify({
388598
+ query: `query SolverInventory($solver: String!) {
388599
+ ${queries.join("\n")}
388600
+ }`,
388601
+ variables: { solver: this.options.solver.toLowerCase() }
388602
+ }),
388603
+ signal: AbortSignal.timeout(READ_TIMEOUT_MS)
388604
+ });
388605
+ if (!response.ok) throw new Error(`Indexer responded ${response.status}`);
388606
+ const body = await response.json();
388607
+ if (body.errors?.length) throw new Error(body.errors.map((error) => error.message).join("; "));
388608
+ const readings = [];
388609
+ for (const [index3, instant] of instants.entries()) {
388610
+ const rows = body.data?.[`r${index3}`]?.nodes;
388611
+ if (rows) readings.push(await this.readingOf(instant, rows));
388612
+ }
388613
+ return readings;
388614
+ }
388615
+ async readingOf(at, rows) {
388616
+ const balances = {};
388617
+ const chains2 = {};
388618
+ for (const row of rows) {
388619
+ const token = await this.describe(row.chain, row.tokenAddress);
388620
+ if (!token) continue;
388621
+ let amount;
388622
+ try {
388623
+ amount = Number(formatUnits(BigInt(row.balance), token.decimals));
388624
+ } catch {
388625
+ continue;
388626
+ }
388627
+ balances[token.symbol] = (balances[token.symbol] ?? 0) + amount;
388628
+ chains2[token.symbol] = [...chains2[token.symbol] ?? [], row.chain];
388629
+ }
388630
+ return { at, balances, chains: chains2 };
388631
+ }
388632
+ /** A token's symbol and decimals, remembered once both are known. */
388633
+ async describe(chain2, token) {
388634
+ const key = `${chain2}:${token.toLowerCase()}`;
388635
+ const known3 = this.described.get(key);
388636
+ if (known3) return known3;
388637
+ const { symbol: symbol3, decimals } = await this.options.describeToken(chain2, token);
388638
+ if (!symbol3 || decimals === null) return null;
388639
+ const description = { symbol: symbol3, decimals };
388640
+ this.described.set(key, description);
388641
+ return description;
388642
+ }
388643
+ };
388644
+
387804
388645
  // src/simplex.ts
387805
388646
  var EVENT_MAP = {
387806
388647
  newOrder: "order:detected",
@@ -387846,10 +388687,66 @@ var LimitOrderController = class {
387846
388687
  ]);
387847
388688
  return { order, fills, bids };
387848
388689
  }
388690
+ /**
388691
+ * What the operator's buys and sells earned over `period`, bucketed on a clock
388692
+ * `tzOffsetMinutes` behind UTC.
388693
+ *
388694
+ * Profit is the change the period's fills made to what the solver holds, and
388695
+ * it is compared with the inventory the period began with. That inventory
388696
+ * comes from the stored snapshots and, when the balances have been read in
388697
+ * full, from what is held now, each carried to the period's start by what
388698
+ * moved between. The indexer says what moved, deposits and withdrawals
388699
+ * included, wherever it has followed the solver; elsewhere the fills and
388700
+ * sends on record stand in. It answers whether or not an orderbook is
388701
+ * configured, and whether or not the indexer does.
388702
+ */
388703
+ async profitability(period, tzOffsetMinutes = 0) {
388704
+ const now = Date.now();
388705
+ const { data: data4, balanceProvider } = this.runtime;
388706
+ const [fills, snapshots, ledger] = await Promise.all([
388707
+ data4.limitOrders.fillHistory(),
388708
+ data4.inventory.since(/* @__PURE__ */ new Date(0)),
388709
+ data4.activity.walletTxs(500)
388710
+ ]);
388711
+ const inventory = snapshots.map((snapshot) => ({
388712
+ at: parseUtc(snapshot.takenAt),
388713
+ balances: snapshot.balances
388714
+ }));
388715
+ const held = inventoryOf(balanceProvider.getSnapshot());
388716
+ if (held) inventory.push({ at: now, balances: held, live: true });
388717
+ const outflows = [];
388718
+ for (const tx of ledger) {
388719
+ const amount = Number(tx.amount);
388720
+ if (tx.kind === "send" && tx.token && Number.isFinite(amount) && amount > 0) {
388721
+ outflows.push({ at: tx.ts, symbol: tx.token, amount });
388722
+ }
388723
+ }
388724
+ const instants = inventoryInstants(fills, { period, now, tzOffsetMinutes, inventory });
388725
+ const readings = await this.indexerInventory().at(instants, now);
388726
+ return summarizeProfit(fills, { period, now, tzOffsetMinutes, inventory, outflows, readings });
388727
+ }
388728
+ /** One reader for the life of the filler, so the readings it has already fetched are kept. */
388729
+ indexerInventory() {
388730
+ this.indexed ?? (this.indexed = new IndexerInventory({
388731
+ indexerUrl: this.runtime.indexerUrl,
388732
+ solver: this.runtime.fillerAddress,
388733
+ describeToken: this.runtime.describeToken,
388734
+ logger: this.runtime.loggers.get("inventory")
388735
+ }));
388736
+ return this.indexed;
388737
+ }
387849
388738
  /** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
387850
388739
  books() {
387851
388740
  return this.service.books();
387852
388741
  }
388742
+ /** Public bids and asks across solvers, optionally restricted to a route. */
388743
+ orderbookSnapshot(book, filters = {}) {
388744
+ return this.service.orderbookSnapshot(book, filters);
388745
+ }
388746
+ /** Every solver's order behind one public price level. */
388747
+ orderbookLevelOrders(book, level) {
388748
+ return this.service.orderbookLevelOrders(book, level);
388749
+ }
387853
388750
  /** Creates the order, posts it, and reports what the orderbook made of it. */
387854
388751
  async create(request3) {
387855
388752
  const posted = await this.service.create(request3);
@@ -388940,6 +389837,40 @@ var SqliteBidStore = class {
388940
389837
  }
388941
389838
  };
388942
389839
 
389840
+ // src/data/sqlite/inventory.ts
389841
+ init_esm_shims();
389842
+ var SqliteInventoryStore = class {
389843
+ constructor(db) {
389844
+ this.db = db;
389845
+ this.db.exec(`
389846
+ CREATE TABLE IF NOT EXISTS inventory_snapshots (
389847
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
389848
+ taken_at TEXT NOT NULL DEFAULT (datetime('now')),
389849
+ balances TEXT NOT NULL
389850
+ );
389851
+
389852
+ CREATE INDEX IF NOT EXISTS idx_inventory_snapshots_taken_at ON inventory_snapshots(taken_at);
389853
+ `);
389854
+ }
389855
+ toSnapshot(row) {
389856
+ return { id: row.id, takenAt: row.takenAt, balances: JSON.parse(row.balances) };
389857
+ }
389858
+ async record(balances) {
389859
+ this.db.prepare("INSERT INTO inventory_snapshots (balances) VALUES (?)").run(JSON.stringify(balances));
389860
+ }
389861
+ async latest() {
389862
+ const row = this.db.prepare("SELECT id, taken_at as takenAt, balances FROM inventory_snapshots ORDER BY id DESC LIMIT 1").get();
389863
+ return row ? this.toSnapshot(row) : null;
389864
+ }
389865
+ async since(from32) {
389866
+ const cutoff = from32.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
389867
+ const rows = this.db.prepare(
389868
+ "SELECT id, taken_at as takenAt, balances FROM inventory_snapshots WHERE taken_at >= ? ORDER BY taken_at, id"
389869
+ ).all(cutoff);
389870
+ return rows.map((row) => this.toSnapshot(row));
389871
+ }
389872
+ };
389873
+
388943
389874
  // src/data/sqlite/limit-orders.ts
388944
389875
  init_esm_shims();
388945
389876
  init_Logger();
@@ -389013,12 +389944,17 @@ var SqliteLimitOrderStore = class {
389013
389944
  commitment TEXT NOT NULL,
389014
389945
  bid TEXT,
389015
389946
  amount TEXT NOT NULL,
389947
+ amount_in TEXT,
389016
389948
  transaction_hash TEXT,
389017
389949
  filled_at TEXT NOT NULL DEFAULT (datetime('now'))
389018
389950
  );
389019
389951
 
389020
389952
  CREATE INDEX IF NOT EXISTS idx_limit_order_fills_order ON limit_order_fills(limit_order_id);
389021
389953
  `);
389954
+ if (!columnNames(this.db, "limit_order_fills").has("amount_in")) {
389955
+ this.db.exec("ALTER TABLE limit_order_fills ADD COLUMN amount_in TEXT");
389956
+ this.logger.info({ column: "amount_in" }, "Migrated limit order fill schema");
389957
+ }
389022
389958
  }
389023
389959
  // biome-ignore lint/suspicious/noExplicitAny: raw sqlite row
389024
389960
  toLimitOrder(row) {
@@ -389128,12 +390064,27 @@ var SqliteLimitOrderStore = class {
389128
390064
  }
389129
390065
  async recordFill(fill) {
389130
390066
  this.db.prepare(
389131
- "INSERT INTO limit_order_fills (limit_order_id, commitment, bid, amount, transaction_hash) VALUES (?, ?, ?, ?, ?)"
389132
- ).run(fill.limitOrderId, fill.commitment, fill.bid ?? null, fill.amount, fill.transactionHash ?? null);
390067
+ "INSERT INTO limit_order_fills (limit_order_id, commitment, bid, amount, amount_in, transaction_hash) VALUES (?, ?, ?, ?, ?, ?)"
390068
+ ).run(
390069
+ fill.limitOrderId,
390070
+ fill.commitment,
390071
+ fill.bid ?? null,
390072
+ fill.amount,
390073
+ fill.amountIn ?? null,
390074
+ fill.transactionHash ?? null
390075
+ );
390076
+ }
390077
+ async fillHistory() {
390078
+ return this.db.prepare(
390079
+ `SELECT f.id, f.limit_order_id as limitOrderId, o.book, o.base, o.quote, o.side, o.price,
390080
+ f.amount, f.amount_in as amountIn, f.filled_at as filledAt
390081
+ FROM limit_order_fills f JOIN limit_orders o ON o.id = f.limit_order_id
390082
+ ORDER BY f.filled_at, f.id`
390083
+ ).all();
389133
390084
  }
389134
390085
  async fills(limitOrderId, limit = 100) {
389135
390086
  return this.db.prepare(
389136
- `SELECT id, limit_order_id as limitOrderId, commitment, bid, amount, transaction_hash as transactionHash,
390087
+ `SELECT id, limit_order_id as limitOrderId, commitment, bid, amount, amount_in as amountIn, transaction_hash as transactionHash,
389137
390088
  filled_at as filledAt
389138
390089
  FROM limit_order_fills WHERE limit_order_id = ? ORDER BY id DESC LIMIT ?`
389139
390090
  ).all(limitOrderId, Math.min(Math.max(limit, 1), 500));
@@ -389322,6 +390273,7 @@ var SqliteDataStore = class {
389322
390273
  this.activity = new SqliteActivityStore(activityDb, loggers);
389323
390274
  this.state = new SqliteStateStore(bidsDb, dataDir, loggers);
389324
390275
  this.limitOrders = new SqliteLimitOrderStore(bidsDb, loggers);
390276
+ this.inventory = new SqliteInventoryStore(activityDb);
389325
390277
  this.logger.info({ dataDir }, "SQLite data store opened");
389326
390278
  }
389327
390279
  /**