Repository navigation
HyperFx Custom Bundler Tracking issue #1363
Copy link
Copy link
Closed
6 / 66 of 6 issues completedMilestone
Description
This issue tracks the the things needed to get a custom Hyperfx bundler running.
Why our own bundler
- Bids are user operations sponsored by
SimplexPaymaster. We need a bundler that carries them on every chain we fill on, without depending on a hosted bundler's rules about which paymasters it accepts. - [evm]: Select UserOpHash instead of solver address in solver selection #816 needs EntryPoint v0.9, so the bundler has to serve v0.9.
- On a chain with a public mempool a pending bundle can be copied and front-run. [simplex] Bundler whitelists in paymaster #1364 ties sponsored operations to our bundler's wallets, which only works if we run the bundler.
What we run
rundler, forked at polytope-labs/rundler. One process per chain: Ethereum, Arbitrum, Base, Polygon and BNB Chain.
From reading the source at 2dc1788:
- EntryPoint versions. It supports v0.6 to v0.9 and can serve several at once. Only v0.7 is on by default, so v0.9 has to be enabled with
--enabled_entry_points. - Unsafe mode. We run without traced validation. [simplex] Bundler whitelists in paymaster #1364 needs that anyway, because it reads
tx.originduring validation. - BNB Chain. No chain spec is bundled, so we write one.
- Submission. The default sender is the public mempool. Private senders are built in for Ethereum (
flashbots) and Polygon (polygonprivate,bloxroute). There is none for BNB Chain. - Signer wallets. Each signer is one funded wallet per chain. On v0.9 they must be plain EOAs, and with [simplex] Bundler whitelists in paymaster #1364 each one has to be on the paymaster's list.
- RPC usage. The chain watcher polls every 100 ms and makes several calls per block whether or not there is anything to bundle. On a metered provider that is most of the cost at low traffic. Raising the poll interval, turning flashblocks off on Base, and pausing the watcher while the pool is empty bring it close to zero when idle. The pause is a small change in the fork. It is scoped but not built.
Work
Contract and off-chain changes are the sub-issues of this issue, all in the EntryPoint v0.9 milestone.
Bundler work that is not filed as issues yet:
- Chain spec for BNB Chain
- One instance per chain with v0.9 enabled, and funded signer wallets
- Decide how bundles are submitted on Polygon and BNB Chain
- Idle pause in the fork
- Point the app and simplex at these endpoints
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
royvardhan commentedon Oct 2, 2026
On "Decide how bundles are submitted on Polygon and BNB Chain": I think we can use the default public sender (
raw) on every chain and leave the private senders out.The only reason to submit privately is to stop a pending bundle from being copied and front-run, and #1364 already covers that. The paymaster only sponsors when
tx.originis one of our bundler wallets, so operations copied out of the public mempool fail paymaster validation for anyone else and our bundle still lands. That means there's no BNB sender to write, and no need to set upflashbots,polygonprivateorbloxroute.This holds only while the whitelist is set on that chain. If #1364 ends up treating an empty list as "any bundler", we should make sure our wallets are always listed.
Wizdave97 commentedon Oct 2, 2026
With bundler whitelists on paymaster we can use the public mempool on all bnb and polygon. That's what alchemy already does on polygon. Private mempool would be a nice to have if possible for faster transaction inclusion.
royvardhan commentedon Oct 8, 2026
Live testnet E2E
Run on 2026-10-08 on BSC Chapel (97) and Polygon Amoy (80002), governed from Gargantua. The simplex testnet harness from #1375 ran against the HyperFX testnet orderbook. Our rundler (polytope-labs/rundler#3) bundled on each chain, with one solver.
Setup on testnet
Gargantua sudo:
addPaymasterDeployment: Chapel0x9f4a6F1254f27373C2aca5bDd0e48FbA45EA3aeC, Amoy0x6085a14078B7d26259145a894140f7C8361e02ae;0x38a82f8283a0fAdB763888707aD18fCd34AF719DthroughexecuteOnGateway(upgradeToAndCall).Both upgrades landed at 12:38 UTC. The new
SELECT_SOLVER_TYPEHASHis0xe706bdab…e09e.Results
All five scenarios were filled:
https://testnet.bscscan.com/tx/0xb136288f2f0cbffa50fa7b4738a40477732b4d09b9f310a873dd2a1ccfb5a1b7
https://testnet.bscscan.com/tx/0x2cacb0bf2cf4b5922a185360ebdb1fc806026646df56624d9b0e0e1511104d6c
https://testnet.bscscan.com/tx/0x4df98330a46c393239e1f4d9a4725387bf1816783c16f3eb27aec74276710326
Every one of the 8 fills was checked on chain:
UserOperationEventnames that chain's SimplexPaymaster with success = 1;OrderFilledorPartialFillevent is in the same transaction.What each PR got from the run
0x7484C6Ff790b898bf7e6960Ffc82f63330445Dc2, and every bid validated through it.LimitOrderExceeded.bsc_testnetspec.-32602) and bundled every sponsored fill.55ba75f.Not covered live yet
migrate(): it only applies to the mainnet proxies, since the testnet proxies started at version 3.Mainnet rollout
Merge [evm, pallet-intents-coprocessor]: bundler allowlist and EntryPoint v0.9 for SimplexPaymaster and SolverAccount #1371.
mainis green. Nothing changes on chain yet.main.Merge feat: pause idle chain polling and add a paymaster allowlist rundler#3 and run one rundler per mainnet chain. Each instance needs:
--enabled_entry_points v0.9 --unsafeand a funded signer;--pool.paymaster_allowlistset to that chain's paymaster proxy, with idle pause on;--builder.max_blocks_to_wait_for_mine;Check that
eth_supportedEntryPointsreturns only v0.9 and that an operation with no paymaster gets-32602.Deploy the mainnet contracts on each chain: SolverAccount v0.9, the new IntentGatewayV2 implementation and the new SimplexPaymaster implementation.
SolverAccount.entryPoint()is v0.9 and that the gateway implementation'sSELECT_SOLVER_TYPEHASHis0xe706…e09e.Upgrade the Nexus runtime to include the [evm, pallet-intents-coprocessor]: bundler allowlist and EntryPoint v0.9 for SimplexPaymaster and SolverAccount #1371 pallet change (
set_paymaster_bundlers). Check that the call is in the runtime metadata.Merge [evm, sdk, simplex, indexer]: EntryPoint v0.9 end to end with userOpHash solver selection #1375, release the SDK, and deploy the mainnet indexer and the HyperFX mainnet orderbook (with [DOCS] ISMP #74).
Cut over all chains in one governance window.
executeOnGateway(upgradeToAndCall(newImpl, ""))andupgrade_paymaster(newImpl, migrate()).version()is 3;Fills on a chain stop until step 7, so keep the gap short.
Solvers move to the new simplex. It re-delegates to the new SolverAccount at boot, and they point at our bundlers.
Set the bundler list on each chain with
set_paymaster_bundlers, right after the cutover. It must include every rundler signer plus the simulation origin0x0643866dA50efE0b055Cd15aF95191968c8411b5. Until it is set, someone can copy a bundle from the public mempool and land it first.getBundlers()and run another swap.After the 24 h unstake delay, call
withdrawStakeV08()on chains that had a v0.8 stake (Ethereum and BSC; Base had none). Check that the v0.9 deposit grew by the stake.royvardhan commentedon Oct 8, 2026
Live testnet E2E: multiple solvers
These are the scenarios the earlier run couldn't cover. Three solvers ran on BSC Chapel and Polygon Amoy, and every fill went through our rundler (polytope-labs/rundler#3), with SimplexPaymaster sponsoring it.
The deployed testnet indexer doesn't know the new SolverAccount yet, so these runs used a local HyperFX orderbook (hyperfx-orderbook
main, including #74). That orderbook read a local indexer built from #1375. Everything else was live testnet.https://testnet.bscscan.com/tx/0x015acd2d5f66a3f99290ae4436b59a9d856dfb6fed2adb0a5d077a093c4ef662
LimitOrderExceeded, and solver2 filled the order instead0x7484C6Ff790b898bf7e6960Ffc82f63330445Dc2on both chains, and the orderbook kept all their postings.seunlanlege commentedon Oct 9, 2026
closed in #1371