An Open-Source MEV Engine in Typescript.

An Open-Source MEV Engine in Typescript.

To understand the power of a MEV bot engine that you can build upon we must first discuss what MEV is. For years, many referred to the concept as Miner Extractable Value. However, recently it has come to take on the more accurate meaning of Maximum Extractable Value. The value to realize is not simply limited to Miners; anyone can participate.

For years, many defended the idea that a significant majority of MEV accrues to Validators. In reality though, much of the MEV in the current market goes to bots operating as "protocol market makers" that work to balance the state of all the open primitives and protocols in the industry. These individuals and bots and referred to as Searchers.

MEV can be explained as running a transaction and immediately making more money than you spent. Sometimes it is simple. Sometimes it is complex. Sometimes it only requires a single trade. Sometimes it requires multiple. The conditions and precise specifications vary widely between strategies.

The Current State

With a general grasp on what MEV is, let's now catch you up on the current state of the market. It is vital to your success that you remain constantly aware of the current shape of activities outside your daily operations. There is a significant profit to be made and that means that an enormous amount of resources are devoted to securing that profit by each participating actor and party. If you do not stay learning someone will come in to steal your niche from you.

Thanks to the generous developers in this industry, you do not have to start from square one. There are many incredible resources and open-source implementations providing momentum that would have otherwise taken many months to build up. Your success in MEV is almost entirely driven by your awareness and knowledge. Therefore, it is important to inform yourself with every opportunity. When someone is talking about something that you can't parse and find meaning in, listen closely.

When you find a repository that does something in a unique way, mark it down and use the tricks that developer has found useful. There is no shame in it in chasing the logic and money. If you're reading this I probably don't need to tell you that, though. So, let's catch you up on a few surface level things.

The Competition

For the most lucrative transactions and strategies, there often exists a required bribeexternal link where the bot pays a piece of their margin to Validators incentivizing inclusion in the soonest block possible. Although this is the case, Validators don't do anything beyond perform their normal activities. When bribed, they do not receive a standard margin of the profit generated.

In practice, there could be little to no bribe included when it is not a market of high demand or competition. Many are quick to parrot that in theory a Searcher should be willing to pay up to 100% of their "profits" so that their transaction filled before anyone else. Rarely is that the case. Because there is so little competition, you are rarely required to actually operate with such a tight margin. The only places that you really ever see complete margin use is on very crowded strategies such as DEX arbitrageexternal link.

Before moving on it is important we cover one last thing. You will hear people reference Generalized Frontrunners constantly, which are bots aimed at finding profitable transactions in the mempool and executing it themselves before you do. Not only are these extremely rare, but they don't really exist in the way that people often speak or think of them.

How do we know this? Exploits happen almost daily. Yet, very few are caught and stopped in their tracks by a whitehat. Exploits are the most valuable transactions that could be captured and they fail to do so a vast majority of the time. Of course, with extremely simple transactions, one may be frontrunexternal link. But, unless you are doing a simple linear transaction that requires no predefined state such as token balances, role access, etc. you will likely never have first-hand experience with a bot of this nature.

The MEV market is not just filled with exceptionally talented individuals, but also intentional psyops and misinformation designed to keep the unknowing from entering and competing. There are social games played to trick you. The opportunity is larger and more accessible than you could ever imagine. Don't fall for it.

The Options and Resources

There are thousands of resources that will prove exceptionally valuable on your journey. Sometimes you will run across something that you are not yet able to fully comprehend. When this happens it is critical to read through it, note down the things you don't understand, and keep this lack of knowledge and understanding in your mind.

Don't obsess over comprehending everything immediately. Allow your knowledge to accumulate. Over the months the understanding will form on its own while you are focusing on the things that will actively make your strategies better.

As you are getting started, it can be difficult to wrap your head around the basic things. Below, we will cover a few priceless resources to improve your learning and success rate during the early days of your initial efforts:

