In the article, we understood how we can implement a swap between tokens using Uniswap. previous In this article, we will be moving one level ahead by exploring contract and run tests on it! Flash Swap by coding a flash swap We highly recommend you go through the previous article before. What is a Flash Loan? Unlike Traditional Loans, in Flash Loans, funds are borrowed and returned in . This is a MUST. one transaction In traders (usually through bots) keep looking out for arbitrage opportunities to gain benefits by trading between platforms supplying different prices for the same asset. Defi, This is where the Flash Loans come into the picture (albeit usually). With the help of , traders can borrow a large sum of money to execute an arbitrage trade. Flash Loan How does Flash Loan Arbitrage work? Consider a situation, where Ethan buys a book for $10 from the Bookstore and then sells that book to Jennifer for $20. In this situation, Ethan buys a book using his money and then straight away doubles it by selling it to Jennifer. This is exactly how trading arbitrage works. But unlike Ethan, who used his own money for buying the book from the bookstore, where we could simply use Flash Loans to borrow $10 and then execute a trade, similar to selling the book and then repaying the loan (yes, all in 1 single transaction). Let’s dive into coding our own Flash swap contract and test it out!! 😎 1. Create a Project and Install Dependencies Use the following commands on your CLI to initialize your project. mkdir Flash_swap && cd Flash_swap npm init -y Now, install the required dependencies we’ll use for the project. Use the command provided below and run it on your CLI to install them. npm install --save hardhat @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle ethers @uniswap/v2-core dotenv hardhat chai 2. Initialize your Hardhat Project Run the command on your CLI and create an empty hardhat config file, since we’re going to build everything from scratch. npx hardhat Customize your hardhat config: Because we are going to fork the mainnet to test the Flashswap. Therefore, your hardhat config should look something similar to this: Note: Replace the component of the URL with your personal API key. <key> Alchemy Also, if you’re new to mainnet forking, read our based on it and then follow along with this article. article 3. Write a smart contract for Flash swap Create directories for contracts and tests for better code organisation. Use the following code in your CLI. mkdir contracts && mkdir tests In order to write the flash swap contract, create a file inside the contracts directory and name it flashswap.sol Writing the smart contract: First, import the interfaces required and create a contract named as . flashSwap We will import to use its functions. You can get the interface using this . Uniswap’s interface link We have also imported interface. Uniswap will call this function when we execute the flash swap. Technically this is the callback function that Uniswap will call. IUniswapV2Callee It should look similar to this: Next, we create our contract which inherits from . Solidity supports inheritance between smart contracts, where multiple contracts can be inherited into a single contract. The contract from which other contracts inherit features is known as a base contract, while the contract which inherits the features is called a derived contract. flashSwap IUniswapV2Callee This contract will have 2 functions: : This is the function we will use to call to trigger the flash swap transaction. testFlashSwap() This is the function that the Uniswap uniswapV2Call(): Writing the function: testFlashSwap This function will take 2 parameters (A) the token that is to be borrowed from Uniswap {the address| and (B) the amount we want to borrow. First thing that we’ll do is, check that the pair contract for _ and exists. We can do that by calling the function on tokenBorrow WETH getPair UniswapV2Factory NOTE: In Uniswap v2, all token pairs are with WETH as of of the coin and hence, to check if a particular token is available on Uniswap, we simply check its availability with WETH. More on what is WETH —> https://weth.io/ address pair = IUniswapV2Factory(UniswapV2Factory).getPair(_tokenBorrow, WETH); require(pair != address(0), "!pair"); {Please note, we have defined WETH as a variable in our contract already. Please refer to our entire contract at the end} Inside the UniswapV2Pair, we have 2 tokens, and . token0, token1 We now will check if is equal to or not. _tokenBorrow _token0 If the values are equal, then will be the _amount argument that we pass to the function, otherwise, it would be equal to zero. amount0Out Similarly, we will check the same for _ token1. address token0 = IUniswapV2Pair(pair).token0(); address token1 = IUniswapV2Pair(pair).token1(); uint256 amount0Out = _tokenBorrow == token0 ? _amount : 0; uint256 amount1Out = _tokenBorrow == token1 ? _amount : 0; As a result, either you’ll have equal to _amount and equal to 0 or vice-versa. amount0Out amount1Out Then we pass these amount values in Uniswap’s swap function. bytes memory data = abi.encode(_tokenBorrow, _amount); IUniswapV2Pair(pair).swap(amount0Out, amount1Out, address(this), data); As you’ll notice, this is the exact same function that we call to perform a simple swap on Uniswap. Refer to our previous article over here. The only difference is the last input. If it is empty, then Uniswap will try a simple swap execution. If it is not empty, that is it has any data, then it would trigger a flashswap. To pass in the input, we will encode the and as bytes and then pass it to the swap function. tokenBorrow amount The function should look similar to: testFlashSwap Let’s write the 🤩 uniswapV2Call: Right now, anyone can access this function, therefore the first thing that we’re going to do is check that this function can only be called by the pair contract. And then, we’ll also check that the sender is equal to the pair contract. For that, use the following code: Basic housekeeping: address token0 = IUniswapV2Pair(msg.sender).token0(); address token1 = IUniswapV2Pair(msg.sender).token1(); // call uniswapv2factory to getpair address pair = IUniswapV2Factory(UniswapV2Factory).getPair(token0, token1); require(msg.sender == pair, "!pair"); Then, we will decode the data that is passed to us by Uniswap, this is not a mandatory step, but encouraged to do so. Decoding data: (address tokenBorrow, uint amount) = abi.decode(_data, (address, uint)); Uniswap charges 0.3% for any form of swap. Using the following code, we are computing the fee that our contract will have to bear for undertaking the flashswap. Computing fee: uint fee = ((amount * 3) / 997) + 1; uint amountToRepay = amount + fee; Lastly, we have to pay Uniswap the borrowed token including the fee. It can be achieved by using the following code. Repayment: IERC20(tokenBorrow).transfer(pair, amountToRepay); That completes our function, which would look similar to the following image: uniswapV2Call Now, our flashSwap contract is complete, and should be similar to this: 4. Let’s write some Test Script for our Contract First of all, we will import the necessary libraries, ERC20 abi, and will also create a basic structure of our test script. Use the following reference to get a better understanding: Now, we will define the address of the contract which we’re going to impersonate and the USDC address. We will also define the amount that we’re going to borrow. Use the following: const USDCHolder = "0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE"; const USDCAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; const borrowAmount = 1000000000; // since USDC is 6 decimals, this corresponds to 1000 USDC Coins Next, we will begin with the block use the contract we created and deploy it. before before(async () => { const TestFlashSwapFactory = await ethers.getContractFactory("flashSwap"); TestFlashSwapContract = await TestFlashSwapFactory.deploy(); await TestFlashSwapContract.deployed(); }); Finally, we write our test script to check our Flash swap execution. A: First, we will impersonate the account which we want to use: await hre.network.provider.request({ method: "hardhat_impersonateAccount", params: [USDCHolder], }); const impersonateSigner = await ethers.getSigner(USDCHolder); B: Then, we will define the USDC Contract const USDCContract = new ethers.Contract(USDCAddress, ERC20ABI, impersonateSigner) B: We already knew that Uniswap charges a fee to implement a Flash Swap. Therefore, we will calculate the value of the fee. const fee = Math.round(((borrowAmount * 3) / 997)) + 1; C: We need to understand that, since our contract just got deployed in the block, it does not have any Ether or any USDC. Then, once the flash loan is implemented, the contract has to return the borrowed amount plus the fee. before Therefore, we have to transfer the amount of the fee from the impersonated account to our contract, in order to complete the trade. Therefore, we will use the following command to transfer the fee to the contract: await USDCContract.connect(impersonateSigner).transfer(TestFlashSwapContract.address, fee) D: Then, we will call the function which we have defined in our contract. testFlashSwap await TestFlashSwapContract.testFlashSwap(USDCContract.address, borrowAmount) E: It’s time to check whether the Flashswap executed was correct or not. For that, we will check whether the balance of our contract, after the flashswap and the payment of the exact fee that we computed is 0, as it should be, or not. const TestFlashSwapContractBalance = await USDCContract.balanceOf(TestFlashSwapContract.address) expect(TestFlashSwapContractBalance.eq(BigNumber.from("0"))).to.be.true; Your final test script should look like this: Finally, run your test in your CLI using the command npx hardhat test tests/flashswaptest The result should look like this: 🥳🥳🥳 flashswap passed with flying colours 🤣 Voila Our Summary Flash swap allows you to borrow any ERC20 token on Uniswap and execute any code logic, as long as you repay the same token or any other token of the same value plus the fee . in the same transaction Hope you like our efforts and try and run this by yourselves. :) Of course! If you get any error, you can ask us by tagging on Twitter. @uv_labs Again, all the code that we just ran through is over here 👉 . Do give us a star, and clap if you liked our work. Github repository Authors (open to feedback): 👇 and Amateur-Dev Pari Tomar