The MEV (miner extractable value) is a problem that exist now in PoW (proof of work) but will continue in PoS (proof of staking). The MEV gives an on-chain advantage to miners over normal users. We don’t want this, because is unfair to normal users. A couple of ideas are currently on the work for both short and long term: solutions like flashbots or even at the ethereum protocol level. Despite this, it is a problem that persists and we should attack it from different fronts. In this video we show how from Solidity we can suppress one of the most commons attacks, we are going to explore how we can prevent the sandwich attack from an ERC20 token.
In eigenphi you can find examples from sandwich attacks in mainnet.
Before we start
For this tutorial you will need Metamask or another wallet compatible with Goerli funds that you can get from a faucet.
ERC20 token with anti-sandwich mechanism
Make sure you're using a Router address that exists on the blockchain you're using.
- Uniswap Router:
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
- Quickswap Router:
0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff
- Pancakeswap Router:
0x10ED43C718714eb63d5aA57B78B54704E256024E
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
interface IUniswapV2Factory {
function createPair(address tokenA, address tokenB) external returns (address pair);
}
interface IUniswapV2Router {
function factory() external pure returns (address);
function WETH() external pure returns (address);
}
contract MyToken is ERC20 {
mapping(address => uint256) public lastTxByAddress;
uint public blockCooldownAmount = 1;
address pair;
constructor() ERC20("My Token", "MTKN") {
_mint(msg.sender, 1_000_000 ether);
IUniswapV2Router uniswapRouter = IUniswapV2Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
pair = IUniswapV2Factory(uniswapRouter.factory()).createPair(address(this), uniswapRouter.WETH());
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal override {
amount;
if(from == pair)
{
ensureMaxTxFrequency(to);
lastTxByAddress[to] = block.number;
} else if(to == pair)
{
ensureMaxTxFrequency(from);
lastTxByAddress[from] = block.number;
}else
{
ensureMaxTxFrequency(from);
lastTxByAddress[from] = block.number;
ensureMaxTxFrequency(to);
lastTxByAddress[to] = block.number;
}
}
function ensureMaxTxFrequency(address addr) internal virtual {
bool isAllowed = lastTxByAddress[addr] == 0 ||
((lastTxByAddress[addr] + blockCooldownAmount) < (block.number + 1));
require(isAllowed, "Max tx frequency exceeded!");
}
}
Thanks for watching this video!
Follows us on dev.to and in Youtube for everything related to Blockchain development.