My personal list has several hundred resources. I have only included a few beginner resources that will form key concepts for you. From the items on my list, I have gone through 100% of them and understand 95% of them to the point where I can implement on top of the discussed concepts. Yet, the 5% remains a targeted need to further improve the systems and strategies that I have running. You will most certainly be in the same situation unless you are exceptionally skilled at blockchain development.

If you have written something and would like to be added here, let me know!

The Intention of Sharing

For years the MEV niche of the blockchain industry has been run by a few select parties. At any given time there are thousands of valuable strategies to run. Yet, the market has found itself in a position where there is little real competition. The ratio of publicly held knowledge and awareness has reached an all time low even though there are more opportunities and people running MEV bots than ever before.

When discussing MEV, many immediately assume that all the money to be made is already spoken for by someone that is highly technical and better suited. That is not the case. In reality, most actors hone in on a specific set of strategies that they make their domain while leaving the 99.99% of remaining surface area empty and open to you.

Even after having built and used this framework for almost a year, I myself am still in this position. There are simply too many ways to make money for me to give them all attention. That is why I am open-sourcing the framework I've establishedexternal link. The market does not benefit when a single actor has no competition. Competitive markets are what lead to true innovation and progress. It is time the average person has a way to access the deeper value that lies within the ecosystem so many of us call home.

What do I get out of this? Transparently, I am going to build strategies of people that ask for help. Because they don't know how to do it on their own, I can offer a lending hand and share the access with the person that identified and crafted the strategy. Like I said, there are so many viable strategies these days that there simply isn't any way to know about every single opportunity. The pie is so large we could share for years and still not reach 100% coverage.

MEV and development surrounding it is not technically capital constrained. Instead, the amount of value you can capture is almost entirely dependent on how much time and labor you can devote to it. So, the release of my engine serves as a way to generate constant inbound requests for strategies to build. This way, I can focus on what is important and worry less about digging in the deepest parts of the industry for hidden alpha while solving the opposite problem for those that have time to research, but not the skill to develop the mechanisms needed to run the strategy.

The release of this framework is not some altruistic fulfillment. I'm here to make money and together we can make much more. That is the reason for open-sourcing my MEV framework. If you prefer to go straight to the repository and get to work you can do so hereexternal link.

The Potential of Modularity

As you read this article, you should quickly realize that MEV is not one size-fits-all. Instead, it takes many shapes and requires many different approaches. This is why having the ability to reuse your previous solutions and building blocks is so pivotal.

As an industry, many millions are spent every year on designing and running the exact same processes. An unimaginable amount of venture capital money pour into companies that are doing little more than rebuilding the process that an open-source repository could have fulfilled in minutes. The operational cash and profit margin could be significantly larger with the adoption of a modular settlement engine driving the core actions of their protocol and company offering.

If you have to do a lot of manual work to fulfill every strategy then it is unlikely you will ever reach the point of daily sustainability. To do so, you have to be extremely efficient both with your development and the execution of your transactions. If you only shoot to get to the point of functioning barebones systems then it is a certainty you will always be behind.

A modular MEV engine solves all this by allowing you to reuse the systems you already have in place while adopting the broader components that countless industry leaders rely upon. You do not have to figure everything out yourself. You do not have to rebuild everything from the ground up. Instead, you get to focus on the strategies that are going to make you the most money.

Before diving in, it will help you to have context about the common patterns and strategies most new Searchers experiment with before finding their niche. There is a wide range of opportunities. So, we will only cover the primary ones that are most commonly referenced in the social sphere.

Arbitrage

As mentioned earlier, Searchers are effectively market makers for protocols. They are not always bad actors or simply extractive in a self-serving means. Arbitrage is one of the most common and highly contested pieces of the market that remains in flux at all times.

In our industry, arbitrage arises in many forms. The simplest to approach is DEX arbitrage where a token can be swapped on two different protocols. As competing protocols are rarely integrated with one another there is the opportunity for an individual to make trades that keep the price the same on each market respectively. When the price is low on one, a Searcher can buy the token and go sell it where there is deeper and higher priced liquidity.

