On MEV Agents and Forward Routing Problems
Gm anon, I am Luca, member of the Team Agents at Urani. We develop and open-source MEV agents searching for optimal order execution paths.
Funnily enough, searching somehow mirrors my life's journey. In this post, I introduce myself and I discuss the problem that helped me land a job at Urani.
The technical challenges included developing an MEV agent that maximizes the order's surplus by matching a set of order intents to various potential liquidity sources.
Looking Up in the Sky
I was born in a small town in northern Italy, nestled between lakes and the Alps, and I was fascinated by space.
At 8, charmed by magnets, I designed a project for a magnetic engine for spacecraft, which I thought would be tremendously effective (spoiler alert - it wasn’t). Strangely, it didn't reach space, but instead ended up in the trash can.
However, this experience made me realize I wanted to design technologies that benefit people.
My quest to find ways to impact people positively started with studying Engineering Physics, specializing in semiconductor materials and electronic devices.
Studying organic electronics made me passionate about molecular systems, and I collaborated with a lab conducting experiments with ultrafast lasers on organic molecules for biocompatible optoelectronic devices.
Then, attracted by clean energy applications, I worked on my M.Sc. thesis in Germany, modeling and developing new catalytic materials. Lastly, I returned to Italy to pursue a Ph.D. in Theoretical and Computational Chemistry.
During this period, I unleashed my geeky potential, delving deeper into the quantum mechanical modeling of molecules and joining a team developing scientific software.
As I traveled the world to speak at various conferences, I felt my path should change again. I was having fun in the scientific community but felt the push to do something concretely valuable for people.
Discovering Urani
While searching the web for highly impactful start-ups, I stumbled upon Urani 🪐.
They seemed like the perfect match for me. Not only does developing advanced strategies and AI-centric MEV agents trigger my geeky and technical side, but the concept of promoting fairness, accessibility, and meritocracy in DeFi, and protecting users from toxic-MEV bots, makes me feel I’d be doing something truly impactful for people.
(Really, they hooked me with a job post that showed a thumbnail image featuring a piece of the time-independent perturbation theory in quantum mechanics.)
Calling all scientists, PhD students, mathematicians, and physicists:
— URANI (@urani_trade) May 9, 2024
Come develop optimization strategies with us!https://t.co/dM9VtmLcmT pic.twitter.com/KmmnpDLWdS
So, I dropped my resume and crossed my fingers. When I got the reply, I was ready to take on the challenge they gave me.
My First MEV Agent and How I Got a Job at Urani
MEV agents function by analyzing the current batch of orders to detect peer-to-peer matches and ring trades that could provide the best prices for executing trades or limit orders. They assess various factors, including liquidity, order book depth, and price slippage, to ensure traders receive the optimal execution.
Additionally, these agents can directly interact with on-chain automated market makers (AMMs) or use DEX aggregators to find the most advantageous prices and routes."
I was tasked with developing an MEV agent that can match a set of order intents (in JSON
) to various potential liquidity sources and determine the optimal execution to maximize the order's surplus.
For instance, here is a simple example of an order intent:
Setting the Framework
Suppose a user with an order expressing their intent to buy buy_token
and sell sell_token
with specified restrictions. These restrictions include the maximum amount of sell_token
the user is willing to sell (s_lim
) and the minimum amount of buy_token
the user is willing to receive (b_lim
).
We have a set of venues or liquidity pools with an AMM where coins can be swapped. All the coins and venues define an undirected graph, with the former being the vertices and the latter being the edges. For example:
The order's surplus Γ
is defined as:
Where π = s_lim / b_lim
is the user's worst acceptable exchange rate.
The problem posed is: if users has 1000 MU
(sell_token
), what is the optimal way to convert it into NU
(buy_token
)? These problems are called forward routing.
Interestingly, for such problems, direct routes (i.e., selling all 1000 MU
along a single simple path connecting MU
to N
U), are not always the most efficient, and a combination of multiple routes can often yield better results.
Thus, the task translates into helping the user solve this problem:
With N
being the number of simple paths connecting sell_token
with buy_token
(MU
and NU
in our example, respectively).
Gladly, the forward routing problem is convex, so we do not have to embark on a global optimization problem but can employ local minimizers.
My Approach
There are many ways to solve this problem. In my solution, the main components are a set of classes:
Order
: Represents an order with user intent for trading.Venue
: Represents a trading venue with token reserves.Market
: Represents a market of trading venues; it is a graph with tokens at the vertices and venues at the edges.Agent
: Represents a market agent that formulates and optimizes trading strategies.
Given a generic intent
, with the user Order
and the list of Venues
forming a Market
graph, Agent
reads the Market
and constructs a strategy
to fulfill the user's swap needs.
In this case, strategy
is a directed subgraph of Market
containing all the simple paths connecting the user requested sell_token
to buy_token
.
The Agent
now searches for the optimal coin exchange among the paths in the strategy
to maximize the user surplus by applying a constrained maximization of the order's surplus Γ
function defined above.
This is the scheme of such an approach:
Which translates into this code:
One can call such function with a simple Python program:
That when is run for the previous intent outputs:
Generating the following results:
That's enough to make it work.
However, If you want more technical details on how such code works, sit down and fasten your belt.
Technical Discussion
In this first steps, we build:
- The user
Order
containing the intent of buyingbuy_token
and sellingsell_token
, with the worst acceptable exchange rate; - The
Venue
objects containing the trading venues; - The
Market
object, i.e., the graph of coins and trading venues.
The User Order's
First of all, we ingest the intent into the variable data and read values from it:
This allows to initialize an instance of the class order
:
The Venues
Next, we include all the venues:
This allows to initialize a list of instances of the class venue
:
The Market
Having stored the venues, we can now build the Market
graph:
The initialization of a market
instance generates the graph as follows:
In our example, Market
will look something like:
The Agent
Now, that we have all the necessary inputs, we can initialize the Agent
and construct the strategy
graph:
Where the Agent
object looks like:
Strategy Construction
Strategy
is a directed subgraph of Market
. Agent
, stores also all the paths connecting sell_token
to buy_token
. In our case, this will look something like:
In case of more complex graphs, multiple paths are stored for future propagation and optimization, for example:
To do this, we first read the Market
with the method:
And then, we construct the strategy multigraph:
The Strategy Optimization
Now we have all the ingredients to try to solve our forward routing problem:
- We have the intents of the user defining the constraint of our problem;
- We have the paths along which we can propagate to reach
buy_token
fromsell_token
; - Having set the direction of propagation, we also have the price function for each edge, allowing swapping from one coin to another through an AMM venue.
Note: the price function of the venue AMM C-A
will be in an AMM:
Where a
is the amount of coin A
sold to buy the amount c
of coin C
. [A], [C]
are the initial liquidities of the tokens.
Now we can use the optimize_strategy
method bound to Agent
:
In this routine, we make use of the propagate along (path) which is defined as:
And that's all.
Now we have obtained the optimal amount of coins sold and bought through each channel, we just need to output the results to the user.
Final Thoughts
The approach I employed works on acyclic graphs. Adding an edge between two coins makes the graph become a multigraph. From a theoretical point of view, the problem of routing on multigraphs is still convex (e.g., see this reference).
The code works fine for very simple multigraphs (i.e., only two coins are connected by more than one venue).
However, when multiple venues connect multiple coins, the problem of detecting all simple paths connecting sell_token
to buy_token
arises. This problem is combinatorial by nature and gets extremely complex as the graph's dimensions increase.
In addition to this, even if the directed graph wouldn't have multi-edged connected vertices, the problem of simple path detection from A
to B
is highly complex (#P-complete), and requires complex numerical techniques to be solved efficiently.
Moreover, I never considered multi-asset venues, which would imply more complex graphs and for which the price functions could change during propagation. Indeed, if I consider a multi-asset pool, with tokens A
, B
, and C
, it can happen that the price function c(a)
might change if I first visited this venue but along a different edge of the graph (e.g., A
/B
).
Another source of additional complexity is the possibility that the graph changes through time. Indeed, it might be possible for new venues to be added to the market, introducing new connections among the nodes.
Eventually, I did not consider that in real swaps, liquidity pool providers' fees, venues' price functions, and price slippage, among other factors, might influence the executed outcome.
Until Next Time, Anon
The real world is much more complex than this challenge, but this was a fun start. By the way, if you like what you learned today, you can try it yourself: here is the repository for my project.
We at Urani are working hard to leverage advanced algorithms for price-routing, ring-trade detection, and MEV-minimization.
I am particularly excited about being part of this fantastic group, protecting users in decentralized finance.