How to Front-run in Ethereum

In this post, we will write a Solidity code that front-runs our trades.

Naz
8 min readFeb 17, 2020
graffitti of a man running
Photo by Renee Fisher on Unsplash

Note

I am intentionally not including the backend code and omitting some key points.

Backstory

I have been prolifically reading everything I can about the current crypto scene for the past 2 months. The front running idea seemed very elusive, so I have set out on a course to explore it. My approach to digging deeper comprised of two steps:

[1] learn Solidity as quick as a flash

[2] explore the idea more and come up with a plan to write my own, most rudimentary front running prototype

After I finished [1], I have set out to come up with a plan to tackle the problem at hand. At this point, I have learned what I need to front-run someone in broad strokes. But let’s start from the beginning. What does “front running” mean, in the first place? It means that you “run in front” of someone, trying to accomplish something before them. When talking about the financial industry, it may mean something like the following: you are aware of someone’s BIG order coming in, this will inevitably impact the price. Before this order goes through, you position your order in “front” of it, as well as placing a counter trade too, once the BIG order goes through, PROFIT. You are essentially ripping off whoever is making the BIG trade

If you want an explicit example, say there is an order that will move the market 5% that you know will happen at some point in the immediate future. For example, someone wants to buy a large amount of Netflix shares. You buy shares before them, say for $380 a pop, and instantly place a limit sell order for $400. The big order goes through, and you instantly sell to the big order for $400, thus making a profit of $20 a share. This means, roughly, that you can make a risk-free 5% right off the bat. There is book “Flash Boys” by Michael Lewis, that talks about this, and it explains the lengths that people went or go to, to rip off other people, classic. In crypto, the problem is exaggerated because all the transactions are public before they are even “acted on”

Let’s front-run ourselves, shall we?

What are decentralised exchanges? These are the crypto exchanges where the users trade either with each other directly without any intermediary, or trade with smart contracts like in the case of Uniswap. Arguably, no one is using such naive strategies as I am about to demonstrate, anymore. With no further ado, let’s front-run ourselves

Let’s break down the problem into smaller pieces

picture of the stack of the bot. Node.js client can be connected to Infura or our own Ethereum Node
communication channels with Ethereum

First, we need the means to communicate with the Ethereum blockchain. This can be achieved by either using a service like Infura or running our Ethereum node. A node in Ethereum is responsible for validating the transactions on the blockchain, as well as perpetuating them by “mining” the blocks, basically “storing” them in this permanent ledger that blockchain is. Since we are building the dumbest front runner ever, we go with the service

meme about no time for running own Ethereum node
Fuhgeddaboudit

Let’s decide now where we want to front-run ourselves. There are many DEXes, and their flavours vary, these are Uniswap, Bancor, Bamboo Relay, to name the few. Uniswap is interesting because it is an automated market maker (AMM), this means that it’s mechanism is deterministic, plus it works through smart contracts, which means, that we can directly communicate with it via our smart contract

Alright alright alright, what is next? Let’s now take a moment to think about how we will spot these trades? Mempool, my friends. The pool where pending transactions go to chill, before being picked up by a mining node and later included in a mined block. As soon as a transaction hits the pool, we see it, BEFORE it is even “included in the blockchain”

Next, we will decide what stack to use. Since the course, I have done, taught me the Node.js (don’t confuse this with Ethereum nodes) ways, let’s stick to it. Python could have been used as well. Alright, so the backend will run in Node.js and will communicate with our smart contract to exchange the tokens and ether. Now, in this particular case, a smart contract is not necessarily required. I wanted to pluck it in though, to learn more about Solidity

To summarise, we have our backend code looking for mempool transactions, waiting to intercept our trades, this is done by listening on the Infura WebSocket channel (very efficient…), and querying the transactions by hash, to get information like value transferred, address to which it is transferred, gas, gas price, and so on. Once the transaction meets our filter criteria, we call our deployed smart contract to execute the front running trades

Solidity code

compiler version, storage variables and event definitions

First, we define the version of the Solidity compiler at the top, all versions 0.7.x and higher won’t work (and lower than 0.6.1). I define two storage variables: manager and EOA. manager is the creator of the contract, and EOA is the address where the tokens and Ethereum from this contract can be transferred to, in case we want to remove the contract from the blockchain. Note, that selfdestruct only sends ether value on destruction. Tokens aren’t sent along. We then have three events, they are what they sound like. In Ethereum you can emit the events, which will then appear in the log section of the mined transaction

ability to send payments to contract, modifier and constructor