Due to the extreme competition of this specific strategy you will often resort to using Flashloansexternal link that enable you to trade at size outside the means of the money held in your wallet. On a DEX, you can solve for the largest trade size with ease allowing you to realize the entire arbitrage in a single transaction.

We will not be going into that today, though. While arbitrage is exciting and simple to understand, as a new Searcher you are exceptionally unlikely to beat the people that have worked on establishing a proven system that can run at huge scales over the last years.

Sandwiching

Sandwiching trades on a DEX has been an incredibly hot topic for several years now. If you've spent any time in the industry you are sure to know about jaredfromsubway.eth. As one of the most prolific individuals in the MEV market they have carved niche unlike many others. You always hear about how they make an asinine amount of money, but what are they really doing?

Effectively, a bot watches a specific token for incoming buys from users and frontruns them. Then, after the unsuspecting user acquires their tokens they are swiftly dumped on to extract the liquidity that had just been used to purchase said tokens.

This niche is highly contested and often quite dangerous to play with because developers can set traps that will catch your bot and all the funds you had used to acquire the tokens. If you don't know what you're doing, you better be careful.

Due to the competition and danger, not every token experiences the effects of bots that do this. However, as liquidity and trust accumulate bots quickly arrive and begin to sift out the incoming value as quick as it enters.

Thankfully for you, there have been many discussions about this and you can browse one of the original repositories, Subwayexternal link, that provided a reference implementation for sandwiching by libevmexternal link.

Token Sniping

As a degen in the industry, it is of almost absolute certainty that you wish you had been able to snipe a token launch right as it was happening. But, you miss it time and time again only to ever think about it hours before the launch happens.

With a token sniping bot you can watch the state of a contract and execute your transaction as soon as it updates without you having to do a thing. Ever wondered how there is always a set of bots that beat normal users to the market resulting in significant gains? This is how.

Token sniping is a good skill to get under your belt even if you don't expect to have the opportunity often. It requires access to a flow of information that is rare to hold in this industry, but if you hit it just once a year you will immediately make more than the typical annual salary of a very skilled and in-demand developer.

Keeper Networks

Maintenance is required for a lot of things in this industry. Maintenance is often incentivized in a way that enables Searchers to fill the technical need at a fair cost. In practice, maintenance providing bots are commonly referred to as Keepers that assist in keeping the protocol and network not only operational, but timely.

One of the most commonly referenced protocols that utilizes this mechanism is Splitsexternal link. This protocol is entirely onchain and offers a set of simple, modular contracts for efficient payment distribution. Money inside the protocol is automatically split by the smart contracts, but critically, it is not automatically delivered to the recipients.

This is where a Keeper comes in. By democratizing access to the process of distribution, anyone can opt in to assist in the distribution and earn a small amount of funds back for it. There are not strict requirements of operation either. If something isn't profitable to run, you don't have to run it. However, when your bot finds a transaction that it can execute while still making a profit there's no reason not to! It is effectively free money as a thank you for lowering the technical burden of the protocol itself and enabling continued service to users.

Like Splits, there are hundreds of other protocols that follow a similar model. These are a great place to start your adventure because they often carry a smaller financial incentive. With such a small incentive the individuals operating bots are often far less mercenary-like.

Protocol Backends

As you build up experience it becomes much simpler to spin up a new backend for the project or protocol you are working on. Instead of having to deal with the burden of re-inventing the wheel you can simply fall back on ol' trusty and use the MEV framework you already have an immense amount of experience with.

By doing so, you not only maintain the context of a territory you are already familiar with. But, you can reuse the driving pieces that you've already developed and battle tested. Instead of being terrified on launch day and having to hope that everything is going to work the way you want, you can rest easy knowing you've already put your system through the fight of its' life on the MEV battlefield.

Personally, I use a slightly modified version of this engine to drive the simulation and execution of the protocol I have been working on for the last year. Plugexternal link, brings "If This, Then That" statements much like Zapierexternal link for all EVM blockchain interaction with a simple drag-and-drop interface. Instead of needing to create a whole new system and start from scratch, I have been able to use the Collectors and Executors I had already built and saved many months of work and refinement.

