# 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.

**If you are interested in my personal story, you can keep reading. Otherwise, skip to the next section for the technical discussion.**

**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 are crucial components of the Urani Protocol, serving as matching engines to identify the best execution paths for orders.**

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 buying`buy_token`

and selling`sell_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`

from`sell_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.