{"id":21936100,"url":"https://github.com/learnweb3dao/mev-practical","last_synced_at":"2026-01-07T15:39:12.138Z","repository":{"id":37032088,"uuid":"487037118","full_name":"LearnWeb3DAO/MEV-Practical","owner":"LearnWeb3DAO","description":null,"archived":false,"fork":false,"pushed_at":"2022-09-04T10:59:07.000Z","size":194,"stargazers_count":0,"open_issues_count":3,"forks_count":10,"subscribers_count":0,"default_branch":"main","last_synced_at":"2023-03-04T00:36:35.976Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LearnWeb3DAO.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-04-29T16:18:25.000Z","updated_at":"2022-04-29T19:53:40.000Z","dependencies_parsed_at":"2023-01-17T13:15:56.445Z","dependency_job_id":null,"html_url":"https://github.com/LearnWeb3DAO/MEV-Practical","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FMEV-Practical","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FMEV-Practical/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FMEV-Practical/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FMEV-Practical/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LearnWeb3DAO","download_url":"https://codeload.github.com/LearnWeb3DAO/MEV-Practical/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227017788,"owners_count":17717797,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-29T01:13:12.299Z","updated_at":"2026-01-07T15:39:12.087Z","avatar_url":"https://github.com/LearnWeb3DAO.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MEV Practical\n\nIn MEV Theory, we understood what MEV is, what Flashbots are, and some use cases of Flashbots. In this level we will learn how to mint an NFT using Flashbots. This is going to be a very simple use case designed to teach you how to use Flashbots, not necessarily make a profit. Finding opportunities where you can make profit using MEV is a hard problem and are typically not public information. Every Searcher is trying to do their best, and if they tell you exactly what strategies they're using, they are shooting themselves in the foot.\n\nThis tutorial is just meant to show you how you use Flashbots to send transactions in the first place, the rest is up to you!\n\n## Build\n\nLets build an example on how to usee flashbots\n\n- To setup a Hardhat project, Open up a terminal and execute these commands\n\n  ```bash\n  npm init --yes\n  npm install --save-dev hardhat\n  ```\n  \n- If you are on a Windows machine, please do this extra step and install these libraries as well :)\n\n  ```bash\n  npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers\n  ```\n\n- In the same directory where you installed Hardhat run:\n\n  ```bash\n  npx hardhat\n  ```\n\n  - Select `Create a basic sample project`\n  - Press enter for the already specified `Hardhat Project root`\n  - Press enter for the question if you want to add a `.gitignore`\n  - Press enter for `Do you want to install this sample project's dependencies with npm (@nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers)?`\n\nNow you have a hardhat project ready to go!\n\nLet's install a few more dependencies to help us further\n\n```bash\nnpm install @flashbots/ethers-provider-bundle @openzeppelin/contracts dotenv\n```\n\nLet's start off by creating a FakeNFT Contract. Under your contracts folder create a new file named `FakeNFT.sol` and add the following lines of code to it\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\nimport \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\n\ncontract FakeNFT is ERC721 {\n\n    uint256 tokenId = 1;\n    uint256 constant price = 0.01 ether;\n    constructor() ERC721(\"FAKE\", \"FAKE\") {\n    }\n\n    function mint() public payable {\n        require(msg.value == price, \"Ether sent is incorrect\");\n        _mint(msg.sender, tokenId);\n        tokenId += 1;\n    }\n}\n```\n\nThis is a pretty simple ERC-721 contract that allows minting an NFT for 0.01 ETH.\n\nNow let's replace the code present in `hardhat.config.js` with the following lines of code\n\n```javascript\nrequire(\"@nomiclabs/hardhat-waffle\");\nrequire(\"dotenv\").config({ path: \".env\" });\n\nconst QUICKNODE_RPC_URL = process.env.QUICKNODE_RPC_URL;\n\nconst PRIVATE_KEY = process.env.PRIVATE_KEY;\n\nmodule.exports = {\n  solidity: \"0.8.4\",\n  networks: {\n    goerli: {\n      url: QUICKNODE_RPC_URL,\n      accounts: [PRIVATE_KEY],\n    },\n  },\n};\n\n```\n\nNote that we are using `goerli` here which is an Ethereum testnet, similar to Rinkeby and Ropsten, but the only one supported by Flashbots.\n\nNow its time to set up some environment variables, create a new file `.env` under your root folder, and add the following lines of code to it. \n\n```\nQUICKNODE_RPC_URL=\"QUICKNODE_RPC_URL\"\nPRIVATE_KEY=\"YOUR-PRIVATE-KEY\"\nQUICKNODE_WS_URL=\"QUICKNODE_WS_URL\"\n```\n\nTo get your `QUICKNODE_RPC_URL` and `QUICKNODE_WS_URL` go to [Quicknode](https://www.quicknode.com/?utm_source=learnweb3\u0026utm_campaign=generic\u0026utm_content=sign-up\u0026utm_medium=learnweb3), sign in, and create a new endpoint. Select `Ethereum` and then `Goerli`, and create the endpoint in `Discover` mode to remain on the free tier.\n\n![](https://i.imgur.com/l5H9Whh.png)\n\nNow copy the `HTTP Provider` url and paste it inplace of `QUICKNODE_RPC_URL` and copy `WSS Provider` and paste it in place of `QUICKNODE_WS_URL`.\n\nReplace `YOUR-PRIVATE-KEY` with the private key of an account in which you have Goerli Ether, to get some Goerli ether try out [this faucet](https://goerlifaucet.com/)\n\nNow it's time to write some code that will help us interact with Flashbots.\n\nCreate a new file under `scripts` folder and name it `flashbots.js` and add the following lines of code to it\n\n```javascript\nconst {\n  FlashbotsBundleProvider,\n} = require(\"@flashbots/ethers-provider-bundle\");\nconst { BigNumber } = require(\"ethers\");\nconst { ethers } = require(\"hardhat\");\nrequire(\"dotenv\").config({ path: \".env\" });\n\nasync function main() {\n  // Deploy FakeNFT Contract\n  const fakeNFT = await ethers.getContractFactory(\"FakeNFT\");\n  const FakeNFT = await fakeNFT.deploy();\n  await FakeNFT.deployed();\n\n  console.log(\"Address of Fake NFT Contract:\", FakeNFT.address);\n\n  // Create a Alchemy WebSocket Provider\n  const provider = new ethers.providers.WebSocketProvider(\n    process.env.QUICKNODE_WS_URL,\n    \"goerli\"\n  );\n\n  // Wrap your private key in the ethers Wallet class\n  const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);\n\n  // Create a Flashbots Provider which will forward the request to the relayer\n  // Which will further send it to the flashbot miner\n  const flashbotsProvider = await FlashbotsBundleProvider.create(\n    provider,\n    signer,\n    // URL for the flashbots relayer\n    \"https://relay-goerli.flashbots.net\",\n    \"goerli\"\n  );\n\n  provider.on(\"block\", async (blockNumber) =\u003e {\n    console.log(\"Block Number: \", blockNumber);\n    // Send a bundle of transactions to the flashbot relayer\n    const bundleResponse = await flashbotsProvider.sendBundle(\n      [\n        {\n          transaction: {\n            // ChainId for the Goerli network\n            chainId: 5,\n            // EIP-1559\n            type: 2,\n            // Value of 1 FakeNFT\n            value: ethers.utils.parseEther(\"0.01\"),\n            // Address of the FakeNFT\n            to: FakeNFT.address,\n            // In the data field, we pass the function selctor of the mint function\n            data: FakeNFT.interface.getSighash(\"mint()\"),\n            // Max Gas Fes you are willing to pay\n            maxFeePerGas: BigNumber.from(10).pow(9).mul(3),\n            // Max Priority gas fees you are willing to pay\n            maxPriorityFeePerGas: BigNumber.from(10).pow(9).mul(2),\n          },\n          signer: signer,\n        },\n      ],\n      blockNumber + 1\n    );\n\n    // If an error is present, log it\n    if (\"error\" in bundleResponse) {\n      console.log(bundleResponse.error.message);\n    }\n  });\n}\n\nmain();\n\n```\n\nNow let's try to understand what's happening in these lines of code.\n\nIn the initial lines of code, we deployed the `FakeNFT` contract which we wrote.\n\nAfter that we created an Quicknode WebSocket Provider, a signer and a Flashbots provider. Note the reason why we created a WebSocket provider this time is because we want to create a socket to listen to every new block that comes in `Goerli` network. HTTP Providers, as we had been using previously, work on a request-response model, where a client sends a request to a server, and the server responds back. In the case of WebSockets, however, the client opens a connection with the WebSocket server once, and then the server continuously sends them updates as long as the connection remains open. Therefore the client does not need to send requests again and again.\n\nThe reason to do that is that all miners in `Goerli` network are not flashbot miners. This means for some blocks it might happen that the bundle of transactions you send dont get included. \n\nAs a reason, we listen for each block and send a request in each block so that when the coinbase miner(miner of the current block) is a flashbots miner, our transaction gets included.\n\n\n```javascript\n// Create a Alchemy WebSocket Provider\n  const provider = new ethers.providers.WebSocketProvider(\n    process.env.QUICKNODE_WS_URL,\n    \"goerli\"\n  );\n\n  // Wrap your private key in the ethers Wallet class\n  const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);\n\n  // Create a Flashbots Provider which will forward the request to the relayer\n  // Which will further send it to the flashbot miner\n  const flashbotsProvider = await FlashbotsBundleProvider.create(\n    provider,\n    signer,\n    // URL for the goerli flashbots relayer\n    \"https://relay-goerli.flashbots.net\",\n    \"goerli\"\n  );\n```\n\nAfter initializing the providers and signers, we use our provider to listen for the `block` event. Every time a `block` event is called, we print the block number and send a bundle of transactions to mint the NFT. Note the bundle we are sending may or may not get included in the current block depending on whether the coinbase miner is a flashbot miner or not.\n\nNow to create the transaction object, we specify the `chainId` which is `5` for Goerli, `type` which is `2` because we will use the `Post-London Upgrade` gas model which is `EIP-1559`. To refresh your memory on how this gas model works, check out the `Gas` module in Sophomore.\n\nWe specify `value` which is `0.01` because that's the amount for minting 1 NFT and the `to` address which is the address of `FakeNFT` contract.\n\nNow for `data` we need to specify the function selector which is the first four bytes of the  Keccak-256 (SHA-3) hash of  the name  and the arguments of the function\nThis will determine which function are we trying to call, in our case, it will be the mint function.\n\nThen we specify the `maxFeePerGas` and `maxPriorityFeePerGas` to be `3 GWEI` and `2 GWEI` respectively. Note the values I got here are from looking at the transactions which were mined previously in the network and what `Gas Fees` were they using.\n\nalso,\n`1 GWEI = 10*WEI = 10*10^8 = 10^9`\n\nWe want the transaction to be mined in the next block, so we add 1 to the current blocknumber and send this bundle of transactions.\n\nAfter sending the bundle, we get a `bundleResponse` on which we check if there was an error or not, if yes we log it.\n\nNow note, getting a response doesn't guarantee that our bundle will get included in the next block or not. To check if it will get included in the next block or not you can use `bundleResponse.wait()` but for the sake of this tutorial, we will just wait patiently for a few blocks and observe. \n\n```javascript\n  provider.on(\"block\", async (blockNumber) =\u003e {\n    console.log(\"Block Number: \", blockNumber);\n    // Send a bundle of transactions to the flashbot relayer\n    const bundleResponse = await flashbotsProvider.sendBundle(\n      [\n        {\n          transaction: {\n            // ChainId for the Goerli network\n            chainId: 5,\n            // EIP-1559\n            type: 2,\n            // Value of 1 FakeNFT\n            value: ethers.utils.parseEther(\"0.01\"),\n            // Address of the FakeNFT\n            to: FakeNFT.address,\n            // In the data field, we pass the function selctor of the mint function\n            data: FakeNFT.interface.getSighash(\"mint()\"),\n            // Max Gas Fees you are willing to pay\n            maxFeePerGas: BigNumber.from(10).pow(9).mul(3),\n            // Max Priority gas fees you are willing to pay\n            maxPriorityFeePerGas: BigNumber.from(10).pow(9).mul(2),\n          },\n          signer: signer,\n        },\n      ],\n      blockNumber + 1\n    );\n\n    // If an error is present, log it\n    if (\"error\" in bundleResponse) {\n      console.log(bundleResponse.error.message);\n    }\n  });\n```\n\nNow to run this code, in your terminal pointing to the root directory execute the following command:\n\n```bash\nnpx hardhat run scripts/flashbots.js --network goerli\n```\n\nAfter an address is printed on your terminal, go to [Goerli Etherscan](https://goerli.etherscan.io/) and keep refreshing the page till you see `Mint` transaction appear(Note it takes some time for it to appear cause the flashbot miner has to be the coinbase miner for our bundle to be included in the block)\n\n![](https://i.imgur.com/sVwacVp.png)\n\n\n![](https://i.imgur.com/Aawg5gK.png)\n\n\n\nBoom 🤯 We now learned how to use flashbots to mint a NFT but you can do so much more 👀\n\n\nGG 🥳\n\n## Readings\n- [Flashbots Docs](https://docs.flashbots.net/)\n- [Arbitrage bot using Flashbots](https://github.com/flashbots/simple-arbitrage)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearnweb3dao%2Fmev-practical","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flearnweb3dao%2Fmev-practical","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearnweb3dao%2Fmev-practical/lists"}