Additionally, I have been able to integrate MEV connectors into the base protocol that will allow countless users exposure to the MEV market without them having to write a single line of code. From what would have taken months of labor and thousands of dollars in costs, to only a few days of work and an architecture that stacks on top of the systems we already run.

A High Throughput Model

An important note to make about this engine is that unlike your typical script, it is entirely architected around the idea of reusing existing data streams. When most get started with MEV they write one-off scripts that consume a high level of the resources making it difficult, expensive, and time consuming to operate with many strategies.

Instead, the MEV engine here is designed around the reuse of your existing data feeds. If we have 10 strategies that each fire events every new block we do not need to check for a new block in all the scripts. Instead, we simply have 1 top-level Collector that will distribute the events accordingly and make it simple to update the state of multiple strategies at the same time.

A stack of a single collector strategies

Inspired by Artemisexternal link, the engine is built with three key concepts:

  • Collectors: Streams of data that consume and forward events/triggers.
  • Executors: Actions taken when an event is received.
  • Strategies: A single-lane outcome combining Collectors and Executors.

By combining these pieces we have an engine that can power multiple strategies without stumbling over itself. In a single configuration file you can manage the state of all your strategies and reuse every core mechanism that you have built and run. Whether it be RPC requests and responses, API GET and POST requests, submitting transaction, or even simple platform interactions like forwarding messages to a Discord server.

This means that you can fulfill opportunities more broadly than many other market participants. You can arrive in a new market at a rate unparalleled by those that are writing and managing one-off scripts. You can snipe every trade until your heart is content.

In 2023, there was a significant growth in "MEV bot" potential coming in the form of Intents and Account Abstraction. With these concepts cementing their spot in the market there are now non-extractive means of profitable service for bots to provide to end-users. With ease, you can settle the state of EIP-4337external link transactions across any blockchain with just a few minutes of effort. Are you beginning to see the power of having a powerful execution framework at your fingertips?

The size of opportunity will only grow in the coming years. Solidifying your niche early will provide the largest gains you've ever made.

Batteries Not Included

Notably, this engine does not include strategies pre-built for you. While I want to see some new competition in the market, I am not going to give up my edge for no reason. The key building blocks are already packaged up and provided for you.

Install the package and be off to the races

Included in the @nftchance/mev package you will find a range of Collectors and Executors such as:

  • BlockCollector: A trigger that fires on each new block of the network your RPC client supports.
  • HeartbeatCollector: A trigger that will fire on a regular basis while you are in development.
  • OpenseaCollector: A trigger that can be configured to fire on the key events of Opensea.
  • DiscordExecutor: Forward embeds to a Discord server and channel with a webhook.
  • FlashbotsExecutor: Execute a private transaction on Ethereum mainnet using Flashbots.
  • LogExecutor: Log the updates to the console of your terminal.
  • MempoolExecutor: Execute a public transaction on any EVM-compatible blockchain.

With these, you already have all the pieces needed to build 99% of strategies that you likely have in your mind. Of course, as you scope new APIs similar to OpenSea you will need a Collector. You can either submit an issue asking for one, send me a message on Twitterexternal link, or just write it yourself and submit a PR.

To get started all you have to do is open your terminal to a new directory and run npm i @nftchance/mev & npx mev init. With just this simple installation, you are already nearly to the point where you can start developing your first Strategy. You can find a full breakdown of the documentation appended to the README in the repository that has been made publicexternal link today.

Running Your First Strategy

While I am not going to give away all the alpha, let me provide you a simple step by step process of how you may go about creating your first Strategy. Here, I will not be highlighting a money-making strategy. Instead, this strategy will provide you an advantage against most of the degens of the industry with just a few lines of code.

For this Strategy, we are going to listen to new Pairs and Pools created on Uniswapexternal link. Each time we detect that liquidity is fixing to be provisioned we will forward a message to our Discord server where we can do further analysis. We will not worry about protecting against poison attacks, solving of taxes, or anything else at this stage. I leave that as homework on the first day of MEV adventures.

