@hyperbridge/simplex 0.16.12 → 0.17.0

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);
@@ -30656,7 +30656,8 @@ var init_asset_registry = __esm({
30656
30656
  ZARP: { resolve: (r7, chain2) => r7.getAssetBySymbol(chain2, "ZARP") },
30657
30657
  EURC: { resolve: (r7, chain2) => r7.getAssetBySymbol(chain2, "EURC") },
30658
30658
  XSGD: { resolve: (r7, chain2) => r7.getAssetBySymbol(chain2, "XSGD") },
30659
- TRYB: { resolve: (r7, chain2) => r7.getAssetBySymbol(chain2, "TRYB") }
30659
+ TRYB: { resolve: (r7, chain2) => r7.getAssetBySymbol(chain2, "TRYB") },
30660
+ BRIDGE: { resolve: (r7, chain2) => r7.getAssetBySymbol(chain2, "BRIDGE") }
30660
30661
  };
30661
30662
  USD_STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT", "DAI"]);
30662
30663
  ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
@@ -30755,7 +30756,7 @@ function assertPairSymbolsResolve(pairs, registry, chainNames) {
30755
30756
  const pairSymbols = new Set(pairs.flatMap((pair) => [pair.token0, pair.token1]));
30756
30757
  for (const pair of pairs) {
30757
30758
  for (const symbol3 of [pair.token0, pair.token1]) {
30758
- const resolvesSomewhere = chainNames.some((chainName) => registry.getAddress(symbol3, chainName) !== null);
30759
+ const resolvesSomewhere = chainNames.some((chainName2) => registry.getAddress(symbol3, chainName2) !== null);
30759
30760
  if (!resolvesSomewhere) {
30760
30761
  throw new Error(
30761
30762
  `pairs.${pair.token0}/${pair.token1}: '${symbol3}' does not resolve to a deployed contract on any configured chain`
@@ -30763,15 +30764,15 @@ function assertPairSymbolsResolve(pairs, registry, chainNames) {
30763
30764
  }
30764
30765
  }
30765
30766
  }
30766
- for (const chainName of chainNames) {
30767
+ for (const chainName2 of chainNames) {
30767
30768
  const addressOwner = /* @__PURE__ */ new Map();
30768
30769
  for (const symbol3 of pairSymbols) {
30769
- const address = registry.getAddress(symbol3, chainName)?.toLowerCase();
30770
+ const address = registry.getAddress(symbol3, chainName2)?.toLowerCase();
30770
30771
  if (!address) continue;
30771
30772
  const owner = addressOwner.get(address);
30772
30773
  if (owner && normalizeSymbol(owner) !== normalizeSymbol(symbol3)) {
30773
30774
  throw new Error(
30774
- `assets: '${symbol3}' and '${owner}' both resolve to ${address} on ${chainName} \u2014 symbols must map to distinct contracts`
30775
+ `assets: '${symbol3}' and '${owner}' both resolve to ${address} on ${chainName2} \u2014 symbols must map to distinct contracts`
30775
30776
  );
30776
30777
  }
30777
30778
  addressOwner.set(address, symbol3);
@@ -48802,11 +48803,11 @@ var init_esm2 = __esm({
48802
48803
  init_encodeFunctionData2();
48803
48804
  init_encodePacked2();
48804
48805
  init_parseEventLogs2();
48805
- init_isAddress2();
48806
48806
  init_defineChain2();
48807
48807
  init_concat2();
48808
48808
  init_isHex2();
48809
48809
  init_pad2();
48810
+ init_slice2();
48810
48811
  init_fromBytes2();
48811
48812
  init_fromHex2();
48812
48813
  init_toBytes2();
@@ -48814,6 +48815,7 @@ var init_esm2 = __esm({
48814
48815
  init_keccak2562();
48815
48816
  init_toEventSelector2();
48816
48817
  init_toFunctionSelector2();
48818
+ init_recoverAddress2();
48817
48819
  init_formatUnits2();
48818
48820
  init_parseUnits2();
48819
48821
  }
@@ -51069,7 +51071,7 @@ var require_bn = __commonJS({
51069
51071
  }
51070
51072
  return this;
51071
51073
  };
51072
- BN3.prototype.add = function add3(num2) {
51074
+ BN3.prototype.add = function add4(num2) {
51073
51075
  var res;
51074
51076
  if (num2.negative !== 0 && this.negative === 0) {
51075
51077
  num2.negative = 0;
@@ -52906,7 +52908,7 @@ var require_bn = __commonJS({
52906
52908
  }
52907
52909
  return this.m.sub(a6)._forceRed(this);
52908
52910
  };
52909
- Red.prototype.add = function add3(a6, b5) {
52911
+ Red.prototype.add = function add4(a6, b5) {
52910
52912
  this._verify2(a6, b5);
52911
52913
  var res = a6.add(b5);
52912
52914
  if (res.cmp(this.m) >= 0) {
@@ -55786,10 +55788,10 @@ var init_fflate = __esm({
55786
55788
  lm = void 0;
55787
55789
  break;
55788
55790
  } else {
55789
- let add3 = sym - 254;
55791
+ let add4 = sym - 254;
55790
55792
  if (sym > 264) {
55791
55793
  const i6 = sym - 257, b5 = fleb[i6];
55792
- add3 = bits(dat, pos, (1 << b5) - 1) + fl[i6];
55794
+ add4 = bits(dat, pos, (1 << b5) - 1) + fl[i6];
55793
55795
  pos += b5;
55794
55796
  }
55795
55797
  const d5 = dm[bits16(dat, pos) & dms], dsym = d5 >>> 4;
@@ -55805,7 +55807,7 @@ var init_fflate = __esm({
55805
55807
  throw "unexpected EOF";
55806
55808
  if (noBuf)
55807
55809
  cbuf(bt + 131072);
55808
- const end = bt + add3;
55810
+ const end = bt + add4;
55809
55811
  for (; bt < end; bt += 4) {
55810
55812
  buf[bt] = buf[bt - dt];
55811
55813
  buf[bt + 1] = buf[bt + 1 - dt];
@@ -83509,15 +83511,15 @@ var require_fromEvent = __commonJS({
83509
83511
  return function(handler) {
83510
83512
  return target[methodName](eventName, handler, options);
83511
83513
  };
83512
- }) : isNodeStyleEventEmitter(target) ? nodeEventEmitterMethods.map(toCommonHandlerRegistry(target, eventName)) : isJQueryStyleEventEmitter(target) ? jqueryMethods.map(toCommonHandlerRegistry(target, eventName)) : [], 2), add3 = _a10[0], remove = _a10[1];
83513
- if (!add3) {
83514
+ }) : isNodeStyleEventEmitter(target) ? nodeEventEmitterMethods.map(toCommonHandlerRegistry(target, eventName)) : isJQueryStyleEventEmitter(target) ? jqueryMethods.map(toCommonHandlerRegistry(target, eventName)) : [], 2), add4 = _a10[0], remove = _a10[1];
83515
+ if (!add4) {
83514
83516
  if (isArrayLike_1.isArrayLike(target)) {
83515
83517
  return mergeMap_1.mergeMap(function(subTarget) {
83516
83518
  return fromEvent(subTarget, eventName, options);
83517
83519
  })(innerFrom_1.innerFrom(target));
83518
83520
  }
83519
83521
  }
83520
- if (!add3) {
83522
+ if (!add4) {
83521
83523
  throw new TypeError("Invalid event target");
83522
83524
  }
83523
83525
  return new Observable_1.Observable(function(subscriber) {
@@ -83528,7 +83530,7 @@ var require_fromEvent = __commonJS({
83528
83530
  }
83529
83531
  return subscriber.next(1 < args.length ? args : args[0]);
83530
83532
  };
83531
- add3(handler);
83533
+ add4(handler);
83532
83534
  return function() {
83533
83535
  return remove(handler);
83534
83536
  };
@@ -135098,21 +135100,21 @@ var init_upgrades = __esm({
135098
135100
  });
135099
135101
 
135100
135102
  // ../../node_modules/.pnpm/@polkadot+types-known@16.5.6/node_modules/@polkadot/types-known/util.js
135101
- function withNames(chainName, specName, fn) {
135102
- return fn(chainName.toString(), specName.toString());
135103
+ function withNames(chainName2, specName, fn) {
135104
+ return fn(chainName2.toString(), specName.toString());
135103
135105
  }
135104
135106
  function filterVersions(versions = [], specVersion) {
135105
135107
  return versions.filter(({ minmax: [min2, max3] }) => (min2 === void 0 || min2 === null || specVersion >= min2) && (max3 === void 0 || max3 === null || specVersion <= max3)).reduce((result, { types: types4 }) => ({ ...result, ...types4 }), {});
135106
135108
  }
135107
- function getSpecExtensions({ knownTypes: knownTypes2 }, chainName, specName) {
135108
- return withNames(chainName, specName, (c6, s4) => ({
135109
+ function getSpecExtensions({ knownTypes: knownTypes2 }, chainName2, specName) {
135110
+ return withNames(chainName2, specName, (c6, s4) => ({
135109
135111
  ...knownTypes2.typesBundle?.spec?.[s4]?.signedExtensions ?? {},
135110
135112
  ...knownTypes2.typesBundle?.chain?.[c6]?.signedExtensions ?? {}
135111
135113
  }));
135112
135114
  }
135113
- function getSpecTypes({ knownTypes: knownTypes2 }, chainName, specName, specVersion) {
135115
+ function getSpecTypes({ knownTypes: knownTypes2 }, chainName2, specName, specVersion) {
135114
135116
  const _specVersion = bnToBn(specVersion).toNumber();
135115
- return withNames(chainName, specName, (c6, s4) => ({
135117
+ return withNames(chainName2, specName, (c6, s4) => ({
135116
135118
  // The order here is always, based on -
135117
135119
  // - spec then chain
135118
135120
  // - typesBundle takes higher precedence
@@ -135126,23 +135128,23 @@ function getSpecTypes({ knownTypes: knownTypes2 }, chainName, specName, specVers
135126
135128
  ...knownTypes2.types ?? {}
135127
135129
  }));
135128
135130
  }
135129
- function getSpecHasher({ knownTypes: knownTypes2 }, chainName, specName) {
135130
- return withNames(chainName, specName, (c6, s4) => knownTypes2.hasher || knownTypes2.typesBundle?.chain?.[c6]?.hasher || knownTypes2.typesBundle?.spec?.[s4]?.hasher || null);
135131
+ function getSpecHasher({ knownTypes: knownTypes2 }, chainName2, specName) {
135132
+ return withNames(chainName2, specName, (c6, s4) => knownTypes2.hasher || knownTypes2.typesBundle?.chain?.[c6]?.hasher || knownTypes2.typesBundle?.spec?.[s4]?.hasher || null);
135131
135133
  }
135132
- function getSpecRpc({ knownTypes: knownTypes2 }, chainName, specName) {
135133
- return withNames(chainName, specName, (c6, s4) => ({
135134
+ function getSpecRpc({ knownTypes: knownTypes2 }, chainName2, specName) {
135135
+ return withNames(chainName2, specName, (c6, s4) => ({
135134
135136
  ...knownTypes2.typesBundle?.spec?.[s4]?.rpc ?? {},
135135
135137
  ...knownTypes2.typesBundle?.chain?.[c6]?.rpc ?? {}
135136
135138
  }));
135137
135139
  }
135138
- function getSpecRuntime({ knownTypes: knownTypes2 }, chainName, specName) {
135139
- return withNames(chainName, specName, (c6, s4) => ({
135140
+ function getSpecRuntime({ knownTypes: knownTypes2 }, chainName2, specName) {
135141
+ return withNames(chainName2, specName, (c6, s4) => ({
135140
135142
  ...knownTypes2.typesBundle?.spec?.[s4]?.runtime ?? {},
135141
135143
  ...knownTypes2.typesBundle?.chain?.[c6]?.runtime ?? {}
135142
135144
  }));
135143
135145
  }
135144
- function getSpecAlias({ knownTypes: knownTypes2 }, chainName, specName) {
135145
- return withNames(chainName, specName, (c6, s4) => ({
135146
+ function getSpecAlias({ knownTypes: knownTypes2 }, chainName2, specName) {
135147
+ return withNames(chainName2, specName, (c6, s4) => ({
135146
135148
  // as per versions, first spec, then chain then finally non-versioned
135147
135149
  ...knownTypes2.typesBundle?.spec?.[s4]?.alias ?? {},
135148
135150
  ...knownTypes2.typesBundle?.chain?.[c6]?.alias ?? {},
@@ -156390,7 +156392,7 @@ var require_isTime = __commonJS({
156390
156392
  hourFormat: "hour24",
156391
156393
  mode: "default"
156392
156394
  };
156393
- var formats = {
156395
+ var formats2 = {
156394
156396
  hour24: {
156395
156397
  default: /^([01]?[0-9]|2[0-3]):([0-5][0-9])$/,
156396
156398
  withSeconds: /^([01]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/,
@@ -156405,7 +156407,7 @@ var require_isTime = __commonJS({
156405
156407
  function isTime(input, options) {
156406
156408
  options = (0, _merge.default)(options, default_time_options);
156407
156409
  if (typeof input !== "string") return false;
156408
- return formats[options.hourFormat][options.mode].test(input);
156410
+ return formats2[options.hourFormat][options.mode].test(input);
156409
156411
  }
156410
156412
  module2.exports = exports2.default;
156411
156413
  module2.exports.default = exports2.default;
@@ -233930,8 +233932,8 @@ function addToExternrefTable0(obj) {
233930
233932
  function passArrayJsValueToWasm0(array, malloc) {
233931
233933
  const ptr = malloc(array.length * 4, 4) >>> 0;
233932
233934
  for (let i6 = 0; i6 < array.length; i6++) {
233933
- const add3 = addToExternrefTable0(array[i6]);
233934
- getDataViewMemory0().setUint32(ptr + 4 * i6, add3, true);
233935
+ const add4 = addToExternrefTable0(array[i6]);
233936
+ getDataViewMemory0().setUint32(ptr + 4 * i6, add4, true);
233935
233937
  }
233936
233938
  WASM_VECTOR_LEN = array.length;
233937
233939
  return ptr;
@@ -234901,6 +234903,10 @@ function createSessionKeyStorage(options = {}) {
234901
234903
  listSessionKeys
234902
234904
  });
234903
234905
  }
234906
+ async function signTypedDigest(domainSeparator2, structHash, privateKey) {
234907
+ const digest = keccak2563(concat3(["0x1901", domainSeparator2, structHash]));
234908
+ return privateKeyToAccount(privateKey).sign({ hash: digest });
234909
+ }
234904
234910
  function encodeERC7821ExecuteBatch(calls) {
234905
234911
  const executionData = encodeAbiParameters2(
234906
234912
  [{ type: "tuple[]", components: erc7281_default.ABI[1].components }],
@@ -235162,6 +235168,9 @@ function cancelFromSourceContext(order, commitment) {
235162
235168
  ]
235163
235169
  );
235164
235170
  }
235171
+ function supportedReleases(stateMachineId) {
235172
+ return TESTNET_CHAINS.has(stateMachineId) ? TESTNET_INTENTS_VERSIONS : [SUPPORTED_INTENTS_VERSION];
235173
+ }
235165
235174
  async function readContractVersion(client, address) {
235166
235175
  try {
235167
235176
  return await client.readContract({ address, abi: CONTRACT_VERSION_ABI, functionName: "version" });
@@ -235170,14 +235179,17 @@ async function readContractVersion(client, address) {
235170
235179
  throw error;
235171
235180
  }
235172
235181
  }
235173
- async function supportsRateFills(client, gateway) {
235174
- return await readContractVersion(client, gateway) === SUPPORTED_INTENTS_VERSION;
235182
+ async function supportsRateFills(client, gateway, stateMachineId) {
235183
+ const version17 = await readContractVersion(client, gateway);
235184
+ return supportedReleases(stateMachineId).some((release) => release === version17);
235175
235185
  }
235176
- async function assertGatewayRelease(client, gateway) {
235186
+ async function assertGatewayRelease(client, gateway, stateMachineId) {
235187
+ const supported = supportedReleases(stateMachineId);
235177
235188
  const version17 = await readContractVersion(client, gateway);
235178
- if (version17 === SUPPORTED_INTENTS_VERSION) return;
235189
+ if (supported.some((release) => release === version17)) return;
235190
+ const required2 = supported.join(" or ");
235179
235191
  throw new Error(
235180
- version17 === void 0 ? `IntentGateway ${gateway} reports no version(); this SDK requires release ${SUPPORTED_INTENTS_VERSION}` : `IntentGateway ${gateway} reports release ${String(version17)}; this SDK requires release ${SUPPORTED_INTENTS_VERSION}`
235192
+ version17 === void 0 ? `IntentGateway ${gateway} reports no version(); this SDK requires release ${required2}` : `IntentGateway ${gateway} reports release ${String(version17)}; this SDK requires release ${required2}`
235181
235193
  );
235182
235194
  }
235183
235195
  function isCanonicalEvmToken(token) {
@@ -235219,6 +235231,40 @@ function decodeFillOrder(data4) {
235219
235231
  return null;
235220
235232
  }
235221
235233
  }
235234
+ function readSelectionFormat(client, chainId, gateway) {
235235
+ const key = `${chainId}:${gateway.toLowerCase()}`;
235236
+ const cached = formats.get(key);
235237
+ if (cached) return cached;
235238
+ const format2 = client.readContract({ address: gateway, abi: ABI3, functionName: "SELECT_SOLVER_TYPEHASH" }).then((typehash) => {
235239
+ switch (typehash.toLowerCase()) {
235240
+ case SELECT_SOLVER_TYPEHASH:
235241
+ return "userOpHash";
235242
+ case LEGACY_SELECT_SOLVER_TYPEHASH:
235243
+ return "address";
235244
+ default:
235245
+ throw new Error(
235246
+ `IntentGatewayV2 ${gateway} on chain ${chainId} has unknown SELECT_SOLVER_TYPEHASH ${typehash}`
235247
+ );
235248
+ }
235249
+ });
235250
+ formats.set(key, format2);
235251
+ format2.catch(() => {
235252
+ if (formats.get(key) === format2) formats.delete(key);
235253
+ });
235254
+ return format2;
235255
+ }
235256
+ function refreshSelectionFormat(client, chainId, gateway, stale) {
235257
+ const key = `${chainId}:${gateway.toLowerCase()}`;
235258
+ if (formats.get(key) === stale) formats.delete(key);
235259
+ return readSelectionFormat(client, chainId, gateway);
235260
+ }
235261
+ async function selectionOffStateDiff(client, gateway) {
235262
+ const word = await client.getStorageAt({ address: gateway, slot: SELECTION_PARAMS_SLOT });
235263
+ return {
235264
+ slot: SELECTION_PARAMS_SLOT,
235265
+ value: toHex3(hexToBigInt2(word ?? "0x0") & SOLVER_SELECTION_CLEARED, { size: 32 })
235266
+ };
235267
+ }
235222
235268
  function userOperationLogs(logs, entryPoint, userOpHash) {
235223
235269
  const [beforeExecution, userOperationEvent] = ENTRY_POINT_EVENT_ABI.map((event) => toEventSelector2(event));
235224
235270
  const isBoundary = (log4) => log4.address.toLowerCase() === entryPoint.toLowerCase() && (log4.topics[0] === beforeExecution || log4.topics[0] === userOperationEvent);
@@ -235237,6 +235283,40 @@ function userOperationLogs(logs, entryPoint, userOpHash) {
235237
235283
  while (start >= 0 && !isBoundary(logs[start])) start--;
235238
235284
  return logs.slice(start + 1, end);
235239
235285
  }
235286
+ async function fetchRundlerPriorityFee(bundlerUrl) {
235287
+ if (bundlersWithoutRundlerFees.has(bundlerUrl)) return null;
235288
+ try {
235289
+ const response = await fetch(bundlerUrl, {
235290
+ method: "POST",
235291
+ headers: { "Content-Type": "application/json" },
235292
+ body: JSON.stringify({
235293
+ jsonrpc: "2.0",
235294
+ id: 1,
235295
+ method: BundlerMethod.RUNDLER_MAX_PRIORITY_FEE_PER_GAS,
235296
+ params: []
235297
+ })
235298
+ });
235299
+ const { result, error } = await response.json();
235300
+ if (error) {
235301
+ if (error.code !== void 0 && UNSUPPORTED_REQUEST_CODES.has(error.code)) {
235302
+ bundlersWithoutRundlerFees.add(bundlerUrl);
235303
+ }
235304
+ return null;
235305
+ }
235306
+ return result == null ? null : BigInt(result);
235307
+ } catch {
235308
+ return null;
235309
+ }
235310
+ }
235311
+ function applyRundlerPriorityFee(fees4, params) {
235312
+ const { rundlerPriorityFee, baseFeePerGas, priorityFeeBumpPercent, maxFeeBumpPercent } = params;
235313
+ const priorityFloor = rundlerPriorityFee + rundlerPriorityFee * priorityFeeBumpPercent / 100n;
235314
+ const maxFeeFloor = baseFeePerGas + baseFeePerGas * maxFeeBumpPercent / 100n + priorityFloor;
235315
+ return {
235316
+ maxPriorityFeePerGas: fees4.maxPriorityFeePerGas > priorityFloor ? fees4.maxPriorityFeePerGas : priorityFloor,
235317
+ maxFeePerGas: fees4.maxFeePerGas > maxFeeFloor ? fees4.maxFeePerGas : maxFeeFloor
235318
+ };
235319
+ }
235240
235320
  function escrowSlot(commitment, leg) {
235241
235321
  const byCommitment = keccak2563(
235242
235322
  encodeAbiParameters2([{ type: "bytes32" }, { type: "uint256" }], [commitment, ESCROW_MAPPING_SLOT])
@@ -235422,7 +235502,7 @@ function encodePhantomBidDeclaration(declaration) {
235422
235502
  }
235423
235503
  return u8aToHex(new Uint8Array(bytes5));
235424
235504
  }
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;
235505
+ 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, ENTRY_POINT_V08, ENTRY_POINT_V09, KNOWN_ENTRY_POINTS, 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, LEGACY_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, TESTNET_INTENTS_VERSIONS, CONTRACT_VERSION_ABI, CANONICAL_EVM_TOKEN, formats, SELECTION_PARAMS_SLOT, SOLVER_SELECTION_CLEARED, SELECTION_FORMAT_BY_ENTRY_POINT, ENTRY_POINT_EVENT_ABI, BidImpl, BidManager, UNSUPPORTED_REQUEST_CODES, bundlersWithoutRundlerFees, 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, BID_PVG_ESTIMATION_PAYMASTER_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;
235426
235506
  var init_node10 = __esm({
235427
235507
  "../sdk/dist/node/index.js"() {
235428
235508
  "use strict";
@@ -237775,6 +237855,9 @@ var init_node10 = __esm({
237775
237855
  default: { name: "Nile Tronscan", url: "https://nile.tronscan.org" }
237776
237856
  }
237777
237857
  });
237858
+ ENTRY_POINT_V08 = "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108";
237859
+ ENTRY_POINT_V09 = "0x433709009B8330FDa32311DF1C2AFA402eD8D009";
237860
+ KNOWN_ENTRY_POINTS = [ENTRY_POINT_V09, ENTRY_POINT_V08];
237778
237861
  chainConfigs = {
237779
237862
  97: {
237780
237863
  chainId: 97,
@@ -237807,8 +237890,10 @@ var init_node10 = __esm({
237807
237890
  UniversalRouter: "0xcc6d5ece3d4a57245bf5a2f64f3ed9179b81f714",
237808
237891
  UniswapV3Quoter: "0x0000000000000000000000000000000000000000",
237809
237892
  UniswapV4Quoter: "0x0000000000000000000000000000000000000000",
237810
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
237811
- SolverAccount: "0xf98E484858F59C30e31D2695ac18F92bc7C7b799"
237893
+ Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
237894
+ EntryPoint: "0x433709009B8330FDa32311DF1C2AFA402eD8D009",
237895
+ SolverAccount: "0x7484C6Ff790b898bf7e6960Ffc82f63330445Dc2",
237896
+ SimplexPaymaster: "0x9f4a6F1254f27373C2aca5bDd0e48FbA45EA3aeC"
237812
237897
  },
237813
237898
  rpcEnvKey: "BSC_CHAPEL",
237814
237899
  defaultRpcUrl: "https://bnb-testnet.api.onfinality.io/public",
@@ -237843,7 +237928,7 @@ var init_node10 = __esm({
237843
237928
  UniswapRouter02: "0x0000000000000000000000000000000000000000",
237844
237929
  UniswapV2Factory: "0x0000000000000000000000000000000000000000",
237845
237930
  UniswapV3Factory: "0x0000000000000000000000000000000000000000",
237846
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
237931
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
237847
237932
  },
237848
237933
  rpcEnvKey: "GNOSIS_CHIADO",
237849
237934
  defaultRpcUrl: "https://gnosis-chiado-rpc.publicnode.com",
@@ -237883,7 +237968,7 @@ var init_node10 = __esm({
237883
237968
  UniswapV2Factory: "0x0000000000000000000000000000000000000000",
237884
237969
  UniswapV3Factory: "0x0000000000000000000000000000000000000000",
237885
237970
  Calldispatcher: "0xC7f13b6D03A0A7F3239d38897503E90553ABe155",
237886
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
237971
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
237887
237972
  },
237888
237973
  rpcEnvKey: "SEPOLIA",
237889
237974
  defaultRpcUrl: "https://1rpc.io/sepolia",
@@ -237934,7 +238019,7 @@ var init_node10 = __esm({
237934
238019
  },
237935
238020
  addresses: {
237936
238021
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
237937
- SolverAccount: "0x77c3394CA5881A74f18139AC87D0c11F8Faa90cC",
238022
+ SolverAccount: "0xaAd062555800a97Af062795189e32a3CBd045612",
237938
238023
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
237939
238024
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
237940
238025
  UniswapRouter02: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
@@ -237948,7 +238033,7 @@ var init_node10 = __esm({
237948
238033
  UniswapV4StateView: "0x7ffe42c4a5deea5b0fec41c94c136cf115597227",
237949
238034
  Calldispatcher: "0xc71251c8b3e7b02697a84363eef6dce8dfbdf333",
237950
238035
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
237951
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238036
+ EntryPoint: "0x433709009B8330FDa32311DF1C2AFA402eD8D009",
237952
238037
  CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
237953
238038
  SimplexPaymaster: "0xD4340d7466e040626383cb9cda9307ba8E081149",
237954
238039
  Usdt0Oft: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee"
@@ -237980,7 +238065,8 @@ var init_node10 = __esm({
237980
238065
  USDC: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
237981
238066
  USDT: "0x55d398326f99059ff775485246999027b3197955",
237982
238067
  EXT: "0x7C8c11ADb8EF7cd3CFa718008Ea048445C6E7209",
237983
- cNGN: "0xa8AEA66B361a8d53e8865c62D142167Af28Af058"
238068
+ cNGN: "0xa8AEA66B361a8d53e8865c62D142167Af28Af058",
238069
+ BRIDGE: "0x5b0c50fDd52ECC0d4c682c441eaBaD41FfDEABBB"
237984
238070
  },
237985
238071
  tokenDecimals: {
237986
238072
  USDC: 18,
@@ -237989,19 +238075,21 @@ var init_node10 = __esm({
237989
238075
  // Binance-pegged stables above. Every phantom standard_amount and pool rate divides
237990
238076
  // by this, so the divergence from its neighbours here is load-bearing, not a typo.
237991
238077
  cNGN: 6,
237992
- EXT: 18
238078
+ EXT: 18,
238079
+ BRIDGE: 18
237993
238080
  },
237994
238081
  tokenStorageSlots: {
237995
238082
  USDT: { balanceSlot: 1, allowanceSlot: 2 },
237996
238083
  USDC: { balanceSlot: 1, allowanceSlot: 2 },
237997
238084
  WETH: { balanceSlot: 3, allowanceSlot: 4 },
237998
238085
  DAI: { balanceSlot: 0, allowanceSlot: 0 },
237999
- cNGN: { balanceSlot: 201, allowanceSlot: 202 }
238086
+ cNGN: { balanceSlot: 201, allowanceSlot: 202 },
238000
238087
  // custom upgradeable layout, as on Base
238088
+ BRIDGE: { balanceSlot: 0, allowanceSlot: 1 }
238001
238089
  },
238002
238090
  addresses: {
238003
238091
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
238004
- SolverAccount: "0x77c3394CA5881A74f18139AC87D0c11F8Faa90cC",
238092
+ SolverAccount: "0xaAd062555800a97Af062795189e32a3CBd045612",
238005
238093
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
238006
238094
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
238007
238095
  UniswapRouter02: "0x10ED43C718714eb63d5aA57B78B54704E256024E",
@@ -238015,7 +238103,7 @@ var init_node10 = __esm({
238015
238103
  UniswapV4StateView: "0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4",
238016
238104
  Calldispatcher: "0xc71251c8b3e7b02697a84363eef6dce8dfbdf333",
238017
238105
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
238018
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238106
+ EntryPoint: "0x433709009B8330FDa32311DF1C2AFA402eD8D009",
238019
238107
  SimplexPaymaster: "0xeD02f9f0df8F562B89cC5b25867Ad3C2d61252A9"
238020
238108
  // "Usdt0Oft": Not available on BSC
238021
238109
  },
@@ -238062,7 +238150,7 @@ var init_node10 = __esm({
238062
238150
  },
238063
238151
  addresses: {
238064
238152
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
238065
- SolverAccount: "0x77c3394CA5881A74f18139AC87D0c11F8Faa90cC",
238153
+ SolverAccount: "0xaAd062555800a97Af062795189e32a3CBd045612",
238066
238154
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
238067
238155
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
238068
238156
  UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
@@ -238076,7 +238164,7 @@ var init_node10 = __esm({
238076
238164
  UniswapV4StateView: "0x76fd297e2d437cd7f76d50f01afe6160f86e9990",
238077
238165
  Calldispatcher: "0xc71251c8b3e7b02697a84363eef6dce8dfbdf333",
238078
238166
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
238079
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238167
+ EntryPoint: "0x433709009B8330FDa32311DF1C2AFA402eD8D009",
238080
238168
  CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
238081
238169
  SimplexPaymaster: "0x7281Bccb4f0BCE44F3B8542d1fC5e51c2F5fC08C",
238082
238170
  Usdt0Oft: "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"
@@ -238136,7 +238224,7 @@ var init_node10 = __esm({
238136
238224
  },
238137
238225
  addresses: {
238138
238226
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
238139
- SolverAccount: "0x77c3394CA5881A74f18139AC87D0c11F8Faa90cC",
238227
+ SolverAccount: "0xaAd062555800a97Af062795189e32a3CBd045612",
238140
238228
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
238141
238229
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
238142
238230
  UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
@@ -238147,7 +238235,7 @@ var init_node10 = __esm({
238147
238235
  UniswapV4Quoter: "0x0d5e0f971ed27fbff6c2837bf31316121532048d",
238148
238236
  Calldispatcher: "0xc71251c8b3e7b02697a84363eef6dce8dfbdf333",
238149
238237
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
238150
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238238
+ EntryPoint: "0x433709009B8330FDa32311DF1C2AFA402eD8D009",
238151
238239
  CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
238152
238240
  SimplexPaymaster: "0x15b3B03C870c7ef252029c35A12d3b339F5c8d7f",
238153
238241
  AerodromeRouter: "0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43",
@@ -238186,7 +238274,8 @@ var init_node10 = __esm({
238186
238274
  cNGN: "0x52828daa48C1a9A06F37500882b42daf0bE04C3B",
238187
238275
  ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
238188
238276
  XSGD: "0xDC3326e71D45186F113a2F448984CA0e8D201995",
238189
- USDR: "0x3B5F2810fB2168FfA9C73160F97BF9f2461fFa5c"
238277
+ USDR: "0x3B5F2810fB2168FfA9C73160F97BF9f2461fFa5c",
238278
+ BRIDGE: "0x5b0c50fDd52ECC0d4c682c441eaBaD41FfDEABBB"
238190
238279
  },
238191
238280
  tokenDecimals: {
238192
238281
  USDC: 6,
@@ -238195,7 +238284,8 @@ var init_node10 = __esm({
238195
238284
  cNGN: 6,
238196
238285
  ZARP: 18,
238197
238286
  XSGD: 6,
238198
- USDR: 6
238287
+ USDR: 6,
238288
+ BRIDGE: 18
238199
238289
  },
238200
238290
  tokenStorageSlots: {
238201
238291
  USDT: { balanceSlot: 0, allowanceSlot: 1 },
@@ -238206,11 +238296,12 @@ var init_node10 = __esm({
238206
238296
  // custom upgradeable layout
238207
238297
  ZARP: { balanceSlot: 51, allowanceSlot: 52 },
238208
238298
  XSGD: { balanceSlot: 7, allowanceSlot: 8 },
238209
- USDR: { balanceSlot: 51, allowanceSlot: 52 }
238299
+ USDR: { balanceSlot: 51, allowanceSlot: 52 },
238300
+ BRIDGE: { balanceSlot: 0, allowanceSlot: 1 }
238210
238301
  },
238211
238302
  addresses: {
238212
238303
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
238213
- SolverAccount: "0x77c3394CA5881A74f18139AC87D0c11F8Faa90cC",
238304
+ SolverAccount: "0xaAd062555800a97Af062795189e32a3CBd045612",
238214
238305
  TokenGateway: "0x8b536105b6Fae2aE9199f5146D3C57Dfe53b614E",
238215
238306
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
238216
238307
  UniswapRouter02: "0xd2f9496824951D5237cC71245D659E48d0d5f9E8",
@@ -238224,7 +238315,7 @@ var init_node10 = __esm({
238224
238315
  UniswapV4StateView: "0x5ea1bd7974c8a611cbab0bdcafcb1d9cc9b3ba5a",
238225
238316
  Calldispatcher: "0xc71251c8b3e7b02697a84363eef6dce8dfbdf333",
238226
238317
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
238227
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238318
+ EntryPoint: "0x433709009B8330FDa32311DF1C2AFA402eD8D009",
238228
238319
  CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
238229
238320
  SimplexPaymaster: "0xe99acFe0f5fC4C8ea54A187D8D3b05f136150095",
238230
238321
  Usdt0Oft: "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13"
@@ -238303,8 +238394,9 @@ var init_node10 = __esm({
238303
238394
  Host: "0x9AA003594d59C62EE17A73A569Fd7B1DbdBd71E1",
238304
238395
  Calldispatcher: "0x876F1891982E260026630c233A4897160A281Fb8",
238305
238396
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
238306
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238307
- SolverAccount: "0xf98E484858F59C30e31D2695ac18F92bc7C7b799"
238397
+ EntryPoint: "0x433709009B8330FDa32311DF1C2AFA402eD8D009",
238398
+ SolverAccount: "0x7484C6Ff790b898bf7e6960Ffc82f63330445Dc2",
238399
+ SimplexPaymaster: "0x6085a14078B7d26259145a894140f7C8361e02ae"
238308
238400
  },
238309
238401
  rpcEnvKey: "POLYGON_AMOY",
238310
238402
  defaultRpcUrl: "https://rpc-amoy.polygon.technology",
@@ -238336,7 +238428,7 @@ var init_node10 = __esm({
238336
238428
  UniswapV3Factory: "0x1F98431c8aD98523631AE4a59f267346ea31F984",
238337
238429
  Calldispatcher: "0xC71251c8b3e7B02697A84363Eef6DcE8DfBdF333",
238338
238430
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
238339
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238431
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238340
238432
  CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
238341
238433
  UniswapV4PositionManager: "0x3c3ea4b57a46241e54610e5f022e5c45859a1017",
238342
238434
  UniswapV4PoolManager: "0x9a13f98cb987694c9f086b1f5eb990eea8264ec3"
@@ -238370,7 +238462,7 @@ var init_node10 = __esm({
238370
238462
  // Mocked
238371
238463
  UniswapV2Factory: "0x0000000000000000000000000000000000000000",
238372
238464
  Calldispatcher: "0xC71251c8b3e7B02697A84363Eef6DcE8DfBdF333",
238373
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238465
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238374
238466
  },
238375
238467
  defaultRpcUrl: "https://rpc.gnosischain.com",
238376
238468
  consensusStateId: "GNO0",
@@ -238395,7 +238487,7 @@ var init_node10 = __esm({
238395
238487
  addresses: {
238396
238488
  TokenGateway: "0xCe304770236f39F9911BfCC51afBdfF3b8635718",
238397
238489
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
238398
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238490
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
238399
238491
  UniswapV4PositionManager: "0x1b35d13a2e2528f192637f14b05f0dc0e7deb566",
238400
238492
  UniswapV4PoolManager: "0x360e68faccca8ca495c1b759fd9eee466db9fb32"
238401
238493
  },
@@ -238421,7 +238513,7 @@ var init_node10 = __esm({
238421
238513
  addresses: {
238422
238514
  TokenGateway: "0xFcDa26cA021d5535C3059547390E6cCd8De7acA6",
238423
238515
  Host: "0x9AA003594d59C62EE17A73A569Fd7B1DbdBd71E1",
238424
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238516
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238425
238517
  },
238426
238518
  defaultRpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
238427
238519
  consensusStateId: "ETH0",
@@ -238445,7 +238537,7 @@ var init_node10 = __esm({
238445
238537
  addresses: {
238446
238538
  TokenGateway: "0xFcDa26cA021d5535C3059547390E6cCd8De7acA6",
238447
238539
  Host: "0x9AA003594d59C62EE17A73A569Fd7B1DbdBd71E1",
238448
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238540
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238449
238541
  },
238450
238542
  defaultRpcUrl: "https://sepolia.optimism.io",
238451
238543
  consensusStateId: "ETH0",
@@ -238469,7 +238561,7 @@ var init_node10 = __esm({
238469
238561
  addresses: {
238470
238562
  TokenGateway: "0xFcDa26cA021d5535C3059547390E6cCd8De7acA6",
238471
238563
  Host: "0x9AA003594d59C62EE17A73A569Fd7B1DbdBd71E1",
238472
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238564
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238473
238565
  },
238474
238566
  defaultRpcUrl: "https://sepolia.base.org",
238475
238567
  consensusStateId: "ETH0",
@@ -238495,7 +238587,7 @@ var init_node10 = __esm({
238495
238587
  IntentGateway: "0x606ba811aa6cb424ce2108e8977c5284686f0d1f",
238496
238588
  TokenGateway: "0x1c1e5be83df4a54c7a2230c337e4a3e8b7354b1c",
238497
238589
  Host: "0x9AA003594d59C62EE17A73A569Fd7B1DbdBd71E1",
238498
- EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238590
+ EntryPoint: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
238499
238591
  },
238500
238592
  defaultRpcUrl: "https://testnet-asset-hub-eth-rpc.polkadot.io",
238501
238593
  consensusStateId: "PAS0",
@@ -238519,7 +238611,7 @@ var init_node10 = __esm({
238519
238611
  },
238520
238612
  addresses: {
238521
238613
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
238522
- SolverAccount: "0x77c3394CA5881A74f18139AC87D0c11F8Faa90cC",
238614
+ SolverAccount: "0xaAd062555800a97Af062795189e32a3CBd045612",
238523
238615
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
238524
238616
  Calldispatcher: "0xE2C7e576E26E0bE7aC97c6fE925bcDAbD87c4bEd"
238525
238617
  },
@@ -239924,9 +240016,9 @@ var init_node10 = __esm({
239924
240016
  internalType: "bytes32"
239925
240017
  },
239926
240018
  {
239927
- name: "solver",
239928
- type: "address",
239929
- internalType: "address"
240019
+ name: "userOpHash",
240020
+ type: "bytes32",
240021
+ internalType: "bytes32"
239930
240022
  },
239931
240023
  {
239932
240024
  name: "signature",
@@ -240972,8 +241064,8 @@ var init_node10 = __esm({
240972
241064
  getPopularTokens(chain2) {
240973
241065
  return this.getConfig(chain2)?.popularTokens ?? [];
240974
241066
  }
240975
- getEntryPointV08Address(chain2) {
240976
- return this.getConfig(chain2)?.addresses.EntryPointV08;
241067
+ getEntryPointAddress(chain2) {
241068
+ return this.getConfig(chain2)?.addresses.EntryPoint;
240977
241069
  }
240978
241070
  getCirclePaymasterAddress(chain2) {
240979
241071
  return this.getConfig(chain2)?.addresses.CirclePaymaster;
@@ -243235,6 +243327,9 @@ Failed HTTP Request: ${JSON.stringify(
243235
243327
  * fails on-chain, and a second `retractBid` that pulls the bid just placed. When the signed nonce
243236
243328
  * cannot be read, the stalled result is returned rather than guessed at.
243237
243329
  *
243330
+ * A stall in `Future` first fills the missing nonce below it ({@link fillNonceGap}), so the
243331
+ * replacement can land.
243332
+ *
243238
243333
  * A rejection that bounced off a copy already pooled (1013/1014) is likewise left alone: that
243239
243334
  * copy is in flight and its outcome is unknown here, so the `pending` result goes back to the
243240
243335
  * caller to confirm later.
@@ -243259,6 +243354,7 @@ Failed HTTP Request: ${JSON.stringify(
243259
243354
  stalled = result;
243260
243355
  nonce ?? (nonce = this.signedNonce(extrinsic));
243261
243356
  if (nonce === void 0) return result;
243357
+ if (result.future) await this.fillNonceGap(keyPair, nonce, timeoutMs);
243262
243358
  continue;
243263
243359
  }
243264
243360
  if (result.pending) return stalled ?? result;
@@ -243298,19 +243394,6 @@ Failed HTTP Request: ${JSON.stringify(
243298
243394
  const inFlight = code === 1013 || code === 1014 || /already imported|already in the pool|priority is too low/i.test(err.message);
243299
243395
  return { success: false, pending: inFlight || void 0, error: err.message };
243300
243396
  }
243301
- /**
243302
- * Sends an extrinsic with a timeout.
243303
- *
243304
- * A timeout is only a failure when the extrinsic never made it into the transaction pool.
243305
- * Once a pool-entry status (Future/Ready/Broadcast/Retracted) has been seen, the extrinsic is
243306
- * in flight and may well execute after the watch is abandoned — the result is then `pending`
243307
- * and `stalled`, telling the caller to replace it under the same nonce or confirm it later,
243308
- * never to re-sign the same call under a fresh one.
243309
- *
243310
- * `nonce` pins the submission to a specific account nonce, which is what makes a retry a pool
243311
- * replacement rather than a second extrinsic queued behind the first. Left undefined on the
243312
- * first attempt, where the api's auto-nonce is correct.
243313
- */
243314
243397
  /**
243315
243398
  * The nonce the next submission signs with, or undefined when the node cannot be asked.
243316
243399
  *
@@ -243327,6 +243410,7 @@ Failed HTTP Request: ${JSON.stringify(
243327
243410
  * queued as future, or simply not counted yet — is covered by `lastPooledNonce` instead, which
243328
243411
  * is the highest nonce this instance has put into the pool. The two are combined rather than
243329
243412
  * trusted separately, so a burst never reuses a nonce and a restart still picks up the chain's.
243413
+ * A nonce left missing by an extrinsic the node dropped is filled by {@link fillNonceGap}.
243330
243414
  */
243331
243415
  async nextNonce(api) {
243332
243416
  try {
@@ -243340,11 +243424,53 @@ Failed HTTP Request: ${JSON.stringify(
243340
243424
  return void 0;
243341
243425
  }
243342
243426
  }
243427
+ /**
243428
+ * A stall in `Future` means the node is missing a lower nonce of this account's, one whose
243429
+ * extrinsic it dropped, and everything signed above that nonce waits behind it. This fills the
243430
+ * lowest missing nonce, which `system_accountNextIndex` reports on the same node, with a
243431
+ * `system.remark` rather than the next call, so the calls already pooled above it land in the
243432
+ * order they were signed. One nonce per stall; a further gap shows up as the next `Future` stall.
243433
+ *
243434
+ * The remark carries no tip, so on any node or pool view that still holds a live call of ours at
243435
+ * that nonce, the call outranks it and the remark only ever fills a real gap.
243436
+ *
243437
+ * It runs inside the submission queue, so it submits directly rather than enqueueing, and gives
243438
+ * up after `timeoutMs` so a silent socket cannot hold the queue. A failure or timeout is logged
243439
+ * and left to the next stall.
243440
+ */
243441
+ async fillNonceGap(keyPair, nonce, timeoutMs) {
243442
+ const fill = async () => {
243443
+ const accountNextIndex = this.api.rpc?.system?.accountNextIndex;
243444
+ if (!accountNextIndex) return;
243445
+ const missing = Number((await accountNextIndex(keyPair.address)).toString());
243446
+ if (!Number.isFinite(missing) || missing >= nonce) return;
243447
+ await this.api.tx.system.remark("0x").signAndSend(keyPair, { tip: 0n, nonce: missing });
243448
+ };
243449
+ try {
243450
+ await Promise.race([fill(), rejectAfter(timeoutMs, `Timed out after ${timeoutMs}ms`)]);
243451
+ } catch (err) {
243452
+ console.warn(`Could not fill the nonce gap below ${nonce}:`, err);
243453
+ }
243454
+ }
243455
+ /**
243456
+ * Sends an extrinsic with a timeout.
243457
+ *
243458
+ * A timeout is only a failure when the extrinsic never made it into the transaction pool.
243459
+ * Once a pool-entry status (Future/Ready/Broadcast/Retracted) has been seen, the extrinsic is
243460
+ * in flight and may well execute after the watch is abandoned — the result is then `pending`
243461
+ * and `stalled`, telling the caller to replace it under the same nonce or confirm it later,
243462
+ * never to re-sign the same call under a fresh one.
243463
+ *
243464
+ * `nonce` pins the submission to a specific account nonce, which is what makes a retry a pool
243465
+ * replacement rather than a second extrinsic queued behind the first. Left undefined on the
243466
+ * first attempt, where the api's auto-nonce is correct.
243467
+ */
243343
243468
  async sendWithTimeout(extrinsic, keyPair, tip, timeoutMs, nonce) {
243344
243469
  return new Promise((resolve12) => {
243345
243470
  let resolved = false;
243346
243471
  let unsubscribe = null;
243347
243472
  let enteredPool = false;
243473
+ let inFuture = false;
243348
243474
  const timeoutId = setTimeout(() => {
243349
243475
  if (!resolved) {
243350
243476
  resolved = true;
@@ -243355,6 +243481,7 @@ Failed HTTP Request: ${JSON.stringify(
243355
243481
  success: false,
243356
243482
  pending: enteredPool || void 0,
243357
243483
  stalled: enteredPool || void 0,
243484
+ future: enteredPool && inFuture || void 0,
243358
243485
  extrinsicHash: enteredPool ? extrinsic.hash.toHex() : void 0,
243359
243486
  error: `Transaction timed out after ${timeoutMs}ms${enteredPool ? " while in the transaction pool" : ""}`
243360
243487
  });
@@ -243364,6 +243491,7 @@ Failed HTTP Request: ${JSON.stringify(
243364
243491
  if (resolved) return;
243365
243492
  if (result.status.isFuture || result.status.isReady || result.status.isBroadcast || result.status.isRetracted) {
243366
243493
  enteredPool = true;
243494
+ inFuture = result.status.isFuture;
243367
243495
  }
243368
243496
  if (result.dispatchError && (result.status.isInBlock || result.status.isFinalized)) {
243369
243497
  resolved = true;
@@ -246659,7 +246787,8 @@ query OrderStatus($commitment: String!) {
246659
246787
  /** Alchemy (Rundler) method to fetch recommended priority fee for UserOperations. */
246660
246788
  RUNDLER_MAX_PRIORITY_FEE_PER_GAS: "rundler_maxPriorityFeePerGas"
246661
246789
  };
246662
- SELECT_SOLVER_TYPEHASH = keccak2563(toHex3("SelectSolver(bytes32 commitment,address solver)"));
246790
+ SELECT_SOLVER_TYPEHASH = keccak2563(toHex3("SelectSolver(bytes32 commitment,bytes32 userOpHash)"));
246791
+ LEGACY_SELECT_SOLVER_TYPEHASH = keccak2563(toHex3("SelectSolver(bytes32 commitment,address solver)"));
246663
246792
  PACKED_USEROP_TYPEHASH = keccak2563(
246664
246793
  toHex3(
246665
246794
  "PackedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterAndData)"
@@ -246698,29 +246827,50 @@ query OrderStatus($commitment: String!) {
246698
246827
  );
246699
246828
  }
246700
246829
  /**
246701
- * Signs a `SelectSolver` EIP-712 message with a session key.
246830
+ * Signs `SelectSolver(bytes32 commitment,bytes32 userOpHash)` with the
246831
+ * order's session key, for gateways whose `SELECT_SOLVER_TYPEHASH` is
246832
+ * {@link SELECT_SOLVER_TYPEHASH}.
246702
246833
  *
246703
- * The session key authorises the selection of a specific solver for the
246704
- * given order commitment. The resulting signature is appended to the
246705
- * solver's UserOperation signature before bundle submission.
246834
+ * The signature selects one bid UserOperation for the order and is appended
246835
+ * to that operation's signature before it is sent to the bundler.
246706
246836
  *
246707
- * @param commitment - The order commitment (bytes32) being fulfilled.
246708
- * @param solverAddress - Address of the solver account selected to fill the order.
246709
- * @param domainSeparator - EIP-712 domain separator for the IntentGatewayV2 contract.
246710
- * @param privateKey - Hex-encoded private key of the session key that signs the message.
246711
- * @returns The ECDSA signature as a hex string, or `null` if signing fails.
246837
+ * @param commitment - The order commitment.
246838
+ * @param userOpHash - EntryPoint userOpHash of the selected bid UserOperation.
246839
+ * @param domainSeparator - EIP-712 domain separator of the IntentGatewayV2 contract.
246840
+ * @param privateKey - Private key of the order's session key.
246841
+ * @returns The 65-byte ECDSA signature.
246712
246842
  */
246713
- static async signSolverSelection(commitment, solverAddress, domainSeparator2, privateKey) {
246714
- const account5 = privateKeyToAccount(privateKey);
246843
+ static async signUserOpHashSelection(commitment, userOpHash, domainSeparator2, privateKey) {
246844
+ const structHash = keccak2563(
246845
+ encodeAbiParameters2(
246846
+ [{ type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }],
246847
+ [SELECT_SOLVER_TYPEHASH, commitment, userOpHash]
246848
+ )
246849
+ );
246850
+ return signTypedDigest(domainSeparator2, structHash, privateKey);
246851
+ }
246852
+ /**
246853
+ * Signs `SelectSolver(bytes32 commitment,address solver)` with the order's
246854
+ * session key, for gateways whose `SELECT_SOLVER_TYPEHASH` is
246855
+ * {@link LEGACY_SELECT_SOLVER_TYPEHASH}.
246856
+ *
246857
+ * The signature selects a solver account for the order and is appended to
246858
+ * that solver's bid UserOperation signature before it is sent to the bundler.
246859
+ *
246860
+ * @param commitment - The order commitment.
246861
+ * @param solverAddress - Address of the selected solver account.
246862
+ * @param domainSeparator - EIP-712 domain separator of the IntentGatewayV2 contract.
246863
+ * @param privateKey - Private key of the order's session key.
246864
+ * @returns The 65-byte ECDSA signature.
246865
+ */
246866
+ static async signLegacySolverSelection(commitment, solverAddress, domainSeparator2, privateKey) {
246715
246867
  const structHash = keccak2563(
246716
246868
  encodeAbiParameters2(
246717
246869
  [{ type: "bytes32" }, { type: "bytes32" }, { type: "address" }],
246718
- [SELECT_SOLVER_TYPEHASH, commitment, solverAddress]
246870
+ [LEGACY_SELECT_SOLVER_TYPEHASH, commitment, solverAddress]
246719
246871
  )
246720
246872
  );
246721
- const digest = keccak2563(concat3(["0x1901", domainSeparator2, structHash]));
246722
- const signature = await account5.sign({ hash: digest });
246723
- return signature;
246873
+ return signTypedDigest(domainSeparator2, structHash, privateKey);
246724
246874
  }
246725
246875
  /**
246726
246876
  * Computes the EIP-712 hash of a `PackedUserOperation` as defined by
@@ -247247,16 +247397,13 @@ query OrderStatus($commitment: String!) {
247247
247397
  }
247248
247398
  /**
247249
247399
  * Creates a closure that computes the deduplication hash key for a
247250
- * UserOperation, pre-bound to the order's destination chain and entry point.
247400
+ * UserOperation, pre-bound to the destination chain.
247251
247401
  */
247252
- createUserOpHasher(order) {
247253
- const entryPointAddress = this.ctx.dest.configService.getEntryPointV08Address(
247254
- normalizeStateMachineId(order.destination)
247255
- );
247402
+ createUserOpHasher() {
247256
247403
  const chainId = BigInt(
247257
247404
  this.ctx.dest.client.chain?.id ?? Number.parseInt(this.ctx.dest.config.stateMachineId.split("-")[1])
247258
247405
  );
247259
- return (userOp) => CryptoUtils.computeUserOpHash(userOp, entryPointAddress, chainId);
247406
+ return (userOp) => CryptoUtils.computeUserOpHash(userOp, ENTRY_POINT_V08, chainId);
247260
247407
  }
247261
247408
  /**
247262
247409
  * Fetches bids from the coprocessor for a given order commitment.
@@ -247372,7 +247519,7 @@ query OrderStatus($commitment: String!) {
247372
247519
  return;
247373
247520
  }
247374
247521
  const usedUserOps = await this.loadUsedUserOps(commitment);
247375
- const userOpHashKey = this.createUserOpHasher(order);
247522
+ const userOpHashKey = this.createUserOpHasher();
247376
247523
  const targetAssets = order.output.assets.map((a6) => ({ token: a6.token, amount: a6.amount }));
247377
247524
  let initial;
247378
247525
  try {
@@ -247568,9 +247715,17 @@ query OrderStatus($commitment: String!) {
247568
247715
  * order. Frontends can use `relayerFee` to approve the ERC-20 spend before
247569
247716
  * submitting the cancel transaction.
247570
247717
  *
247718
+ * A cross-chain cancel is paid on the chain that receives the cancel
247719
+ * transaction: the source chain for the source route, the destination chain
247720
+ * for the destination route. When that chain is a testnet, the quote has no
247721
+ * native value, since testnet hosts have no router to swap it into the fee
247722
+ * token. The caller must first approve `relayerFee` of that host's fee token
247723
+ * to that chain's IntentGateway.
247724
+ *
247571
247725
  * @param order - The order to quote.
247572
247726
  * @param options - Choose the initiation side. Defaults to source-side cancellation.
247573
- * @returns `{ nativeValue }` — native token amount (wei) to send as `value`;
247727
+ * @returns `{ nativeValue }` — native token amount (wei) to send as `value`,
247728
+ * or `0n` when the relayer fee must be paid in the fee token;
247574
247729
  * `{ relayerFee }` — relayer incentive denominated in the chain's fee token.
247575
247730
  */
247576
247731
  async quoteCancelOrder(order, options = {}) {
@@ -247584,11 +247739,12 @@ query OrderStatus($commitment: String!) {
247584
247739
  *
247585
247740
  * Constructs the ISMP GET request the source gateway will dispatch, one
247586
247741
  * `_partialFills` key per leg, and calls `quoteNative` on the source host to
247587
- * obtain the dispatch fee.
247742
+ * obtain the dispatch fee. Returns a zero `nativeValue` when the source is a
247743
+ * testnet chain, leaving the relayer fee to be paid in its fee token.
247588
247744
  * Returns 0 for same-chain orders (no ISMP call needed).
247589
247745
  *
247590
247746
  * @param order - The order to quote.
247591
- * @returns The native token dispatch fee in wei.
247747
+ * @returns The native token dispatch fee in wei and the relayer fee.
247592
247748
  */
247593
247749
  async quoteCancelFromSource(order) {
247594
247750
  const sourceStateMachine = normalizeStateMachineId(order.source);
@@ -247596,6 +247752,14 @@ query OrderStatus($commitment: String!) {
247596
247752
  if (sourceStateMachine === destStateMachine) {
247597
247753
  return { nativeValue: 0n, relayerFee: 0n };
247598
247754
  }
247755
+ const feeInSourceFeeToken = await convertGasToFeeToken(
247756
+ this.ctx,
247757
+ _a6.SOURCE_GET_RESPONSE_GAS,
247758
+ "source",
247759
+ sourceStateMachine
247760
+ );
247761
+ const relayerFee = feeInSourceFeeToken * 1005n / 1000n;
247762
+ if (TESTNET_CHAINS.has(sourceStateMachine)) return { nativeValue: 0n, relayerFee };
247599
247763
  const height = order.deadline + 1n;
247600
247764
  const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
247601
247765
  const commitment = this.orderId(order);
@@ -247604,20 +247768,13 @@ query OrderStatus($commitment: String!) {
247604
247768
  const getRequest = {
247605
247769
  source: sourceStateMachine,
247606
247770
  dest: destStateMachine,
247607
- from: this.ctx.source.configService.getIntentGatewayAddress(destStateMachine),
247771
+ from: this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachine),
247608
247772
  nonce: await this.ctx.source.getHostNonce(),
247609
247773
  height,
247610
247774
  keys: keys3,
247611
247775
  timeoutTimestamp: 0n,
247612
247776
  context
247613
247777
  };
247614
- const feeInSourceFeeToken = await convertGasToFeeToken(
247615
- this.ctx,
247616
- _a6.SOURCE_GET_RESPONSE_GAS,
247617
- "source",
247618
- sourceStateMachine
247619
- );
247620
- const relayerFee = feeInSourceFeeToken * 1005n / 1000n;
247621
247778
  const nativeValue = await this.ctx.source.quoteNative(getRequest, relayerFee) * 101n / 100n;
247622
247779
  return { nativeValue, relayerFee };
247623
247780
  }
@@ -247638,7 +247795,10 @@ query OrderStatus($commitment: String!) {
247638
247795
  * cancellation. Same-chain orders always use the direct local route.
247639
247796
  * @yields {@link CancelEvent} objects describing each stage of the
247640
247797
  * cancellation lifecycle. The caller signs or broadcasts the yielded
247641
- * transaction, so its signer may differ from `order.user` after expiry.
247798
+ * transaction, and the gateway decides who may sign it. A same-chain cancel
247799
+ * or a cross-chain cancel from the destination accepts only `order.user`
247800
+ * through the deadline and any signer strictly after it. A cross-chain
247801
+ * cancel from the source accepts only `order.user`, before or after expiry.
247642
247802
  */
247643
247803
  async *cancelOrder(order, indexerClient, options = {}) {
247644
247804
  const sourceStateMachine = normalizeStateMachineId(order.source);
@@ -247858,11 +248018,13 @@ query OrderStatus($commitment: String!) {
247858
248018
  *
247859
248019
  * Estimates the relayer fee for delivering the refund POST request from the
247860
248020
  * destination chain back to the source chain, converts it to the destination
247861
- * fee token, and calls `quoteNative` on the destination host.
248021
+ * fee token, and calls `quoteNative` on the destination host. Returns a zero
248022
+ * `nativeValue` when the destination is a testnet chain, leaving the relayer
248023
+ * fee to be paid in its fee token.
247862
248024
  * Returns 0 for same-chain orders.
247863
248025
  *
247864
248026
  * @param order - The order to quote.
247865
- * @returns The native token dispatch fee in wei.
248027
+ * @returns The native token dispatch fee in wei and the relayer fee.
247866
248028
  */
247867
248029
  async quoteCancelFromDest(order) {
247868
248030
  const sourceStateMachine = normalizeStateMachineId(order.source);
@@ -247873,6 +248035,7 @@ query OrderStatus($commitment: String!) {
247873
248035
  const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
247874
248036
  const sourceIntentGateway = this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachine);
247875
248037
  const relayerFee = await this.estimateRelayerFee(sourceStateMachine, destStateMachine);
248038
+ if (TESTNET_CHAINS.has(destStateMachine)) return { nativeValue: 0n, relayerFee };
247876
248039
  const body = constructRefundEscrowRequestBody(order, order.user);
247877
248040
  const postRequest = {
247878
248041
  source: destStateMachine,
@@ -248220,7 +248383,8 @@ query OrderStatus($commitment: String!) {
248220
248383
  }, __publicField(_a6, "DEFAULT_MAX_RECOVERY_RESTARTS", 1), __publicField(_a6, "PROOF_FRESHNESS_MAX_RETRIES", 3), __publicField(_a6, "PROOF_FRESHNESS_BACKOFF_MS", 500), /** Gas budgets used to price cancellation delivery on the source chain. */
248221
248384
  __publicField(_a6, "SOURCE_GET_RESPONSE_GAS", 1000000n), __publicField(_a6, "REFUND_POST_GAS", 1000000n), _a6);
248222
248385
  FILL_ORDER_SELECTOR = "0x68ddf058";
248223
- SUPPORTED_INTENTS_VERSION = 3n;
248386
+ SUPPORTED_INTENTS_VERSION = 4n;
248387
+ TESTNET_INTENTS_VERSIONS = [3n, SUPPORTED_INTENTS_VERSION];
248224
248388
  CONTRACT_VERSION_ABI = [
248225
248389
  {
248226
248390
  type: "function",
@@ -248231,6 +248395,13 @@ query OrderStatus($commitment: String!) {
248231
248395
  }
248232
248396
  ];
248233
248397
  CANONICAL_EVM_TOKEN = /^0x0{24}[0-9a-fA-F]{40}$/;
248398
+ formats = /* @__PURE__ */ new Map();
248399
+ SELECTION_PARAMS_SLOT = pad4(toHex3(5n), { size: 32 });
248400
+ SOLVER_SELECTION_CLEARED = maxUint2563 ^ 0xffn << 160n;
248401
+ SELECTION_FORMAT_BY_ENTRY_POINT = /* @__PURE__ */ new Map([
248402
+ [ENTRY_POINT_V09.toLowerCase(), "userOpHash"],
248403
+ [ENTRY_POINT_V08.toLowerCase(), "address"]
248404
+ ]);
248234
248405
  ENTRY_POINT_EVENT_ABI = [
248235
248406
  { type: "event", name: "BeforeExecution", inputs: [] },
248236
248407
  {
@@ -248265,6 +248436,8 @@ query OrderStatus($commitment: String!) {
248265
248436
  __publicField(this, "domainSeparator");
248266
248437
  /** Cached session-key signature over the `SelectSolver` message. */
248267
248438
  __publicField(this, "cachedSignature");
248439
+ /** Cached result of {@link route}. */
248440
+ __publicField(this, "cachedRoute");
248268
248441
  this.ctx = params.ctx;
248269
248442
  this.crypto = params.crypto;
248270
248443
  this.order = params.order;
@@ -248294,69 +248467,117 @@ query OrderStatus($commitment: String!) {
248294
248467
  );
248295
248468
  }
248296
248469
  /**
248297
- * Resolves the session key, signs the `SelectSolver` message for this bid's
248298
- * solver, and caches the signature. Signs at most once per bid.
248470
+ * Finds the EntryPoint this bid's solver signed it for and checks that the
248471
+ * destination gateway's selection format pairs with it, reading the format
248472
+ * again before refusing a bid in case the gateway was upgraded since it was
248473
+ * cached. Caches the result.
248474
+ *
248475
+ * @throws If the solver's signature recovers under no known EntryPoint, the
248476
+ * EntryPoint does not pair with the gateway's selection format, or the
248477
+ * format cannot be read.
248478
+ */
248479
+ async route() {
248480
+ if (this.cachedRoute) return this.cachedRoute;
248481
+ const chainId = this.chainId();
248482
+ let signed;
248483
+ for (const entryPoint of KNOWN_ENTRY_POINTS) {
248484
+ const userOpHash = CryptoUtils.computeUserOpHash(this.userOp, entryPoint, chainId);
248485
+ if (await this.signedBySolver(userOpHash)) {
248486
+ signed = { entryPoint, userOpHash };
248487
+ break;
248488
+ }
248489
+ }
248490
+ if (!signed) {
248491
+ throw new Error(`Bid from solver=${this.solverAddress} is not signed for a known EntryPoint`);
248492
+ }
248493
+ const pairsWith = SELECTION_FORMAT_BY_ENTRY_POINT.get(signed.entryPoint.toLowerCase());
248494
+ const cached = readSelectionFormat(this.ctx.dest.client, chainId, this.intentGatewayV2Address);
248495
+ let format2 = await cached;
248496
+ if (pairsWith !== format2) {
248497
+ format2 = await refreshSelectionFormat(this.ctx.dest.client, chainId, this.intentGatewayV2Address, cached);
248498
+ }
248499
+ if (pairsWith !== format2) {
248500
+ throw new Error(
248501
+ `Bid from solver=${this.solverAddress} is signed for EntryPoint ${signed.entryPoint}, which the destination gateway's ${format2} selection does not pair with`
248502
+ );
248503
+ }
248504
+ this.cachedRoute = { ...signed, format: format2 };
248505
+ return this.cachedRoute;
248506
+ }
248507
+ /**
248508
+ * Whether the solver's signature in the bid, `signature[32:97]` after the order
248509
+ * commitment, recovers to the solver over `userOpHash`.
248510
+ */
248511
+ async signedBySolver(userOpHash) {
248512
+ try {
248513
+ const recovered = await recoverAddress3({
248514
+ hash: userOpHash,
248515
+ signature: slice4(this.userOp.signature, 32, 97)
248516
+ });
248517
+ return recovered.toLowerCase() === this.solverAddress.toLowerCase();
248518
+ } catch {
248519
+ return false;
248520
+ }
248521
+ }
248522
+ /**
248523
+ * Resolves the session key, signs the `SelectSolver` message for this bid in
248524
+ * the destination gateway's selection format, and caches the signature. Signs
248525
+ * at most once per bid.
248299
248526
  *
248300
- * @throws If the session key is missing or signing fails.
248527
+ * @throws If the bid cannot be routed, or the session key is missing.
248301
248528
  */
248302
248529
  async signSelection() {
248303
248530
  if (this.cachedSignature) return this.cachedSignature;
248531
+ const { format: format2, userOpHash } = await this.route();
248304
248532
  const commitment = this.order.id;
248305
248533
  const sessionKeyAddress = this.order.session;
248306
248534
  const sessionKeyData = this.sessionPrivateKey ? { privateKey: this.sessionPrivateKey } : await this.ctx.sessionKeyStorage.getSessionKeyByAddress(sessionKeyAddress);
248307
248535
  if (!sessionKeyData) {
248308
248536
  throw new Error(`SessionKey not found for commitment: ${commitment}`);
248309
248537
  }
248310
- const signature = await CryptoUtils.signSolverSelection(
248538
+ this.cachedSignature = format2 === "userOpHash" ? await CryptoUtils.signUserOpHashSelection(
248539
+ commitment,
248540
+ userOpHash,
248541
+ this.domainSeparator,
248542
+ sessionKeyData.privateKey
248543
+ ) : await CryptoUtils.signLegacySolverSelection(
248311
248544
  commitment,
248312
248545
  this.solverAddress,
248313
248546
  this.domainSeparator,
248314
248547
  sessionKeyData.privateKey
248315
248548
  );
248316
- if (!signature) {
248317
- throw new Error("Failed to sign solver selection");
248318
- }
248319
- this.cachedSignature = signature;
248320
- return signature;
248549
+ return this.cachedSignature;
248321
248550
  }
248322
248551
  /**
248323
- * Simulates this bid on-chain by batching the `select` and `fillOrder` calls
248324
- * via `eth_call` from the solver's account, using the IntentGatewayV2 ERC-7821
248325
- * batch-execute pattern.
248552
+ * Simulates this bid on-chain with an `eth_call` of its calldata from the
248553
+ * solver's account, after checking that the bid can be routed.
248554
+ *
248555
+ * The gateway's solver selection is turned off for the call with
248556
+ * {@link selectionOffStateDiff} and no `select` is made: a gateway that selects
248557
+ * by userOpHash checks the selection against the EntryPoint's current
248558
+ * operation, which only exists inside a bundle. The session's selection is
248559
+ * therefore not part of the simulation.
248326
248560
  *
248327
248561
  * The native value forwarded to the simulation is the sum of any native-token
248328
248562
  * (`address(0)`) output amounts plus the Hyperbridge dispatch fee.
248329
248563
  *
248330
- * @throws If the `eth_call` simulation reverts or errors.
248564
+ * @throws If the bid cannot be routed, or the `eth_call` simulation reverts or errors.
248331
248565
  */
248332
248566
  async simulate() {
248333
- const signature = await this.signSelection();
248334
- const selectOptions = {
248335
- commitment: this.order.id,
248336
- solver: this.solverAddress,
248337
- signature
248338
- };
248567
+ await this.route();
248339
248568
  const nativeOutputs = this.fillOptions.outputs.reduce(
248340
248569
  (acc, o6) => bytes32ToBytes20(o6.token) === ADDRESS_ZERO2 ? acc + o6.amount : acc,
248341
248570
  0n
248342
248571
  );
248343
248572
  const simulationValue = nativeOutputs + this.fillOptions.nativeDispatchFee;
248344
- const selectCalldata = encodeFunctionData2({
248345
- abi: ABI3,
248346
- functionName: "select",
248347
- args: [selectOptions]
248348
- });
248349
- const calls = [
248350
- { target: this.intentGatewayV2Address, value: 0n, data: selectCalldata },
248351
- { target: this.solverAddress, value: simulationValue, data: this.userOp.callData }
248352
- ];
248353
- const batchedCalldata = this.crypto.encodeERC7821Execute(calls);
248354
248573
  try {
248574
+ const selectionOff = await selectionOffStateDiff(this.ctx.dest.client, this.intentGatewayV2Address);
248355
248575
  await this.ctx.dest.client.call({
248356
248576
  account: this.solverAddress,
248357
248577
  to: this.solverAddress,
248358
- data: batchedCalldata,
248359
- value: simulationValue
248578
+ data: this.userOp.callData,
248579
+ value: simulationValue,
248580
+ stateOverride: [{ address: this.intentGatewayV2Address, stateDiff: [selectionOff] }]
248360
248581
  });
248361
248582
  } catch (e4) {
248362
248583
  throw new Error(`Simulation failed: ${e4 instanceof Error ? e4.message : String(e4)}`);
@@ -248365,31 +248586,29 @@ query OrderStatus($commitment: String!) {
248365
248586
  /**
248366
248587
  * Signs the `SelectSolver` message with the session key, appends it to the
248367
248588
  * solver's existing UserOp signature, and submits the UserOperation to the
248368
- * bundler. Waits for the receipt and reads `OrderFilled` / `PartialFill`
248369
- * logs to determine fill status.
248589
+ * bundler on the EntryPoint the bid was signed for. Waits for the receipt and
248590
+ * reads `OrderFilled` / `PartialFill` logs to determine fill status.
248370
248591
  *
248371
248592
  * @returns A {@link SelectBidResult} with the submitted UserOperation, its hash,
248372
248593
  * the solver address, transaction hash, and fill status.
248373
- * @throws If the bundler is not configured, the session key is missing, or the
248374
- * bundler rejects the UserOperation.
248594
+ * @throws If the bundler is not configured, the bid cannot be routed, the
248595
+ * session key is missing, or the bundler rejects the UserOperation.
248375
248596
  */
248376
248597
  async execute() {
248377
248598
  const commitment = this.order.id;
248378
248599
  if (!this.ctx.bundlerUrl) {
248379
248600
  throw new Error("Bundler URL not configured");
248380
248601
  }
248602
+ const { entryPoint } = await this.route();
248381
248603
  const sessionSignature = await this.signSelection();
248382
248604
  const finalSignature = concat3([this.userOp.signature, sessionSignature]);
248383
248605
  const signedUserOp = {
248384
248606
  ...this.userOp,
248385
248607
  signature: finalSignature
248386
248608
  };
248387
- const entryPointAddress = this.ctx.dest.configService.getEntryPointV08Address(
248388
- normalizeStateMachineId(this.order.destination)
248389
- );
248390
248609
  const userOpHash = await this.crypto.sendBundler(BundlerMethod.ETH_SEND_USER_OPERATION, [
248391
248610
  CryptoUtils.prepareBundlerCall(signedUserOp),
248392
- entryPointAddress
248611
+ entryPoint
248393
248612
  ]);
248394
248613
  let txnHash;
248395
248614
  let fillStatus;
@@ -248415,7 +248634,7 @@ query OrderStatus($commitment: String!) {
248415
248634
  );
248416
248635
  const fills = parseEventLogs2({
248417
248636
  abi: ABI3,
248418
- logs: userOperationLogs(chainReceipt.logs, entryPointAddress, userOpHash),
248637
+ logs: userOperationLogs(chainReceipt.logs, entryPoint, userOpHash),
248419
248638
  eventName: ["OrderFilled", "PartialFill"]
248420
248639
  }).filter(
248421
248640
  (event) => event.address.toLowerCase() === gateway.toLowerCase() && event.args.commitment.toLowerCase() === commitment.toLowerCase()
@@ -248467,12 +248686,15 @@ query OrderStatus($commitment: String!) {
248467
248686
  * Constructs a signed `PackedUserOperation` that a solver can submit to the
248468
248687
  * Hyperbridge coprocessor as a bid to fill an order.
248469
248688
  *
248470
- * The solver signs the operation as EntryPoint v0.8 EIP-712 typed data,
248471
- * whose digest is the plain userOpHash. The binding to the order lives in
248472
- * the operation itself: the 4337 nonce key must be the lower 192 bits of
248473
- * the order commitment (`SolverAccount` enforces this during validation),
248474
- * and the callData carries the order. This keeps the signed payload fully
248475
- * transparent to signing infrastructure instead of an opaque digest.
248689
+ * The solver signs the operation as EIP-712 typed data for
248690
+ * `entryPointAddress`, whose digest is that EntryPoint's userOpHash. It must
248691
+ * be the EntryPoint the solver's `SolverAccount` accepts: the user's SDK
248692
+ * recovers it from this signature and executes the bid there. The binding
248693
+ * to the order lives in the operation itself: the 4337 nonce key must derive
248694
+ * from the order commitment, the session key and the calldata
248695
+ * (`SolverAccount` enforces this during validation), and the callData
248696
+ * carries the order. This keeps the signed payload fully transparent to
248697
+ * signing infrastructure instead of an opaque digest.
248476
248698
  *
248477
248699
  * @param options - Parameters describing the solver account, gas limits, fee
248478
248700
  * market values, and pre-built `callData` for the fill operation.
@@ -248505,7 +248727,7 @@ query OrderStatus($commitment: String!) {
248505
248727
  }
248506
248728
  const chain2 = normalizeStateMachineId(order.destination);
248507
248729
  const gateway = this.ctx.dest.configService.getIntentGatewayAddress(chain2);
248508
- if (!await supportsRateFills(this.ctx.dest.client, gateway)) {
248730
+ if (!await supportsRateFills(this.ctx.dest.client, gateway, chain2)) {
248509
248731
  throw new Error("Fills are not supported by the destination gateway");
248510
248732
  }
248511
248733
  const chainId = BigInt(
@@ -248545,6 +248767,11 @@ query OrderStatus($commitment: String!) {
248545
248767
  * silently dropped with a warning. The returned `Bid` instances are ready to
248546
248768
  * be ranked, simulated, and executed by the consumer.
248547
248769
  *
248770
+ * A bid signed for no known EntryPoint, or for one the destination gateway's
248771
+ * selection format does not pair with, is kept here and fails
248772
+ * {@link Bid.simulate} and {@link Bid.execute}, so {@link selectAndExecuteBest}
248773
+ * skips it.
248774
+ *
248548
248775
  * @param order - The placed order the bids are competing to fill.
248549
248776
  * @param bids - Raw filler bids fetched from the coprocessor.
248550
248777
  * @param sessionPrivateKey - Optional session-key override; looked up from
@@ -248837,12 +249064,15 @@ query OrderStatus($commitment: String!) {
248837
249064
  }
248838
249065
  }
248839
249066
  };
249067
+ UNSUPPORTED_REQUEST_CODES = /* @__PURE__ */ new Set([-32600, -32601, -32602]);
249068
+ bundlersWithoutRundlerFees = /* @__PURE__ */ new Set();
248840
249069
  RELAYER_MESSAGE_GAS = 1000000n;
248841
249070
  NO_BUNDLER_FILL_GAS_BASE = 700000n;
248842
249071
  NO_BUNDLER_FILL_GAS_PER_OUTPUT = 150000n;
248843
249072
  NO_BUNDLER_PAYMASTER_VERIFICATION_GAS = 250000n;
248844
249073
  NO_BUNDLER_PAYMASTER_POST_OP_GAS = 100000n;
248845
249074
  BID_PVG_ESTIMATION_ACCOUNT_CODE = "0x60003560e01c631626ba7e1460145760206000f35b631626ba7e60e01b60005260206000f3";
249075
+ BID_PVG_ESTIMATION_PAYMASTER_CODE = "0x604060005260606000f3";
248846
249076
  SELECTED_BID_SIGNATURE_BYTES = 32 + 65 + 65;
248847
249077
  BID_PVG_HEADROOM_PERCENT = 10n;
248848
249078
  ESCROW_MAPPING_SLOT = 9n;
@@ -248856,8 +249086,6 @@ query OrderStatus($commitment: String!) {
248856
249086
  constructor(ctx, crypto10) {
248857
249087
  __publicField(this, "ctx");
248858
249088
  __publicField(this, "crypto");
248859
- /** Call dispatchers read from a gateway, for chains whose config carries none. */
248860
- __publicField(this, "dispatchers", /* @__PURE__ */ new Map());
248861
249089
  this.ctx = ctx;
248862
249090
  this.crypto = crypto10;
248863
249091
  }
@@ -248879,7 +249107,10 @@ query OrderStatus($commitment: String!) {
248879
249107
  * ephemeral keypair, applies state overrides, and calls
248880
249108
  * `eth_estimateUserOperationGas`. Gas limits are bumped by 5-10% for
248881
249109
  * headroom. If the bundler is Pimlico, gas prices are refined with
248882
- * `pimlico_getUserOperationGasPrice`. If the bundler rejects the estimate,
249110
+ * `pimlico_getUserOperationGasPrice`, and if it is Alchemy, with
249111
+ * `rundler_maxPriorityFeePerGas`. For any other bundler that answers
249112
+ * `rundler_maxPriorityFeePerGas`, the fees are raised to at least that
249113
+ * priority fee (see {@link applyRundlerPriorityFee}). If the bundler rejects the estimate,
248883
249114
  * fixed gas limits are returned in its place, or the call throws when
248884
249115
  * `params.requireBundlerEstimate` is set.
248885
249116
  *
@@ -248914,7 +249145,7 @@ query OrderStatus($commitment: String!) {
248914
249145
  const souceStateMachineId = isHex3(order.source) ? hexToString2(order.source) : order.source;
248915
249146
  const destStateMachineId = isHex3(order.destination) ? hexToString2(order.destination) : order.destination;
248916
249147
  const intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachineId);
248917
- const entryPointAddress = this.ctx.dest.configService.getEntryPointV08Address(destStateMachineId);
249148
+ const entryPointAddress = this.ctx.dest.configService.getEntryPointAddress(destStateMachineId);
248918
249149
  const chainId = BigInt(Number.parseInt(destStateMachineId.split("-")[1]));
248919
249150
  const totalEthValue = quotedOutputs.filter((output3) => bytes32ToBytes20(output3.token) === ADDRESS_ZERO2).reduce((sum2, output3) => sum2 + output3.amount, 0n);
248920
249151
  const [sourceFeeToken, destFeeToken, gasPrice, latestBlock] = await Promise.all([
@@ -248976,7 +249207,7 @@ query OrderStatus($commitment: String!) {
248976
249207
  const maxFeeBumpPercent = params.maxFeePerGasBumpPercent ?? 10;
248977
249208
  let maxPriorityFeePerGas = gasPrice + gasPrice * BigInt(priorityFeeBumpPercent) / 100n;
248978
249209
  let maxFeePerGas = gasPrice + gasPrice * BigInt(maxFeeBumpPercent) / 100n;
248979
- await assertGatewayRelease(this.ctx.dest.client, intentGatewayV2Address);
249210
+ await assertGatewayRelease(this.ctx.dest.client, intentGatewayV2Address, destStateMachineId);
248980
249211
  const fillOrderCalldata = encodeFillOrder(transformOrderForContract(order), fillOptions);
248981
249212
  let callGasLimit = 500000n;
248982
249213
  let verificationGasLimit = 100000n;
@@ -248985,6 +249216,7 @@ query OrderStatus($commitment: String!) {
248985
249216
  let paymasterPostOpGasLimit = 0n;
248986
249217
  if (this.ctx.bundlerUrl) {
248987
249218
  try {
249219
+ if (!entryPointAddress) throw new Error(`No EntryPoint configured for ${destStateMachineId}`);
248988
249220
  const callData = this.crypto.encodeERC7821Execute([
248989
249221
  ...params.prependCalls ?? [],
248990
249222
  { target: intentGatewayV2Address, value: totalNativeValue, data: fillOrderCalldata }
@@ -249017,7 +249249,13 @@ query OrderStatus($commitment: String!) {
249017
249249
  chainId,
249018
249250
  intentGatewayV2Address
249019
249251
  );
249020
- const sessionSignature = await CryptoUtils.signSolverSelection(
249252
+ const selectionFormat = await readSelectionFormat(this.ctx.dest.client, chainId, intentGatewayV2Address);
249253
+ const sessionSignature = selectionFormat === "userOpHash" ? await CryptoUtils.signUserOpHashSelection(
249254
+ commitment,
249255
+ userOpHash,
249256
+ domainSeparator2,
249257
+ solverPrivateKey
249258
+ ) : await CryptoUtils.signLegacySolverSelection(
249021
249259
  commitment,
249022
249260
  solverAccountAddress,
249023
249261
  domainSeparator2,
@@ -249031,6 +249269,7 @@ query OrderStatus($commitment: String!) {
249031
249269
  const bundlerUrlLower = this.ctx.bundlerUrl.toLowerCase();
249032
249270
  const isPimlico = bundlerUrlLower.includes("pimlico.io");
249033
249271
  const isAlchemy = bundlerUrlLower.includes("alchemy.com");
249272
+ const rundlerPriorityFeeRequest = isPimlico || isAlchemy ? Promise.resolve(null) : fetchRundlerPriorityFee(this.ctx.bundlerUrl);
249034
249273
  const bundlerRequests = [
249035
249274
  {
249036
249275
  method: BundlerMethod.ETH_ESTIMATE_USER_OPERATION_GAS,
@@ -249086,13 +249325,27 @@ query OrderStatus($commitment: String!) {
249086
249325
  }
249087
249326
  }
249088
249327
  if (alchemyMaxPriorityFee) {
249089
- const rundlerPriorityFee = BigInt(alchemyMaxPriorityFee);
249328
+ const rundlerPriorityFee2 = BigInt(alchemyMaxPriorityFee);
249090
249329
  const isArbitrum = chainId === 42161n;
249091
249330
  const alchemyPrioBump = isArbitrum ? 0n : 25n;
249092
- maxPriorityFeePerGas = rundlerPriorityFee + rundlerPriorityFee * alchemyPrioBump / 100n;
249331
+ maxPriorityFeePerGas = rundlerPriorityFee2 + rundlerPriorityFee2 * alchemyPrioBump / 100n;
249093
249332
  const bufferedBaseFee = baseFeePerGas + baseFeePerGas * 50n / 100n;
249094
249333
  maxFeePerGas = bufferedBaseFee + maxPriorityFeePerGas;
249095
249334
  }
249335
+ const rundlerPriorityFee = await rundlerPriorityFeeRequest;
249336
+ if (rundlerPriorityFee !== null) {
249337
+ const rundlerFees = applyRundlerPriorityFee(
249338
+ { maxFeePerGas, maxPriorityFeePerGas },
249339
+ {
249340
+ rundlerPriorityFee,
249341
+ baseFeePerGas,
249342
+ priorityFeeBumpPercent: BigInt(priorityFeeBumpPercent),
249343
+ maxFeeBumpPercent: BigInt(maxFeeBumpPercent)
249344
+ }
249345
+ );
249346
+ maxFeePerGas = rundlerFees.maxFeePerGas;
249347
+ maxPriorityFeePerGas = rundlerFees.maxPriorityFeePerGas;
249348
+ }
249096
249349
  } catch (e4) {
249097
249350
  if (params.requireBundlerEstimate) {
249098
249351
  throw new Error(`Bundler gas estimation failed: ${e4 instanceof Error ? e4.message : String(e4)}`, {
@@ -249133,27 +249386,6 @@ query OrderStatus($commitment: String!) {
249133
249386
  inputs
249134
249387
  };
249135
249388
  }
249136
- /**
249137
- * The call dispatcher the gateway on `chain` uses: the configured address, or the one
249138
- * the gateway reports when the chain's config carries none.
249139
- *
249140
- * {@link buildStateOverride} writes it back into the gateway's params slot. Without an
249141
- * address that value is 12 bytes instead of 32, and the bundler rejects the whole
249142
- * estimate as `Invalid params`.
249143
- */
249144
- async callDispatcher(chain2, gateway) {
249145
- const configured = this.ctx.dest.configService.getCalldispatcherAddress(chain2);
249146
- if (isAddress2(configured)) return configured;
249147
- const known3 = this.dispatchers.get(chain2);
249148
- if (known3) return known3;
249149
- const params = await this.ctx.dest.client.readContract({
249150
- abi: ABI3,
249151
- address: gateway,
249152
- functionName: "params"
249153
- });
249154
- this.dispatchers.set(chain2, params.dispatcher);
249155
- return params.dispatcher;
249156
- }
249157
249389
  /**
249158
249390
  * Asks the bundler for the `preVerificationGas` of the bid UserOperation that will
249159
249391
  * actually be signed, with every funding call, approval and signature byte in it.
@@ -249167,7 +249399,9 @@ query OrderStatus($commitment: String!) {
249167
249399
  * `maxFeePerGas`.
249168
249400
  *
249169
249401
  * The solver account's code is overridden with {@link BID_PVG_ESTIMATION_ACCOUNT_CODE}
249170
- * for the call, so only the bytes are priced and the fill itself is not simulated.
249402
+ * and the paymaster's, if the bid has one, with {@link BID_PVG_ESTIMATION_PAYMASTER_CODE}
249403
+ * for the call, so only the bytes are priced and neither the fill nor the paymaster's
249404
+ * validation is simulated.
249171
249405
  *
249172
249406
  * @param params - The bid's fields as they will be signed.
249173
249407
  * @returns The `preVerificationGas` to sign, with {@link BID_PVG_HEADROOM_PERCENT} added.
@@ -249191,13 +249425,15 @@ query OrderStatus($commitment: String!) {
249191
249425
  paymasterAndData: params.paymasterAndData ?? "0x",
249192
249426
  signature: `0x${"ff".repeat(SELECTED_BID_SIGNATURE_BYTES)}`
249193
249427
  };
249428
+ const stateOverride = {
249429
+ [solverAccount]: { code: BID_PVG_ESTIMATION_ACCOUNT_CODE }
249430
+ };
249431
+ if (userOp.paymasterAndData.length >= 42) {
249432
+ stateOverride[userOp.paymasterAndData.slice(0, 42)] = { code: BID_PVG_ESTIMATION_PAYMASTER_CODE };
249433
+ }
249194
249434
  const estimate = await this.crypto.sendBundler(
249195
249435
  BundlerMethod.ETH_ESTIMATE_USER_OPERATION_GAS,
249196
- [
249197
- CryptoUtils.prepareBundlerCall(userOp),
249198
- params.entryPointAddress,
249199
- { [solverAccount]: { code: BID_PVG_ESTIMATION_ACCOUNT_CODE } }
249200
- ]
249436
+ [CryptoUtils.prepareBundlerCall(userOp), params.entryPointAddress, stateOverride]
249201
249437
  );
249202
249438
  return BigInt(estimate.preVerificationGas) * (100n + BID_PVG_HEADROOM_PERCENT) / 100n;
249203
249439
  }
@@ -249245,9 +249481,8 @@ query OrderStatus($commitment: String!) {
249245
249481
  * slots should be overridden.
249246
249482
  * @param params.spenderAddress - Address that needs allowance from the solver
249247
249483
  * account (i.e. the IntentGatewayV2 contract).
249248
- * @param params.intentGatewayV2Address - If provided, overrides slot 5 of
249249
- * IntentGatewayV2 with the call-dispatcher address so dispatch calls
249250
- * succeed during estimation.
249484
+ * @param params.intentGatewayV2Address - If provided, turns the gateway's
249485
+ * solver selection off with {@link selectionOffStateDiff}.
249251
249486
  * @param params.entryPointAddress - If provided, overrides the EntryPoint
249252
249487
  * deposit mapping to give the solver account a large deposit.
249253
249488
  * @returns An object with `viem` and `bundler` state-override collections.
@@ -249266,10 +249501,7 @@ query OrderStatus($commitment: String!) {
249266
249501
  const viemOverrides = [];
249267
249502
  const bundlerOverrides = {};
249268
249503
  if (intentGatewayV2Address) {
249269
- const paramsSlot5 = pad4(toHex3(5n), { size: 32 });
249270
- const dispatcherAddress = await this.callDispatcher(chain2, intentGatewayV2Address);
249271
- const newSlot5Value = "0x" + "0".repeat(22) + "00" + dispatcherAddress.slice(2).toLowerCase();
249272
- const gatewayDiffs = [{ slot: paramsSlot5, value: newSlot5Value }];
249504
+ const gatewayDiffs = [await selectionOffStateDiff(this.ctx.dest.client, intentGatewayV2Address)];
249273
249505
  for (const [leg, input] of (escrow?.inputs ?? []).entries()) {
249274
249506
  gatewayDiffs.push({
249275
249507
  slot: escrowSlot(escrow.commitment, leg),
@@ -250232,6 +250464,8 @@ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
250232
250464
  * order. Use `relayerFee` to approve the ERC-20 spend before submitting.
250233
250465
  * Same-chain orders use the direct local route for either `from` option and
250234
250466
  * return `{ nativeValue: 0n, relayerFee: 0n }`.
250467
+ * When the chain receiving the cancel transaction is a testnet, `nativeValue` is `0n` and the caller
250468
+ * approves `relayerFee` of that host's fee token to that chain's gateway.
250235
250469
  *
250236
250470
  * Delegates to {@link OrderCanceller.quoteCancelOrder}.
250237
250471
  *
@@ -250928,7 +251162,7 @@ var init_FillerConfigService = __esm({
250928
251162
  return this.fillerConfig?.substratePrivateKey;
250929
251163
  }
250930
251164
  getEntryPointAddress(chain2) {
250931
- return this.chainConfigService.getEntryPointV08Address(chain2);
251165
+ return this.chainConfigService.getEntryPointAddress(chain2);
250932
251166
  }
250933
251167
  getSolverAccountContractAddress(chain2) {
250934
251168
  return this.chainConfigService.getSolverAccountAddress(chain2);
@@ -251380,7 +251614,7 @@ function chainByChainId(chainId) {
251380
251614
  function chainByAlchemySubdomain(subdomain) {
251381
251615
  return INIT_CHAINS.find((chain2) => chain2.alchemySubdomain === subdomain);
251382
251616
  }
251383
- var FREE_RPC_URLS, CHAIN_NATIVE_SYMBOLS, INIT_CHAINS, HYPERBRIDGE_WS_DEFAULTS;
251617
+ var FREE_RPC_URLS, HYPERBRIDGE_BUNDLER_URLS, CHAIN_NATIVE_SYMBOLS, INIT_CHAINS, HYPERBRIDGE_WS_DEFAULTS;
251384
251618
  var init_chains2 = __esm({
251385
251619
  "src/cli/init/chains.ts"() {
251386
251620
  "use strict";
@@ -251432,6 +251666,13 @@ var init_chains2 = __esm({
251432
251666
  "https://arb1.arbitrum.io/rpc"
251433
251667
  ]
251434
251668
  };
251669
+ HYPERBRIDGE_BUNDLER_URLS = {
251670
+ 1: "https://bundler.polytope.technology/ethereum",
251671
+ 56: "https://bundler.polytope.technology/bsc",
251672
+ 137: "https://bundler.polytope.technology/polygon",
251673
+ 8453: "https://bundler.polytope.technology/base",
251674
+ 42161: "https://bundler.polytope.technology/arbitrum"
251675
+ };
251435
251676
  CHAIN_NATIVE_SYMBOLS = {
251436
251677
  1: "ETH",
251437
251678
  56: "BNB",
@@ -251457,7 +251698,8 @@ var init_chains2 = __esm({
251457
251698
  network: "mainnet",
251458
251699
  alchemySubdomain: "eth-mainnet",
251459
251700
  explorerUrl: external_links_default.chainExplorers.ethereum,
251460
- defaultRpcUrls: FREE_RPC_URLS[1]
251701
+ defaultRpcUrls: FREE_RPC_URLS[1],
251702
+ hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[1]
251461
251703
  },
251462
251704
  {
251463
251705
  chainId: 42161,
@@ -251466,7 +251708,8 @@ var init_chains2 = __esm({
251466
251708
  network: "mainnet",
251467
251709
  alchemySubdomain: "arb-mainnet",
251468
251710
  explorerUrl: external_links_default.chainExplorers.arbitrum,
251469
- defaultRpcUrls: FREE_RPC_URLS[42161]
251711
+ defaultRpcUrls: FREE_RPC_URLS[42161],
251712
+ hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[42161]
251470
251713
  },
251471
251714
  {
251472
251715
  chainId: 8453,
@@ -251475,7 +251718,8 @@ var init_chains2 = __esm({
251475
251718
  network: "mainnet",
251476
251719
  alchemySubdomain: "base-mainnet",
251477
251720
  explorerUrl: external_links_default.chainExplorers.base,
251478
- defaultRpcUrls: FREE_RPC_URLS[8453]
251721
+ defaultRpcUrls: FREE_RPC_URLS[8453],
251722
+ hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[8453]
251479
251723
  },
251480
251724
  {
251481
251725
  chainId: 137,
@@ -251484,7 +251728,8 @@ var init_chains2 = __esm({
251484
251728
  network: "mainnet",
251485
251729
  alchemySubdomain: "polygon-mainnet",
251486
251730
  explorerUrl: external_links_default.chainExplorers.polygon,
251487
- defaultRpcUrls: FREE_RPC_URLS[137]
251731
+ defaultRpcUrls: FREE_RPC_URLS[137],
251732
+ hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[137]
251488
251733
  },
251489
251734
  {
251490
251735
  chainId: 56,
@@ -251493,7 +251738,8 @@ var init_chains2 = __esm({
251493
251738
  network: "mainnet",
251494
251739
  alchemySubdomain: "bnb-mainnet",
251495
251740
  explorerUrl: external_links_default.chainExplorers.bnb,
251496
- defaultRpcUrls: FREE_RPC_URLS[56]
251741
+ defaultRpcUrls: FREE_RPC_URLS[56],
251742
+ hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[56]
251497
251743
  },
251498
251744
  {
251499
251745
  chainId: 11155111,
@@ -254232,7 +254478,7 @@ var require_bn2 = __commonJS({
254232
254478
  }
254233
254479
  return this;
254234
254480
  };
254235
- BN3.prototype.add = function add3(num2) {
254481
+ BN3.prototype.add = function add4(num2) {
254236
254482
  var res;
254237
254483
  if (num2.negative !== 0 && this.negative === 0) {
254238
254484
  num2.negative = 0;
@@ -256059,7 +256305,7 @@ var require_bn2 = __commonJS({
256059
256305
  }
256060
256306
  return this.m.sub(a6)._forceRed(this);
256061
256307
  };
256062
- Red.prototype.add = function add3(a6, b5) {
256308
+ Red.prototype.add = function add4(a6, b5) {
256063
256309
  this._verify2(a6, b5);
256064
256310
  var res = a6.add(b5);
256065
256311
  if (res.cmp(this.m) >= 0) {
@@ -267070,7 +267316,7 @@ var require_namespace = __commonJS({
267070
267316
  return this.nested[name3].values;
267071
267317
  throw Error("no such enum: " + name3);
267072
267318
  };
267073
- Namespace.prototype.add = function add3(object) {
267319
+ Namespace.prototype.add = function add4(object) {
267074
267320
  if (!(object instanceof Field6 && object.extend !== void 0 || object instanceof Type2 || object instanceof OneOf || object instanceof Enum3 || object instanceof Service || object instanceof Namespace))
267075
267321
  throw TypeError("object must be a valid nested object");
267076
267322
  if (!this.nested)
@@ -267450,7 +267696,7 @@ var require_service2 = __commonJS({
267450
267696
  });
267451
267697
  return this;
267452
267698
  };
267453
- Service.prototype.add = function add3(object) {
267699
+ Service.prototype.add = function add4(object) {
267454
267700
  if (this.get(object.name))
267455
267701
  throw Error("duplicate name '" + object.name + "' in " + this);
267456
267702
  if (object instanceof Method2) {
@@ -268155,7 +268401,7 @@ var require_type2 = __commonJS({
268155
268401
  Type2.prototype.get = function get4(name3) {
268156
268402
  return this.fields[name3] || this.oneofs && this.oneofs[name3] || this.nested && this.nested[name3] || null;
268157
268403
  };
268158
- Type2.prototype.add = function add3(object) {
268404
+ Type2.prototype.add = function add4(object) {
268159
268405
  if (this.get(object.name))
268160
268406
  throw Error("duplicate name '" + object.name + "' in " + this);
268161
268407
  if (object instanceof Field6 && object.extend === void 0) {
@@ -269066,7 +269312,7 @@ var require_oneof = __commonJS({
269066
269312
  oneof.parent.add(oneof.fieldsArray[i6]);
269067
269313
  }
269068
269314
  }
269069
- OneOf.prototype.add = function add3(field) {
269315
+ OneOf.prototype.add = function add4(field) {
269070
269316
  if (!(field instanceof Field6))
269071
269317
  throw TypeError("field must be a Field");
269072
269318
  if (field.parent && field.parent !== this.parent)
@@ -269395,7 +269641,7 @@ var require_enum = __commonJS({
269395
269641
  keepComments ? this.comments : void 0
269396
269642
  ]);
269397
269643
  };
269398
- Enum3.prototype.add = function add3(name3, id2, comment, options) {
269644
+ Enum3.prototype.add = function add4(name3, id2, comment, options) {
269399
269645
  if (!util7.isString(name3))
269400
269646
  throw TypeError("name must be a string");
269401
269647
  if (!util7.isInteger(id2))
@@ -273872,7 +274118,7 @@ var require_umd = __commonJS({
273872
274118
  return this.not().add(ONE2);
273873
274119
  };
273874
274120
  LongPrototype.neg = LongPrototype.negate;
273875
- LongPrototype.add = function add3(addend) {
274121
+ LongPrototype.add = function add4(addend) {
273876
274122
  if (!isLong(addend)) addend = fromValue(addend);
273877
274123
  var a48 = this.high >>> 16;
273878
274124
  var a32 = this.high & 65535;
@@ -337563,7 +337809,7 @@ var init_help_text = __esm({
337563
337809
  chains: "Simplex listens for orders and fills only on the chains you pick. Each chain needs its own RPC, an ERC-4337 bundler, and funded balances (native gas + stablecoins).",
337564
337810
  rpc: "The RPC is used to scan order events, read balances and simulate fills. Use a premium endpoint with archive access (Alchemy, Infura, QuickNode) \u2014 free tiers rate-limit and break event scanning.",
337565
337811
  quorum: "Listing a second, organisationally independent RPC enables quorum log scanning: every event batch must match across providers, so one lying or compromised RPC can't feed you fake orders.",
337566
- bundler: "Fills execute as ERC-4337 UserOperations; the bundler submits them on-chain. Alchemy RPC endpoints double as bundlers, or use a dedicated provider like Pimlico.",
337812
+ bundler: "Fills execute as ERC-4337 UserOperations; the bundler submits them on-chain. Hyperbridge runs no bundler for these chains, so use a provider such as Pimlico.",
337567
337813
  signer: "This wallet signs every fill and holds your stablecoin float on each chain. It's the identity of your filler.",
337568
337814
  secretPhrase: "A secret phrase is a BIP-39 mnemonic of 12 to 24 English words. The filler signs with the wallet at m/44'/60'/0'/0/0 unless you pick another account index. The phrase is written to the config file in plain text, protected only by the file's permissions, the same as a private key.",
337569
337815
  substrateKey: "Solver-selection orders are won by submitting signed bids to Hyperbridge. This Substrate account signs those bid extrinsics and must hold BRIDGE tokens for fees \u2014 the fees are claimed back automatically after fills.",
@@ -337850,6 +338096,10 @@ var init_pimlico = __esm({
337850
338096
 
337851
338097
  // src/cli/init/steps/bundlers.ts
337852
338098
  async function stepBundlers(state, prefill) {
338099
+ for (const chain2 of state.chains) {
338100
+ if (chain2.meta.hyperbridgeBundlerUrl) chain2.bundlerUrl = chain2.meta.hyperbridgeBundlerUrl;
338101
+ }
338102
+ if (state.chains.every((chain2) => chain2.bundlerUrl)) return;
337853
338103
  why(WHY.bundler);
337854
338104
  const alchemyChains = state.chains.filter((chain2) => {
337855
338105
  if (chain2.bundlerUrl || !isAlchemyUrl(chain2.rpcUrls[0])) return false;
@@ -338435,9 +338685,9 @@ function chainComments(state) {
338435
338685
  function showSummary(state, outputPath) {
338436
338686
  const lines = [];
338437
338687
  for (const chain2 of state.chains) {
338438
- const bundlerHost = chain2.bundlerUrl ? new URL(chain2.bundlerUrl).hostname : "?";
338688
+ const bundlerHost2 = chain2.bundlerUrl ? new URL(chain2.bundlerUrl).hostname : "?";
338439
338689
  lines.push(
338440
- `${chain2.meta.label}: ${chain2.rpcUrls.length} RPC${chain2.rpcUrls.length > 1 ? "s (quorum)" : ""}, bundler ${bundlerHost}`
338690
+ `${chain2.meta.label}: ${chain2.rpcUrls.length} RPC${chain2.rpcUrls.length > 1 ? "s (quorum)" : ""}, bundler ${bundlerHost2}`
338441
338691
  );
338442
338692
  }
338443
338693
  if (state.passthroughChains.length > 0) {
@@ -340232,6 +340482,35 @@ var LIMITS_QUERY = `
340232
340482
  chains { id name tokens { symbol decimals } }
340233
340483
  }
340234
340484
  `;
340485
+ var LEVEL_FIELDS = "fillChain priceBucket price worstPrice baseSize quoteSize orderCount solverCount";
340486
+ var SNAPSHOT_QUERY = `
340487
+ query OrderbookSnapshot($id: ID!, $sourceChain: String, $fillChain: String) {
340488
+ book(id: $id) {
340489
+ id base quote
340490
+ bids: levels(side: BID, sourceChain: $sourceChain, fillChain: $fillChain) { ${LEVEL_FIELDS} }
340491
+ asks: levels(side: ASK, sourceChain: $sourceChain, fillChain: $fillChain) { ${LEVEL_FIELDS} }
340492
+ bidLiquidity: availableLiquidity(side: BID, sourceChain: $sourceChain, fillChain: $fillChain)
340493
+ askLiquidity: availableLiquidity(side: ASK, sourceChain: $sourceChain, fillChain: $fillChain)
340494
+ }
340495
+ serverInfo { priceGranularities { book granularity } }
340496
+ }
340497
+ `;
340498
+ var LEVEL_ORDERS_QUERY = `
340499
+ query OrderbookLevelOrders($id: ID!, $side: Side!, $fillChain: String, $sourceChain: String, $after: String) {
340500
+ book(id: $id) {
340501
+ orders(side: $side, fillChain: $fillChain, sourceChain: $sourceChain, first: 50, after: $after) {
340502
+ edges {
340503
+ node {
340504
+ solver { address }
340505
+ commitment fillChain price priceBucket advertisedSize quotedAmount resized expiresAt acceptedSources
340506
+ }
340507
+ }
340508
+ pageInfo { hasNextPage endCursor }
340509
+ }
340510
+ }
340511
+ }
340512
+ `;
340513
+ var LEVEL_ORDER_PAGES = 20;
340235
340514
  var POSTED_ORDER_FIELDS = "commitment side status price quotedAmount advertisedSize expiresAt acceptedSources";
340236
340515
  var SUBMIT_ORDER_MUTATION = `
340237
340516
  mutation SubmitOrder($userOp: Bytes!) {
@@ -340296,6 +340575,44 @@ var OrderbookClient = class {
340296
340575
  async limits() {
340297
340576
  return this.request(LIMITS_QUERY, {});
340298
340577
  }
340578
+ /** The public book across all solvers, restricted to the selected route. */
340579
+ async snapshot(id2, filters = {}) {
340580
+ const { book, serverInfo } = await this.request(SNAPSHOT_QUERY, {
340581
+ id: id2,
340582
+ sourceChain: filters.sourceChain || null,
340583
+ fillChain: filters.fillChain || null
340584
+ });
340585
+ if (!book) return null;
340586
+ const granularity = serverInfo.priceGranularities.find((entry) => entry.book === book.id)?.granularity ?? null;
340587
+ return { ...book, granularity };
340588
+ }
340589
+ /**
340590
+ * The orders behind one price level. The orderbook filters orders by side and route but not by
340591
+ * price bucket, so this walks the side's orders on the level's fill chain and keeps the bucket.
340592
+ * Null when the book does not exist.
340593
+ */
340594
+ async levelOrders(id2, level) {
340595
+ const found = [];
340596
+ let after = null;
340597
+ for (let page = 0; page < LEVEL_ORDER_PAGES; page++) {
340598
+ const { book } = await this.request(LEVEL_ORDERS_QUERY, {
340599
+ id: id2,
340600
+ side: level.side,
340601
+ fillChain: level.fillChain,
340602
+ sourceChain: level.sourceChain || null,
340603
+ after
340604
+ });
340605
+ if (!book) return null;
340606
+ for (const { node } of book.orders.edges) {
340607
+ if (node.priceBucket !== level.priceBucket) continue;
340608
+ const { solver, priceBucket: _bucket, ...order } = node;
340609
+ found.push({ ...order, solver: solver.address });
340610
+ }
340611
+ if (!book.orders.pageInfo.hasNextPage || !book.orders.pageInfo.endCursor) break;
340612
+ after = book.orders.pageInfo.endCursor;
340613
+ }
340614
+ return found;
340615
+ }
340299
340616
  async submitOrder(userOp) {
340300
340617
  const { submitOrder } = await this.request(SUBMIT_ORDER_MUTATION, { userOp });
340301
340618
  switch (submitOrder.__typename) {
@@ -340484,16 +340801,16 @@ function signedAmounts(params) {
340484
340801
  function offerFor(params) {
340485
340802
  const { side, inputAmount, price, outputDecimals } = params;
340486
340803
  if (price <= 0n) throw new Error("A limit order's price must be greater than zero");
340487
- const scaled = side === "BID" ? inputAmount * price / ORDERBOOK_SCALE2 : inputAmount * ORDERBOOK_SCALE2 / price;
340804
+ const scaled2 = side === "BID" ? inputAmount * price / ORDERBOOK_SCALE2 : inputAmount * ORDERBOOK_SCALE2 / price;
340488
340805
  const unit = outputDecimals >= 18 ? 1n : 10n ** BigInt(18 - outputDecimals);
340489
- return scaled / unit * unit;
340806
+ return scaled2 / unit * unit;
340490
340807
  }
340491
340808
  function inputFor(params) {
340492
340809
  const { side, outputAmount, price, inputDecimals } = params;
340493
340810
  if (price <= 0n) throw new Error("A limit order's price must be greater than zero");
340494
- const scaled = side === "BID" ? divCeil2(outputAmount * ORDERBOOK_SCALE2, price) : divCeil2(outputAmount * price, ORDERBOOK_SCALE2);
340811
+ const scaled2 = side === "BID" ? divCeil2(outputAmount * ORDERBOOK_SCALE2, price) : divCeil2(outputAmount * price, ORDERBOOK_SCALE2);
340495
340812
  const unit = inputDecimals >= 18 ? 1n : 10n ** BigInt(18 - inputDecimals);
340496
- return divCeil2(scaled, unit) * unit;
340813
+ return divCeil2(scaled2, unit) * unit;
340497
340814
  }
340498
340815
 
340499
340816
  // src/orderbook/matching.ts
@@ -340616,6 +340933,8 @@ var LimitOrderService = class {
340616
340933
  this.delegationService = delegationService;
340617
340934
  this.startingNonce = startingNonce;
340618
340935
  this.vaultBalances = vaultBalances;
340936
+ /** Re-signs in flight, by order id. A fill's resize waits one out rather than reposting alongside it. */
340937
+ this.resigning = /* @__PURE__ */ new Map();
340619
340938
  this.logger = loggers.get("limit-orders");
340620
340939
  }
340621
340940
  /**
@@ -340709,6 +341028,7 @@ var LimitOrderService = class {
340709
341028
  bookExpiresAt: null,
340710
341029
  bookPrice: null,
340711
341030
  orderNonce: order.orderNonce,
341031
+ entryPoint: null,
340712
341032
  status: "cancelled",
340713
341033
  lastError: null
340714
341034
  }),
@@ -340751,6 +341071,14 @@ var LimitOrderService = class {
340751
341071
  throw err;
340752
341072
  }
340753
341073
  }
341074
+ /** Public market levels read through the configured orderbook client. */
341075
+ orderbookSnapshot(book, filters = {}) {
341076
+ return this.client.snapshot(book, filters);
341077
+ }
341078
+ /** Every solver's order behind one public price level. */
341079
+ orderbookLevelOrders(book, level) {
341080
+ return this.client.levelOrders(book, level);
341081
+ }
340754
341082
  /**
340755
341083
  * The pairs an order can be written against, the smallest payout each token may carry, and
340756
341084
  * the tokens the orderbook registers on each chain.
@@ -340805,16 +341133,16 @@ var LimitOrderService = class {
340805
341133
  if (!this.configService.getConfiguredChainIds().includes(getChainId3(request3.fillChain) ?? -1)) {
340806
341134
  throw new LimitOrderValidationError(`'${request3.fillChain}' is not a chain this filler is configured for`);
340807
341135
  }
340808
- const scaled = { amountIn: 0n, amountOut: 0n };
341136
+ const scaled2 = { amountIn: 0n, amountOut: 0n };
340809
341137
  for (const name3 of ["amountIn", "amountOut"]) {
340810
341138
  try {
340811
- scaled[name3] = fromHuman(request3[name3] ?? "");
341139
+ scaled2[name3] = fromHuman(request3[name3] ?? "");
340812
341140
  } catch (err) {
340813
341141
  throw new LimitOrderValidationError(
340814
341142
  `${name3} must be an amount in whole tokens, like "1000" or "1500.25"; ${err.message}`
340815
341143
  );
340816
341144
  }
340817
- if (scaled[name3] <= 0n) throw new LimitOrderValidationError(`${name3} must be greater than zero`);
341145
+ if (scaled2[name3] <= 0n) throw new LimitOrderValidationError(`${name3} must be greater than zero`);
340818
341146
  }
340819
341147
  const sources = request3.acceptedSources ?? [];
340820
341148
  if (sources.length === 0) {
@@ -340861,7 +341189,7 @@ var LimitOrderService = class {
340861
341189
  );
340862
341190
  }
340863
341191
  const floor2 = limits.serverInfo.minOrderSizes.find((entry) => entry.symbol === request3.tokenOut);
340864
- if (floor2 && scaled.amountOut < BigInt(floor2.size)) {
341192
+ if (floor2 && scaled2.amountOut < BigInt(floor2.size)) {
340865
341193
  throw new LimitOrderValidationError(
340866
341194
  `amountOut is below the orderbook's dust floor for ${request3.tokenOut} (${toHuman(BigInt(floor2.size))})`
340867
341195
  );
@@ -340871,7 +341199,7 @@ var LimitOrderService = class {
340871
341199
  throw new LimitOrderValidationError(`'${symbol3}' does not resolve to a token address on ${request3.fillChain}`);
340872
341200
  }
340873
341201
  }
340874
- return { amountIn: scaled.amountIn, amountOut: scaled.amountOut };
341202
+ return { amountIn: scaled2.amountIn, amountOut: scaled2.amountOut };
340875
341203
  }
340876
341204
  /**
340877
341205
  * Refuses an order the wallet cannot pay out.
@@ -340967,24 +341295,32 @@ var LimitOrderService = class {
340967
341295
  async resize(remaining, delivered) {
340968
341296
  const id2 = remaining.id;
340969
341297
  const floor2 = await this.dustFloor(remaining);
340970
- if (BigInt(remaining.remaining) < floor2) {
341298
+ for (let resigning = this.resigning.get(id2); resigning; resigning = this.resigning.get(id2)) await resigning;
341299
+ const order = await this.store.setStatus(id2, "resizing", null, POSTABLE);
341300
+ if (!order) return null;
341301
+ if (BigInt(order.remaining) < floor2) {
340971
341302
  this.logger.info(
340972
- { id: id2, remaining: remaining.remaining, floor: floor2.toString() },
341303
+ { id: id2, remaining: order.remaining, floor: floor2.toString() },
340973
341304
  "Limit order worked down past the dust floor; closing it"
340974
341305
  );
340975
- if (remaining.commitment) await this.withdraw(remaining.commitment);
340976
- const closed = await this.store.setPosting(id2, {
340977
- commitment: null,
340978
- bookExpiresAt: null,
340979
- bookPrice: null,
340980
- orderNonce: remaining.orderNonce,
340981
- status: "filled",
340982
- lastError: null
340983
- });
341306
+ if (order.commitment) await this.withdraw(order.commitment);
341307
+ const closed = await this.store.setPosting(
341308
+ id2,
341309
+ {
341310
+ commitment: null,
341311
+ bookExpiresAt: null,
341312
+ bookPrice: null,
341313
+ orderNonce: order.orderNonce,
341314
+ entryPoint: null,
341315
+ status: "filled",
341316
+ lastError: null
341317
+ },
341318
+ POSTABLE
341319
+ );
340984
341320
  if (closed) this.report({ kind: "filled", order: closed });
340985
341321
  return closed;
340986
341322
  }
340987
- const resized = await this.repost(await this.store.setStatus(id2, "resizing"));
341323
+ const resized = await this.repost(order);
340988
341324
  if (resized) this.report({ kind: "resized", order: resized, delivered });
340989
341325
  return resized;
340990
341326
  }
@@ -340997,7 +341333,7 @@ var LimitOrderService = class {
340997
341333
  */
340998
341334
  async repost(order) {
340999
341335
  if (!order) return null;
341000
- if (isLocal(order)) return this.store.setStatus(order.id, "open");
341336
+ if (isLocal(order)) return this.store.setStatus(order.id, "open", null, POSTABLE);
341001
341337
  if (hasExpired(order.expiresAt, /* @__PURE__ */ new Date())) return this.retire(order);
341002
341338
  if (order.commitment) {
341003
341339
  const withdrawn = await this.withdraw(order.commitment);
@@ -341008,10 +341344,13 @@ var LimitOrderService = class {
341008
341344
  { id: order.id, commitment: order.commitment, err: message },
341009
341345
  "Could not clear the old entry; leaving the posting alone rather than adding a second"
341010
341346
  );
341011
- return this.store.setStatus(order.id, "open", message);
341347
+ return this.store.setStatus(order.id, "open", message, POSTABLE);
341012
341348
  }
341013
341349
  }
341014
- await this.store.setStatus(order.id, "resizing");
341350
+ if (!await this.store.setStatus(order.id, "resizing", null, POSTABLE)) {
341351
+ this.logger.info({ id: order.id }, "Limit order closed while it was being reposted; leaving it closed");
341352
+ return null;
341353
+ }
341015
341354
  const nonce = (BigInt(order.orderNonce) + 1n).toString();
341016
341355
  return (await this.post({ ...order, commitment: null, orderNonce: nonce })).order;
341017
341356
  }
@@ -341054,7 +341393,9 @@ var LimitOrderService = class {
341054
341393
  * They drift apart when a request never got an answer or the process died
341055
341394
  * between two of them: an entry the orderbook still lists that nothing here
341056
341395
  * owns, an order here whose entry has gone, or an entry the orderbook has cut
341057
- * down because the solver cannot cover what it quoted.
341396
+ * down because the solver cannot cover what it quoted. A posted op is also
341397
+ * signed over one EntryPoint, so when the fill chain moves to another, every
341398
+ * entry signed before the move is withdrawn and posted again.
341058
341399
  */
341059
341400
  async reconcile(now = /* @__PURE__ */ new Date()) {
341060
341401
  const [entries, live] = await Promise.all([this.postedOrders(), this.live()]);
@@ -341081,7 +341422,17 @@ var LimitOrderService = class {
341081
341422
  }
341082
341423
  for (const order of live) {
341083
341424
  if (isLocal(order)) continue;
341084
- if (order.commitment && found.has(order.commitment.toLowerCase())) continue;
341425
+ if (order.commitment && found.has(order.commitment.toLowerCase())) {
341426
+ try {
341427
+ if (await this.resign(order)) report.reposted++;
341428
+ } catch (err) {
341429
+ this.logger.error(
341430
+ { id: order.id, err },
341431
+ "Could not sign the limit order again for its chain's EntryPoint"
341432
+ );
341433
+ }
341434
+ continue;
341435
+ }
341085
341436
  if (sinceMs(order.updatedAt, now) < POST_GRACE_MS) continue;
341086
341437
  this.logger.warn({ id: order.id }, "Limit order has no entry on the orderbook; posting it again");
341087
341438
  try {
@@ -341093,6 +341444,45 @@ var LimitOrderService = class {
341093
341444
  }
341094
341445
  return report;
341095
341446
  }
341447
+ /**
341448
+ * Withdraws a posting signed for an EntryPoint its fill chain no longer uses,
341449
+ * and posts it again signed for the one it uses now. Answers whether a new
341450
+ * posting landed.
341451
+ *
341452
+ * The row is read again and claimed out of `open` first: a fill can have worked
341453
+ * it down or started its own repost since reconciliation listed it, and that
341454
+ * write is the one to follow. A fill that lands once the claim is taken waits
341455
+ * in `resize` for this to finish. The EntryPoint is only written with a posting
341456
+ * that landed, so a pass cut short anywhere leaves a row the next pass still
341457
+ * reads as stale, or as having no entry, and finishes the job.
341458
+ */
341459
+ async resign(listed) {
341460
+ const entryPoint = this.configService.getEntryPointAddress(listed.fillChain);
341461
+ if (!entryPoint || sameAddress(signedFor(listed), entryPoint)) return false;
341462
+ const order = await this.store.get(listed.id);
341463
+ if (!order || order.status !== "open" || order.commitment !== listed.commitment) return false;
341464
+ if (sameAddress(signedFor(order), entryPoint) || this.resigning.has(order.id)) return false;
341465
+ const resigned = this.signAgain(order, entryPoint);
341466
+ this.resigning.set(
341467
+ order.id,
341468
+ resigned.catch(() => false)
341469
+ );
341470
+ try {
341471
+ return await resigned;
341472
+ } finally {
341473
+ this.resigning.delete(order.id);
341474
+ }
341475
+ }
341476
+ async signAgain(order, entryPoint) {
341477
+ const claimed = await this.store.setStatus(order.id, "resizing", null, ["open"]);
341478
+ if (!claimed) return false;
341479
+ this.logger.warn(
341480
+ { id: order.id, commitment: order.commitment, signedFor: signedFor(order), entryPoint },
341481
+ "Limit order was signed for an EntryPoint its chain no longer uses; signing it again"
341482
+ );
341483
+ const posted = await this.repost(claimed);
341484
+ return !!posted?.commitment && sameAddress(signedFor(posted), entryPoint);
341485
+ }
341096
341486
  /**
341097
341487
  * Lowers every open order's `remaining` to what the solver's account will still
341098
341488
  * pay for it, and puts the order back on the book at that size.
@@ -341174,6 +341564,7 @@ var LimitOrderService = class {
341174
341564
  bookExpiresAt: null,
341175
341565
  bookPrice: null,
341176
341566
  orderNonce: order.orderNonce,
341567
+ entryPoint: null,
341177
341568
  status: "expired",
341178
341569
  lastError: null
341179
341570
  });
@@ -341230,11 +341621,11 @@ var LimitOrderService = class {
341230
341621
  }
341231
341622
  /** Builds and submits the posting, then writes the orderbook's answer onto the row. */
341232
341623
  async post(order) {
341233
- const { result, orderNonce } = await this.submit(order);
341624
+ const { result, orderNonce, entryPoint } = await this.submit(order);
341234
341625
  if (result.kind === "accepted" || result.kind === "unchanged") {
341235
341626
  const posted = result.order;
341236
341627
  this.logger.info(
341237
- { id: order.id, commitment: posted.commitment, price: posted.price },
341628
+ { id: order.id, commitment: posted.commitment, price: posted.price, entryPoint },
341238
341629
  "Limit order posted to the orderbook"
341239
341630
  );
341240
341631
  const stored = await this.store.setPosting(
@@ -341244,6 +341635,10 @@ var LimitOrderService = class {
341244
341635
  bookExpiresAt: posted.expiresAt,
341245
341636
  bookPrice: posted.price,
341246
341637
  orderNonce: orderNonce.toString(),
341638
+ // An entry the orderbook already held was signed by an earlier attempt,
341639
+ // for whichever EntryPoint was current then. Recorded as unknown, which
341640
+ // reconciliation reads as v0.8 and signs again once the chain is past it.
341641
+ entryPoint: result.kind === "accepted" ? entryPoint : null,
341247
341642
  status: "open",
341248
341643
  lastError: null
341249
341644
  },
@@ -341273,6 +341668,7 @@ var LimitOrderService = class {
341273
341668
  bookExpiresAt: null,
341274
341669
  bookPrice: null,
341275
341670
  orderNonce: orderNonce.toString(),
341671
+ entryPoint: null,
341276
341672
  status: "open",
341277
341673
  lastError: message
341278
341674
  }),
@@ -341295,22 +341691,30 @@ var LimitOrderService = class {
341295
341691
  * only the second one.
341296
341692
  */
341297
341693
  async submit(order) {
341694
+ const entryPoint = this.entryPointFor(order.fillChain);
341298
341695
  const orderNonce = BigInt(order.orderNonce);
341299
- const attempt = await this.buildAndSubmit(order, orderNonce);
341696
+ const attempt = await this.buildAndSubmit(order, orderNonce, entryPoint);
341300
341697
  const first3 = attempt.result;
341301
341698
  if (first3.kind !== "rejected" || first3.code !== "REPLAYED" && first3.code !== "ORDER_EXISTS") {
341302
- return { result: first3, orderNonce };
341699
+ return { result: first3, orderNonce, entryPoint };
341303
341700
  }
341304
341701
  if (first3.code === "ORDER_EXISTS" && attempt.commitment) {
341305
341702
  const live = await this.entryAt(order, attempt.commitment);
341306
341703
  if (live) {
341307
341704
  this.logger.info({ id: order.id, commitment: live.commitment }, "This posting is already on the orderbook");
341308
- return { result: { kind: "unchanged", order: live }, orderNonce };
341705
+ return { result: { kind: "unchanged", order: live }, orderNonce, entryPoint };
341309
341706
  }
341310
341707
  }
341311
341708
  this.logger.warn({ id: order.id, code: first3.code }, "Orderbook has seen this op before; reposting on a new nonce");
341312
341709
  const retried = orderNonce + 1n;
341313
- return { result: (await this.buildAndSubmit(order, retried)).result, orderNonce: retried };
341710
+ const { result } = await this.buildAndSubmit(order, retried, entryPoint);
341711
+ return { result, orderNonce: retried, entryPoint };
341712
+ }
341713
+ /** The EntryPoint a posting on `chain` is signed for now. */
341714
+ entryPointFor(chain2) {
341715
+ const entryPoint = this.configService.getEntryPointAddress(chain2);
341716
+ if (!entryPoint) throw new LimitOrderValidationError(`No EntryPoint is configured for ${chain2}`);
341717
+ return entryPoint;
341314
341718
  }
341315
341719
  /**
341316
341720
  * The entry the orderbook holds at `commitment`, if it holds one. A lookup that
@@ -341326,8 +341730,8 @@ var LimitOrderService = class {
341326
341730
  }
341327
341731
  }
341328
341732
  /** The orderbook's answer, and the commitment the op we sent hashes to. */
341329
- async buildAndSubmit(order, orderNonce) {
341330
- const { commitment, userOp } = await this.buildUserOp(order, orderNonce);
341733
+ async buildAndSubmit(order, orderNonce, entryPoint) {
341734
+ const { commitment, userOp } = await this.buildUserOp(order, orderNonce, entryPoint);
341331
341735
  try {
341332
341736
  return { result: await this.client.submitOrder(userOp), commitment };
341333
341737
  } catch (err) {
@@ -341337,7 +341741,7 @@ var LimitOrderService = class {
341337
341741
  throw err;
341338
341742
  }
341339
341743
  }
341340
- async buildUserOp(order, orderNonce) {
341744
+ async buildUserOp(order, orderNonce, entryPointAddress) {
341341
341745
  const baseToken = this.assetRegistry.getAddress(order.base, order.fillChain);
341342
341746
  const quoteToken = this.assetRegistry.getAddress(order.quote, order.fillChain);
341343
341747
  const [baseDecimals, quoteDecimals] = await Promise.all([
@@ -341352,10 +341756,6 @@ var LimitOrderService = class {
341352
341756
  baseDecimals,
341353
341757
  quoteDecimals
341354
341758
  });
341355
- const entryPointAddress = this.configService.getEntryPointAddress(order.fillChain);
341356
- if (!entryPointAddress) {
341357
- throw new LimitOrderValidationError(`No EntryPoint is configured for ${order.fillChain}`);
341358
- }
341359
341759
  return this.contractService.prepareLimitOrderUserOp({
341360
341760
  fillChain: order.fillChain,
341361
341761
  entryPointAddress,
@@ -341386,6 +341786,12 @@ function hasExpired(expiresAt, now) {
341386
341786
  const at = Date.parse(expiresAt);
341387
341787
  return !Number.isNaN(at) && at <= now.getTime();
341388
341788
  }
341789
+ function signedFor(order) {
341790
+ return order.entryPoint ?? ENTRY_POINT_V08;
341791
+ }
341792
+ function sameAddress(a6, b5) {
341793
+ return a6.toLowerCase() === b5.toLowerCase();
341794
+ }
341389
341795
  function sinceMs(updatedAt, now) {
341390
341796
  const written = Date.parse(`${updatedAt.replace(" ", "T")}Z`);
341391
341797
  return Number.isNaN(written) ? Number.POSITIVE_INFINITY : now.getTime() - written;
@@ -341397,6 +341803,459 @@ function underFunded(entry) {
341397
341803
  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";
341398
341804
  }
341399
341805
 
341806
+ // src/orderbook/profitability.ts
341807
+ init_esm_shims();
341808
+ init_decimal();
341809
+ init_asset_registry();
341810
+
341811
+ // src/data/inventory.ts
341812
+ init_esm_shims();
341813
+ var INVENTORY_INTERVAL_MS = 24 * 60 * 60 * 1e3;
341814
+ function inventoryOf(snapshot) {
341815
+ if (snapshot.status !== "fresh") return null;
341816
+ const held = {};
341817
+ for (const chain2 of snapshot.chains) {
341818
+ for (const asset of chain2.assets) {
341819
+ if (asset.total === null) return null;
341820
+ held[asset.symbol] = (held[asset.symbol] ?? 0) + asset.total;
341821
+ }
341822
+ }
341823
+ return Object.keys(held).length > 0 ? held : null;
341824
+ }
341825
+ function parseUtc(value) {
341826
+ return Date.parse(`${value.replace(" ", "T")}Z`);
341827
+ }
341828
+ var InventoryRecorder = class {
341829
+ constructor(store, balances, logger8, now = Date.now) {
341830
+ this.store = store;
341831
+ this.balances = balances;
341832
+ this.logger = logger8;
341833
+ this.now = now;
341834
+ this.recording = false;
341835
+ this.onSnapshot = (snapshot) => void this.consider(snapshot);
341836
+ }
341837
+ start() {
341838
+ this.balances.on("snapshot", this.onSnapshot);
341839
+ void this.consider(this.balances.getSnapshot());
341840
+ }
341841
+ stop() {
341842
+ this.balances.off("snapshot", this.onSnapshot);
341843
+ }
341844
+ /** Takes a snapshot from `snapshot` if one is due and the read is complete. Never rejects. */
341845
+ async consider(snapshot) {
341846
+ if (this.recording) return;
341847
+ const held = inventoryOf(snapshot);
341848
+ if (!held) return;
341849
+ this.recording = true;
341850
+ try {
341851
+ if (this.lastTakenAt === void 0) {
341852
+ const latest2 = await this.store.latest();
341853
+ this.lastTakenAt = latest2 ? parseUtc(latest2.takenAt) : 0;
341854
+ }
341855
+ if (this.now() - this.lastTakenAt < INVENTORY_INTERVAL_MS) return;
341856
+ await this.store.record(held);
341857
+ this.lastTakenAt = this.now();
341858
+ this.logger.info({ tokens: Object.keys(held).length }, "Recorded an inventory snapshot");
341859
+ } catch (err) {
341860
+ this.logger.warn({ err }, "Could not record an inventory snapshot");
341861
+ } finally {
341862
+ this.recording = false;
341863
+ }
341864
+ }
341865
+ };
341866
+
341867
+ // src/orderbook/usd.ts
341868
+ init_esm_shims();
341869
+ init_decimal();
341870
+ init_asset_registry();
341871
+ function limitOrderUsdEdges(orders) {
341872
+ return orders.filter((order) => normalizeSymbol(order.base) !== normalizeSymbol(order.quote)).map((order) => ({
341873
+ base: normalizeSymbol(order.base),
341874
+ quote: normalizeSymbol(order.quote),
341875
+ rate: new Decimal(order.price).div(ORDERBOOK_SCALE2.toString())
341876
+ })).filter((edge) => edge.rate.isFinite() && edge.rate.gt(0));
341877
+ }
341878
+ function usdFactorsFrom(edges) {
341879
+ const factors = /* @__PURE__ */ new Map();
341880
+ for (const symbol3 of USD_STABLE_SYMBOLS) factors.set(symbol3, new Decimal(1));
341881
+ const ordered = [...edges].sort((a6, b5) => a6.base.localeCompare(b5.base) || a6.quote.localeCompare(b5.quote));
341882
+ let grew = true;
341883
+ while (grew) {
341884
+ grew = false;
341885
+ for (const edge of ordered) {
341886
+ const usdBase = factors.get(edge.base);
341887
+ const usdQuote = factors.get(edge.quote);
341888
+ if (usdBase && !usdQuote) {
341889
+ factors.set(edge.quote, usdBase.div(edge.rate));
341890
+ grew = true;
341891
+ } else if (usdQuote && !usdBase) {
341892
+ factors.set(edge.base, usdQuote.mul(edge.rate));
341893
+ grew = true;
341894
+ }
341895
+ }
341896
+ }
341897
+ return factors;
341898
+ }
341899
+ function usdValueOf(factors, symbol3, amount) {
341900
+ const factor = factors.get(normalizeSymbol(symbol3));
341901
+ return factor ? amount.mul(factor) : null;
341902
+ }
341903
+
341904
+ // src/orderbook/profitability.ts
341905
+ var Decimal2 = Decimal.clone({ precision: 40 });
341906
+ var PROFIT_PERIODS = {
341907
+ "7d": { bucket: "day", count: 7 },
341908
+ "30d": { bucket: "day", count: 30 },
341909
+ "12w": { bucket: "week", count: 12 },
341910
+ "12m": { bucket: "month", count: 12 },
341911
+ all: { bucket: "year", count: null }
341912
+ };
341913
+ function isProfitPeriod(value) {
341914
+ return value in PROFIT_PERIODS;
341915
+ }
341916
+ var MINUTE = 6e4;
341917
+ var DAY = 864e5;
341918
+ var BALANCES_REACH_MS = 7 * DAY;
341919
+ function bucketStart(local, bucket) {
341920
+ const date = new Date(local);
341921
+ const year = date.getUTCFullYear();
341922
+ const month = date.getUTCMonth();
341923
+ if (bucket === "year") return Date.UTC(year, 0, 1);
341924
+ if (bucket === "month") return Date.UTC(year, month, 1);
341925
+ const day = Date.UTC(year, month, date.getUTCDate());
341926
+ return bucket === "day" ? day : day - (date.getUTCDay() + 6) % 7 * DAY;
341927
+ }
341928
+ function shiftBucket(start, bucket, steps) {
341929
+ if (bucket === "day") return start + steps * DAY;
341930
+ if (bucket === "week") return start + steps * 7 * DAY;
341931
+ const date = new Date(start);
341932
+ return bucket === "month" ? Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + steps, 1) : Date.UTC(date.getUTCFullYear() + steps, 0, 1);
341933
+ }
341934
+ function scaled(value) {
341935
+ return new Decimal2(value).div(ORDERBOOK_SCALE2.toString());
341936
+ }
341937
+ function tradeOf(fill, at) {
341938
+ try {
341939
+ const paid = scaled(fill.amount);
341940
+ const taken = fill.amountIn === null ? null : scaled(fill.amountIn);
341941
+ const price = scaled(fill.price);
341942
+ if (!paid.gt(0) || !price.gt(0) || taken !== null && !taken.gt(0)) return null;
341943
+ const buying = fill.side === "BID";
341944
+ const [baseAmount, quoteAmount] = buying ? [taken ?? paid.div(price), paid] : [paid, taken ?? paid.mul(price)];
341945
+ return {
341946
+ at,
341947
+ book: fill.book,
341948
+ base: normalizeSymbol(fill.base),
341949
+ quote: normalizeSymbol(fill.quote),
341950
+ buying,
341951
+ baseAmount,
341952
+ quoteAmount,
341953
+ estimated: taken === null
341954
+ };
341955
+ } catch {
341956
+ return null;
341957
+ }
341958
+ }
341959
+ function add3(holdings, symbol3, amount) {
341960
+ holdings.set(symbol3, (holdings.get(symbol3) ?? new Decimal2(0)).add(amount));
341961
+ }
341962
+ function apply3(holdings, trade, sign6) {
341963
+ const base4 = trade.buying ? trade.baseAmount : trade.baseAmount.neg();
341964
+ const quote = trade.buying ? trade.quoteAmount.neg() : trade.quoteAmount;
341965
+ add3(holdings, trade.base, base4.mul(sign6));
341966
+ add3(holdings, trade.quote, quote.mul(sign6));
341967
+ }
341968
+ var Sums = class {
341969
+ constructor() {
341970
+ this.change = /* @__PURE__ */ new Map();
341971
+ this.boughtUsd = new Decimal2(0);
341972
+ this.soldUsd = new Decimal2(0);
341973
+ this.buys = 0;
341974
+ this.sells = 0;
341975
+ }
341976
+ };
341977
+ var BookLedger = class {
341978
+ constructor(id2, base4, quote) {
341979
+ this.id = id2;
341980
+ this.base = base4;
341981
+ this.quote = quote;
341982
+ this.bought = new Decimal2(0);
341983
+ this.boughtQuote = new Decimal2(0);
341984
+ this.sold = new Decimal2(0);
341985
+ this.soldQuote = new Decimal2(0);
341986
+ /** The rates of the book's latest buy and latest sell, over all of its history. */
341987
+ this.latestBuy = null;
341988
+ this.latestSell = null;
341989
+ this.inPeriod = false;
341990
+ }
341991
+ /**
341992
+ * The rate the book is valued at. Midway between the latest buy and the latest sell, because
341993
+ * either alone sits half a spread to one side and would price open volume against whichever
341994
+ * way the book happened to trade last.
341995
+ */
341996
+ rate() {
341997
+ if (this.latestBuy && this.latestSell) return this.latestBuy.add(this.latestSell).div(2);
341998
+ return this.latestBuy ?? this.latestSell;
341999
+ }
342000
+ };
342001
+ function readingOf(reading) {
342002
+ const held = /* @__PURE__ */ new Map();
342003
+ const cover = /* @__PURE__ */ new Map();
342004
+ for (const [symbol3, amount] of Object.entries(reading.balances)) {
342005
+ const chains2 = reading.chains[symbol3];
342006
+ if (!Number.isFinite(amount) || !chains2?.length) continue;
342007
+ add3(held, normalizeSymbol(symbol3), new Decimal2(amount));
342008
+ cover.set(normalizeSymbol(symbol3), [...chains2].sort().join(","));
342009
+ }
342010
+ return { held, cover };
342011
+ }
342012
+ function movedBetween(earlier, later) {
342013
+ if (!earlier || !later) return null;
342014
+ const moved = /* @__PURE__ */ new Map();
342015
+ for (const [symbol3, cover] of later.cover) {
342016
+ if (earlier.cover.get(symbol3) !== cover) continue;
342017
+ const before = earlier.held.get(symbol3) ?? new Decimal2(0);
342018
+ moved.set(symbol3, (later.held.get(symbol3) ?? new Decimal2(0)).sub(before));
342019
+ }
342020
+ return moved;
342021
+ }
342022
+ function nearestRecord(instant, records) {
342023
+ let record;
342024
+ let gap = Number.POSITIVE_INFINITY;
342025
+ for (const candidate of records) {
342026
+ const distance = Math.abs(candidate.at - instant);
342027
+ if (candidate.live && distance > BALANCES_REACH_MS) continue;
342028
+ if (distance < gap || distance === gap && record?.live && !candidate.live) {
342029
+ record = candidate;
342030
+ gap = distance;
342031
+ }
342032
+ }
342033
+ return record;
342034
+ }
342035
+ function inventoryAt(instant, records, trades, outflows, readings) {
342036
+ const record = nearestRecord(instant, records);
342037
+ if (!record) return null;
342038
+ const recorded = /* @__PURE__ */ new Map();
342039
+ for (const [symbol3, amount] of Object.entries(record.balances)) {
342040
+ if (Number.isFinite(amount)) add3(recorded, normalizeSymbol(symbol3), new Decimal2(amount));
342041
+ }
342042
+ const held = new Map(recorded);
342043
+ const after = record.at >= instant;
342044
+ const [from32, to] = after ? [instant, record.at] : [record.at, instant];
342045
+ const sign6 = after ? -1 : 1;
342046
+ for (const trade of trades) {
342047
+ if (trade.at >= from32 && trade.at < to) apply3(held, trade, sign6);
342048
+ }
342049
+ for (const outflow of outflows) {
342050
+ if (outflow.at >= from32 && outflow.at < to) {
342051
+ add3(held, normalizeSymbol(outflow.symbol), new Decimal2(outflow.amount).mul(-sign6));
342052
+ }
342053
+ }
342054
+ const moved = movedBetween(readings.get(from32), readings.get(to));
342055
+ if (moved) {
342056
+ for (const [symbol3, amount] of moved) {
342057
+ held.set(symbol3, (recorded.get(symbol3) ?? new Decimal2(0)).add(amount.mul(sign6)));
342058
+ }
342059
+ }
342060
+ for (const [symbol3, amount] of held) if (amount.isNegative()) held.set(symbol3, new Decimal2(0));
342061
+ return { held, record };
342062
+ }
342063
+ function tradesOf(fills, now) {
342064
+ const trades = [];
342065
+ for (const fill of fills) {
342066
+ const at = parseUtc(fill.filledAt);
342067
+ if (!Number.isFinite(at) || at > now) continue;
342068
+ const trade = tradeOf(fill, at);
342069
+ if (trade) trades.push(trade);
342070
+ }
342071
+ return trades.sort((a6, b5) => a6.at - b5.at);
342072
+ }
342073
+ function bucketStarts(period, now, offset, firstAt) {
342074
+ const { bucket, count } = PROFIT_PERIODS[period];
342075
+ const last = bucketStart(now - offset, bucket);
342076
+ const first3 = count === null ? bucketStart((firstAt ?? now) - offset, bucket) : shiftBucket(last, bucket, 1 - count);
342077
+ const starts = [];
342078
+ for (let start = first3; start <= last; start = shiftBucket(start, bucket, 1)) starts.push(start);
342079
+ return starts;
342080
+ }
342081
+ function inventoryInstants(fills, options) {
342082
+ const { period, now } = options;
342083
+ const offset = (options.tzOffsetMinutes ?? 0) * MINUTE;
342084
+ const records = (options.inventory ?? []).filter((record) => record.at <= now);
342085
+ const instants = /* @__PURE__ */ new Set([now]);
342086
+ for (const start of bucketStarts(period, now, offset, tradesOf(fills, now)[0]?.at)) {
342087
+ instants.add(start + offset);
342088
+ const record = nearestRecord(start + offset, records);
342089
+ if (record) instants.add(record.at);
342090
+ }
342091
+ return [...instants].sort((a6, b5) => a6 - b5);
342092
+ }
342093
+ function summarizeProfit(fills, options) {
342094
+ const { period, now } = options;
342095
+ const offset = (options.tzOffsetMinutes ?? 0) * MINUTE;
342096
+ const { bucket } = PROFIT_PERIODS[period];
342097
+ const records = (options.inventory ?? []).filter((record) => record.at <= now);
342098
+ const outflows = options.outflows ?? [];
342099
+ const readings = new Map((options.readings ?? []).map((reading) => [reading.at, readingOf(reading)]));
342100
+ const trades = tradesOf(fills, now);
342101
+ const starts = bucketStarts(period, now, offset, trades[0]?.at);
342102
+ const first3 = starts[0];
342103
+ const books = /* @__PURE__ */ new Map();
342104
+ const spelling2 = /* @__PURE__ */ new Map();
342105
+ for (const record of records) {
342106
+ for (const symbol3 of Object.keys(record.balances)) {
342107
+ if (!spelling2.has(normalizeSymbol(symbol3))) spelling2.set(normalizeSymbol(symbol3), symbol3);
342108
+ }
342109
+ }
342110
+ for (const fill of fills) {
342111
+ for (const symbol3 of [fill.base, fill.quote]) {
342112
+ if (!spelling2.has(normalizeSymbol(symbol3))) spelling2.set(normalizeSymbol(symbol3), symbol3);
342113
+ }
342114
+ }
342115
+ for (const trade of trades) {
342116
+ let book = books.get(trade.book);
342117
+ if (!book) {
342118
+ book = new BookLedger(trade.book, trade.base, trade.quote);
342119
+ books.set(trade.book, book);
342120
+ }
342121
+ const rate = trade.quoteAmount.div(trade.baseAmount);
342122
+ if (trade.buying) book.latestBuy = rate;
342123
+ else book.latestSell = rate;
342124
+ }
342125
+ const edges = [];
342126
+ for (const book of books.values()) {
342127
+ const rate = book.rate();
342128
+ if (rate && book.base !== book.quote) {
342129
+ edges.push({ base: book.base, quote: book.quote, rate: new Decimal(rate.toString()) });
342130
+ }
342131
+ }
342132
+ const factors = usdFactorsFrom(edges);
342133
+ const priceOf = (symbol3) => {
342134
+ const factor = factors.get(symbol3);
342135
+ return factor ? new Decimal2(factor.toString()) : null;
342136
+ };
342137
+ const unpriced = /* @__PURE__ */ new Set();
342138
+ const valueOf = (holdings) => {
342139
+ let total = new Decimal2(0);
342140
+ for (const [symbol3, amount] of holdings) {
342141
+ if (amount.isZero()) continue;
342142
+ const price = priceOf(symbol3);
342143
+ if (price) total = total.add(amount.mul(price));
342144
+ else unpriced.add(symbol3);
342145
+ }
342146
+ return total;
342147
+ };
342148
+ const totals = new Sums();
342149
+ const buckets = new Map(starts.map((start) => [start, new Sums()]));
342150
+ let estimatedFills = 0;
342151
+ for (const trade of trades) {
342152
+ const sums = buckets.get(bucketStart(trade.at - offset, bucket));
342153
+ if (!sums) continue;
342154
+ const book = books.get(trade.book);
342155
+ if (!book) continue;
342156
+ book.inPeriod = true;
342157
+ if (trade.estimated) estimatedFills += 1;
342158
+ if (trade.buying) {
342159
+ book.bought = book.bought.add(trade.baseAmount);
342160
+ book.boughtQuote = book.boughtQuote.add(trade.quoteAmount);
342161
+ } else {
342162
+ book.sold = book.sold.add(trade.baseAmount);
342163
+ book.soldQuote = book.soldQuote.add(trade.quoteAmount);
342164
+ }
342165
+ const basePrice = priceOf(trade.base);
342166
+ const quotePrice = priceOf(trade.quote);
342167
+ const volume = basePrice ? trade.baseAmount.mul(basePrice) : quotePrice ? trade.quoteAmount.mul(quotePrice) : new Decimal2(0);
342168
+ for (const sum2 of [totals, sums]) {
342169
+ apply3(sum2.change, trade, 1);
342170
+ if (trade.buying) {
342171
+ sum2.buys += 1;
342172
+ sum2.boughtUsd = sum2.boughtUsd.add(volume);
342173
+ } else {
342174
+ sum2.sells += 1;
342175
+ sum2.soldUsd = sum2.soldUsd.add(volume);
342176
+ }
342177
+ }
342178
+ }
342179
+ const figures = (sums, startsAt, endsAt) => {
342180
+ const profit = valueOf(sums.change);
342181
+ const began2 = inventoryAt(startsAt, records, trades, outflows, readings);
342182
+ const inventory = began2 ? valueOf(began2.held) : null;
342183
+ const moved = movedBetween(readings.get(startsAt), readings.get(endsAt));
342184
+ if (moved) {
342185
+ for (const [symbol3, amount] of sums.change) {
342186
+ if (moved.has(symbol3)) moved.set(symbol3, (moved.get(symbol3) ?? new Decimal2(0)).sub(amount));
342187
+ }
342188
+ }
342189
+ return {
342190
+ profitUsd: profit.toNumber(),
342191
+ startInventoryUsd: inventory ? inventory.toNumber() : null,
342192
+ returnPct: inventory?.gt(0) ? profit.div(inventory).mul(100).toNumber() : null,
342193
+ transfersUsd: moved && moved.size > 0 ? valueOf(moved).toNumber() : null,
342194
+ boughtUsd: sums.boughtUsd.toNumber(),
342195
+ soldUsd: sums.soldUsd.toNumber(),
342196
+ buys: sums.buys,
342197
+ sells: sums.sells
342198
+ };
342199
+ };
342200
+ const from32 = first3 + offset;
342201
+ const began = inventoryAt(from32, records, trades, outflows, readings);
342202
+ const startInventory = began && {
342203
+ at: from32,
342204
+ source: began.record.live ? "balances" : "snapshot",
342205
+ recordedAt: began.record.at,
342206
+ usd: valueOf(began.held).toNumber(),
342207
+ tokens: [...began.held].filter(([, amount]) => amount.gt(0)).map(([symbol3, amount]) => {
342208
+ const price = priceOf(symbol3);
342209
+ return {
342210
+ symbol: spelling2.get(symbol3) ?? symbol3,
342211
+ amount: amount.toNumber(),
342212
+ usd: price ? amount.mul(price).toNumber() : null
342213
+ };
342214
+ }).sort((a6, b5) => (b5.usd ?? 0) - (a6.usd ?? 0) || a6.symbol.localeCompare(b5.symbol))
342215
+ };
342216
+ const rows = [];
342217
+ for (const book of books.values()) {
342218
+ if (!book.inPeriod) continue;
342219
+ const averageBuy = book.bought.gt(0) ? book.boughtQuote.div(book.bought) : null;
342220
+ const averageSell = book.sold.gt(0) ? book.soldQuote.div(book.sold) : null;
342221
+ const baseChange = book.bought.sub(book.sold);
342222
+ const quoteChange = book.soldQuote.sub(book.boughtQuote);
342223
+ const basePrice = priceOf(book.base);
342224
+ const quotePrice = priceOf(book.quote);
342225
+ rows.push({
342226
+ book: book.id,
342227
+ base: spelling2.get(book.base) ?? book.base,
342228
+ quote: spelling2.get(book.quote) ?? book.quote,
342229
+ bought: book.bought.toNumber(),
342230
+ sold: book.sold.toNumber(),
342231
+ averageBuy: averageBuy ? averageBuy.toNumber() : null,
342232
+ averageSell: averageSell ? averageSell.toNumber() : null,
342233
+ spreadPct: averageBuy && averageSell ? averageSell.div(averageBuy).sub(1).mul(100).toNumber() : null,
342234
+ baseChange: baseChange.toNumber(),
342235
+ quoteChange: quoteChange.toNumber(),
342236
+ rate: book.rate()?.toNumber() ?? null,
342237
+ profitUsd: basePrice && quotePrice ? baseChange.mul(basePrice).add(quoteChange.mul(quotePrice)).toNumber() : null
342238
+ });
342239
+ }
342240
+ rows.sort((a6, b5) => (b5.profitUsd ?? 0) - (a6.profitUsd ?? 0) || a6.book.localeCompare(b5.book));
342241
+ return {
342242
+ period,
342243
+ bucket,
342244
+ from: from32,
342245
+ to: now,
342246
+ totals: figures(totals, from32, now),
342247
+ series: starts.map((start, index3) => {
342248
+ const sums = buckets.get(start) ?? new Sums();
342249
+ const endsAt = index3 + 1 < starts.length ? starts[index3 + 1] + offset : now;
342250
+ return { start: start + offset, ...figures(sums, start + offset, endsAt) };
342251
+ }),
342252
+ books: rows,
342253
+ startInventory,
342254
+ estimatedFills,
342255
+ unpricedTokens: [...unpriced].map((symbol3) => spelling2.get(symbol3) ?? symbol3).sort()
342256
+ };
342257
+ }
342258
+
341400
342259
  // src/services/server/UiServer.ts
341401
342260
  init_Logger();
341402
342261
 
@@ -342727,6 +343586,86 @@ init_pairs();
342727
343586
  init_interpolated_curve();
342728
343587
  init_asset_registry();
342729
343588
  init_FillerConfigService();
343589
+
343590
+ // src/services/bundler-preflight.ts
343591
+ init_esm_shims();
343592
+ init_chains2();
343593
+ init_interpolated_curve();
343594
+ init_Logger();
343595
+ var BUNDLER_PREFLIGHT_TIMEOUT_MS = 1e4;
343596
+ async function assertBundlersServeEntryPoint(chains2, configService, options = {}) {
343597
+ const timeoutMs = options.timeoutMs ?? BUNDLER_PREFLIGHT_TIMEOUT_MS;
343598
+ const logger8 = moduleLogger(configService.loggers, "bundler-preflight");
343599
+ const failures = await Promise.all(
343600
+ chains2.map(async ({ chainId, bundlerUrl }) => {
343601
+ if (options.watchOnly?.[chainId] === true) return void 0;
343602
+ if (!bundlerUrl?.trim()) return void 0;
343603
+ const entryPoint = configService.getEntryPointAddress(formatChainKey(chainId));
343604
+ if (!entryPoint) return void 0;
343605
+ return checkBundler(chainId, bundlerUrl.trim(), entryPoint, timeoutMs, logger8);
343606
+ })
343607
+ );
343608
+ const messages = failures.filter((failure) => failure !== void 0);
343609
+ if (messages.length > 0) throw new Error(messages.join("; "));
343610
+ }
343611
+ function servesEntryPoint(listed, entryPoint) {
343612
+ return Array.isArray(listed) && listed.some((address) => String(address).toLowerCase() === entryPoint.toLowerCase());
343613
+ }
343614
+ function describeEntryPoints(listed) {
343615
+ return Array.isArray(listed) && listed.length > 0 ? listed.map(String).join(", ") : "none";
343616
+ }
343617
+ function bundlerHost(url2) {
343618
+ try {
343619
+ return new URL(url2).host || "an unparseable URL";
343620
+ } catch {
343621
+ return "an unparseable URL";
343622
+ }
343623
+ }
343624
+ function chainName(chainId) {
343625
+ const label = chainByChainId(chainId)?.label;
343626
+ return label ? `${label} (${formatChainKey(chainId)})` : formatChainKey(chainId);
343627
+ }
343628
+ async function checkBundler(chainId, bundlerUrl, entryPoint, timeoutMs, logger8) {
343629
+ const host = bundlerHost(bundlerUrl);
343630
+ const subject = `Bundler ${host} for ${chainName(chainId)}`;
343631
+ const signal = AbortSignal.timeout(timeoutMs);
343632
+ let listed;
343633
+ try {
343634
+ listed = await supportedEntryPoints(bundlerUrl, signal);
343635
+ } catch (err) {
343636
+ const reason = signal.aborted ? `timed out after ${timeoutMs}ms` : errorReason(err);
343637
+ logger8.warn(
343638
+ { chain: formatChainKey(chainId), bundler: host },
343639
+ `${subject} did not answer eth_supportedEntryPoints: ${reason.split(bundlerUrl).join(host)}. Continuing without confirming it serves EntryPoint ${entryPoint}`
343640
+ );
343641
+ return void 0;
343642
+ }
343643
+ if (servesEntryPoint(listed, entryPoint)) return void 0;
343644
+ return `${subject} does not support EntryPoint ${entryPoint}; it lists ${describeEntryPoints(listed)}`;
343645
+ }
343646
+ async function supportedEntryPoints(bundlerUrl, signal) {
343647
+ const response = await fetch(bundlerUrl, {
343648
+ method: "POST",
343649
+ headers: { "Content-Type": "application/json" },
343650
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_supportedEntryPoints", params: [] }),
343651
+ signal
343652
+ });
343653
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
343654
+ const json = await response.json();
343655
+ if (json.error) throw new Error(json.error.message ?? JSON.stringify(json.error));
343656
+ const { result } = json;
343657
+ if (!Array.isArray(result) || !result.every((address) => typeof address === "string")) {
343658
+ throw new Error("the result is not a list of addresses");
343659
+ }
343660
+ return result;
343661
+ }
343662
+ function errorReason(err) {
343663
+ if (!(err instanceof Error)) return String(err);
343664
+ const { cause } = err;
343665
+ return cause instanceof Error ? `${err.message} (${cause.message})` : err.message;
343666
+ }
343667
+
343668
+ // src/services/server/setup-api.ts
342730
343669
  init_wallet3();
342731
343670
 
342732
343671
  // src/services/substrate-key.ts
@@ -342925,6 +343864,23 @@ async function validateBundler(body, deps) {
342925
343864
  PROBE_TIMEOUT_MS,
342926
343865
  "Bundler probe"
342927
343866
  );
343867
+ const chainId = Number(body.chainId);
343868
+ const required2 = Number.isInteger(chainId) ? new ChainConfigService({}).getEntryPointAddress(formatChainKey(chainId)) : void 0;
343869
+ if (!required2) return { ok: true, entryPoints };
343870
+ if (!Array.isArray(entryPoints) || !entryPoints.every((address) => typeof address === "string")) {
343871
+ return {
343872
+ ok: true,
343873
+ entryPoints,
343874
+ warning: `Bundler's eth_supportedEntryPoints answer is not a list of addresses, so it could not be confirmed to support EntryPoint ${required2}, which this chain's solver account uses. Simplex will use it and warn.`
343875
+ };
343876
+ }
343877
+ if (!servesEntryPoint(entryPoints, required2)) {
343878
+ return {
343879
+ ok: true,
343880
+ entryPoints,
343881
+ warning: `Bundler does not support EntryPoint ${required2}, which this chain's solver account uses; it lists ${describeEntryPoints(entryPoints)}. Simplex will refuse to use it.`
343882
+ };
343883
+ }
342928
343884
  return { ok: true, entryPoints };
342929
343885
  } catch (err) {
342930
343886
  return {
@@ -343639,6 +344595,55 @@ var UiServer = class {
343639
344595
  if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
343640
344596
  return this.handleLimitOrders(res, () => this.operator.limitOrders.books());
343641
344597
  }
344598
+ if (path2 === "/api/analytics/profitability") {
344599
+ if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
344600
+ if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
344601
+ const params = new URL(req.url ?? "/", "http://localhost").searchParams;
344602
+ const period = params.get("period") ?? "7d";
344603
+ if (!isProfitPeriod(period)) return sendJson(res, 400, { error: "period must be 7d, 30d, 12w, 12m or all" });
344604
+ const tz = Number(params.get("tz") ?? 0);
344605
+ if (!Number.isInteger(tz) || Math.abs(tz) > 14 * 60) {
344606
+ return sendJson(res, 400, { error: "tz must be a whole number of minutes" });
344607
+ }
344608
+ return this.handleLimitOrders(res, () => this.operator.limitOrders.profitability(period, tz));
344609
+ }
344610
+ if (path2 === "/api/orderbook/snapshot") {
344611
+ if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
344612
+ if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
344613
+ const params = new URL(req.url ?? "/", "http://localhost").searchParams;
344614
+ const book = params.get("book")?.trim();
344615
+ if (!book) return sendJson(res, 400, { error: "A book is required" });
344616
+ return this.handleLimitOrders(
344617
+ res,
344618
+ () => this.operator.limitOrders.orderbookSnapshot(book, {
344619
+ sourceChain: params.get("sourceChain") || void 0,
344620
+ fillChain: params.get("fillChain") || void 0
344621
+ })
344622
+ );
344623
+ }
344624
+ if (path2 === "/api/orderbook/level-orders") {
344625
+ if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
344626
+ if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
344627
+ const params = new URL(req.url ?? "/", "http://localhost").searchParams;
344628
+ const book = params.get("book")?.trim();
344629
+ const side = params.get("side");
344630
+ const fillChain = params.get("fillChain")?.trim();
344631
+ const priceBucket = params.get("priceBucket")?.trim();
344632
+ if (!book) return sendJson(res, 400, { error: "A book is required" });
344633
+ if (side !== "BID" && side !== "ASK") return sendJson(res, 400, { error: "side must be BID or ASK" });
344634
+ if (!fillChain) return sendJson(res, 400, { error: "A fill chain is required" });
344635
+ if (!priceBucket || !/^\d+$/.test(priceBucket))
344636
+ return sendJson(res, 400, { error: "priceBucket must be an integer" });
344637
+ return this.handleLimitOrders(
344638
+ res,
344639
+ () => this.operator.limitOrders.orderbookLevelOrders(book, {
344640
+ side,
344641
+ fillChain,
344642
+ priceBucket,
344643
+ sourceChain: params.get("sourceChain") || void 0
344644
+ })
344645
+ );
344646
+ }
343642
344647
  const limitOrderMatch = path2.match(/^\/api\/limit-orders\/([\w-]+)$/);
343643
344648
  if (limitOrderMatch) {
343644
344649
  if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
@@ -345000,6 +346005,7 @@ var CacheService = class {
345000
346005
  })),
345001
346006
  partialFill: plan.partialFill,
345002
346007
  profit: plan.profit,
346008
+ ...plan.inputDecimals !== void 0 ? { inputDecimals: plan.inputDecimals } : {},
345003
346009
  ...plan.budget ? { budget: budgetFromCache(plan.budget) } : {}
345004
346010
  }));
345005
346011
  } catch (error) {
@@ -345030,6 +346036,7 @@ var CacheService = class {
345030
346036
  })),
345031
346037
  partialFill: plan.partialFill,
345032
346038
  profit: plan.profit,
346039
+ ...plan.inputDecimals !== void 0 ? { inputDecimals: plan.inputDecimals } : {},
345033
346040
  ...plan.budget ? { budget: budgetToCache(plan.budget) } : {}
345034
346041
  })),
345035
346042
  timestamp: Date.now()
@@ -348917,9 +349924,9 @@ var INTENT_GATEWAY_V2_ABI = [
348917
349924
  internalType: "bytes32"
348918
349925
  },
348919
349926
  {
348920
- name: "solver",
348921
- type: "address",
348922
- internalType: "address"
349927
+ name: "userOpHash",
349928
+ type: "bytes32",
349929
+ internalType: "bytes32"
348923
349930
  },
348924
349931
  {
348925
349932
  name: "signature",
@@ -350307,10 +351314,13 @@ var LIMIT_ORDER_CALL_GAS_LIMIT = 500000n;
350307
351314
  var LIMIT_ORDER_VERIFICATION_GAS_LIMIT = 150000n;
350308
351315
  var LIMIT_ORDER_PRE_VERIFICATION_GAS = 50000n;
350309
351316
  var BID_DISCOVERY_PAD_SECONDS = 30;
351317
+ var BLOCK_TIME_SAMPLE_BLOCKS = 1000n;
351318
+ var FALLBACK_BLOCK_TIME_SECONDS = 2;
350310
351319
  var ContractInteractionService = class {
350311
351320
  constructor(clientManager, configService, signer, sharedCacheService) {
350312
351321
  this.clientManager = clientManager;
350313
351322
  this.sdkHelperCache = /* @__PURE__ */ new Map();
351323
+ this.blockTimes = /* @__PURE__ */ new Map();
350314
351324
  this.logger = moduleLogger(configService.loggers, "contract-service");
350315
351325
  this.configService = configService;
350316
351326
  this.cacheService = sharedCacheService || new CacheService();
@@ -350370,11 +351380,20 @@ var ContractInteractionService = class {
350370
351380
  try {
350371
351381
  const chainIds = this.configService.getConfiguredChainIds();
350372
351382
  const chainNames = chainIds.map((id2) => `EVM-${id2}`);
350373
- for (const chainName of chainNames) {
351383
+ await Promise.all(
351384
+ chainNames.map(async (chainName2) => {
351385
+ try {
351386
+ await this.blockTimeSeconds(chainName2);
351387
+ } catch (err) {
351388
+ this.logger.warn({ err, chain: chainName2 }, "Could not measure the block time at startup");
351389
+ }
351390
+ })
351391
+ );
351392
+ for (const chainName2 of chainNames) {
350374
351393
  try {
350375
- await this.getFeeTokenWithDecimals(chainName);
351394
+ await this.getFeeTokenWithDecimals(chainName2);
350376
351395
  } catch (err) {
350377
- this.logger.warn({ err, chain: chainName }, "Could not prewarm fee token decimals");
351396
+ this.logger.warn({ err, chain: chainName2 }, "Could not prewarm fee token decimals");
350378
351397
  }
350379
351398
  }
350380
351399
  for (const destChain of chainNames) {
@@ -350959,18 +351978,59 @@ var ContractInteractionService = class {
350959
351978
  *
350960
351979
  * Operators configure seconds because that is the unit the risk is actually in, but the
350961
351980
  * contract compares against block numbers so `order.deadline` and this read the same
350962
- * clock. The conversion uses the chain's nominal block time; it is deliberately rounded
350963
- * up, since erring long costs a slightly stale quote while erring short silently drops
350964
- * winnable bids.
351981
+ * clock. The conversion uses the chain's block time from {@link blockTimeSeconds}; it is
351982
+ * deliberately rounded up, since erring long costs a slightly stale quote while erring
351983
+ * short silently drops winnable bids.
350965
351984
  */
350966
351985
  async bidValidUntilBlock(chain2) {
350967
351986
  const client = this.clientManager.getPublicClient(chain2);
350968
- const currentBlock = await client.getBlockNumber();
350969
- const blockTimeMs = client.chain?.blockTime;
350970
- const blockTimeSec = blockTimeMs ? blockTimeMs / 1e3 : 2;
351987
+ const [currentBlock, blockTimeSec] = await Promise.all([client.getBlockNumber(), this.blockTimeSeconds(chain2)]);
350971
351988
  const windowSec = this.configService.getBidValiditySeconds() + BID_DISCOVERY_PAD_SECONDS;
350972
351989
  return currentBlock + BigInt(Math.ceil(windowSec / blockTimeSec));
350973
351990
  }
351991
+ /**
351992
+ * Seconds per block on `chain`. Measured once, when simplex starts ({@link initCache}), and
351993
+ * kept until it stops. A chain with no measurement yet, because it was added later or its
351994
+ * blocks could not be read at startup, is measured at its first bid.
351995
+ *
351996
+ * The nominal `Chain.blockTime` cannot be relied on alone: it goes stale when a chain
351997
+ * speeds up (BSC makes a block every 0.45s against a declared 0.75s) and some chains
351998
+ * declare none. Converting at a slower block time than the chain's ends a bid early, so
351999
+ * the faster of the measured and the declared figure is used, and a stalled or idle
352000
+ * stretch in the sample never shortens a bid.
352001
+ *
352002
+ * Blocks that cannot be read fall back to the declared figure, then to
352003
+ * {@link FALLBACK_BLOCK_TIME_SECONDS}. That answer is not kept.
352004
+ */
352005
+ blockTimeSeconds(chain2) {
352006
+ const known3 = this.blockTimes.get(chain2);
352007
+ if (known3) return known3;
352008
+ const client = this.clientManager.getPublicClient(chain2);
352009
+ const declaredMs = client.chain?.blockTime;
352010
+ const declared = declaredMs ? declaredMs / 1e3 : void 0;
352011
+ const seconds = this.measureBlockTimeSeconds(client).then((measured) => declared ? Math.min(measured, declared) : measured).catch((err) => {
352012
+ this.blockTimes.delete(chain2);
352013
+ const fallback2 = declared ?? FALLBACK_BLOCK_TIME_SECONDS;
352014
+ this.logger.warn(
352015
+ { err, chain: chain2, seconds: fallback2 },
352016
+ "Could not measure the block time; using the declared one"
352017
+ );
352018
+ return fallback2;
352019
+ });
352020
+ this.blockTimes.set(chain2, seconds);
352021
+ return seconds;
352022
+ }
352023
+ /** The average block time over the chain's last {@link BLOCK_TIME_SAMPLE_BLOCKS} blocks, in seconds. */
352024
+ async measureBlockTimeSeconds(client) {
352025
+ const head = await client.getBlock({ blockTag: "latest" });
352026
+ const span = head.number < BLOCK_TIME_SAMPLE_BLOCKS ? head.number : BLOCK_TIME_SAMPLE_BLOCKS;
352027
+ const tail = await client.getBlock({ blockNumber: head.number - span });
352028
+ const measured = Number(head.timestamp - tail.timestamp) / Number(span);
352029
+ if (!Number.isFinite(measured) || measured <= 0) {
352030
+ throw new Error(`${span} blocks span ${head.timestamp - tail.timestamp}s`);
352031
+ }
352032
+ return measured;
352033
+ }
350974
352034
  /**
350975
352035
  * Builds the signed, never-executed UserOperation that carries one limit order
350976
352036
  * to the HyperFX orderbook.
@@ -351154,6 +352214,8 @@ init_Logger();
351154
352214
  var FALLBACK_VERIFICATION_GAS_LIMIT = 250000n;
351155
352215
  var FALLBACK_CALL_GAS_LIMIT = 1500000n;
351156
352216
  var FALLBACK_PRE_VERIFICATION_GAS = 150000n;
352217
+ var PRIORITY_FEE_BUMP_PERCENT = 8n;
352218
+ var MAX_FEE_BUMP_PERCENT = 10n;
351157
352219
  var UserOpSender = class {
351158
352220
  constructor(clientManager, configService, signer) {
351159
352221
  this.clientManager = clientManager;
@@ -351334,7 +352396,11 @@ var UserOpSender = class {
351334
352396
  );
351335
352397
  return userOp;
351336
352398
  }
351337
- /** Mirrors the gas-price selection used by the delegation and fill paths. */
352399
+ /**
352400
+ * Mirrors the gas-price selection used by the delegation and fill paths: Pimlico's and
352401
+ * Alchemy's own pricing, otherwise the chain's gas price raised to the priority fee a
352402
+ * rundler bundler requires, when the bundler gives one.
352403
+ */
351338
352404
  async getGasPrice(bundlerUrl, publicClient, chainId) {
351339
352405
  const lower2 = bundlerUrl.toLowerCase();
351340
352406
  if (lower2.includes("pimlico.io")) {
@@ -351346,19 +352412,33 @@ var UserOpSender = class {
351346
352412
  return { maxFeePerGas: BigInt(res.fast.maxFeePerGas), maxPriorityFeePerGas: BigInt(res.fast.maxPriorityFeePerGas) };
351347
352413
  }
351348
352414
  if (lower2.includes("alchemy.com")) {
351349
- const [rundlerPriorityFee, latestBlock] = await Promise.all([
352415
+ const [rundlerPriorityFee2, latestBlock2] = await Promise.all([
351350
352416
  this.sendBundlerRpc(bundlerUrl, BundlerMethod.RUNDLER_MAX_PRIORITY_FEE_PER_GAS, []),
351351
352417
  publicClient.getBlock({ blockTag: "latest" })
351352
352418
  ]);
351353
- const baseFeePerGas = latestBlock.baseFeePerGas ?? await publicClient.getGasPrice();
352419
+ const baseFeePerGas = latestBlock2.baseFeePerGas ?? await publicClient.getGasPrice();
351354
352420
  const isArbitrum = BigInt(chainId) === 42161n;
351355
352421
  const prioBump = isArbitrum ? 0n : 25n;
351356
- const maxPriorityFeePerGas = BigInt(rundlerPriorityFee) + BigInt(rundlerPriorityFee) * prioBump / 100n;
352422
+ const maxPriorityFeePerGas = BigInt(rundlerPriorityFee2) + BigInt(rundlerPriorityFee2) * prioBump / 100n;
351357
352423
  const bufferedBaseFee = baseFeePerGas + baseFeePerGas * 50n / 100n;
351358
352424
  return { maxFeePerGas: bufferedBaseFee + maxPriorityFeePerGas, maxPriorityFeePerGas };
351359
352425
  }
351360
- const gasPrice = await publicClient.getGasPrice();
351361
- return { maxFeePerGas: gasPrice + gasPrice * 10n / 100n, maxPriorityFeePerGas: gasPrice + gasPrice * 8n / 100n };
352426
+ const [gasPrice, rundlerPriorityFee] = await Promise.all([
352427
+ publicClient.getGasPrice(),
352428
+ fetchRundlerPriorityFee(bundlerUrl)
352429
+ ]);
352430
+ const fees4 = {
352431
+ maxFeePerGas: gasPrice + gasPrice * MAX_FEE_BUMP_PERCENT / 100n,
352432
+ maxPriorityFeePerGas: gasPrice + gasPrice * PRIORITY_FEE_BUMP_PERCENT / 100n
352433
+ };
352434
+ if (rundlerPriorityFee === null) return fees4;
352435
+ const latestBlock = await publicClient.getBlock({ blockTag: "latest" });
352436
+ return applyRundlerPriorityFee(fees4, {
352437
+ rundlerPriorityFee,
352438
+ baseFeePerGas: latestBlock.baseFeePerGas ?? gasPrice,
352439
+ priorityFeeBumpPercent: PRIORITY_FEE_BUMP_PERCENT,
352440
+ maxFeeBumpPercent: MAX_FEE_BUMP_PERCENT
352441
+ });
351362
352442
  }
351363
352443
  async sendBundlerRpc(bundlerUrl, method, params) {
351364
352444
  const response = await fetch(bundlerUrl, {
@@ -366777,8 +367857,8 @@ var validateBalanceForTransaction = async (params) => {
366777
367857
  }, operationContext);
366778
367858
  const balance = await balancePrepared.execute();
366779
367859
  if (BigInt(balance) < BigInt(amount)) {
366780
- const chainName = extractChainInfo(operationContext.chain).name;
366781
- throw createInsufficientTokenBalanceError(chainName, token, {
367860
+ const chainName2 = extractChainInfo(operationContext.chain).name;
367861
+ throw createInsufficientTokenBalanceError(chainName2, token, {
366782
367862
  balance: balance.toString(),
366783
367863
  amount,
366784
367864
  tokenAddress,
@@ -366793,8 +367873,8 @@ var validateNativeBalanceForTransaction = async (params) => {
366793
367873
  }, operationContext);
366794
367874
  const balance = await balancePrepared.execute();
366795
367875
  if (BigInt(balance) === 0n) {
366796
- const chainName = extractChainInfo(operationContext.chain).name;
366797
- throw createInsufficientGasError(chainName, {
367876
+ const chainName2 = extractChainInfo(operationContext.chain).name;
367877
+ throw createInsufficientGasError(chainName2, {
366798
367878
  balance: "0",
366799
367879
  walletAddress: operationContext.address
366800
367880
  });
@@ -384432,7 +385512,12 @@ var IntentFiller = class {
384432
385512
  else cache.clearFundingPrepends(order.id);
384433
385513
  }
384434
385514
  reservation = await this.holdAll([
384435
- { limitOrderId: plan.limitOrderId, payout: plan.payout, take: plan.fillerInputs[plan.leg]?.amount }
385515
+ {
385516
+ limitOrderId: plan.limitOrderId,
385517
+ payout: plan.payout,
385518
+ take: plan.fillerInputs[plan.leg]?.amount,
385519
+ takeDecimals: plan.inputDecimals
385520
+ }
384436
385521
  ]);
384437
385522
  if (reservation.length === 0) {
384438
385523
  this.logger.info(
@@ -384602,7 +385687,8 @@ var IntentFiller = class {
384602
385687
  taken.push({
384603
385688
  limitOrderId: hold.limitOrderId,
384604
385689
  amount,
384605
- ...hold.take !== void 0 ? { take: hold.take.toString() } : {}
385690
+ ...hold.take !== void 0 ? { take: hold.take.toString() } : {},
385691
+ ...hold.takeDecimals !== void 0 ? { takeDecimals: hold.takeDecimals } : {}
384606
385692
  });
384607
385693
  }
384608
385694
  return taken;
@@ -384629,6 +385715,7 @@ var IntentFiller = class {
384629
385715
  }
384630
385716
  const settled = executedHold(claimed, delivered.scaled, released);
384631
385717
  const charged = chargedFor(settled, delivered, released);
385718
+ const takenIn = released > 0n && settled.takeDecimals !== void 0 ? toScaled(released, settled.takeDecimals) : null;
384632
385719
  const settledBid = rows.find(
384633
385720
  (row) => row.reservations.some(
384634
385721
  (hold) => hold.limitOrderId === settled.limitOrderId && hold.amount === settled.amount && hold.take === settled.take
@@ -384647,6 +385734,7 @@ var IntentFiller = class {
384647
385734
  commitment,
384648
385735
  bid: settledBid,
384649
385736
  amount: share.toString(),
385737
+ amountIn: takenIn?.toString() ?? null,
384650
385738
  transactionHash: transactionHash ?? null
384651
385739
  });
384652
385740
  }
@@ -384799,45 +385887,6 @@ init_esm();
384799
385887
  init_Logger();
384800
385888
  init_asset_registry();
384801
385889
  init_decimal();
384802
-
384803
- // src/orderbook/usd.ts
384804
- init_esm_shims();
384805
- init_decimal();
384806
- init_asset_registry();
384807
- function limitOrderUsdEdges(orders) {
384808
- return orders.filter((order) => normalizeSymbol(order.base) !== normalizeSymbol(order.quote)).map((order) => ({
384809
- base: normalizeSymbol(order.base),
384810
- quote: normalizeSymbol(order.quote),
384811
- rate: new Decimal(order.price).div(ORDERBOOK_SCALE2.toString())
384812
- })).filter((edge) => edge.rate.isFinite() && edge.rate.gt(0));
384813
- }
384814
- function usdFactorsFrom(edges) {
384815
- const factors = /* @__PURE__ */ new Map();
384816
- for (const symbol3 of USD_STABLE_SYMBOLS) factors.set(symbol3, new Decimal(1));
384817
- const ordered = [...edges].sort((a6, b5) => a6.base.localeCompare(b5.base) || a6.quote.localeCompare(b5.quote));
384818
- let grew = true;
384819
- while (grew) {
384820
- grew = false;
384821
- for (const edge of ordered) {
384822
- const usdBase = factors.get(edge.base);
384823
- const usdQuote = factors.get(edge.quote);
384824
- if (usdBase && !usdQuote) {
384825
- factors.set(edge.quote, usdBase.div(edge.rate));
384826
- grew = true;
384827
- } else if (usdQuote && !usdBase) {
384828
- factors.set(edge.base, usdQuote.mul(edge.rate));
384829
- grew = true;
384830
- }
384831
- }
384832
- }
384833
- return factors;
384834
- }
384835
- function usdValueOf(factors, symbol3, amount) {
384836
- const factor = factors.get(normalizeSymbol(symbol3));
384837
- return factor ? amount.mul(factor) : null;
384838
- }
384839
-
384840
- // src/strategies/fx.ts
384841
385890
  function adjustDecimalsFloor(amount, fromDecimals, toDecimals) {
384842
385891
  if (fromDecimals === toDecimals) return amount;
384843
385892
  if (fromDecimals < toDecimals) return amount * BigInt(10 ** (toDecimals - fromDecimals));
@@ -385318,6 +386367,7 @@ var FXFiller = class _FXFiller {
385318
386367
  payout: toScaled(finalOutputAmount, outputDecimals),
385319
386368
  fillerOutputs,
385320
386369
  fillerInputs,
386370
+ inputDecimals,
385321
386371
  fundingCalls: [...fundingCalls],
385322
386372
  partialFill,
385323
386373
  profit: totalProfit,
@@ -386611,8 +387661,8 @@ async function bootFiller(config5, options) {
386611
387661
  };
386612
387662
  const configService = new FillerConfigService(resolvedChains, fillerConfigForService, options.loggers);
386613
387663
  const chainConfigs2 = resolvedChains.map((chain2) => {
386614
- const chainName = `EVM-${chain2.chainId}`;
386615
- return configService.getChainConfig(chainName);
387664
+ const chainName2 = `EVM-${chain2.chainId}`;
387665
+ return configService.getChainConfig(chainName2);
386616
387666
  });
386617
387667
  let watchOnlyConfig;
386618
387668
  const globalWatchOnly = options.watchOnlyOverride === true || config5.simplex.watchOnly === true;
@@ -386650,6 +387700,8 @@ async function bootFiller(config5, options) {
386650
387700
  "A signer is required unless every chain is watch-only: pass `signer` to Simplex.start (privateKeySigner, secretPhraseSigner, turnkeySigner, mpcVaultSigner, viemSigner, or your own Signer implementation). Running the binary? Configure it under [simplex.signer]."
386651
387701
  );
386652
387702
  }
387703
+ logger8.info("Checking each bundler serves its chain's EntryPoint...");
387704
+ await assertBundlersServeEntryPoint(resolvedChains, configService, { watchOnly: watchOnlyConfig });
386653
387705
  const chainClientManager = new ChainClientManager(configService, options.signer);
386654
387706
  const runtimeSigner = chainClientManager.getSigner();
386655
387707
  if (options.signer) {
@@ -386812,9 +387864,10 @@ async function bootFiller(config5, options) {
386812
387864
  const activity = new ActivityRecorder(options.data.activity, options.loggers, { describeToken });
386813
387865
  activity.attach(intentFiller.monitor);
386814
387866
  const network = resolvedChains.some((chain2) => chainByChainId(chain2.chainId)?.network === "testnet") ? "testnet" : "mainnet";
387867
+ const indexerUrl = config5.simplex.indexerUrl ?? DEFAULT_INDEXER_URLS[network];
386815
387868
  void backfillOrderSummaries({
386816
387869
  store: options.data.activity,
386817
- indexerUrl: config5.simplex.indexerUrl ?? DEFAULT_INDEXER_URLS[network],
387870
+ indexerUrl,
386818
387871
  fillerAddress: runtimeSigner.address,
386819
387872
  describeToken,
386820
387873
  onUpdated: (rows) => {
@@ -386839,10 +387892,10 @@ async function bootFiller(config5, options) {
386839
387892
  const token1 = {};
386840
387893
  for (const pair of config5.pairs ?? []) {
386841
387894
  if (normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)) continue;
386842
- for (const chainName of configuredChainNames) {
386843
- const address = assetRegistry.getAddress(pair.token1, chainName);
387895
+ for (const chainName2 of configuredChainNames) {
387896
+ const address = assetRegistry.getAddress(pair.token1, chainName2);
386844
387897
  if (!address) continue;
386845
- const list = token1[chainName] ?? (token1[chainName] = []);
387898
+ const list = token1[chainName2] ?? (token1[chainName2] = []);
386846
387899
  if (!list.includes(address)) list.push(address);
386847
387900
  }
386848
387901
  }
@@ -386880,6 +387933,15 @@ async function bootFiller(config5, options) {
386880
387933
  vaultVenue?.startSweeping();
386881
387934
  started.push(() => balanceProvider.stop());
386882
387935
  await balanceProvider.start();
387936
+ if (options.signer) {
387937
+ const inventoryRecorder = new InventoryRecorder(
387938
+ options.data.inventory,
387939
+ balanceProvider,
387940
+ moduleLogger(options.loggers, "inventory")
387941
+ );
387942
+ started.push(() => inventoryRecorder.stop());
387943
+ inventoryRecorder.start();
387944
+ }
386883
387945
  const watchOnlyChains = watchOnlyConfig ? Object.entries(watchOnlyConfig).filter(([, value]) => value === true).map(([chainId]) => Number.parseInt(chainId, 10)) : [];
386884
387946
  logger8.info(
386885
387947
  {
@@ -386925,6 +387987,8 @@ async function bootFiller(config5, options) {
386925
387987
  rebalancingService,
386926
387988
  resolvedChains,
386927
387989
  fillerAddress: runtimeSigner.address,
387990
+ indexerUrl,
387991
+ describeToken,
386928
387992
  watchOnly: watchOnlyConfig,
386929
387993
  config: config5,
386930
387994
  configPath: options.configPath,
@@ -387208,10 +388272,20 @@ var MemoryLimitOrderStore = class {
387208
388272
  commitment: fill.commitment,
387209
388273
  bid: fill.bid ?? null,
387210
388274
  amount: fill.amount,
388275
+ amountIn: fill.amountIn ?? null,
387211
388276
  transactionHash: fill.transactionHash ?? null,
387212
388277
  filledAt: sqliteDatetime(/* @__PURE__ */ new Date())
387213
388278
  });
387214
388279
  }
388280
+ async fillHistory() {
388281
+ return this.fillRows.flatMap((fill) => {
388282
+ const order = this.orders.get(fill.limitOrderId);
388283
+ if (!order) return [];
388284
+ const { id: id2, limitOrderId, amount, amountIn, filledAt } = fill;
388285
+ const { book, base: base4, quote, side, price } = order;
388286
+ return [{ id: id2, limitOrderId, book, base: base4, quote, side, price, amount, amountIn, filledAt }];
388287
+ });
388288
+ }
387215
388289
  async fills(limitOrderId, limit = 100) {
387216
388290
  return this.fillRows.filter((fill) => fill.limitOrderId === limitOrderId).slice(-capLimit(limit)).reverse().map((fill) => ({ ...fill }));
387217
388291
  }
@@ -387226,6 +388300,7 @@ var MemoryLimitOrderStore = class {
387226
388300
  status: "open",
387227
388301
  commitment: null,
387228
388302
  orderNonce: order.orderNonce ?? "0",
388303
+ entryPoint: null,
387229
388304
  bookExpiresAt: null,
387230
388305
  bookPrice: null,
387231
388306
  lastError: null,
@@ -387318,12 +388393,29 @@ var MemoryStateStore = class {
387318
388393
  this.state = { ...state };
387319
388394
  }
387320
388395
  };
388396
+ var MemoryInventoryStore = class {
388397
+ constructor() {
388398
+ this.rows = [];
388399
+ }
388400
+ async record(balances) {
388401
+ this.rows.push({ id: this.rows.length + 1, takenAt: sqliteDatetime(/* @__PURE__ */ new Date()), balances: { ...balances } });
388402
+ }
388403
+ async latest() {
388404
+ const last = this.rows[this.rows.length - 1];
388405
+ return last ? { ...last, balances: { ...last.balances } } : null;
388406
+ }
388407
+ async since(from32) {
388408
+ const cutoff = sqliteDatetime(from32);
388409
+ return this.rows.filter((row) => row.takenAt >= cutoff).map((row) => ({ ...row, balances: { ...row.balances } }));
388410
+ }
388411
+ };
387321
388412
  var MemoryDataStore = class {
387322
388413
  constructor() {
387323
388414
  this.bids = new MemoryBidStore();
387324
388415
  this.activity = new MemoryActivityStore();
387325
388416
  this.state = new MemoryStateStore();
387326
388417
  this.limitOrders = new MemoryLimitOrderStore();
388418
+ this.inventory = new MemoryInventoryStore();
387327
388419
  }
387328
388420
  };
387329
388421
 
@@ -387935,6 +389027,107 @@ var OrderScanner = class _OrderScanner {
387935
389027
  }
387936
389028
  };
387937
389029
 
389030
+ // src/data/indexer-inventory.ts
389031
+ init_esm_shims();
389032
+ init_esm();
389033
+ var READ_TIMEOUT_MS = 1e4;
389034
+ var SETTLED_AFTER_MS = 10 * 6e4;
389035
+ var BATCH = 40;
389036
+ var MAX_KEPT = 2e3;
389037
+ var IndexerInventory = class {
389038
+ constructor(options) {
389039
+ this.options = options;
389040
+ this.kept = /* @__PURE__ */ new Map();
389041
+ this.described = /* @__PURE__ */ new Map();
389042
+ this.fetch = options.fetch ?? fetch;
389043
+ }
389044
+ /**
389045
+ * A reading for each of `instants` the indexer could answer for. Never rejects: an instant it
389046
+ * could not be asked about is simply missing from the answer.
389047
+ */
389048
+ async at(instants, now) {
389049
+ const readings = [];
389050
+ const wanted = [];
389051
+ for (const instant of new Set(instants)) {
389052
+ const kept = this.kept.get(instant);
389053
+ if (kept) readings.push(kept);
389054
+ else wanted.push(instant);
389055
+ }
389056
+ try {
389057
+ for (let index3 = 0; index3 < wanted.length; index3 += BATCH) {
389058
+ for (const reading of await this.read(wanted.slice(index3, index3 + BATCH))) {
389059
+ readings.push(reading);
389060
+ if (reading.at <= now - SETTLED_AFTER_MS) this.keep(reading);
389061
+ }
389062
+ }
389063
+ } catch (err) {
389064
+ this.options.logger.warn(
389065
+ { err: err instanceof Error ? err.message : String(err) },
389066
+ "Could not read inventory from the indexer; carrying records by fills and sends alone"
389067
+ );
389068
+ }
389069
+ return readings;
389070
+ }
389071
+ keep(reading) {
389072
+ if (this.kept.size >= MAX_KEPT) this.kept.clear();
389073
+ this.kept.set(reading.at, reading);
389074
+ }
389075
+ /** One request for several instants, each its own aliased query at that moment in history. */
389076
+ async read(instants) {
389077
+ const queries = instants.map(
389078
+ (instant, index3) => `r${index3}: solverInventories(filter: { solver: { equalTo: $solver } }, blockHeight: "${Math.floor(instant)}", first: 1000) { nodes { chain tokenAddress balance } }`
389079
+ );
389080
+ const response = await this.fetch(this.options.indexerUrl, {
389081
+ method: "POST",
389082
+ headers: { "content-type": "application/json" },
389083
+ body: JSON.stringify({
389084
+ query: `query SolverInventory($solver: String!) {
389085
+ ${queries.join("\n")}
389086
+ }`,
389087
+ variables: { solver: this.options.solver.toLowerCase() }
389088
+ }),
389089
+ signal: AbortSignal.timeout(READ_TIMEOUT_MS)
389090
+ });
389091
+ if (!response.ok) throw new Error(`Indexer responded ${response.status}`);
389092
+ const body = await response.json();
389093
+ if (body.errors?.length) throw new Error(body.errors.map((error) => error.message).join("; "));
389094
+ const readings = [];
389095
+ for (const [index3, instant] of instants.entries()) {
389096
+ const rows = body.data?.[`r${index3}`]?.nodes;
389097
+ if (rows) readings.push(await this.readingOf(instant, rows));
389098
+ }
389099
+ return readings;
389100
+ }
389101
+ async readingOf(at, rows) {
389102
+ const balances = {};
389103
+ const chains2 = {};
389104
+ for (const row of rows) {
389105
+ const token = await this.describe(row.chain, row.tokenAddress);
389106
+ if (!token) continue;
389107
+ let amount;
389108
+ try {
389109
+ amount = Number(formatUnits(BigInt(row.balance), token.decimals));
389110
+ } catch {
389111
+ continue;
389112
+ }
389113
+ balances[token.symbol] = (balances[token.symbol] ?? 0) + amount;
389114
+ chains2[token.symbol] = [...chains2[token.symbol] ?? [], row.chain];
389115
+ }
389116
+ return { at, balances, chains: chains2 };
389117
+ }
389118
+ /** A token's symbol and decimals, remembered once both are known. */
389119
+ async describe(chain2, token) {
389120
+ const key = `${chain2}:${token.toLowerCase()}`;
389121
+ const known3 = this.described.get(key);
389122
+ if (known3) return known3;
389123
+ const { symbol: symbol3, decimals } = await this.options.describeToken(chain2, token);
389124
+ if (!symbol3 || decimals === null) return null;
389125
+ const description = { symbol: symbol3, decimals };
389126
+ this.described.set(key, description);
389127
+ return description;
389128
+ }
389129
+ };
389130
+
387938
389131
  // src/simplex.ts
387939
389132
  var EVENT_MAP = {
387940
389133
  newOrder: "order:detected",
@@ -387980,10 +389173,66 @@ var LimitOrderController = class {
387980
389173
  ]);
387981
389174
  return { order, fills, bids };
387982
389175
  }
389176
+ /**
389177
+ * What the operator's buys and sells earned over `period`, bucketed on a clock
389178
+ * `tzOffsetMinutes` behind UTC.
389179
+ *
389180
+ * Profit is the change the period's fills made to what the solver holds, and
389181
+ * it is compared with the inventory the period began with. That inventory
389182
+ * comes from the stored snapshots and, when the balances have been read in
389183
+ * full, from what is held now, each carried to the period's start by what
389184
+ * moved between. The indexer says what moved, deposits and withdrawals
389185
+ * included, wherever it has followed the solver; elsewhere the fills and
389186
+ * sends on record stand in. It answers whether or not an orderbook is
389187
+ * configured, and whether or not the indexer does.
389188
+ */
389189
+ async profitability(period, tzOffsetMinutes = 0) {
389190
+ const now = Date.now();
389191
+ const { data: data4, balanceProvider } = this.runtime;
389192
+ const [fills, snapshots, ledger] = await Promise.all([
389193
+ data4.limitOrders.fillHistory(),
389194
+ data4.inventory.since(/* @__PURE__ */ new Date(0)),
389195
+ data4.activity.walletTxs(500)
389196
+ ]);
389197
+ const inventory = snapshots.map((snapshot) => ({
389198
+ at: parseUtc(snapshot.takenAt),
389199
+ balances: snapshot.balances
389200
+ }));
389201
+ const held = inventoryOf(balanceProvider.getSnapshot());
389202
+ if (held) inventory.push({ at: now, balances: held, live: true });
389203
+ const outflows = [];
389204
+ for (const tx of ledger) {
389205
+ const amount = Number(tx.amount);
389206
+ if (tx.kind === "send" && tx.token && Number.isFinite(amount) && amount > 0) {
389207
+ outflows.push({ at: tx.ts, symbol: tx.token, amount });
389208
+ }
389209
+ }
389210
+ const instants = inventoryInstants(fills, { period, now, tzOffsetMinutes, inventory });
389211
+ const readings = await this.indexerInventory().at(instants, now);
389212
+ return summarizeProfit(fills, { period, now, tzOffsetMinutes, inventory, outflows, readings });
389213
+ }
389214
+ /** One reader for the life of the filler, so the readings it has already fetched are kept. */
389215
+ indexerInventory() {
389216
+ this.indexed ?? (this.indexed = new IndexerInventory({
389217
+ indexerUrl: this.runtime.indexerUrl,
389218
+ solver: this.runtime.fillerAddress,
389219
+ describeToken: this.runtime.describeToken,
389220
+ logger: this.runtime.loggers.get("inventory")
389221
+ }));
389222
+ return this.indexed;
389223
+ }
387983
389224
  /** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
387984
389225
  books() {
387985
389226
  return this.service.books();
387986
389227
  }
389228
+ /** Public bids and asks across solvers, optionally restricted to a route. */
389229
+ orderbookSnapshot(book, filters = {}) {
389230
+ return this.service.orderbookSnapshot(book, filters);
389231
+ }
389232
+ /** Every solver's order behind one public price level. */
389233
+ orderbookLevelOrders(book, level) {
389234
+ return this.service.orderbookLevelOrders(book, level);
389235
+ }
387987
389236
  /** Creates the order, posts it, and reports what the orderbook made of it. */
387988
389237
  async create(request3) {
387989
389238
  const posted = await this.service.create(request3);
@@ -388045,10 +389294,10 @@ var PairController = class {
388045
389294
  if (assets && Object.keys(assets).length > 0) config5.assets = nextAssets;
388046
389295
  if (normalizeSymbol(pair.token0) !== normalizeSymbol(pair.token1)) {
388047
389296
  for (const chain2 of this.runtime.resolvedChains) {
388048
- const chainName = formatChainKey(chain2.chainId);
388049
- const address = assetRegistry.getAddress(pair.token1, chainName);
389297
+ const chainName2 = formatChainKey(chain2.chainId);
389298
+ const address = assetRegistry.getAddress(pair.token1, chainName2);
388050
389299
  if (!address) continue;
388051
- const list = balanceTokens[chainName] ?? (balanceTokens[chainName] = []);
389300
+ const list = balanceTokens[chainName2] ?? (balanceTokens[chainName2] = []);
388052
389301
  if (!list.includes(address)) list.push(address);
388053
389302
  }
388054
389303
  }
@@ -388153,8 +389402,9 @@ var ChainController = class {
388153
389402
  `Chain ${chainId} cannot be added as filling: this solver was started without a signer. Restart with \`signer\` to fill.`
388154
389403
  );
388155
389404
  }
388156
- configService.addChain(resolved);
388157
389405
  const watchOnly = chain2.watchOnly ?? (this.runtime.signerless || this.runtime.globalWatchOnly);
389406
+ await assertBundlersServeEntryPoint([resolved], configService, { watchOnly: { [chainId]: watchOnly } });
389407
+ configService.addChain(resolved);
388158
389408
  if (watchOnly) intentFiller.setWatchOnly(chainId, true);
388159
389409
  try {
388160
389410
  if (!inScanner) {
@@ -388246,6 +389496,9 @@ var ChainController = class {
388246
389496
  if (!bundlerUrl?.trim()) throw new Error("A bundler URL is required");
388247
389497
  const index3 = this.runtime.resolvedChains.findIndex((chain2) => chain2.chainId === chainId);
388248
389498
  if (index3 < 0) throw new Error(`Chain ${chainId} is not configured`);
389499
+ await assertBundlersServeEntryPoint([{ chainId, bundlerUrl }], this.runtime.configService, {
389500
+ watchOnly: this.runtime.intentFiller.getWatchOnly()
389501
+ });
388249
389502
  this.runtime.configService.setBundlerUrl(chainId, bundlerUrl);
388250
389503
  this.runtime.resolvedChains[index3].bundlerUrl = bundlerUrl;
388251
389504
  this.runtime.config.chains[index3].bundlerUrl = bundlerUrl;
@@ -388260,6 +389513,10 @@ var ChainController = class {
388260
389513
  "Watch-only cannot be disabled: this solver was started without a signer. Restart with `signer` to fill."
388261
389514
  );
388262
389515
  }
389516
+ if (!watchOnly) {
389517
+ const chain2 = this.runtime.resolvedChains.find((resolved) => resolved.chainId === chainId);
389518
+ if (chain2) await assertBundlersServeEntryPoint([chain2], this.runtime.configService);
389519
+ }
388263
389520
  this.runtime.intentFiller.setWatchOnly(chainId, watchOnly);
388264
389521
  this.syncWatchOnlyToConfig();
388265
389522
  await this.persist();
@@ -389074,6 +390331,40 @@ var SqliteBidStore = class {
389074
390331
  }
389075
390332
  };
389076
390333
 
390334
+ // src/data/sqlite/inventory.ts
390335
+ init_esm_shims();
390336
+ var SqliteInventoryStore = class {
390337
+ constructor(db) {
390338
+ this.db = db;
390339
+ this.db.exec(`
390340
+ CREATE TABLE IF NOT EXISTS inventory_snapshots (
390341
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
390342
+ taken_at TEXT NOT NULL DEFAULT (datetime('now')),
390343
+ balances TEXT NOT NULL
390344
+ );
390345
+
390346
+ CREATE INDEX IF NOT EXISTS idx_inventory_snapshots_taken_at ON inventory_snapshots(taken_at);
390347
+ `);
390348
+ }
390349
+ toSnapshot(row) {
390350
+ return { id: row.id, takenAt: row.takenAt, balances: JSON.parse(row.balances) };
390351
+ }
390352
+ async record(balances) {
390353
+ this.db.prepare("INSERT INTO inventory_snapshots (balances) VALUES (?)").run(JSON.stringify(balances));
390354
+ }
390355
+ async latest() {
390356
+ const row = this.db.prepare("SELECT id, taken_at as takenAt, balances FROM inventory_snapshots ORDER BY id DESC LIMIT 1").get();
390357
+ return row ? this.toSnapshot(row) : null;
390358
+ }
390359
+ async since(from32) {
390360
+ const cutoff = from32.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
390361
+ const rows = this.db.prepare(
390362
+ "SELECT id, taken_at as takenAt, balances FROM inventory_snapshots WHERE taken_at >= ? ORDER BY taken_at, id"
390363
+ ).all(cutoff);
390364
+ return rows.map((row) => this.toSnapshot(row));
390365
+ }
390366
+ };
390367
+
389077
390368
  // src/data/sqlite/limit-orders.ts
389078
390369
  init_esm_shims();
389079
390370
  init_Logger();
@@ -389094,6 +390385,7 @@ var LIMIT_ORDER_COLUMNS = `
389094
390385
  status,
389095
390386
  commitment,
389096
390387
  order_nonce as orderNonce,
390388
+ entry_point as entryPoint,
389097
390389
  book_expires_at as bookExpiresAt,
389098
390390
  book_price as bookPrice,
389099
390391
  last_error as lastError,
@@ -389130,6 +390422,7 @@ var SqliteLimitOrderStore = class {
389130
390422
  status TEXT NOT NULL,
389131
390423
  commitment TEXT,
389132
390424
  order_nonce TEXT NOT NULL DEFAULT '0',
390425
+ entry_point TEXT,
389133
390426
  book_expires_at TEXT,
389134
390427
  book_price TEXT,
389135
390428
  last_error TEXT,
@@ -389147,12 +390440,21 @@ var SqliteLimitOrderStore = class {
389147
390440
  commitment TEXT NOT NULL,
389148
390441
  bid TEXT,
389149
390442
  amount TEXT NOT NULL,
390443
+ amount_in TEXT,
389150
390444
  transaction_hash TEXT,
389151
390445
  filled_at TEXT NOT NULL DEFAULT (datetime('now'))
389152
390446
  );
389153
390447
 
389154
390448
  CREATE INDEX IF NOT EXISTS idx_limit_order_fills_order ON limit_order_fills(limit_order_id);
389155
390449
  `);
390450
+ if (!columnNames(this.db, "limit_orders").has("entry_point")) {
390451
+ this.db.exec("ALTER TABLE limit_orders ADD COLUMN entry_point TEXT");
390452
+ this.logger.info({ column: "entry_point" }, "Migrated limit order storage schema");
390453
+ }
390454
+ if (!columnNames(this.db, "limit_order_fills").has("amount_in")) {
390455
+ this.db.exec("ALTER TABLE limit_order_fills ADD COLUMN amount_in TEXT");
390456
+ this.logger.info({ column: "amount_in" }, "Migrated limit order fill schema");
390457
+ }
389156
390458
  }
389157
390459
  // biome-ignore lint/suspicious/noExplicitAny: raw sqlite row
389158
390460
  toLimitOrder(row) {
@@ -389213,13 +390515,14 @@ var SqliteLimitOrderStore = class {
389213
390515
  const result = this.db.prepare(`
389214
390516
  UPDATE limit_orders
389215
390517
  SET commitment = ?, book_expires_at = ?, book_price = ?, order_nonce = ?,
389216
- status = ?, last_error = ?, updated_at = datetime('now')
390518
+ entry_point = ?, status = ?, last_error = ?, updated_at = datetime('now')
389217
390519
  WHERE id = ?${statusGuard(only)}
389218
390520
  `).run(
389219
390521
  posting.commitment,
389220
390522
  posting.bookExpiresAt,
389221
390523
  posting.bookPrice,
389222
390524
  posting.orderNonce,
390525
+ posting.entryPoint,
389223
390526
  posting.status,
389224
390527
  posting.lastError,
389225
390528
  id2,
@@ -389262,12 +390565,27 @@ var SqliteLimitOrderStore = class {
389262
390565
  }
389263
390566
  async recordFill(fill) {
389264
390567
  this.db.prepare(
389265
- "INSERT INTO limit_order_fills (limit_order_id, commitment, bid, amount, transaction_hash) VALUES (?, ?, ?, ?, ?)"
389266
- ).run(fill.limitOrderId, fill.commitment, fill.bid ?? null, fill.amount, fill.transactionHash ?? null);
390568
+ "INSERT INTO limit_order_fills (limit_order_id, commitment, bid, amount, amount_in, transaction_hash) VALUES (?, ?, ?, ?, ?, ?)"
390569
+ ).run(
390570
+ fill.limitOrderId,
390571
+ fill.commitment,
390572
+ fill.bid ?? null,
390573
+ fill.amount,
390574
+ fill.amountIn ?? null,
390575
+ fill.transactionHash ?? null
390576
+ );
390577
+ }
390578
+ async fillHistory() {
390579
+ return this.db.prepare(
390580
+ `SELECT f.id, f.limit_order_id as limitOrderId, o.book, o.base, o.quote, o.side, o.price,
390581
+ f.amount, f.amount_in as amountIn, f.filled_at as filledAt
390582
+ FROM limit_order_fills f JOIN limit_orders o ON o.id = f.limit_order_id
390583
+ ORDER BY f.filled_at, f.id`
390584
+ ).all();
389267
390585
  }
389268
390586
  async fills(limitOrderId, limit = 100) {
389269
390587
  return this.db.prepare(
389270
- `SELECT id, limit_order_id as limitOrderId, commitment, bid, amount, transaction_hash as transactionHash,
390588
+ `SELECT id, limit_order_id as limitOrderId, commitment, bid, amount, amount_in as amountIn, transaction_hash as transactionHash,
389271
390589
  filled_at as filledAt
389272
390590
  FROM limit_order_fills WHERE limit_order_id = ? ORDER BY id DESC LIMIT ?`
389273
390591
  ).all(limitOrderId, Math.min(Math.max(limit, 1), 500));
@@ -389456,6 +390774,7 @@ var SqliteDataStore = class {
389456
390774
  this.activity = new SqliteActivityStore(activityDb, loggers);
389457
390775
  this.state = new SqliteStateStore(bidsDb, dataDir, loggers);
389458
390776
  this.limitOrders = new SqliteLimitOrderStore(bidsDb, loggers);
390777
+ this.inventory = new SqliteInventoryStore(activityDb);
389459
390778
  this.logger.info({ dataDir }, "SQLite data store opened");
389460
390779
  }
389461
390780
  /**