Note: This research spec builds off of my existing writeups: (article1, article2)
Table of Contents
- Introduction to Prop AMMs
- What We Want to Discover
- Validation Criteria
- An exploratory approach: A Priority Lane for Price Updates
- Architecture
- Results
- Reproducibility
Introduction to Prop AMMs
Proprietary Automated Market Makers (Prop AMMs) differ significantly from standard XYK (Uniswap v2) models. Instead of passive liquidity providers, a single active Market Maker (MM) manages the liquidity and sets the pricing curve parameters with price updates. These price updates have a high priority over swaps, which is important as it prevents a market maker from having stale quotes picked off (see more on my writeup on Prop AMMs here). While these models flourish on high-performance chains like Solana due to updates being cheap and having high priority, they aren’t seen on EVM due to the lack of cheap updates with high priority (as discovered in my research writeup linked previously).
What We Want to Discover
The core question driving this R&D is: Can we bring Prop AMMs to EVM?
In our previous research writeup, we discovered that Prop AMMs weren’t on EVM due to the lack of cheap SSTORE write updates with high priority, and why we want to have Prop AMMs on EVM in the first place (article1, article2). That narrows our question further to doing the following below to enable Prop AMMs.
- Is it possible to have updates that are prioritized before swaps?
- Is it possible to have these updates for cheap?
- Is it possible to write a mock Prop AMM smart contract that leverages these?
- Are updates at the Top of Block (ToB) enough, or do we also need updates at the Middle of Block and Bottom of Block?
- How are we able to prevent spam from reaching the ToB?
Validation Criteria (What “Good” Looks Like)
At a high level, success is defined as creating a mock Prop AMM where updates are prioritized before swaps.
- A Prop AMM smart contract
- A priority lane for price curve updates to a pool in the Prop AMM.
- Updates that occur at a cheaper gas price (priority fee / gas used) compared to swaps.
- A solution to updates from being spam.
While a more longer term solution would be to fix the underlying problem of SSTORE writes being expensive, to make write updates comparatively cheaper compared to swaps, this is something that would require more time than my internship provides. It’s not as easy as making SSTOREs cost less gas, as Base has concerns on state growth and XEN spam.
An exploratory approach: A Priority Lane for Price Updates.
Another approach to solving the problem is by implementing a Priority Lane to ensure updates are always executed before swaps. A novel way of implementing a priority lane, suggested by Mark Toda from the Uniswap team, proposes a solution via a Global Storage smart contract (repo here) combined with a specialized builder policy.
The image above depicts the high level flow of how a trade would use the latest Prop AMM update from the Market Maker, and how the update transactions are always Top of Block.
Here’s how it works at a high level. This will be explained in more detail in the subsequent section.
- Global Storage Contract: A single, simple smart contract is deployed to act as a public key-value store. Market makers write their price curve parameters to this contract (e.g., set(ETH-USDC_CONCENTRATION, 4000)). Prop AMMs would read this value when calculating quotes.
- Builder Policy: Block builders would implement a policy that prioritizes transactions sent to the Global Storage contract address.
Note that the transaction having a to value to the Global Storage address is important, as we don’t want to allow transactions that aren’t directly to this address that also call other swap functions to be at the Top of Block.
Architecture
The Global Storage Contract
We introduce a singleton contract called Global Storage. It is a permissionless, neutral key-value store. It does not know about finance or AMMs; it simply stores bytes32 values namespaced by the writer’s address. This makes it generalizable to all types of oracle updates, rather than only price curve updates for Prop AMMs.
The contract is minimal to minimize gas overhead. It uses a namespaced mapping mapping(address => mapping(bytes32 => bytes32)) to ensure MMs can only overwrite their own data. In other words, it’s a dictionary where the key is the user’s wallet address and the value is another dictionary for storing values.
contract GlobalStorage is IGlobalStorage {
/// @dev Stored value per (owner, key).
mapping(address => mapping(bytes32 => bytes32)) private valueOf;
/// @dev Revert when keys and values array lengths do not match.
error MismatchedInputLengths();
function set(bytes32 key, bytes32 value) external {
address owner = msg.sender;
uint64 blockNumber = uint64(block.number);
uint64 timestamp = uint64(block.timestamp);
valueOf[owner][key] = value;
emit GlobalValueSet(owner, key, value, blockNumber, timestamp);
}
function setBatch(bytes32[] calldata keys, bytes32[] calldata values) external {
if (keys.length != values.length) revert MismatchedInputLengths();
address owner = msg.sender;
uint64 blockNumber = uint64(block.number);
uint64 timestamp = uint64(block.timestamp);
uint256 len = keys.length;
for (uint256 i = 0; i < len;) {
bytes32 key = keys[i];
valueOf[owner][key] = values[i];
unchecked { ++i; }
}
emit GlobalValuesSet(owner, keys, values, blockNumber, timestamp);
}
function get(address owner, bytes32 key) external view returns (bytes32 value) {
return valueOf[owner][key];
}
}
The Custom Builder Algorithm (Top-of-Block)
The key innovation lies in the block building logic. We modify the block builder unichain-builder and rblib to implement a specific rule:
- Any transaction where
to == GlobalStorageAddressis first included in a block before other transactions are considered, effectively placing these transactions at the ToB.
This priority is granted even if the gas price is lower than other transactions in the block. In other words, it’s effectively a local fee market which enables it to have lower gas prices compared to non-update transactions. To prevent spam from making it more competitive to land a transaction in the block (and subsequently more expensive), the global storage contract is initially permissioned, only accessible by white-listed market makers. However, a long-term and decentralized solution would be to have the contract permissionless, and instead limit the ToB priority lane to only the top 10% of gas in a block.
This custom block building algorithm is implemented the following way:
- in the block building pipeline for each flashblock, we add a conditional step. If the operator wants to use this algorithm by providing a global storage address, then we order transactions by their destination first followed by the priority fee.
- The
score()function ofOrderByDestinationAndPriorityFeeis implemented in the following way:- Notice the output being a tuple, with the destination being the first value of this tuple. As explained in the code comment, this means transactions going to the global storage address are always prioritized, followed by priority fee.
The Prop AMM
In this PoC, I mocked a Prop AMM using the code from Obric on Sui, and incorporated the global storage contract in it. The Prop AMM contract does not store its own pricing parameters for active trading. Instead, whenever a user attempts a swap, the Prop AMM contract dynamically reads the parameters from Global Storage.
Because the Builder Algorithm guarantees the Global Storage update lands before any other non-update transaction, a swap is guaranteed to execute against a quote based on the latest price curve, which will be seen in the video demo in the Results section.
All together, we get the following flow:
Results
Using this approach, we can answer that we can indeed bring Prop AMMs to EVM.
In the accompanying video demonstration (linked below), we showcase a live simulation of this architecture preventing a front-run attempt. The screenshot above showcases the result of an update transaction occurring Top of Block before a swap transaction, despite it having a lower gas price.
More specifically, our research allows us to answer each of the questions below:
-
Is it possible to have updates that are prioritized before swaps?
- Yes, using the Priority Lane solution.
-
Is it possible to have these updates for cheap?
- Yes, because update transactions in this Priority Lane only compete with each other (in terms of Priority Fee) to get included in the block, rather than all transactions. In other words, this is effectively a local fee market.
This means that a update transaction can be included ToB using only a gas price of 1 gwei, even if the gas price for non-update transactions is 10 gwei.
-
Is it possible to write a mock Prop AMM smart contract that leverages these?
- Yes. By porting Obric’s leaked Prop AMM smart contract on Sui to Solidity, and having it read price curve values from the Global Storage smart contract (as opposed to being stored in the Prop AMM contract itself), we are able to write a mock Prop AMM smart contract that can swap tokens based on the Market Maker’s price curve.
-
Are updates at the Top of Block (ToB) enough, or do we also need updates at the Middle of Block (MoB) and Bottom of Block (BoB)?
- Market makers would benefit from MoB updates, so they can not only adjust price curves before any swaps (ToB update) but also after a few swaps (MoB). However, a similar effect can be achieved by having faster blocktimes, as this would increase the number of effective updates.
- BoB updates do not seem useful, as having an update at the bottom of the block (after all swaps) is the same as having a ToB update on the next block.
-
How are we able to prevent spam from reaching the ToB?
- Two approaches.
- A approach that aligns with the permissionless values of Flashbots is to make the Global Storage smart contract accessible by anyone, and limit these transactions to just the top 10% of the block.
- A faster go-to-market approach is to make the smart contract permissioned by whitelisting it the addresses of a few market makers.
- Two approaches.
Reproducibility
The setup scripts ran for this experiment assume MacOS is used.
To run the demo, run the following commands.
- In your
$HOME/projects/prop-ammdirectory, git clone the following repositories- GitHub - fahimahmedx/prop-amm: A Prop AMM written in Solidity, inspired by Obric's Prop AMM on Sui
- GitHub - flashbots/global-storage-smart-contract: Global Storage Smart Contract for Top of Block building
- GitHub - flashbots/builder-playground: A fast, self-contained environment for end-to-end block building on L1s and L2s.
- GitHub - fahimahmedx/token-deployer
- GitHub - flashbots/rblib at fahim/global-storage
- Branch:
fahim/global-storage
- Branch:
- GitHub - flashbots/unichain-builder
- Branch:
fahim/global-storage
- Branch:
- Run Docker
- Export a private key from
builder-playgroundto the env variable$PRIVATE_KEY - run
$HOME/projects/prop-amm/token-deployer/setup.sh- If
0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512doesn’t end up being the address the global storage smart uses, pass the correct one for--builder.global-storage-addressin the .sh file’s step 3.
- If
- Once the setup has finished running all of its 6 steps, run the test using the following command:
python3 $HOME/projects/prop-amm/prop-amm/script/race_swap_vs_update.py- You will also need to update the private key in this file.
- You may need to create a virtual python environment and install the required libraries it says it’s missing to run this.
- The test should showcase the update transaction occurring before the swap transaction.