To get started, we will need to add the Uniswapexternal link contracts as a reference so that our engine can generate the Typescript interfaces. Additionally, let's go ahead and initialize a BlockCollector and DiscordExecutor. Now, your mev.config.ts that was generated when you ran the init command should now look something like:

import {
  BlockCollector,
  defineConfig,
  DiscordExecutor,
  mainnet,
} from "@nftchance/mev";
import { providers } f...

These few lines of code do a lot so let's break it down before moving on:

  • We retrieve all the private values of the environment variables set in your .env.
  • We initialize all the Collectors and Executors.

This can feel like you just got thrown in the deep end. That is okay, because you did! It will begin making more sense as you play with it. If this feels daunting just install the package, copy-paste this code and get to playing around. The only way to learn is by breaking things.

With our basic configuration in place, we will go ahead and let the package generate our reference interfaces so that we can use them when implementing our Strategy. To do this, run npx mev references. This command will generate a references/ folder that contains the source of the contracts as well as pre-built Typescript interfaces containing all of the commonly needed pieces when interacting with a contract offchain.

Next, we will go ahead and create our Strategy and name it UniswapPairsAndPools. Navigate to src/strategies/ and create a new file named uniswap/pairs.and.pools.ts. Before writing anything, let's think about what we want to do.

  • Each time we get a new block, we want to filter for new creation events from UniswapV2 and UniswapV3.
  • When we detect a new liquidity provision we want to forward the message to Discord as an embed.

Simple, right? We don't need to store anything in memory or in a database. We don't need to sync any state. We're just catching new creations as soon as they happen. Let's go ahead and do it. We're going to start simple and append each new piece. Our starting point will be as follows:

import { BlockCollector, DiscordExecutor, Strategy } from "@nftchance/mev";
import { providers } from "ethers";

export ...

With this, we have imported everything we will need as well as prepared the bones of the Strategy by extending the class that is already provided for you in the @nftchance/mev package. Next, let's go ahead and start handling the response from our BlockCollector by implementing a function inside the Strategy.

This piece of the logic is going to be triggered every time we receive a new block. As noted earlier, we want to filter for events and respond accordingly when one is found. To do this, create a processNewBlock function as follows:

import { BlockCollection } from "@nftchance/mev";

