A searching bot for a fork of Compound protocol using OEV v1 proxies.
This repository documents the necessary steps to update an MEV bot to an OEV bot. While the main branch is the final result of the OEV bot, there are branches for the previous steps, so one can easily compare the changes.
On branch mev
First step is to have an MEV bot that can perform liquidations when the opportunity to do so arises. For more information, refer to From MEV Searching.
Changes mev -> mev-with-signed-apis
Second step is to extend the MEV bot to utilize the public Base Feed Endpoints. The existing MEV bot can utilize this off-chain open source data and make a base feed update on-chain whenever there is OEV to be captured. For more information, refer to MEV with Signed APIs.
Changes mev-with-signed-apis -> oev
Final step is to transition to utilizing the OEV network to acquire the exclusive privilege to update the data feeds. For more information, refer to OEV Searching.
Install node modules.
pnpm install
pnpm run build
The bot has a set of CLI commands, executable by
pnpm run bot:cli-utils <command>
where <command> can be one of the following:
deploy
- This deploys the Liquidator contract, resulting address of which has to be configured in the.env
fileprepare-positions-to-watch
- Prepares positions to watch, continuing from the last block number saved in theall-positions.json
file. More details below in the Bot Concepts section.reset-positions-to-watch
- Similar toprepare-positions-to-watch
, but it starts from block zero.
All bots require configuration.
Refer to .env.example
for the .env configuration. Ensure this file has been copied to .env
and has been configured.
The example ENV file contains recommended ENV variables for the each bot.
Bot can be run directly with ts-node
by
pnpm run bot:run
There are 2 main folders:
contracts
- Smart contracts of necessary Compound V3 and Uniswap V3 interfaces, Multicall3 for batching multiple smart contract calls in one transaction, and our Compound3Liquidator.src
- Source code of the bot and stored data.
The bot has an in-memory storage, which is defined in the src/lib/storage.ts
. The storage is initialized when the bot
is started, and keep all the important data for the bot execution. Created are also the corresponding connectors for
contracts the bot will be interacting with.
A position in the Compound protocol is simply an address of the borrower - this is the identifier when we want to do a
liquidation call. The position tracking functions are located in src/lib/positions.ts
.
The storage variables allPositions
, currentPositions
, and interestingPositions
are used to track different states
of borrowing positions in the Compound protocol. allPositions
tracks all positions that have ever executed a borrow
action. currentPositions
is a subset of allPositions
and includes only those positions with an active borrowing
status. interestingPositions
is a further subset of currentPositions
, representing positions that have crossed a
specific Loan to Value (LTV) threshold.
Since fetching all positions from the beginning of the protocol might take a considerable amount of time, there is an
all-positions.json
file that is loaded during the bot’s initialization phase and serves as a checkpoint, so the bot
does not need to fetch the entire history each time it restarts. This file can be created and updated by running the CLI
command prepare-positions-to-watch
mentioned above.
Liquidations are carried out by interacting with the Compound3Liquidator contract, with specific details depending on
the bot’s current stage. The liquidation logic is slightly different for the OEV bot, as the searcher must first deposit
collateral on the OevAuctionHouse contract before placing a bid to qualify for receiving update details and then must
also pay for the awarded bid. Both actions are conducted on the OEV Network. The liquidation functions are located in
src/lib/oev-liquidation.ts
.
The process in general is as follows:
- A list of possible liquidatable positions is filtered from the
interestingPositions
by simulating the liquidations on-chain. - A liquidation call with the actual liquidatable positions gets constructed.
- The call is simulated to get the estimated gas limit.
- The call is submitted to the network.
- The result is awaited and logged.
The bot (src/oev-liquidation.ts
) runs several asynchronous independent loops at different intervals (defined by .env
variables) which perform certain tasks. These are:
fetch-and-filter-new-positions
- Fetches logs from the last processed block til the current block of the network in use. Filters the logs for theborrow
action and updatesallPositions
. Filters that result based on active borrow position and updatescurrentPositions
. Filters that result based on LTV values and updatesinterestingPositions
.reset-current-positions
- Reevaluates thecurrentPositions
fromallPositions
and updates them. Also reevaluates theinterestingPositions
from the updatedcurrentPositions
and updates them.reset-interesting-positions
- Reevaluates theinterestingPositions
from the updatedcurrentPositions
and updates them.initiate-oev-liquidations
- UsesinterestingPositions
to find out any positions that might be able to be liquidated. If there are any, it executes the liquidation.