Flashbots — Project Improvements: Bundle Submission Hotfix, Multipool Arbitrage, Filtering Mempool Transactions with eth_call, Dropping Failing Arbs
Good, Botter, Best
Coming to you once again with several bot improvements. The first is a critical hotfix that you should apply if you’re running the Two-Pool UniswapV2 Arbitrage Bot. Then I’ll provide several quality-of-life improvements, extending the on-chain and mempool arbitrage functions to support multi-pool arbitrage strategies, filtering bad mempool transactions with eth_call
, and applying a simple heuristic to test and drop bad arbitrage pathways.
Before you get worried, I did not introduce any vulnerabilities in the previous bot, I just overlooked a requirement that meant nobody has been actually submitting bundles. You haven’t lost any money, but you missed out on some opportunities.
Bundle Submission Hotfix
Despite reading the Flashbots documentation thoroughly, I recently discovered that I was skipping an important step in the process! In order for the Flashbots relay to submit a bundle to the miners, it checks if the bundle has been simulated. In my project writeup, I included a section that generated a bundle, simulated it using the relay, and then submitted it for all profitable opportunities.
I knew that modifying the transaction bundle affects the bundle hash, but was not aware that the relay required the bundleHash to be previously simulated before passing it to miners. The documentation implied that the simulation would be performed automatically, but this is not the case. I was simulating all sorts of bundles, then submitting the profitable ones after applying the miner bribe only to have them silently discarded due to not being previously simulated!
Egg on my face. Frustrating, too, since the bot has been running for two weeks with zero relayed transactions.
The fix is simple: after finding a profitable arb and applying the miner bribe, re-simulate the bundle and then submit as usual.
It’s a short section of code to implement this. Simply copy-paste the simulation code block right before the submission code block:
### START OF NEW CODE BLOCK
try:
# simulate with the final gas values before submitting
simulation = w3.flashbots.simulate(
bundled_transactions=bundle,
state_block_tag=newest_block,
block_tag=newest_block + 1,
block_timestamp=newest_block_timestamp + 15,
)
except Exception as e:
print(f"Relay Error: {e}")
return
else:
print(f"bundleHash: {simulation.get('bundleHash')}")
for result in simulation.get("results"):
if result.get("error"):
# TODO: ignore for any bundle that is *allowed* to fail
# abort if any TX in the bundle failed simulation
return
### END OF NEW CODE BLOCK
print(
f"Sending bundle targeting block {newest_block + 1}"
)
try:
send_bundle_result = w3.flashbots.send_bundle(
bundle,
target_block_number=newest_block + 1,
)
except Exception as e:
print(e)
else:
send_bundle_result.wait()
print(send_bundle_result)
I’ve also improved the submission code block slightly. Instead of submitting the bundle for the next block only, it now submits the bundle as valid for the next n blocks, which helps if the bundle lingers in the relay for a bit.
This code is included in the section below.
Multi-pool Arbitrage
As a natural extension of the JSON loading improvements, I can now very easily extend my arbitrage loaders and arbitrage executor functions to work on a common data input format.
Gone are the days of CSV files, now it’s just smooth easy to parse JSON all around.
Here is a sample triangle arb parser you can use: