# Toads MEV 101 Challenge **Published by:** [McToady](https://paragraph.com/@mctoady/) **Published on:** 2023-07-08 **URL:** https://paragraph.com/@mctoady/toads-mev-101-challenge ## Content First thing’s first, what even is MEV?Essentially MEV (maximal extractable value) is any profitable transaction that can be included in a block. This covers a lot of ground, such as frontrunning, acting as a ‘keeper’ calling some incentivized function, sandwich trading and what we’re going to talk about today, dex arbitrage. For a more detailed overview go here (but finish reading this first of course).Our Personal MEV SandpitMEV bots and arbitrage, these are spooky topics we assume are above our pay grade and quickly run away from. And for those of us that don’t run, there’s not a whole load of introductory resources for people to get to grips with it. On top of this, if you attempt to play with MEV on a live chain you’re likely end up eaten by the sharks. So all this considered, it’d be great if you could simulate a stripped back, simpler approximation of a live chain, where we can play around, safe in the knowledge none of the MEV sharks are going to steal our lunch. So that’s what I set out to do. Using Scaffold Eth 2 as a foundation, my plan was to make a series of challenges to help people learn about MEV, ethers scripting and defi, with the benefit of being able to run each of the challenges and their environments locally. GitHub link if you want to play along: https://github.com/McCoady/scaffold-eth-2/tree/mev-arcade-v1Decentralised Exchange (Dex) ArbitrageWhen we use decentralised exchanges we don’t typically shop around to make sure we’re getting the best price for our assets. We can be relatively confident the price on Uniswap V2 will be basically the same as the price on Uniswap V3 or Sushiswap. We have dex arbitrage bots to thank for this, their job is to find any price differences across dexes and even them out, by buying from the cheaper exchange and selling to the more expensive one. But how does this work and what does it look like? We’re starting out relatively simple. Challenge one has two basic decentralised exchange contracts that only allow trading of a generic ERC20 token (Balloons) for Ether. Then we have a script which makes random transactions on these two dexes to imitate actual user transactions. However unlike there would be on a live chain, there’s nothing in place to make sure the two dex (dexes? dexi?) have similar prices. We need to add an MEV bot to our environment to make sure our (fake) users are getting a fair price on their trades, whilst of course also making a little profit ourselves as a pat on the back for our good deed. So how do we approach tackling this problem? First we’re going to need a script that runs in the background monitoring the prices across the two dexes. We want to know which dex has the cheaper tokens so we can buy from that one and try to sell for a profit to the more expensive one.// calculate dex reserves arguments const dexOneEthBalance = await provider.getBalance(dexOneAddress); const dexOneTokenBalance = await balloonsContract.balanceOf(dexOneAddress); const dexTwoEthBalance = await provider.getBalance(dexTwoAddress); const dexTwoTokenBalance = await balloonsContract.balanceOf(dexTwoAddress); // call price on the two dex contracts const dexOneEthPrice = await dexOneContract.price( ethers.utils.parseEther("1"), dexOneEthBalance, dexOneTokenBalance, ); const dexTwoEthPrice = await dexTwoContract.price( ethers.utils.parseEther("1"), dexTwoEthBalance, dexTwoTokenBalance, ); Once we know which is cheaper we need to attempt our transactions. For this we’re going to create a helper smart contract. We’ll use the contract to bundle both transactions together, we want our buy AND sell to either succeed or fail, rather than risk only one of the other going through. The contract will be relatively simple though, we just want to buy token from dex A, sell to dex B and revert if the ether we have at the end of the transaction is less than what was sent in msg.value from our script.function tryArb(address dexOne, address dexTwo) external payable { uint256 startBalance = address(this).balance; uint256 dexOneTokens = IERC20(tokenAddr).balanceOf(dexOne); uint256 tokensBack = IDEX(dexOne).price(msg.value, dexOne.balance, dexOneTokens); // txOne IDEX(dexOne).ethToToken{value: msg.value}(tokensBack); uint256 tokenBalance = IERC20(tokenAddr).balanceOf(address(this)); // approve transfer of ERC20 tokens IERC20(tokenAddr).approve(dexTwo, tokenBalance); // txTwo IDEX(dexTwo).tokenToEth(tokenBalance, 0); // if after the trades we have less eth than we started with, revert if (address(this).balance < startBalance) revert ArbFailed(); } Now we’re almost there. The only problem we’ll have now is that when our transaction attempt fails (every block when there’s no arbitrage to make), currently our script will crash and return the tx Error message, so to avoid this we simply want to wrap our tx attempts into a try catch block and then we should be good to go.if (dexOneEthPrice > dexTwoEthPrice) { try { await arbBotContract.tryArb(dexTwoAddress, dexOneAddress, { value: ethers.utils.parseEther(txSize), maxPriorityFeePerGas: "2", }); const ethToBot = ethers.utils.formatEther(await arbBotContract.totalEthIn()); const ethInBot = ethers.utils.formatEther(await provider.getBalance(arbBotAddress)); console.log(`Eth sent to bot: ${ethToBot}, Eth current in bot: ${ethInBot}`); console.log("---"); } catch { console.log("No arb this block"); console.log("---"); } } else { try { await arbBotContract.tryArb(dexOneAddress, dexTwoAddress, { value: ethers.utils.parseEther(txSize), maxPriorityFeePerGas: "2", }); } catch { console.log("No arb this block"); console.log("---"); } } } Now we should be able to run our script and arbitrage transactions should start going through if prices on the two exchanges get too far apart. You can leave the scripts running in your terminal for a few minutes and watch as your (virtual) fortunes roll in. We’re now on the path to being shady MEV demons, congrats.Potential ImprovementsThis was a good start, but there are still a lot of ways we could improve upon it. First of all, this version of the script doesn’t judge how large the arbitrage gap is. If the price gap is very large, we should make larger trades to wipe out the difference (and make a larger profit). Currently if there’s a big price gap the bot will slowly fix it, one smaller transaction at a time, over a number of blocks. This is fine from the safety of our sandpit, but on a live network rival bots wont be kind enough to wait for us. Another relatively simple improvement would be with the ArbBot solidity contract. The contract works okay as it is, but there’s a lot of optimizations to save on gas. If we were attempting this on a live network we’d have to pay a hefty gas fee to make sure our arbitrage trade was the one that gets put into the block rather than a rival bot’s, so it’s important our contract & it’s functions are as efficient as possible or gas fees will eat away any potential profit. There’s tons more things that could be done and I hope some of you go out and create a version of the bot that absolutely crushes souls in the most efficient way possible!Until next timeMy plan for this project is to keep ramping up the realism so our friendly sandpit can start to look more and more like a real network, be that with more complex defi protocols, the existence of rival bots, head to heads with other peoples scripts, and lots more fun stuff. We’ve taken our first baby steps today, let’s keep it up. ## Publication Information - [McToady](https://paragraph.com/@mctoady/): Publication homepage - [All Posts](https://paragraph.com/@mctoady/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@mctoady): Subscribe to updates - [Twitter](https://twitter.com/TrainTestToad): Follow on Twitter