While browsing CT, a post by @fifikobayashi caught my eye:
MEV out in the wild!
For someone who has only been living in the protocol and application layer, I saw this as the opportunity for me to start a side project ultilizing MEV (somehow). and get familiar with the infrastructure side of things.
The flashbots repository was quite organized, and within minutes, I found out a great entry level project that I could hack on - gasless ERC20 transfers.
Unfortunately it took me about two days before I managed to get it communicating properly with the flashbot's relayer due to a package version mismatch.
Even for someone who was quite familiar with the tooling, it took me a while before I could get it working. It was at moment that I realize a project with the most asymmetic impact would be one that democratizes this power. And so, began the quest to make flashbots more accessible.
Unfortunately, making flashbots work in the browser wasn't as simple as slapping on a frontend to the existing boilerplate repository. But to know why, we first need to understand how a gasless transaction is constructed.
A gasless transaction is actually comprised of 2 signed transactions from 2 different accounts:
Keep in mind that account 1 will have 0 ETH, whereas account 2 needs some ETH to pay the bribe.
Once both transactions have been signed(!!) by their respective private keys, it'll be submitted repeatedly to the flashbot's relayer. The transaction will only be included in a block if the ETH bribe meets certain conditions.
Another important information to understand is the ETH bribing function:
It basically checks if the a function call (_payload
, e.g. balanceOf(0x....123)
) to an address (_target
, e.g. USDC
) matches a certain state (_resultMatch
, e.g. 1e10
), if so, it'll transfer the amount of ETH sent along with the transaction to the miner of the block.
The bribing code above works because Ethereum is a state machine, where the order of each transaction in each block matters. What that means is that we can trustlessly construct a bribing transaction (transaction 2.) that will only be valid (and broadcasted by the participating miner) if our zero gas transaction (transaction 1.) is successful.
I wanted to make an interface where you could ultilize metamask to pay for the bribe. This is however not possible as metamask (and many other web-based wallet providers) doesn't support the crucial eth_signTransaction
.
You could however perform eth_signTransaction
if you had the private key. But I personally didn't liked the idea of prompting people to put in an uncompromised private key into a web browser, and was determined to get this working, somehow.
MEV-Briber is my attempt at making a browser compatible version that works with the current flashbot relayer.
To make it browser compatible, we must solve two problems:
The first problem to overcome is the inability for metamask to perform eth_signTransaction
. Is there any other way for us to verify some arbitrary signature that wallet providers support from a particular user on the contract layer?
Fortunately, ERC-2612 permit does exactly that. It allows us to verify ECDSA signatures on the contract layer, and signing via eth_signTypedData
is supported on most major wallet providers. You might have even seen it before while trying to remove liquidity from UniswapV2:
And luckily for me, openzeppelin already has a draft implementation that I could reference. I added ECDSA signature verification into the contract:
And with that, MEV-Briber can now verify:
Which when combined with our (poor man's) account abstraction, will allow us to make flashbots browser compatible.
Previously, the msg.sender and thereby transaction signer who calls check32BytesAndSend
has to have ETH in their account. However, with eth_signTypedData
msg.sender doesn't need any Eth in their account, provided the transaction signer can transfer ETH to the msg.sender, when the conditional check passes.
Surprise surprise, most ERC-20 compliant tokens already has that behavior: transferFrom
. The only downside is that the user needs to approve
the contract to spend their ERC-20 beforehand.
Therefore, instead of sending ETH to the miner via a conditional transaction, it'll be using wrapped ether, ETH that is ERC-20 compliant.
Since the (W)ETH used to bribed will be provided via a ECDSA signature, we can now use a randomly generated wallet as msg.sender
as we don't need any ETH in it. This is perfect as eth_signTransaction
can be performed as long as you have the private key.
By using ECDSA signatures and WETH, we can call eth_signTypedData
on metamask, and eth_signTransaction
on a randomly generated wallet. That way users don't have to enter their (uncompromised) private key into the browser.
The process to bribe a miner with ETH on the browser is now:
In solidity land, the check32BytesAndSend
function now looks like so:
And with that, we're able to bribe miners purely through most browser wallet providers.
This turned out to be more challenging than anticipated. Excited to dive deeper into the MEV world!
Example transaction in the wild