processNewBlock = async (collection: BlockCollection) => {
  const { ...

We now have a function that will retrieve the block number and then call our function getPairs. Of course, we haven't yet made a function to retrieve the events from Uniswapexternal link. Let's do that now. Once again, we will add it inside the UniswapPairsAndPools class we are building.

import { ethers } from "ethers";

import {
  UNISWAP_V2_FACTORY_ADDRESS,
  UNISWAP_V2_FACTORY_INTERFACE,
} from "@/refer...

Now, we have the ability to detect new blocks, recover the current number, and then filter for all the logs relative to both versions of Uniswapexternal link. Notably, because we are not storing anything into memory we just return the results that are read onchain so that we can pass it on to the DiscordExecutor.

It is worth noting, there are more performant ways to filter the events. However, I have included a verbose implementation here so that you can more easily follow what is happening. If you intend to use this in production, you will want to combine the calls for each version of Uniswapexternal link into one call and then filter based on the responses. Additionally, in production you will likely want to combine all the metadata calls into a single Multicallexternal link that runs all the metadata calls in a single read.

The final step to our Strategy is sending the response to our DiscordExecutor so that it sends the message in the channel of our server. To do this, let's create one last function and name it processCollection. This function is going to coordinate the passing of data between the functions we already have at our disposal.

Up to this point, even though we implemented processNewBlock it isn't ever actually called. So, our final function will determine what kind of event the Collector fired and then respond accordingly by passing on data to the Executor when needed. Let's start with the basics and then we can implement the handling:

processCollection = async (
  key: TCollector["key"],
  collection: Parameters<TCollector["emit"]>[1]
) => {
  switch (k...

It is important to understand that this function uses a switch statement to determine the type of Collection event fired. When it is a NewBlock we will want to call processNewBlock and get all the Pairs/Pools deployed within that block and return the pairs. Each time we have a response, build a Discord message and forward it to the DiscordExecutor with the required parameters like so:

processCollection = async (
  key: TCollector["key"],
  collection: Parameters<TCollector["emit"]>[1]
) => {
  switch (k...

Just like that, we have a functional Strategy defined and are nearly ready to run it. The final thing to do will be adding the Strategy to our mev.config.ts so that it can actually be run by the engine.

To do this, we import the Strategy we created and provide the top-level information that is needed. The arguments passed will vary between each Strategy so you should not concern yourself with trying to create a generic. In this case, explicit definition is the best approach. For the Strategy we just made, all we need to do is pass the WebSocketProvider client and the endpoint for our Discord channel webhook.

import { UniswapPairsAndPools } from "@/strategies/uniswap/pairs.and.pools";

// ... Previous code you wrote.

export de...

Now everything is in place and the only thing left to do is run it with npx mev start. Just like that, you are off to the races with your very own MEV bot.

Syncing Historical State

Before wrapping it up here, we should cover the idea that not every Strategy will be as simple as the one above. In reality, often you will want to sync the historical state of all the actions taken so that you can be pre-informed.

This becomes helpful in places that have accrued value or state. Keepings things simple once again, let's look at a slightly different Strategy. Imagine we are watching an auction market that constantly has incoming bids. As they come in, you won't simply want to assume that it is an entirely new bid. Someone could have been outbid and you need to be able to handle that!

Built into the framework there exists the ability to define a syncState function inside the Strategy that you are building. By doing this, when the engine spins up it will focus on retrieving and balancing the state of all the actions you need before running.

For this to work, we need a few things:

  • A way to store the historical state.
  • A way to retrieve huge chunks of historical data.
  • An efficient process that allows us to dump state state data.

It sounds complicated, but as you scope and plan strategies you will quickly become aware of why this step is so crucial. Often, you do not need to worry about setting up a database and dealing with all the complexities that come with that. Instead, you can simply store it in memory.

Let's look at a quick example by imagining that we are focused on the bids that are constantly flowing into the Nouns Auction Houseexternal link. We are crafting a Strategy to make sure that when an auction is likely to end below our target price we submit a transaction and secure the Noun for ourselves.

First, we will add a synced variable that we can toggle the state of once it has been completed. This will prevent new blocks from being processed until everything has been synced. We do not want to risk running a transaction when we do not have all the data we need. If we ran without syncing the historical state of the auction we could end up under-bidding and wasting gas money on a failed transaction or even over-bidding compared to the auctions of the past.

To do this, we add a syncState function similar to:

syncState = async () => {
  const fromBlock = NOUNS_DEPLOYMENT_BLOCK;
  const toBlock = (await this.client.getBlock("lat...

Effectively, syncing state is just pretending that you've received a batch of emissions from your Collector and handling them accordingly. No arguments are provided to syncState when the engine calls it. So, you will need to prepare the configuration outside of the stand processes. In the above Strategy, during startup we determine the latest block and go back to the time the contract was deployed. All the bids between then and now are retrieved, stored in memory into nounBids with the help of the updateInternalBidState.

That's all there is to it. Officially, you have all the tools at your disposal to compete against the most competitive Searchers and fill the transactions you have always wanted to. If you still can't bring yourself to make a profit, it is simply a skill issue. Hit the grindstone and get to learning.

If you are not sure where to start, reach outexternal link. I am happy to help you get moving, but I will not be holding your hand or teaching you the basics. Good luck and have fun. I will see you in the mempool.


The Cyclical Nature of Learning Something.

The Cyclical Nature of Learning Something.

Alright, lets dive into something weve all experienced but probably never really thought about the way we learn new stuff. Mechanically complex tasks are often difficult to p...