It’s time to wrap up the Aerodrome series. This project builds on the previous efforts of Parts I through IV, which described the DEX and the key differences between it and upstream code it was forked from.
Review those posts below if you need a refresher.
The project is built on the previous Base Backrunner code, which supported with the following DEX:
Uniswap V2
Uniswap V3
SushiSwap V2
SushiSwap V3
PancakeSwap V2
PancakeSwap V3
SwapBased V2
And with these updates, we add:
Aerodrome V2
Aerodrome V3
New Features
Async Web3 Providers
I have updated the degenbot dependency on web3.py to version 7. Among other improvements, it revamps the HTTP, WebSocket, and IPC providers to support fully async behavior. I encourage you to read the web3py v7 release announcement at SnakeCharmers.
The project code uses AsyncWeb3
providers everywhere. I am running against a local node, so I’ve used the AsyncIPCProvider
for interacting with it. I’ve used the WebSocketProvider
to interact with Anvil through the AnvilFork
class, but only because their IPC implementation is buggy. I’ve filed an issue which I hope will be fixed soon.
Direct Sequencer Submission
I use Reth (via their op-reth variant) as my Base node. It broadcasts raw transactions to the sequencer, but I’ve skipped the middleman and set up an AsyncHTTPProvider connected directly to the Base sequencer to submit all transactions. I have not observed any improvement in performance or inclusion using this approach, but it might be helpful to someone as an example.
CVXPY optimizer for V2/V2
I have replaced the standard SciPy optimizer with CVXPY for V2-V2 paths. SciPy is very accurate and flexible, but rather slow and only delivers acceptable performance when paired with a process pool executor. CVXPY however defers much of its calculation to Clarabel, which is written in Rust and extremely fast.
I wrote a four-part series on Convex Optimization several months ago. After writing the Aerodrome components, I took a brief detour last week to revisit the topic.
After some experimentation and much debugging, I’ve made the V2-V2 optimization method use CVXPY exclusively. It is extremely fast and quite close to the integer-perfect results from SciPy.
The example code below implements some very powerful reserve amount scaling techniques to allow the optimizer to run efficiently over pools of any size. This is a true Degen Code innovation, you won’t find this anywhere else.
I’ll extend the approach to V3 next, which promised to really ramp up performance since these pool calculations take the most processing time.
Fork Profit Check
In addition to checking for arbitrage transaction success against the fork, the bot queries for the contract balance before and after, allowing it to compare expected profits against actual. This is an obvious feature that I only implemented after debuggin a string of transactions that executed correctly, but delivered slightly less than expected profits. These were mostly related to transfer tax tokens, so this is a nice protection. I’ve noticed fewer reverts and burned WETH since implementing it.
Opportunities For Improvement
Aerodrome V2 pools with the stable flag are not supported. It should be simple to add this once I finalize the arbitrage helper.
One thing I want to do is separate the path calculation from the encoding of the payloads. There’s currently a lot of tension in how to handle interfacing with a smart contract because the pool amounts are fixed, but the final encoding necessary to deliver them via smart contract is wildly different depending on the version.
I intend to extend the UniswapLpCycle
class to support the various modifications I’ve made here, then move the payload encoding code into a separate class / function.
Version Info
This project uses degenbot version 0.3.0 and Vyper version 0.4.0. You can install both using pip
.
The minimum supported version of Python is 3.10.