Dev
Analyzing a Uniswap Front-Running Bot

I’m Jasper, a security researcher at SOOHO.IO. This article examines a bot that front-runs Uniswap transactions. It documents the analysis and the logic behind the bot’s trading decisions.
What is front-running?
Front-running occurs when someone uses advance knowledge of a pending trade to transact ahead of it for their own benefit. In traditional markets, this can involve a broker or exchange insider with access to order information.
How does this happen on a decentralized exchange?
Uniswap has no broker carrying out trades on users’ behalf; smart contracts perform that role. But Ethereum transactions are not executed immediately. They wait in the mempool until they are included in a block, making pending orders visible before execution.
The bot exploits that interval. It monitors pending Uniswap trades, submits its own purchase ahead of a target trade, then sells after the target has moved the price. This sequence is commonly described as a sandwich attack.
The following historical example illustrates the sequence before we examine the contract code.
Our victim, 0xd7680084e582ac04fb0243a489d2f709dbaeef06 (shortened to 0xd768), wants to purchase FalconSwap Token (shortened to FSW) worth 10 WETH through Uniswap. Therefore, they place a purchase transaction into the waiting pool.
The Front-running bot, which has been constantly watching the waiting pool, submits an attack transaction purchasing FSW worth 30 WETH, paying higher gas fees to ensure that it gets included in the block faster than the above purchase transaction. In doing so, a smart contract is used to create the attack transaction to eliminate the risk of the targeted transaction being processed first.
Simultaneously, the Front-running bot creates a liquidation transaction that pays the same amount of gas fees as the targeted transaction, because it needs to ensure that the liquidation transaction is executed after the targeted transaction has been processed to gain a profit. If the liquidation transaction executes before the attack transaction, it will end up costing just the transaction fee for buying and then immediately selling back. The liquidation transaction realizes profit by converting all the purchased FSW back into WETH.
Ah, there are various strategies for creating a Front-running bot. The Front-running bot I analyzed this time is one of the bots that utilizes the Mempool Hunter strategy, which has effectively carried out attacks for quite some time and is still active.
Let's take a closer look at the code!
The bot has two parts: a server-side process that monitors the mempool and submits instructions, and a smart contract that executes them. The server code is not public, so this analysis focuses on the contract deployed on Ethereum.
The contract’s source code was not available on Etherscan. To make the bytecode easier to inspect, I used an online Solidity decompiler. The analysis below follows the decompiled output alongside the example transaction.
For clarity, I call the transaction before the target the opening transaction, and the transaction that sells the acquired tokens afterward the closing transaction.
Opening transaction
First, here is the data contained in the Opening transaction.
The first eight hexadecimal characters of the call data form the four-byte function selector. Here, selector 0x0000000d maps to func_01EC. Following that function reveals how the remaining parameters are interpreted.
When we go to func_01EC, there is a check to see if msg.sender is 0xdd07249e403979bd79848c27aa5454c7e66bdee7 or 0xe73c1e4d7992a4a4f19f31531ae7b5dc352b74b0. This shows that the two above addresses are the owners of the contract we are analyzing.
if ((arg0 >> 0xdc) & 0xffff >= block.number % 0x2710) { goto label_0C8C; }
From the above code, we can see that the value 0x00001b57 after the method signature indicates the last four digits of the block number where the targeted transaction will enter. If the attack transaction enters a different block than the target transaction, everything will be in vain, hence the inclusion of this code.
The next section XORs part of the input data to derive the Uniswap pair address. The reason for computing it this way, rather than supplying the address directly, is not clear from the contract alone.
The derived pair address is used in a staticcall with selector 0x0902f1ac, corresponding to getReserves().
The WETH reserve obtained in this way is compared with the parameter value provided by the server 00000000000000310c19b9fe491977c0. When expressed in decimal, this is 904.7623921164402, which is exactly the value of 10 WETH plus the amount the targeted transaction intends to purchase. The code sets it so that the WETH reserve must be less than this value to proceed. This is to prevent losses due to interference from other transactions.
After confirming that buying is indeed possible, it calls func_26B5, passing in the amount of WETH to be used for the purchase, the current WETH reserve, and the current FSW reserve to compute the price.
The function applies the 0.3% fee and the pool’s reserve values to calculate the amount of FSW returned for a given WETH input.
Using the same function, it also calculates the amount of FSW that will be purchased from the targeted transaction after the attack.
Now that we know the amount of WETH that the target will use for purchasing, the amount of WETH that will be used for the attack, and the corresponding amount of FSW to be purchased from each, it performs another call to func_26B5 to calculate the profit that will be gained in the Closing process after the targeted transaction is executed.
Here, the attack transaction purchases 111879.58729439399 FSW with 30 WETH, and the targeted transaction purchases 35698.50508403116765319 FSW with 10 WETH. Therefore, if sold after, it would be:
FSW Reserve = 3458775.179288110579731962 (Original Reserve) - 111879.58729439399 (Bought by Bot) - 35698.50508403116765319 (Bought by Victim) = 3311197.0869096853
WETH Reserve = 894.762392116440168384 (Original Reserve) + 30 (Sold by Bot) + 10 (Sold by Victim) = 934.7623921164
In func_26B5,
var2 = 111879.58729439399 * 934.7623921164 * 997
var3 = 111879.58729439399 * 997 + 3311197.0869096853 * 1000
var2/var3 = 30.46303739508232
This means the bot will be able to profit by approximately 0.46 WETH with 30.46 WETH; thus, since this exceeds the combined value of the original investment of 30 WETH and the attack cost of 0x0000b973 Wei, the attack proceeds. If the gas fee cannot be covered, then the attack won't occur.
Once the calculations regarding the profit from the attack are complete, it executes the attack in func_26DE. This function performs a straightforward job: sending WETH to Uniswap and proceeding with the Swap. However, it allows for options to direct the outcome of the Swap to another address, though it is unclear what this is used for.
One interesting aspect to observe in the Opening transaction is that while executing the attack, if the potential profit is low or if it fails due to rising gas fees, it calls func_0CB2 to minimize losses. In func_0CB2, the contract created with CREATE2 is called, and examining the called contract reveals that it simply calls SELFDESTRUCT. This means that through repeatedly invoking the SELFDESTRUCT contract, it aims to recover as much gas cost lost from a failed attack as possible. For why SELFDESTRUCT returns gas fees, please refer to this link.
Closing transaction
The Closing transaction, on the other hand, is processed in func_0796 with the method signature 0xbea175cb. Instead of cautiously calculating something like in the Opening, it simply checks if the targeted transaction has been processed and if WETH has been paid, and then proceeds directly to func_29EE for the Swap. During this process, if it fails or exceeds the gas fee, it employs func_0CB2 to call SELFDESTRUCT just like in the Opening transaction to minimize gas fees.
Conclusion
This analysis covers the main decision logic of a Uniswap bot using the Mempool Hunter strategy. It does not attempt to document every management function or closing option. The aim was to understand how server-supplied inputs are evaluated before the contract proceeds.
One question remains unresolved: how does the server choose the trade size? In this example, the bot used 30 WETH ahead of a 10 WETH target trade. The budget-selection logic sits outside the contract examined here.
Despite the bot’s long operating history, its contract structure was simpler than I expected. A future proof of concept could help examine the interaction between the server and contract in more detail.
For further information or a code review, contact SOOHO.IO.
👉 Contact Us
SOOHO.IO Official Channels
Website: https://www.sooho.io/
X (Twitter): https://twitter.com/soohoio
LinkedIn: https://www.linkedin.com/company/sooho/



