@hyperbridge/simplex 0.17.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -105,12 +105,14 @@ interface VaultOutputFundingConfig {
105
105
  interface UserProvidedChainConfig {
106
106
  /** One or more RPC URLs. When multiple are provided, event scans use quorum consensus. */
107
107
  rpcUrls: string[];
108
- bundlerUrl: string;
108
+ /** Accepted and ignored. Simplex fills through Hyperbridge's bundler for the chain. */
109
+ bundlerUrl?: string;
109
110
  }
110
111
  interface ResolvedChainConfig {
111
112
  chainId: number;
112
113
  /** One or more RPC URLs for this chain. When multiple are provided, event scans use quorum consensus. */
113
114
  rpcUrls: string[];
115
+ /** Hyperbridge's bundler for the chain, or undefined where it runs none. */
114
116
  bundlerUrl?: string;
115
117
  }
116
118
  interface GasFeeBumpConfig {
@@ -223,7 +225,6 @@ declare class FillerConfigService {
223
225
  * change is invisible to anything already holding one.
224
226
  */
225
227
  setRpcUrls(chainId: number, rpcUrls: string[]): void;
226
- setBundlerUrl(chainId: number, bundlerUrl: string): void;
227
228
  /** Replaces the rebalancing config at runtime; trigger checks read it live. */
228
229
  setRebalancing(rebalancing: RebalancingConfig$1 | undefined): void;
229
230
  /** Replaces the allowlist at runtime; takes effect on the next order. */
@@ -937,9 +938,8 @@ declare class UserOpSender {
937
938
  private estimateGas;
938
939
  private buildSignedUserOp;
939
940
  /**
940
- * Mirrors the gas-price selection used by the delegation and fill paths: Pimlico's and
941
- * Alchemy's own pricing, otherwise the chain's gas price raised to the priority fee a
942
- * rundler bundler requires, when the bundler gives one.
941
+ * Prices the op for the rundler bundler, as the fill path does: its suggested fees, or the
942
+ * chain's gas price raised to the priority fee it requires where it suggests none.
943
943
  */
944
944
  private getGasPrice;
945
945
  private sendBundlerRpc;
@@ -4129,12 +4129,12 @@ interface ChainView {
4129
4129
  chainId: number;
4130
4130
  stateMachineId: string;
4131
4131
  rpcUrls: string[];
4132
+ /** Hyperbridge's bundler the chain fills through, empty where it runs none. */
4132
4133
  bundlerUrl: string;
4133
4134
  watchOnly: boolean;
4134
4135
  }
4135
4136
  interface ChainInput {
4136
4137
  rpcUrls: string[];
4137
- bundlerUrl: string;
4138
4138
  watchOnly?: boolean;
4139
4139
  /**
4140
4140
  * Required for chains with no built-in confirmation curve (testnets and
@@ -4282,7 +4282,6 @@ declare class ChainController {
4282
4282
  * leaves no gap in the scanned block range.
4283
4283
  */
4284
4284
  setRpcUrls(chainId: number, rpcUrls: string[]): Promise<void>;
4285
- setBundlerUrl(chainId: number, bundlerUrl: string): Promise<void>;
4286
4285
  /** Monitor without filling. Takes effect on the next order. */
4287
4286
  setWatchOnly(chainId: number, watchOnly: boolean): Promise<void>;
4288
4287
  /** A global boolean is left alone — expanding it per chain changes how the config validates. */
package/dist/index.d.ts CHANGED
@@ -105,12 +105,14 @@ interface VaultOutputFundingConfig {
105
105
  interface UserProvidedChainConfig {
106
106
  /** One or more RPC URLs. When multiple are provided, event scans use quorum consensus. */
107
107
  rpcUrls: string[];
108
- bundlerUrl: string;
108
+ /** Accepted and ignored. Simplex fills through Hyperbridge's bundler for the chain. */
109
+ bundlerUrl?: string;
109
110
  }
110
111
  interface ResolvedChainConfig {
111
112
  chainId: number;
112
113
  /** One or more RPC URLs for this chain. When multiple are provided, event scans use quorum consensus. */
113
114
  rpcUrls: string[];
115
+ /** Hyperbridge's bundler for the chain, or undefined where it runs none. */
114
116
  bundlerUrl?: string;
115
117
  }
116
118
  interface GasFeeBumpConfig {
@@ -223,7 +225,6 @@ declare class FillerConfigService {
223
225
  * change is invisible to anything already holding one.
224
226
  */
225
227
  setRpcUrls(chainId: number, rpcUrls: string[]): void;
226
- setBundlerUrl(chainId: number, bundlerUrl: string): void;
227
228
  /** Replaces the rebalancing config at runtime; trigger checks read it live. */
228
229
  setRebalancing(rebalancing: RebalancingConfig$1 | undefined): void;
229
230
  /** Replaces the allowlist at runtime; takes effect on the next order. */
@@ -937,9 +938,8 @@ declare class UserOpSender {
937
938
  private estimateGas;
938
939
  private buildSignedUserOp;
939
940
  /**
940
- * Mirrors the gas-price selection used by the delegation and fill paths: Pimlico's and
941
- * Alchemy's own pricing, otherwise the chain's gas price raised to the priority fee a
942
- * rundler bundler requires, when the bundler gives one.
941
+ * Prices the op for the rundler bundler, as the fill path does: its suggested fees, or the
942
+ * chain's gas price raised to the priority fee it requires where it suggests none.
943
943
  */
944
944
  private getGasPrice;
945
945
  private sendBundlerRpc;
@@ -4129,12 +4129,12 @@ interface ChainView {
4129
4129
  chainId: number;
4130
4130
  stateMachineId: string;
4131
4131
  rpcUrls: string[];
4132
+ /** Hyperbridge's bundler the chain fills through, empty where it runs none. */
4132
4133
  bundlerUrl: string;
4133
4134
  watchOnly: boolean;
4134
4135
  }
4135
4136
  interface ChainInput {
4136
4137
  rpcUrls: string[];
4137
- bundlerUrl: string;
4138
4138
  watchOnly?: boolean;
4139
4139
  /**
4140
4140
  * Required for chains with no built-in confirmation curve (testnets and
@@ -4282,7 +4282,6 @@ declare class ChainController {
4282
4282
  * leaves no gap in the scanned block range.
4283
4283
  */
4284
4284
  setRpcUrls(chainId: number, rpcUrls: string[]): Promise<void>;
4285
- setBundlerUrl(chainId: number, bundlerUrl: string): Promise<void>;
4286
4285
  /** Monitor without filling. Takes effect on the next order. */
4287
4286
  setWatchOnly(chainId: number, watchOnly: boolean): Promise<void>;
4288
4287
  /** A global boolean is left alone — expanding it per chain changes how the config validates. */
package/dist/index.js CHANGED
@@ -563,6 +563,20 @@ var ConfirmationPolicy = class {
563
563
  // src/core/filler.ts
564
564
  import pQueue from "p-queue";
565
565
 
566
+ // src/config/bundlers.ts
567
+ var HYPERBRIDGE_BUNDLER_URLS = {
568
+ 1: "https://bundler.polytope.technology/ethereum",
569
+ 56: "https://bundler.polytope.technology/bsc",
570
+ 97: "https://bundler.polytope.technology/bsc-chapel",
571
+ 137: "https://bundler.polytope.technology/polygon",
572
+ 8453: "https://bundler.polytope.technology/base",
573
+ 42161: "https://bundler.polytope.technology/arbitrum",
574
+ 80002: "https://bundler.polytope.technology/polygon-amoy"
575
+ };
576
+ function hyperbridgeBundlerUrl(chainId) {
577
+ return HYPERBRIDGE_BUNDLER_URLS[chainId];
578
+ }
579
+
566
580
  // src/config/asset-registry.ts
567
581
  import { isAddress } from "viem";
568
582
  var BUILTIN_ASSETS = {
@@ -1183,7 +1197,7 @@ async function resolveChainConfigs(chains, options = {}) {
1183
1197
  "RPC endpoint could not report its chainId at startup; keeping it for the quorum to judge per call"
1184
1198
  );
1185
1199
  }
1186
- return { chainId: first.chainId, rpcUrls, bundlerUrl: chain.bundlerUrl };
1200
+ return { chainId: first.chainId, rpcUrls, bundlerUrl: hyperbridgeBundlerUrl(first.chainId) };
1187
1201
  })
1188
1202
  );
1189
1203
  }
@@ -1252,12 +1266,6 @@ var FillerConfigService = class {
1252
1266
  }
1253
1267
  this.rpcOverrides.set(chainId, validateRpcUrls(rpcUrls));
1254
1268
  }
1255
- setBundlerUrl(chainId, bundlerUrl) {
1256
- if (!this.rpcOverrides.has(chainId)) {
1257
- throw new Error(`Chain ${chainId} is not configured`);
1258
- }
1259
- this.bundlerUrls.set(chainId, bundlerUrl);
1260
- }
1261
1269
  /** Replaces the rebalancing config at runtime; trigger checks read it live. */
1262
1270
  setRebalancing(rebalancing) {
1263
1271
  if (this.fillerConfig) this.fillerConfig.rebalancing = rebalancing;
@@ -1619,9 +1627,6 @@ function validateConfig(config, cliWatchOnly = false) {
1619
1627
  if (!Array.isArray(chain.rpcUrls) || chain.rpcUrls.length === 0 || chain.rpcUrls.some((u) => !u)) {
1620
1628
  throw new Error("Each chain configuration must have rpcUrls (a non-empty array of strings)");
1621
1629
  }
1622
- if (!chain.bundlerUrl) {
1623
- throw new Error("Each chain configuration must have bundlerUrl");
1624
- }
1625
1630
  }
1626
1631
  const concurrent = config.simplex.maxConcurrentOrders;
1627
1632
  if (concurrent !== void 0) {
@@ -2483,13 +2488,6 @@ var FREE_RPC_URLS = {
2483
2488
  "https://arb1.arbitrum.io/rpc"
2484
2489
  ]
2485
2490
  };
2486
- var HYPERBRIDGE_BUNDLER_URLS = {
2487
- 1: "https://bundler.polytope.technology/ethereum",
2488
- 56: "https://bundler.polytope.technology/bsc",
2489
- 137: "https://bundler.polytope.technology/polygon",
2490
- 8453: "https://bundler.polytope.technology/base",
2491
- 42161: "https://bundler.polytope.technology/arbitrum"
2492
- };
2493
2491
  var CHAIN_NATIVE_SYMBOLS = {
2494
2492
  1: "ETH",
2495
2493
  56: "BNB",
@@ -2591,7 +2589,8 @@ var INIT_CHAINS = [
2591
2589
  label: "Polygon Amoy",
2592
2590
  network: "testnet",
2593
2591
  alchemySubdomain: "polygon-amoy",
2594
- explorerUrl: external_links_default.chainExplorers.polygonAmoy
2592
+ explorerUrl: external_links_default.chainExplorers.polygonAmoy,
2593
+ hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[80002]
2595
2594
  },
2596
2595
  {
2597
2596
  chainId: 97,
@@ -2599,7 +2598,8 @@ var INIT_CHAINS = [
2599
2598
  label: "BSC Chapel",
2600
2599
  network: "testnet",
2601
2600
  alchemySubdomain: "bnb-testnet",
2602
- explorerUrl: external_links_default.chainExplorers.bnbChapel
2601
+ explorerUrl: external_links_default.chainExplorers.bnbChapel,
2602
+ hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[97]
2603
2603
  }
2604
2604
  ];
2605
2605
  function chainByChainId(chainId) {
@@ -4396,80 +4396,6 @@ async function patchRuntimeState(store, patch) {
4396
4396
  return next;
4397
4397
  }
4398
4398
 
4399
- // src/services/bundler-preflight.ts
4400
- var BUNDLER_PREFLIGHT_TIMEOUT_MS = 1e4;
4401
- async function assertBundlersServeEntryPoint(chains, configService, options = {}) {
4402
- const timeoutMs = options.timeoutMs ?? BUNDLER_PREFLIGHT_TIMEOUT_MS;
4403
- const logger = moduleLogger(configService.loggers, "bundler-preflight");
4404
- const failures = await Promise.all(
4405
- chains.map(async ({ chainId, bundlerUrl }) => {
4406
- if (options.watchOnly?.[chainId] === true) return void 0;
4407
- if (!bundlerUrl?.trim()) return void 0;
4408
- const entryPoint = configService.getEntryPointAddress(formatChainKey(chainId));
4409
- if (!entryPoint) return void 0;
4410
- return checkBundler(chainId, bundlerUrl.trim(), entryPoint, timeoutMs, logger);
4411
- })
4412
- );
4413
- const messages = failures.filter((failure) => failure !== void 0);
4414
- if (messages.length > 0) throw new Error(messages.join("; "));
4415
- }
4416
- function servesEntryPoint(listed, entryPoint) {
4417
- return Array.isArray(listed) && listed.some((address) => String(address).toLowerCase() === entryPoint.toLowerCase());
4418
- }
4419
- function describeEntryPoints(listed) {
4420
- return Array.isArray(listed) && listed.length > 0 ? listed.map(String).join(", ") : "none";
4421
- }
4422
- function bundlerHost(url) {
4423
- try {
4424
- return new URL(url).host || "an unparseable URL";
4425
- } catch {
4426
- return "an unparseable URL";
4427
- }
4428
- }
4429
- function chainName(chainId) {
4430
- const label = chainByChainId(chainId)?.label;
4431
- return label ? `${label} (${formatChainKey(chainId)})` : formatChainKey(chainId);
4432
- }
4433
- async function checkBundler(chainId, bundlerUrl, entryPoint, timeoutMs, logger) {
4434
- const host = bundlerHost(bundlerUrl);
4435
- const subject = `Bundler ${host} for ${chainName(chainId)}`;
4436
- const signal = AbortSignal.timeout(timeoutMs);
4437
- let listed;
4438
- try {
4439
- listed = await supportedEntryPoints(bundlerUrl, signal);
4440
- } catch (err) {
4441
- const reason = signal.aborted ? `timed out after ${timeoutMs}ms` : errorReason(err);
4442
- logger.warn(
4443
- { chain: formatChainKey(chainId), bundler: host },
4444
- `${subject} did not answer eth_supportedEntryPoints: ${reason.split(bundlerUrl).join(host)}. Continuing without confirming it serves EntryPoint ${entryPoint}`
4445
- );
4446
- return void 0;
4447
- }
4448
- if (servesEntryPoint(listed, entryPoint)) return void 0;
4449
- return `${subject} does not support EntryPoint ${entryPoint}; it lists ${describeEntryPoints(listed)}`;
4450
- }
4451
- async function supportedEntryPoints(bundlerUrl, signal) {
4452
- const response = await fetch(bundlerUrl, {
4453
- method: "POST",
4454
- headers: { "Content-Type": "application/json" },
4455
- body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_supportedEntryPoints", params: [] }),
4456
- signal
4457
- });
4458
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
4459
- const json = await response.json();
4460
- if (json.error) throw new Error(json.error.message ?? JSON.stringify(json.error));
4461
- const { result } = json;
4462
- if (!Array.isArray(result) || !result.every((address) => typeof address === "string")) {
4463
- throw new Error("the result is not a list of addresses");
4464
- }
4465
- return result;
4466
- }
4467
- function errorReason(err) {
4468
- if (!(err instanceof Error)) return String(err);
4469
- const { cause } = err;
4470
- return cause instanceof Error ? `${err.message} (${cause.message})` : err.message;
4471
- }
4472
-
4473
4399
  // src/services/wallet/mpcvault.ts
4474
4400
  import { concatHex, keccak256 as keccak2562, padHex, toHex } from "viem";
4475
4401
  import * as grpc from "@grpc/grpc-js";
@@ -19949,8 +19875,7 @@ import { encodeERC7821ExecuteBatch as encodeERC7821ExecuteBatch3 } from "@hyperb
19949
19875
  import {
19950
19876
  CryptoUtils as CryptoUtils2,
19951
19877
  BundlerMethod,
19952
- applyRundlerPriorityFee,
19953
- fetchRundlerPriorityFee
19878
+ rundlerUserOperationFees
19954
19879
  } from "@hyperbridge/sdk";
19955
19880
  import { toHex as toHex2 } from "viem";
19956
19881
  var FALLBACK_VERIFICATION_GAS_LIMIT = 250000n;
@@ -19984,7 +19909,7 @@ var UserOpSender = class {
19984
19909
  let auth;
19985
19910
  let fees;
19986
19911
  try {
19987
- fees = await this.getGasPrice(bundlerUrl, publicClient, chainId);
19912
+ fees = await this.getGasPrice(bundlerUrl, publicClient);
19988
19913
  const gasForPrefund = gas ?? {
19989
19914
  verificationGasLimit: FALLBACK_VERIFICATION_GAS_LIMIT,
19990
19915
  callGasLimit: FALLBACK_CALL_GAS_LIMIT,
@@ -20139,48 +20064,26 @@ var UserOpSender = class {
20139
20064
  return userOp;
20140
20065
  }
20141
20066
  /**
20142
- * Mirrors the gas-price selection used by the delegation and fill paths: Pimlico's and
20143
- * Alchemy's own pricing, otherwise the chain's gas price raised to the priority fee a
20144
- * rundler bundler requires, when the bundler gives one.
20067
+ * Prices the op for the rundler bundler, as the fill path does: its suggested fees, or the
20068
+ * chain's gas price raised to the priority fee it requires where it suggests none.
20145
20069
  */
20146
- async getGasPrice(bundlerUrl, publicClient, chainId) {
20147
- const lower = bundlerUrl.toLowerCase();
20148
- if (lower.includes("pimlico.io")) {
20149
- const res = await this.sendBundlerRpc(
20150
- bundlerUrl,
20151
- BundlerMethod.PIMLICO_GET_USER_OPERATION_GAS_PRICE,
20152
- []
20153
- );
20154
- return { maxFeePerGas: BigInt(res.fast.maxFeePerGas), maxPriorityFeePerGas: BigInt(res.fast.maxPriorityFeePerGas) };
20155
- }
20156
- if (lower.includes("alchemy.com")) {
20157
- const [rundlerPriorityFee2, latestBlock2] = await Promise.all([
20158
- this.sendBundlerRpc(bundlerUrl, BundlerMethod.RUNDLER_MAX_PRIORITY_FEE_PER_GAS, []),
20159
- publicClient.getBlock({ blockTag: "latest" })
20160
- ]);
20161
- const baseFeePerGas = latestBlock2.baseFeePerGas ?? await publicClient.getGasPrice();
20162
- const isArbitrum = BigInt(chainId) === 42161n;
20163
- const prioBump = isArbitrum ? 0n : 25n;
20164
- const maxPriorityFeePerGas = BigInt(rundlerPriorityFee2) + BigInt(rundlerPriorityFee2) * prioBump / 100n;
20165
- const bufferedBaseFee = baseFeePerGas + baseFeePerGas * 50n / 100n;
20166
- return { maxFeePerGas: bufferedBaseFee + maxPriorityFeePerGas, maxPriorityFeePerGas };
20167
- }
20168
- const [gasPrice, rundlerPriorityFee] = await Promise.all([
20070
+ async getGasPrice(bundlerUrl, publicClient) {
20071
+ const [gasPrice, latestBlock] = await Promise.all([
20169
20072
  publicClient.getGasPrice(),
20170
- fetchRundlerPriorityFee(bundlerUrl)
20073
+ publicClient.getBlock({ blockTag: "latest" })
20171
20074
  ]);
20172
- const fees = {
20173
- maxFeePerGas: gasPrice + gasPrice * MAX_FEE_BUMP_PERCENT / 100n,
20174
- maxPriorityFeePerGas: gasPrice + gasPrice * PRIORITY_FEE_BUMP_PERCENT / 100n
20175
- };
20176
- if (rundlerPriorityFee === null) return fees;
20177
- const latestBlock = await publicClient.getBlock({ blockTag: "latest" });
20178
- return applyRundlerPriorityFee(fees, {
20179
- rundlerPriorityFee,
20180
- baseFeePerGas: latestBlock.baseFeePerGas ?? gasPrice,
20181
- priorityFeeBumpPercent: PRIORITY_FEE_BUMP_PERCENT,
20182
- maxFeeBumpPercent: MAX_FEE_BUMP_PERCENT
20183
- });
20075
+ return rundlerUserOperationFees(
20076
+ bundlerUrl,
20077
+ {
20078
+ maxFeePerGas: gasPrice + gasPrice * MAX_FEE_BUMP_PERCENT / 100n,
20079
+ maxPriorityFeePerGas: gasPrice + gasPrice * PRIORITY_FEE_BUMP_PERCENT / 100n
20080
+ },
20081
+ {
20082
+ baseFeePerGas: latestBlock.baseFeePerGas ?? gasPrice,
20083
+ priorityFeeBumpPercent: PRIORITY_FEE_BUMP_PERCENT,
20084
+ maxFeeBumpPercent: MAX_FEE_BUMP_PERCENT
20085
+ }
20086
+ );
20184
20087
  }
20185
20088
  async sendBundlerRpc(bundlerUrl, method, params) {
20186
20089
  const response = await fetch(bundlerUrl, {
@@ -23917,6 +23820,82 @@ var TokenSender = class {
23917
23820
  }
23918
23821
  };
23919
23822
 
23823
+ // src/services/bundler-preflight.ts
23824
+ var BUNDLER_PREFLIGHT_TIMEOUT_MS = 1e4;
23825
+ async function assertBundlersServeEntryPoint(chains, configService, options = {}) {
23826
+ const timeoutMs = options.timeoutMs ?? BUNDLER_PREFLIGHT_TIMEOUT_MS;
23827
+ const logger = moduleLogger(configService.loggers, "bundler-preflight");
23828
+ const failures = await Promise.all(
23829
+ chains.map(async ({ chainId, bundlerUrl }) => {
23830
+ if (options.watchOnly?.[chainId] === true) return void 0;
23831
+ if (!bundlerUrl?.trim()) {
23832
+ return `Hyperbridge runs no bundler for ${chainName(chainId)}, so simplex cannot fill there; set it watch-only`;
23833
+ }
23834
+ const entryPoint = configService.getEntryPointAddress(formatChainKey(chainId));
23835
+ if (!entryPoint) return void 0;
23836
+ return checkBundler(chainId, bundlerUrl.trim(), entryPoint, timeoutMs, logger);
23837
+ })
23838
+ );
23839
+ const messages = failures.filter((failure) => failure !== void 0);
23840
+ if (messages.length > 0) throw new Error(messages.join("; "));
23841
+ }
23842
+ function servesEntryPoint(listed, entryPoint) {
23843
+ return Array.isArray(listed) && listed.some((address) => String(address).toLowerCase() === entryPoint.toLowerCase());
23844
+ }
23845
+ function describeEntryPoints(listed) {
23846
+ return Array.isArray(listed) && listed.length > 0 ? listed.map(String).join(", ") : "none";
23847
+ }
23848
+ function bundlerHost(url) {
23849
+ try {
23850
+ return new URL(url).host || "an unparseable URL";
23851
+ } catch {
23852
+ return "an unparseable URL";
23853
+ }
23854
+ }
23855
+ function chainName(chainId) {
23856
+ const label = chainByChainId(chainId)?.label;
23857
+ return label ? `${label} (${formatChainKey(chainId)})` : formatChainKey(chainId);
23858
+ }
23859
+ async function checkBundler(chainId, bundlerUrl, entryPoint, timeoutMs, logger) {
23860
+ const host = bundlerHost(bundlerUrl);
23861
+ const subject = `Bundler ${host} for ${chainName(chainId)}`;
23862
+ const signal = AbortSignal.timeout(timeoutMs);
23863
+ let listed;
23864
+ try {
23865
+ listed = await supportedEntryPoints(bundlerUrl, signal);
23866
+ } catch (err) {
23867
+ const reason = signal.aborted ? `timed out after ${timeoutMs}ms` : errorReason(err);
23868
+ logger.warn(
23869
+ { chain: formatChainKey(chainId), bundler: host },
23870
+ `${subject} did not answer eth_supportedEntryPoints: ${reason.split(bundlerUrl).join(host)}. Continuing without confirming it serves EntryPoint ${entryPoint}`
23871
+ );
23872
+ return void 0;
23873
+ }
23874
+ if (servesEntryPoint(listed, entryPoint)) return void 0;
23875
+ return `${subject} does not support EntryPoint ${entryPoint}; it lists ${describeEntryPoints(listed)}`;
23876
+ }
23877
+ async function supportedEntryPoints(bundlerUrl, signal) {
23878
+ const response = await fetch(bundlerUrl, {
23879
+ method: "POST",
23880
+ headers: { "Content-Type": "application/json" },
23881
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_supportedEntryPoints", params: [] }),
23882
+ signal
23883
+ });
23884
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
23885
+ const json = await response.json();
23886
+ if (json.error) throw new Error(json.error.message ?? JSON.stringify(json.error));
23887
+ const { result } = json;
23888
+ if (!Array.isArray(result) || !result.every((address) => typeof address === "string")) {
23889
+ throw new Error("the result is not a list of addresses");
23890
+ }
23891
+ return result;
23892
+ }
23893
+ function errorReason(err) {
23894
+ if (!(err instanceof Error)) return String(err);
23895
+ const { cause } = err;
23896
+ return cause instanceof Error ? `${err.message} (${cause.message})` : err.message;
23897
+ }
23898
+
23920
23899
  // src/orderbook/lifecycle.ts
23921
23900
  var EXPIRY_SWEEP_MS = 3e4;
23922
23901
  var LimitOrderLifecycle = class {
@@ -25993,7 +25972,7 @@ var OrderScanner = class _OrderScanner {
25993
25972
  async addChainNow(chain) {
25994
25973
  if (this.closed) throw new Error("This OrderScanner is closed");
25995
25974
  const rpcUrls = validateRpcUrls(chain.rpcUrls);
25996
- const chainId = chain.chainId ?? (await resolveChainConfigs([{ rpcUrls, bundlerUrl: chain.bundlerUrl ?? "" }], {
25975
+ const chainId = chain.chainId ?? (await resolveChainConfigs([{ rpcUrls }], {
25997
25976
  loggers: this.loggers,
25998
25977
  tolerateUnreachable: true
25999
25978
  }))[0].chainId;
@@ -26425,10 +26404,7 @@ var ChainController = class {
26425
26404
  async add(chain) {
26426
26405
  return this.serialise(async () => {
26427
26406
  validateRpcUrls(chain.rpcUrls);
26428
- if (!chain.bundlerUrl?.trim()) {
26429
- throw new Error("A bundler URL is required to submit fill UserOperations");
26430
- }
26431
- const [resolved] = await resolveChainConfigs([{ rpcUrls: chain.rpcUrls, bundlerUrl: chain.bundlerUrl }]);
26407
+ const [resolved] = await resolveChainConfigs([{ rpcUrls: chain.rpcUrls }]);
26432
26408
  const { chainId } = resolved;
26433
26409
  const { configService, config, intentFiller } = this.runtime;
26434
26410
  if (configService.getConfiguredChainIds().includes(chainId)) {
@@ -26457,7 +26433,7 @@ var ChainController = class {
26457
26433
  if (watchOnly) intentFiller.setWatchOnly(chainId, true);
26458
26434
  try {
26459
26435
  if (!inScanner) {
26460
- await this.scanner.addChain({ rpcUrls: chain.rpcUrls, bundlerUrl: chain.bundlerUrl, chainId });
26436
+ await this.scanner.addChain({ rpcUrls: chain.rpcUrls, chainId });
26461
26437
  }
26462
26438
  this.runtime.chainClientManager.invalidate(formatChainKey(chainId));
26463
26439
  await intentFiller.addChain(configService.getChainConfig(formatChainKey(chainId)));
@@ -26474,7 +26450,7 @@ var ChainController = class {
26474
26450
  this.runtime.confirmationPolicy.adopt(chainId, resolvedPolicy);
26475
26451
  }
26476
26452
  this.runtime.resolvedChains.push(resolved);
26477
- config.chains = [...config.chains, { rpcUrls: chain.rpcUrls, bundlerUrl: chain.bundlerUrl }];
26453
+ config.chains = [...config.chains, { rpcUrls: chain.rpcUrls }];
26478
26454
  if (Object.keys(confirmationPolicies).length > 0) config.confirmationPolicies = confirmationPolicies;
26479
26455
  this.syncWatchOnlyToConfig();
26480
26456
  await this.persist();
@@ -26519,9 +26495,7 @@ var ChainController = class {
26519
26495
  validateRpcUrls(rpcUrls);
26520
26496
  const index = this.runtime.resolvedChains.findIndex((chain) => chain.chainId === chainId);
26521
26497
  if (index < 0) throw new Error(`Chain ${chainId} is not configured`);
26522
- const [probed] = await resolveChainConfigs([
26523
- { rpcUrls, bundlerUrl: this.runtime.resolvedChains[index].bundlerUrl ?? "" }
26524
- ]);
26498
+ const [probed] = await resolveChainConfigs([{ rpcUrls }]);
26525
26499
  if (probed.chainId !== chainId) {
26526
26500
  throw new Error(`Those endpoints answer for chain ${probed.chainId}, not ${chainId}`);
26527
26501
  }
@@ -26540,20 +26514,6 @@ var ChainController = class {
26540
26514
  await this.persist();
26541
26515
  });
26542
26516
  }
26543
- async setBundlerUrl(chainId, bundlerUrl) {
26544
- return this.serialise(async () => {
26545
- if (!bundlerUrl?.trim()) throw new Error("A bundler URL is required");
26546
- const index = this.runtime.resolvedChains.findIndex((chain) => chain.chainId === chainId);
26547
- if (index < 0) throw new Error(`Chain ${chainId} is not configured`);
26548
- await assertBundlersServeEntryPoint([{ chainId, bundlerUrl }], this.runtime.configService, {
26549
- watchOnly: this.runtime.intentFiller.getWatchOnly()
26550
- });
26551
- this.runtime.configService.setBundlerUrl(chainId, bundlerUrl);
26552
- this.runtime.resolvedChains[index].bundlerUrl = bundlerUrl;
26553
- this.runtime.config.chains[index].bundlerUrl = bundlerUrl;
26554
- await this.persist();
26555
- });
26556
- }
26557
26517
  /** Monitor without filling. Takes effect on the next order. */
26558
26518
  async setWatchOnly(chainId, watchOnly) {
26559
26519
  return this.serialise(async () => {