A receive (or a fallback) function is required to receive Ether into the smart contract. If no function is present, then the contract cannot receive transfers. This function is executed when a call with empty calldata is received in the contract. We then have a restricted modifier that enables us to restrict access to certain functions to only be callable by the manager, the creator of the contract. _ in the function means: “take the code of the function with restricted modifier and paste it down here”. If you are familiar with Python, then this modifier would act similarly to a decorator. Decorator, too, does something before the decorated function is called. In this case, it is checking that the manager has made the call to a function. If not, do nothing. The constructor simply sets the creator of the contract to be the manager

ETH → token & token → ETH exchange functions

I then define two wrapper functions around Uniswap exchange functions that will exchange the eth to tokens and tokens to eth for us. To keep the whole implementation simple, we shall stick to a single ERC20 token. From a quick look, REP looks good enough, volume-wise

selfdestruct, approve and drainToken functions

drainToken can be used to transfer ERC20 tokens from the contract to EOA tokens.kill shall send the ether value of the contract to EOA and delete itself from the Ethereum blockchain

ERC20 and Uniswap’s interfaces

The abstract contracts act as “blueprints” / interfaces for the actual functionality of ERC20 and Uniswap exchange contracts

The Node.js backend code as explained before listens for the mempool transactions and intercepts when required. Infura is used for demonstration purposes. It isn’t very good in this case since it adds that extra layer of communication between you and the Ethereum nodes, thus the delay. This means that it can take some time before the WebSocket sends us the intercepted transaction. Here is a front ran transaction:

  1. front-run trade that was sent off after our transaction (step 2 below) was spotted in the mempool: https://etherscan.io/tx/0xed78e284d9b203b1f34301a26a163694fc2be40c7dcb999c8bc5de05db0d2f3a
  2. victim (us): https://etherscan.io/tx/0xd82bef201a08a32441af5a5f83ce12382c53cd87f1cabcd2747d9dafa6e06d9e
  3. closing the front-run trade: https://etherscan.io/tx/0x3d338abb0380c5fa0c7d316193eb9f98448eeeec685f4de0af7fc054fe9dfff3

From the above, 1 & 2 were mined in the same block 9496771, but the order of the front run transaction is 37 while that of the victim is 42, so the front run bot would get a better price before the large order

Conclusion and gotchas

Front running is detrimental and we should solve this problem. Front running, among other things, is deterring mass adoption of cryptocurrencies. Thankfully, there are ways to be more resistant to it, like commit/reveal setups, faking trades to mislead front-runners, and so on. Uniswap v1 battles front running by allowing you to set your own slippage, if someone tried to front-run you with a big order, it will move the price and so when it is your trade’s turn to get settled, it can be reverted if you have set your slippage low enough.

Some gotchas:

  1. Initially, I made the exchange quote call in the smart contract. Price quote gives you the exchange rate for ETH/ERC20, ERC20/ETH or ERC20/ERC20. Remember, all the code that you execute inside of the smart contract costs real money. Do not write unnecessary code there. This logic could be easily delegated to the backend code. It had cost me $20 to build this bot, due to the failed transactions. As it turned out there is a bug in Ethereum, whereby if the transaction fails for any reason, you pay all of the gas! This bug is assert. On failure, assert gorges on ALL of your gas. Happy days for miners. So either use revert or require
  2. Unrealistic exchange deadline. This was failing all of my transactions. The lifecycle of a transaction is complex. It goes from the client to our node, gets broadcasted in the P2P network and at some point is picked up by miners. Given this, there is no guarantee that a transaction with higher gas price will be included before the intercepted one
  3. Need to approve the Uniswap smart contract for REP ERC20 transactions. A side note about ERC20. This protocol was developed in 2015 and has some security issues, as well as this approve headache. ERC223 is a much better protocol since it [1] eliminates the problem of lost tokens, [2] allows developers to handle incoming transactions and reject non-supported tokens, [3] is cheaper to work with than ERC20
  4. If trying to exchange with Uniswap, remember that the event’s amount like defined above is not the contract’s token balance, and thus ERC20’s token balanceOf has to be called
  5. Slippage has to be set high if working with small amounts on Uniswap

Useful information

  1. To see the order of a transaction in the block, go the block (etherscan), find a transaction, click “Click to see More”, and then navigate to the “Position” label. The second number is the position of the transaction in the block
  2. Miners order the transactions in the same block that originate from the same address by the nonce. This prevents replay attacks

Thanks for the tips M & everyone who helped me along the way

P.S. if there are any suggestions to improve this post, let me know in the comments. Also if there are any errors or omissions, please do let me know. Thank you for reading this, 🤓 squad

P.P.S. what have you come across in crypto recently that grabbed your attention? Let me know in the comments

--

--

Naz

I crave knowledge. Mathematical knowledge and then computer science.