Build your own Decentralized Exchange! Use the xy=k AMM curve on Ethereum using Solidity, ethers.js, Next.js, and Web3Modal

Now its time so that you can launch a DeFi Trade in your Crypto Dev tokens




Necessities

  • Construct an exhange with just one asset pair (Eth <> Crypto Dev)
  • Your Decentralized Trade ought to take a charges of 1% on swaps
  • When consumer provides liquidity, they need to be given Crypto Dev LP tokens (Liquidity Supplier tokens)
  • CD LP tokens must be given propotional to the Ether consumer is keen so as to add to the liquidity

Lets begin constructing 🚀




Conditions




Good Contract

To construct the good contract we might be utilizing Hardhat.
Hardhat is an Ethereum improvement surroundings and framework designed for full stack improvement in Solidity. In easy phrases you possibly can write your good contract, deploy them, run checks, and debug your code.

  • To setup a Hardhat undertaking, Open up a terminal and execute these instructions
  mkdir hardhat-tutorial
  cd hardhat-tutorial
  npm init --yes
  npm set up --save-dev hardhat
Enter fullscreen mode

Exit fullscreen mode

  • In the identical listing the place you put in Hardhat run:
  npx hardhat
Enter fullscreen mode

Exit fullscreen mode

  npm set up --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
Enter fullscreen mode

Exit fullscreen mode

  npm set up @openzeppelin/contracts
Enter fullscreen mode

Exit fullscreen mode

  • Create a brand new file contained in the contracts listing referred to as Trade.sol. On this tutorial we might cowl every a part of the contract seperately

    • First lets begin by importing ERC20.sol
    • We imported ERC20.sol as a result of our Trade must mint and create Crypto Dev LP tokens thats why it must inherit ERC20.sol
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
    
      import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    
      contract Trade is ERC20 {
      }
    
    • Now lets create a constructor for our contract
    • It takes the handle of the _CryptoDevToken that you just deployed within the ICO tutorial as an enter param
    • It then checks if the handle is a null handle
    • After all of the checks, it assigns the worth to the enter param to the cryptoDevTokenAddress variable
    • Constructor additionally units the identify and image for our Crypto Dev LP token
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
    
      import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    
      contract Trade is ERC20 {
    
          handle public cryptoDevTokenAddress;
    
          // Trade is inheriting ERC20, becase our alternate would maintain observe of Crypto Dev LP tokens
          constructor(handle _CryptoDevtoken) ERC20("CryptoDev LP Token", "CDLP") {
              require(_CryptoDevtoken != handle(0), "Token handle handed is a null handle");
              cryptoDevTokenAddress = _CryptoDevtoken;
          }
      }
    
    • Time to create a operate to get reserves of the Eth and Crypto Dev tokens held
      by the contract.
    • Eth reserve as everyone knows can be equal to the steadiness of the contract and may be discovered utilizing handle(this).steadiness so lets simply create a operate just for getting reserves of the Crypto Dev tokens
    • We all know that the Crypto Dev Token contract that we deployed is an ERC20
    • So we will simply name the balanceOf to verify the steadiness of CryptoDev Tokens
      that the contract handle holds
      /**
      * @dev Returns the quantity of `Crypto Dev Tokens` held by the contract
      */
      operate getReserve() public view returns (uint) {
          return ERC20(cryptoDevTokenAddress).balanceOf(handle(this));
      }
    
    • Time to create an addLiquidity operate which might add liquidity within the type of Ether and Crypto Dev tokens to our contract
    • If cryptoDevTokenReserve is zero it means that it’s the first time somebody is including Crypto Dev tokens and Ether to the contract
    • When the consumer is including preliminary liquidity we dont have to take care of any ratio as a result of
      we dont have any liquidity. So we settle for any quantity of tokens that consumer has despatched with the preliminary name
    • If cryptoDevTokenReserve shouldn’t be zero, then we’ve to make it possible for when somebody provides the liquidity it doesnt impression the value which the market at the moment has
    • To make sure this, we keep a ratio which has to stay fixed
    • Ratio is (cryptoDevTokenAmount consumer can add/cryptoDevTokenReserve within the contract) = (Eth Despatched by the consumer/Eth Reserve within the contract)
    • This ratio decides how a lot Crypto Dev tokens consumer can provide given a certain quantity of Eth
    • When consumer provides liquidity, we have to present him with some LP tokens as a result of we have to maintain observe of the quantity of liquiidty he has equipped to the contract
    • The quantity of LP tokens that get minted to the consumer are propotional to the Eth equipped by the consumer
    • Within the inital liquidity case, when there isn’t a liquidity: The quantity of LP tokens that might be minted to the consumer is the same as the Eth steadiness of the contract (as a result of steadiness is the same as the Eth despatched by the consumer within the addLiquidity name)
    • When there’s already liquidity within the contract, the quantity of LP tokens that get minted relies on a ratio.
    • The ratio is (LP tokens to be despatched to the consumer (liquidity) / totalSupply of LP tokens in contract) = (Eth despatched by the consumer) / (Eth reserve within the contract)
    /**
    * @dev Provides liquidity to the alternate.
    */
    operate addLiquidity(uint _amount) public payable returns (uint) {
        uint liquidity;
        uint ethBalance = handle(this).steadiness;
        uint cryptoDevTokenReserve = getReserve();
        ERC20 cryptoDevToken = ERC20(cryptoDevTokenAddress);
        /*
            If the reserve is empty, consumption any consumer equipped worth for
            `Ether` and `Crypto Dev` tokens as a result of there isn't a ratio at the moment
        */
        if(cryptoDevTokenReserve == 0) {
            // Switch the `cryptoDevToken` from the consumer's account to the contract
            cryptoDevToken.transferFrom(msg.sender, handle(this), _amount);
            // Take the present ethBalance and mint `ethBalance` quantity of LP tokens to the consumer.
            // `liquidity` supplied is the same as `ethBalance` as a result of that is the primary time consumer
            // is including `Eth` to the contract, so no matter `Eth` contract has is the same as the one equipped
            // by the consumer within the present `addLiquidity` name
            // `liquidity` tokens that should be minted to the consumer on `addLiquidity` name ought to all the time be proportional
            // to the Eth specified by the consumer
            liquidity = ethBalance;
            _mint(msg.sender, liquidity);
            // _mint is ERC20.sol good contract operate to mint ERC20 tokens
        } else {
            /*
                If the reserve shouldn't be empty, consumption any consumer equipped worth for
                `Ether` and decide in keeping with the ratio what number of `Crypto Dev` tokens
                should be equipped to stop any giant worth impacts due to the extra
                liquidity
            */
            // EthReserve must be the present ethBalance subtracted by the worth of ether despatched by the consumer
            // within the present `addLiquidity` name
            uint ethReserve =  ethBalance - msg.worth;
            // Ratio ought to all the time be maintained in order that there aren't any main worth impacts when including liquidity
            // Ratio right here is -> (cryptoDevTokenAmount consumer can add/cryptoDevTokenReserve within the contract) = (Eth Despatched by the consumer/Eth Reserve within the contract);
            // So doing a little maths, (cryptoDevTokenAmount consumer can add) = (Eth Despatched by the consumer * cryptoDevTokenReserve /Eth Reserve);
            uint cryptoDevTokenAmount = (msg.worth * cryptoDevTokenReserve)/(ethReserve);
            require(_amount >= cryptoDevTokenAmount, "Quantity of tokens despatched is lower than the minimal tokens required");
            // switch solely (cryptoDevTokenAmount consumer can add) quantity of `Crypto Dev tokens` from customers account
            // to the contract
            cryptoDevToken.transferFrom(msg.sender, handle(this), cryptoDevTokenAmount);
            // The quantity of LP tokens that might be despatched to the consumer must be propotional to the liquidity of
            // ether added by the consumer
            // Ratio right here to be maintained is ->
            // (LP tokens to be despatched to the consumer (liquidity)/ totalSupply of LP tokens in contract) = (Eth despatched by the consumer)/(Eth reserve within the contract)
            // by some maths -> liquidity =  (totalSupply of LP tokens in contract * (Eth despatched by the consumer))/(Eth reserve within the contract)
            liquidity = (totalSupply() * msg.worth)/ ethReserve;
            _mint(msg.sender, liquidity);
        }
         return liquidity;
    }
    
    • Now lets create a operate for eradicating liquidity from the contract.
    • The quantity of ether that might be despatched again to the consumer can be based mostly on a ratio
    • Ratio is (Eth despatched again to the consumer) / (present Eth reserve) = (quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
    • The quantity of Crypto Dev tokens that might be despatched again to the consumer would even be based mostly on a ratio
    • Ratio is (Crypto Dev despatched again to the consumer) / (present Crypto Dev token reserve) = (quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
    • The quantity of LP tokens that consumer would use to take away liquidity can be burnt
      /** 
      * @dev Returns the quantity Eth/Crypto Dev tokens that might be returned to the consumer
      * within the swap
      */
      operate removeLiquidity(uint _amount) public returns (uint , uint) {
          require(_amount > 0, "_amount must be larger than zero");
          uint ethReserve = handle(this).steadiness;
          uint _totalSupply = totalSupply();
          // The quantity of Eth that might be despatched again to the consumer relies
          // on a ratio
          // Ratio is -> (Eth despatched again to the consumer) / (present Eth reserve)
          // = (quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
          // Then by some maths -> (Eth despatched again to the consumer)
          // = (present Eth reserve * quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
          uint ethAmount = (ethReserve * _amount)/ _totalSupply;
          // The quantity of Crypto Dev token that might be despatched again to the consumer relies
          // on a ratio
          // Ratio is -> (Crypto Dev despatched again to the consumer) / (present Crypto Dev token reserve)
          // = (quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
          // Then by some maths -> (Crypto Dev despatched again to the consumer)
          // = (present Crypto Dev token reserve * quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
          uint cryptoDevTokenAmount = (getReserve() * _amount)/ _totalSupply;
          // Burn the despatched LP tokens from the consumer's pockets as a result of they're already despatched to
          // take away liquidity
          _burn(msg.sender, _amount);
          // Switch `ethAmount` of Eth from consumer's pockets to the contract
          payable(msg.sender).switch(ethAmount);
          // Switch `cryptoDevTokenAmount` of Crypto Dev tokens from the consumer's pockets to the contract
          ERC20(cryptoDevTokenAddress).switch(msg.sender, cryptoDevTokenAmount);
          return (ethAmount, cryptoDevTokenAmount);
      }
    
    • Subsequent lets implement the swap performance
    • Swap would go two methods. A method can be Eth to Crypto Dev tokens and different can be Crypto Dev to Eth
    • Its essential for us to find out: Given x quantity of Eth/Crypto Dev token despatched by the consumer, what number of Eth/Crypto Dev tokens would he obtain from the swap?
    • So let’s create a operate which calculates this:

      • We are going to cost 1%. This implies the quantity of enter tokens with charges would equal Enter quantity with charges = (enter quantity - (1*(enter quantity)/100)) = ((enter quantity)*99)/100
      • We have to comply with the idea of XY = Okay curve
      • We’d like to ensure (x + Δx) * (y - Δy) = x * y, so the ultimate components is Δy = (y * Δx) / (x + Δx);
      • Δy in our case is tokens to be obtained, Δx = ((enter quantity)*99)/100, x = Enter Reserve, y = Output Reserve
      • Enter Reserve and Ouput Reserve would rely on which swap we’re implementing. Eth to Crypto Dev token or vice versa
      /**
      * @dev Returns the quantity Eth/Crypto Dev tokens that might be returned to the consumer
      * within the swap
      */
      operate getAmountOfTokens(
          uint256 inputAmount,
          uint256 inputReserve,
          uint256 outputReserve
      ) public pure returns (uint256) {
          require(inputReserve > 0 && outputReserve > 0, "invalid reserves");
          // We're charging a payment of `1%`
          // Enter quantity with payment = (enter quantity - (1*(enter quantity)/100)) = ((enter quantity)*99)/100
          uint256 inputAmountWithFee = inputAmount * 99;
          // As a result of we have to comply with the idea of `XY = Okay` curve
          // We'd like to ensure (x + Δx) * (y - Δy) = x * y
          // So the ultimate components is Δy = (y * Δx) / (x + Δx)
          // Δy in our case is `tokens to be obtained`
          // Δx = ((enter quantity)*99)/100, x = inputReserve, y = outputReserve
          // So by placing the values within the formulae you will get the numerator and denominator
          uint256 numerator = inputAmountWithFee * outputReserve;
          uint256 denominator = (inputReserve * 100) + inputAmountWithFee;
          return numerator / denominator;
      }
      
      • Now lets implement a operate to swap Ether for Crypto Dev tokens
      /** 
      * @dev Swaps Eth for CryptoDev Tokens
      */
      operate ethToCryptoDevToken(uint _minTokens) public payable {
          uint256 tokenReserve = getReserve();
          // name the `getAmountOfTokens` to get the quantity of Crypto Dev tokens
          // that might be returned to the consumer after the swap
          // Discover that the `inputReserve` we're sending is the same as  
          // `handle(this).steadiness - msg.worth` as a substitute of simply `handle(this).steadiness`
          // as a result of `handle(this).steadiness` already comprises the `msg.worth` consumer has despatched within the given name
          // so we have to subtract it to get the precise enter reserve
          uint256 tokensBought = getAmountOfTokens(
              msg.worth,
              handle(this).steadiness - msg.worth,
              tokenReserve
          );
      
          require(tokensBought >= _minTokens, "inadequate output quantity");
          // Switch the `Crypto Dev` tokens to the consumer
          ERC20(cryptoDevTokenAddress).switch(msg.sender, tokensBought);
      }
      
      • Now lets implement a operate to swap Crypto Dev tokens to Ether
      /** 
      * @dev Swaps CryptoDev Tokens for Eth
      */
      operate cryptoDevTokenToEth(uint _tokensSold, uint _minEth) public {
          uint256 tokenReserve = getReserve();
          // name the `getAmountOfTokens` to get the quantity of Eth
          // that might be returned to the consumer after the swap
          uint256 ethBought = getAmountOfTokens(
              _tokensSold,
              tokenReserve,
              handle(this).steadiness
          );
          require(ethBought >= _minEth, "inadequate output quantity");
          // Switch `Crypto Dev` tokens from the consumer's handle to the contract
          ERC20(cryptoDevTokenAddress).transferFrom(
              msg.sender,
              handle(this),
              _tokensSold
          );
          // ship the `ethBought` to the consumer from the contract
          payable(msg.sender).switch(ethBought);
      }
      
  • Your ultimate contract ought to seem like this:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Trade is ERC20 {

    handle public cryptoDevTokenAddress;

    // Trade is inheriting ERC20, becase our alternate would maintain observe of Crypto Dev LP tokens
    constructor(handle _CryptoDevtoken) ERC20("CryptoDev LP Token", "CDLP") {
        require(_CryptoDevtoken != handle(0), "Token handle handed is a null handle");
        cryptoDevTokenAddress = _CryptoDevtoken;
    }

    /** 
    * @dev Returns the quantity of `Crypto Dev Tokens` held by the contract
    */
    operate getReserve() public view returns (uint) {
        return ERC20(cryptoDevTokenAddress).balanceOf(handle(this));
    }

    /**
    * @dev Provides liquidity to the alternate.
    */
    operate addLiquidity(uint _amount) public payable returns (uint) {
        uint liquidity;
        uint ethBalance = handle(this).steadiness;
        uint cryptoDevTokenReserve = getReserve();
        ERC20 cryptoDevToken = ERC20(cryptoDevTokenAddress);
        /* 
            If the reserve is empty, consumption any consumer equipped worth for 
            `Ether` and `Crypto Dev` tokens as a result of there isn't a ratio at the moment
        */
        if(cryptoDevTokenReserve == 0) {
            // Switch the `cryptoDevToken` handle from the consumer's account to the contract
            cryptoDevToken.transferFrom(msg.sender, handle(this), _amount);
            // Take the present ethBalance and mint `ethBalance` quantity of LP tokens to the consumer.
            // `liquidity` supplied is the same as `ethBalance` as a result of that is the primary time consumer 
            // is including `Eth` to the contract, so no matter `Eth` contract has is the same as the one equipped 
            // by the consumer within the present `addLiquidity` name
            // `liquidity` tokens that should be minted to the consumer on `addLiquidity` name shouls all the time be proportional
            // to the Eth specified by the consumer
            liquidity = ethBalance;
            _mint(msg.sender, liquidity);
        } else {
            /* 
                If the reserve shouldn't be empty, consumption any consumer equipped worth for 
                `Ether` and decide in keeping with the ratio what number of `Crypto Dev` tokens
                should be equipped to stop any giant worth impacts due to the extra
                liquidity
            */
            // EthReserve must be the present ethBalance subtracted by the worth of ether despatched by the consumer
            // within the present `addLiquidity` name
            uint ethReserve =  ethBalance - msg.worth;
            // Ratio ought to all the time be maintained in order that there aren't any main worth impacts when including liquidity
            // Ration right here is -> (cryptoDevTokenAmount consumer can add/cryptoDevTokenReserve within the contract) = (Eth Despatched by the consumer/Eth Reserve within the contract);
            // So doing a little maths, (cryptoDevTokenAmount consumer can add) = (Eth Despatched by the consumer * cryptoDevTokenReserve /Eth Reserve);
            uint cryptoDevTokenAmount = (msg.worth * cryptoDevTokenReserve)/(ethReserve);
            require(_amount >= cryptoDevTokenAmount, "Quantity of tokens despatched is lower than the minimal tokens required");
            // switch solely (cryptoDevTokenAmount consumer can add) quantity of `Crypto Dev tokens` from customers account
            // to the contract
            cryptoDevToken.transferFrom(msg.sender, handle(this), cryptoDevTokenAmount);
            // The quantity of LP tokens that might be despatched to the consumer must be propotional to the liquidity of
            // ether added by the consumer
            // Ratio right here to be maintained is -> 
            // (lp tokens to be despatched to the consumer (liquidity)/ totalSupply of LP tokens in contract) = (Eth despatched by the consumer)/(Eth reserve within the contract)
            // by some maths -> liquidity =  (totalSupply of LP tokens in contract * (Eth despatched by the consumer))/(Eth reserve within the contract)
            liquidity = (totalSupply() * msg.worth)/ ethReserve;
            _mint(msg.sender, liquidity);
        }
         return liquidity;
    }

    /** 
    * @dev Returns the quantity Eth/Crypto Dev tokens that might be returned to the consumer
    * within the swap
    */
    operate removeLiquidity(uint _amount) public returns (uint , uint) {
        require(_amount > 0, "_amount must be larger than zero");
        uint ethReserve = handle(this).steadiness;
        uint _totalSupply = totalSupply();
        // The quantity of Eth that might be despatched again to the consumer relies
        // on a ratio 
        // Ratio is -> (Eth despatched again to the consumer/ Present Eth reserve)  
        // = (quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
        // Then by some maths -> (Eth despatched again to the consumer) 
        // = (present Eth reserve * quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
        uint ethAmount = (ethReserve * _amount)/ _totalSupply;
        // The quantity of Crypto Dev token that might be despatched again to the consumer relies
        // on a ratio 
        // Ratio is -> (Crypto Dev despatched again to the consumer) / (present Crypto Dev token reserve)
        // = (quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
        // Then by some maths -> (Crypto Dev despatched again to the consumer) 
        // = (present Crypto Dev token reserve * quantity of LP tokens that consumer desires to withdraw) / (whole provide of LP tokens)
        uint cryptoDevTokenAmount = (getReserve() * _amount)/ _totalSupply;
        // Burn the despatched `LP` tokens from the consumer's pockets as a result of they're already despatched to 
        // take away liquidity
        _burn(msg.sender, _amount);
        // Switch `ethAmount` of Eth from consumer's pockets to the contract
        payable(msg.sender).switch(ethAmount);
        // Switch `cryptoDevTokenAmount` of `Crypto Dev` tokens from the consumer's pockets to the contract 
        ERC20(cryptoDevTokenAddress).switch(msg.sender, cryptoDevTokenAmount);
        return (ethAmount, cryptoDevTokenAmount);
    }

    /** 
    * @dev Returns the quantity Eth/Crypto Dev tokens that might be returned to the consumer
    * within the swap
    */
    operate getAmountOfTokens(
        uint256 inputAmount,
        uint256 inputReserve,
        uint256 outputReserve
    ) public pure returns (uint256) {
        require(inputReserve > 0 && outputReserve > 0, "invalid reserves");
        // We're charging a payment of `1%`
        // Enter quantity with payment = (enter quantity - (1*(enter quantity)/100)) = ((enter quantity)*99)/100
        uint256 inputAmountWithFee = inputAmount * 99;
        // As a result of we have to comply with the idea of `XY = Okay` curve
        // We'd like to ensure (x + Δx) * (y - Δy) = x * y
        // So the ultimate components is Δy = (y * Δx) / (x + Δx)
        // Δy in our case is `tokens to be obtained`
        // Δx = ((enter quantity)*99)/100, x = inputReserve, y = outputReserve
        // So by placing the values within the formulae you will get the numerator and denominator
        uint256 numerator = inputAmountWithFee * outputReserve;
        uint256 denominator = (inputReserve * 100) + inputAmountWithFee;
        return numerator / denominator;
    }

    /** 
    * @dev Swaps Eth for CryptoDev Tokens
    */
    operate ethToCryptoDevToken(uint _minTokens) public payable {
        uint256 tokenReserve = getReserve();
        // name the `getAmountOfTokens` to get the quantity of Crypto Dev tokens
        // that might be returned to the consumer after the swap
        // Discover that the `inputReserve` we're sending is the same as  
        // `handle(this).steadiness - msg.worth` as a substitute of simply `handle(this).steadiness`
        // as a result of `handle(this).steadiness` already comprises the `msg.worth` consumer has despatched within the given name
        // so we have to subtract it to get the precise enter reserve
        uint256 tokensBought = getAmountOfTokens(
            msg.worth,
            handle(this).steadiness - msg.worth,
            tokenReserve
        );

        require(tokensBought >= _minTokens, "inadequate output quantity");
        // Switch the `Crypto Dev` tokens to the consumer
        ERC20(cryptoDevTokenAddress).switch(msg.sender, tokensBought);
    }


    /** 
    * @dev Swaps CryptoDev Tokens for Eth
    */
    operate cryptoDevTokenToEth(uint _tokensSold, uint _minEth) public {
        uint256 tokenReserve = getReserve();
        // name the `getAmountOfTokens` to get the quantity of Eth
        // that might be returned to the consumer after the swap
        uint256 ethBought = getAmountOfTokens(
            _tokensSold,
            tokenReserve,
            handle(this).steadiness
        );
        require(ethBought >= _minEth, "inadequate output quantity");
        // Switch `Crypto Dev` tokens from the consumer's handle to the contract
        ERC20(cryptoDevTokenAddress).transferFrom(
            msg.sender,
            handle(this),
            _tokensSold
        );
        // ship the `ethBought` to the consumer from the contract
        payable(msg.sender).switch(ethBought);
    }
}
Enter fullscreen mode

Exit fullscreen mode

  • Now we might set up dotenv bundle to have the ability to import the env file and use it in our config. Open up a terminal pointing at hardhat-tutorial listing and execute this command
  npm set up dotenv
Enter fullscreen mode

Exit fullscreen mode

  • Now create a .env file within the hardhat-tutorial folder and add the next strains, use the directions within the feedback to get your Alchemy API Key URL and Rinkeby Non-public Key. Ensure that the account from which you get your Rinkeby non-public secret is funded with Rinkeby Ether.
  // Go to https://www.alchemyapi.io, join, create
  // a brand new App in its dashboard and choose the community as Rinkeby, and substitute "add-the-alchemy-key-url-here" with its key url
  ALCHEMY_API_KEY_URL="add-the-alchemy-key-url-here"

  // Exchange this non-public key along with your RINKEBY account non-public key
  // To export your non-public key from Metamask, open Metamask and
  // go to Account Particulars > Export Non-public Key
  // Concentrate on NEVER placing actual Ether into testing accounts
  RINKEBY_PRIVATE_KEY="add-the-rinkeby-private-key-here"
Enter fullscreen mode

Exit fullscreen mode

  • Lets additionally create a constants folder to maintain observe of any constants we’ve. Underneath the hardhat-tutorial folder create a brand new folder named constants and below the constants folder create a brand new file index.js

  • Contained in the index.js file add the next strains of code. Bear in mind to interchange ADDRESS-OF-CRYPTO-DEV-TOKEN with the contract handle of the Crypto Dev token contract that you just deployed within the ICO tutorial

  const CRYPTO_DEV_TOKEN_CONTRACT_ADDRESS = "ADDRESS-OF-CRYPTO-DEV-TOKEN";

  module.exports = { CRYPTO_DEV_TOKEN_CONTRACT_ADDRESS };
Enter fullscreen mode

Exit fullscreen mode

  • Lets deploy the contract to rinkeby community. Create a brand new file named deploy.js below the scripts folder

  • Now we might write some code to deploy the contract in deploy.js file.

  const { ethers } = require("hardhat");
  require("dotenv").config({ path: ".env" });
  const { CRYPTO_DEV_TOKEN_CONTRACT_ADDRESS } = require("../constants");

  async operate principal() {
    const cryptoDevTokenAddress = CRYPTO_DEV_TOKEN_CONTRACT_ADDRESS;
    /*
    A ContractFactory in ethers.js is an abstraction used to deploy new good contracts,
    so exchangeContract here's a manufacturing facility for cases of our Trade contract.
    */
    const exchangeContract = await ethers.getContractFactory("Trade");

    // right here we deploy the contract
    const deployedExchangeContract = await exchangeContract.deploy(
      cryptoDevTokenAddress
    );
    await deployedExchangeContract.deployed();

    // print the handle of the deployed contract
    console.log("Trade Contract Deal with:", deployedExchangeContract.handle);
  }

  // Name the principle operate and catch if there's any error
  principal()
    .then(() => course of.exit(0))
    .catch((error) => {
      console.error(error);
      course of.exit(1);
    });
Enter fullscreen mode

Exit fullscreen mode

  • Now open the hardhat.config.js file, we might add the rinkeby community right here in order that we will deploy our contract to rinkeby. Exchange all of the strains within the hardhart.config.js file with the given beneath strains
  require("@nomiclabs/hardhat-waffle");
  require("dotenv").config({ path: ".env" });

  const ALCHEMY_API_KEY_URL = course of.env.ALCHEMY_API_KEY_URL;

  const RINKEBY_PRIVATE_KEY = course of.env.RINKEBY_PRIVATE_KEY;

  module.exports = {
    solidity: "0.8.4",
    networks: {
      rinkeby: {
        url: ALCHEMY_API_KEY_URL,
        accounts: [RINKEBY_PRIVATE_KEY],
      },
    },
  };
Enter fullscreen mode

Exit fullscreen mode

  • Compile the contract, open up a terminal pointing at hardhat-tutorial listing and execute this command
  npx hardhat compile
Enter fullscreen mode

Exit fullscreen mode

  • To deploy, open up a terminal pointing athardhat-tutorial listing and execute this command
   npx hardhat run scripts/deploy.js --network rinkeby
Enter fullscreen mode

Exit fullscreen mode

  • Save the Trade Contract Deal with that was printed in your terminal in your notepad, you would want it additional down within the tutorial.



Web site

  • To develop the web site we might be utilizing React and Next Js. React is a javascript framework which is used to make web sites and Subsequent Js is constructed on prime of React.
  • First, You would want to create a brand new subsequent app. Your folder construction ought to look one thing like
     - DeFi-Trade
         - hardhat-tutorial
         - my-app
Enter fullscreen mode

Exit fullscreen mode

  • To create this my-app, within the terminal level to DeFi-Trade folder and kind
    npx create-next-app@newest
Enter fullscreen mode

Exit fullscreen mode

and press enter for all of the questions

  • Now to run the app, execute these instructions within the terminal
  cd my-app
  npm run dev
Enter fullscreen mode

Exit fullscreen mode

  • Now go to http://localhost:3000, your app must be operating 🤘

  • Now lets set up Web3Modal library(https://github.com/Web3Modal/web3modal). Web3Modal is an easy-to-use library to assist builders add help for a number of suppliers of their apps with a easy customizable configuration. By default Web3Modal Library helps injected suppliers like (Metamask, Dapper, Gnosis Protected, Body, Web3 Browsers, and so forth), You may also simply configure the library to help Portis, Fortmatic, Squarelink, Torus, Authereum, D’CENT Pockets and Arkane.
    Open up a terminal pointing atmy-app listing and execute this command

    npm set up web3modal
Enter fullscreen mode

Exit fullscreen mode

  • In the identical terminal additionally set up ethers.js
  npm i ethers
Enter fullscreen mode

Exit fullscreen mode

  • In your public folder, obtain this image and rename it to cryptodev.svg.

  • Now go to kinds folder and substitute all of the contents of Residence.modules.css file with the next code, this may add some styling to your dapp:

  .principal {
    min-height: 90vh;
    show: flex;
    flex-direction: row;
    justify-content: heart;
    align-items: heart;
    font-family: "Courier New", Courier, monospace;
  }

  .footer {
    show: flex;
    padding: 2rem 0;
    border-top: 1px strong #eaeaea;
    justify-content: heart;
    align-items: heart;
  }

  .picture {
    width: 70%;
    peak: 50%;
    margin-left: 20%;
  }

  .enter {
    width: 200px;
    peak: 100%;
    padding: 1%;
    margin: 2%;
    box-shadow: 0 0 15px 4px rgba(0, 0, 0, 0.06);
    border-radius: 10px;
  }

  .title {
    font-size: 2rem;
    margin: 2rem 0;
  }

  .description {
    line-height: 1;
    margin: 2%;
    font-size: 1.2rem;
  }

  .button {
    border-radius: 4px;
    background-color: purple;
    border: none;
    coloration: #ffffff;
    font-size: 15px;
    padding: 5px;
    width: 100px;
    cursor: pointer;
    margin: 2%;
  }

  .inputDiv {
    width: 200px;
    peak: 100%;
    padding: 1%;
    margin: 2%;
    border: lightslategray;
    box-shadow: 0 0 15px 4px rgba(0, 0, 0, 0.06);
    border-radius: 10px;
  }
  .choose {
    border-radius: 4px;
    font-size: 15px;
    padding: 5px;
    width: 175px;
    cursor: pointer;
    margin: 2%;
  }

  .button1 {
    border-radius: 4px;
    background-color: blue;
    border: none;
    coloration: #ffffff;
    font-size: 15px;
    padding: 5px;
    width: 100px;
    cursor: pointer;
    margin: 2%;
  }
  @media (max-width: 1000px) {
    .principal {
      width: 100%;
      flex-direction: column;
      justify-content: heart;
      align-items: heart;
    }
  }
Enter fullscreen mode

Exit fullscreen mode

  • Now lets create a constants folder to maintain observe of any constants we’d have. Create a constants folder below my-app folder and contained in the constants folder create a file names index.js
  • Paste the next code.

    • Exchange ABI-CRYPTO-DEV-TOKEN-CONTRACT with the abi of the Crypto Dev token contract that you just deployed within the ICO tutorial.
    • Exchange ADDRESS-OF-CRYPTO-DEV-TOKEN-CONTRACT with the handle of the Crypto Dev token contract that you just deployed within the ICO tutorial
    • Exchange ABI-EXCHANGE-CONTRACT with the abi of the Trade Contract. To get the abi in your contract, go to your hardhat-tutorial/artifacts/contracts/Trade.sol folder and out of your Trade.json file get the array marked below the "abi" key.
    • Exchange ADDRESS-EXCHANGE-CONTRACT with the handle of the alternate contract that you just deployed above and saved to your notepad
    export const TOKEN_CONTRACT_ABI = "ABI-CRYPTO-DEV-TOKEN-CONTRACT";
    export const TOKEN_CONTRACT_ADDRESS =
      "ADDRESS-OF-CRYPTO-DEV-TOKEN-CONTRACT";
    export const EXCHANGE_CONTRACT_ABI = "ABI-EXCHANGE-CONTRACT";
    export const EXCHANGE_CONTRACT_ADDRESS = "ADDRESS-EXCHANGE-CONTRACT";
    
  • Now we might create some utility recordsdata which might assist us to raised work together with the contract. Create a utils folder contained in the my-app folder and contained in the folder create 4 recordsdata: addLiquidity.js, removeLiquidity.js, getAmounts.js, and swap.js

  • Lets begin by writing some code in getAmounts.js. This file is used to retrieve balances and reserves for belongings

  import { Contract } from "ethers";
  import {
    EXCHANGE_CONTRACT_ABI,
    EXCHANGE_CONTRACT_ADDRESS,
    TOKEN_CONTRACT_ABI,
    TOKEN_CONTRACT_ADDRESS,
  } from "../constants";

  /**
   * getEtherBalance: Retrieves the ether steadiness of the consumer or the contract
   */
  export const getEtherBalance = async (
    supplier,
    handle,
    contract = false
  ) => {
    strive {
      // If the caller has set the `contract` boolean to true, retrieve the steadiness of
      // ether within the `alternate contract`, whether it is set to false, retrieve the steadiness
      // of the consumer's handle
      if (contract) {
        const steadiness = await supplier.getBalance(EXCHANGE_CONTRACT_ADDRESS);
        return steadiness;
      } else {
        const steadiness = await supplier.getBalance(handle);
        return steadiness;
      }
    } catch (err) {
      console.error(err);
      return 0;
    }
  };

  /**
   * getCDTokensBalance: Retrieves the Crypto Dev tokens within the account
   * of the supplied `handle`
   */
  export const getCDTokensBalance = async (supplier, handle) => {
    strive {
      const tokenContract = new Contract(
        TOKEN_CONTRACT_ADDRESS,
        TOKEN_CONTRACT_ABI,
        supplier
      );
      const balanceOfCryptoDevTokens = await tokenContract.balanceOf(handle);
      return balanceOfCryptoDevTokens;
    } catch (err) {
      console.error(err);
    }
  };

  /**
   * getLPTokensBalance: Retrieves the quantity of LP tokens within the account
   * of the supplied `handle`
   */
  export const getLPTokensBalance = async (supplier, handle) => {
    strive {
      const exchangeContract = new Contract(
        EXCHANGE_CONTRACT_ADDRESS,
        EXCHANGE_CONTRACT_ABI,
        supplier
      );
      const balanceOfLPTokens = await exchangeContract.balanceOf(handle);
      return balanceOfLPTokens;
    } catch (err) {
      console.error(err);
    }
  };

  /**
   * getReserveOfCDTokens: Retrieves the quantity of CD tokens within the
   * alternate contract handle
   */
  export const getReserveOfCDTokens = async (supplier) => {
    strive {
      const exchangeContract = new Contract(
        EXCHANGE_CONTRACT_ADDRESS,
        EXCHANGE_CONTRACT_ABI,
        supplier
      );
      const reserve = await exchangeContract.getReserve();
      return reserve;
    } catch (err) {
      console.error(err);
    }
  };
Enter fullscreen mode

Exit fullscreen mode

  • Lets now write some code for addLiquidity.js.

    • addLiquidity.js has two capabilities addLiquidity and calculateCD
    • addLiquidity is used to name the addLiquidity operate within the contract so as to add liquidity
    • It additionally will get the Crypto Dev tokens authorised for the contract by the consumer. The rationale why Crypto Dev tokens want approval is as a result of they’re an ERC20 token. For the contract to withdraw an ERC20 from a consumer’s account, it wants the approval from the consumer’s account
    • calculateCD tells you for a given quantity of Eth, what number of Crypto Dev tokens may be added to the liquidity
    • We calculate this by sustaining a ratio. The ratio we comply with is (quantity of Crypto Dev tokens to be added) / (Crypto Dev tokens steadiness) = (Eth that might be added) / (Eth reserve within the contract)
    • So by maths we get (quantity of Crypto Dev tokens to be added) = (Eth that might be added * Crypto Dev tokens steadiness) / (Eth reserve within the contract)
    • The ratio is required in order that including liquidity would not largely impression the value
    • Notice tx.wait() means we’re ready for the transaction to get mined
    import { Contract, utils } from "ethers";
    import {
      EXCHANGE_CONTRACT_ABI,
      EXCHANGE_CONTRACT_ADDRESS,
      TOKEN_CONTRACT_ABI,
      TOKEN_CONTRACT_ADDRESS,
    } from "../constants";
    
    /**
     * addLiquidity helps add liquidity to the alternate,
     * If the consumer is including preliminary liquidity, consumer decides the ether and CD tokens he desires so as to add
     * to the alternate. If he's including the liquidity after the preliminary liquidity has already been added
     * then we calculate the Crypto Dev tokens he can add, given the Eth he desires so as to add by protecting the ratios
     * fixed
     */
    export const addLiquidity = async (
      signer,
      addCDAmountWei,
      addEtherAmountWei
    ) => {
      strive {
        // create a brand new occasion of the token contract
        const tokenContract = new Contract(
          TOKEN_CONTRACT_ADDRESS,
          TOKEN_CONTRACT_ABI,
          signer
        );
        // create a brand new occasion of the alternate contract
        const exchangeContract = new Contract(
          EXCHANGE_CONTRACT_ADDRESS,
          EXCHANGE_CONTRACT_ABI,
          signer
        );
        // As a result of CD tokens are an ERC20, consumer would want to provide the contract allowance
        // to take the required quantity CD tokens out of his contract
        let tx = await tokenContract.approve(
          EXCHANGE_CONTRACT_ADDRESS,
          addCDAmountWei.toString()
        );
        await tx.wait();
        // After the contract has the approval, add the ether and cd tokens within the liquidity
        tx = await exchangeContract.addLiquidity(addCDAmountWei, {
          worth: addEtherAmountWei,
        });
        await tx.wait();
      } catch (err) {
        console.error(err);
      }
    };
    
    /**
     * calculateCD calculates the CD tokens that should be added to the liquidity
     * given `_addEtherAmountWei` quantity of ether
     */
    export const calculateCD = async (
      _addEther = "0",
      etherBalanceContract,
      cdTokenReserve
    ) => {
      // `_addEther` is a string, we have to convert it to a Bignumber earlier than we will do our calculations
      // We try this utilizing the `parseEther` operate from `ethers.js`
      const _addEtherAmountWei = utils.parseEther(_addEther);
      // Ratio must be maintained once we add liquidty.
      // We have to let the consumer know for a certain quantity of ether what number of `CD` tokens
      // he can add in order that the value impression shouldn't be giant
      // The ratio we comply with is (quantity of Crypto Dev tokens to be added) / (Crypto Dev tokens steadiness) = (Eth that might be added) / (Eth reserve within the contract)
      // So by maths we get (quantity of Crypto Dev tokens to be added) = (Eth that might be added * Crypto Dev tokens steadiness) / (Eth reserve within the contract)
      const cryptoDevTokenAmount = _addEtherAmountWei
        .mul(cdTokenReserve)
        .div(etherBalanceContract);
      return cryptoDevTokenAmount;
    };
    
  • Now add some code to removeLiquidity.js

    • We’ve two capabilities right here: One is removeLiquidity and the opposite is getTokensAfterRemove
    • removeLiquidity calls the removeLiquidity operate from the contract,
      to take away the quantity of LP tokens specified by the consumer
    • getTokensAfterRemove calulates the quantity of Ether and CD tokens
      that might be despatched again to the consumer after he removes a certain quantity of LP
      tokens from the pool
    • The quantity of Eth that might be despatched again to the consumer after he withdraws the LP token is calculated based mostly on a ratio,
    • Ratio is -> (quantity of Eth that might be despatched again to the consumer / Eth reserve) = (LP tokens withdrawn) / (whole provide of LP tokens)
    • By some maths we get -> (quantity of Eth that might be despatched again to the consumer) = (Eth Reserve * LP tokens withdrawn) / (whole provide of LP tokens)
    • Equally we additionally keep a ratio for the CD tokens, so right here in our case
    • Ratio is -> (quantity of CD tokens despatched again to the consumer / CD Token reserve) = (LP tokens withdrawn) / (whole provide of LP tokens)
    • Then (quantity of CD tokens despatched again to the consumer) = (CD token reserve * LP tokens withdrawn) / (whole provide of LP tokens)
    import { Contract, suppliers, utils, BigNumber } from "ethers";
    import { EXCHANGE_CONTRACT_ABI, EXCHANGE_CONTRACT_ADDRESS } from "../constants";
    
    /**
    * removeLiquidity: Removes the `removeLPTokensWei` quantity of LP tokens from
    * liquidity and in addition the calculated quantity of `ether` and `CD` tokens
    */
    export const removeLiquidity = async (signer, removeLPTokensWei) => {
      // Create a brand new occasion of the alternate contract
      const exchangeContract = new Contract(
        EXCHANGE_CONTRACT_ADDRESS,
        EXCHANGE_CONTRACT_ABI,
        signer
      );
      const tx = await exchangeContract.removeLiquidity(removeLPTokensWei);
      await tx.wait();
    };
    
    /**
    * getTokensAfterRemove: Calculates the quantity of `Eth` and `CD` tokens
    * that might be returned again to consumer after he removes `removeLPTokenWei` quantity
    * of LP tokens from the contract
    */
    export const getTokensAfterRemove = async (
      supplier,
      removeLPTokenWei,
      _ethBalance,
      cryptoDevTokenReserve
    ) => {
      strive {
        // Create a brand new occasion of the alternate contract
        const exchangeContract = new Contract(
          EXCHANGE_CONTRACT_ADDRESS,
          EXCHANGE_CONTRACT_ABI,
          supplier
        );
        // Get the full provide of `Crypto Dev` LP tokens
        const _totalSupply = await exchangeContract.totalSupply();
        // Right here we're utilizing the BigNumber strategies of multiplication and division
        // The quantity of Eth that might be despatched again to the consumer after he withdraws the LP token
        // is calculated based mostly on a ratio,
        // Ratio is -> (quantity of Eth that might be despatched again to the consumer / Eth reserve) = (LP tokens withdrawn) / (whole provide of LP tokens)
        // By some maths we get -> (quantity of Eth that might be despatched again to the consumer) = (Eth Reserve * LP tokens withdrawn) / (whole provide of LP tokens)
        // Equally we additionally keep a ratio for the `CD` tokens, so right here in our case
        // Ratio is -> (quantity of CD tokens despatched again to the consumer / CD Token reserve) = (LP tokens withdrawn) / (whole provide of LP tokens)
        // Then (quantity of CD tokens despatched again to the consumer) = (CD token reserve * LP tokens withdrawn) / (whole provide of LP tokens)
        const _removeEther = _ethBalance.mul(removeLPTokenWei).div(_totalSupply);
        const _removeCD = cryptoDevTokenReserve
          .mul(removeLPTokenWei)
          .div(_totalSupply);
        return {
          _removeEther,
          _removeCD,
        };
      } catch (err) {
        console.error(err);
      }
    };
    
  • Now it is time to write code for swap.js our final utils file

    • It has two capabilities getAmountOfTokenReceivedFromSwap and swapTokens
    • swapTokens swaps specific amount of Eth/Crypto Dev tokens with Crypto Dev/Eth tokens
    • If Eth has been chosen by the consumer from the UI, it implies that the consumer has Eth and he desires to swap it for a certain quantity of Crypto Dev tokens
    • On this case we name the ethToCryptoDevToken operate. Notice that Eth is shipped as a price within the operate as a result of the consumer is paying this Eth to the contract. Eth despatched shouldn’t be an enter param worth on this case
    • However, if Eth shouldn’t be chosen which means the consumer desires to swap Crypto Dev tokens for Eth
    • Right here we name the cryptoDevTokenToEth
    • getAmountOfTokensReceivedFromSwap is a operate which calculates, given a certain quantity of Eth/Crypto Dev tokens, what number of Eth/Crypto Dev tokens can be despatched again to the consumer
    • If Eth is chosen it calls the getAmountOfTokens from the contract which takes in an enter reserve and an output reserve. Right here, enter reserve can be the Eth steadiness of the contract and output reserve can be the Crypto Dev token reserve. Reverse can be true, if Eth shouldn’t be chosen
    import { Contract } from "ethers";
    import {
      EXCHANGE_CONTRACT_ABI,
      EXCHANGE_CONTRACT_ADDRESS,
      TOKEN_CONTRACT_ABI,
      TOKEN_CONTRACT_ADDRESS,
    } from "../constants";
    
    /*
        getAmountOfTokensReceivedFromSwap:  Returns the variety of Eth/Crypto Dev tokens that may be obtained 
        when the consumer swaps `_swapAmountWei` quantity of Eth/Crypto Dev tokens.
    */
    export const getAmountOfTokensReceivedFromSwap = async (
      _swapAmountWei,
      supplier,
      ethSelected,
      ethBalance,
      reservedCD
    ) => {
      // Create a brand new occasion of the alternate contract
      const exchangeContract = new Contract(
        EXCHANGE_CONTRACT_ADDRESS,
        EXCHANGE_CONTRACT_ABI,
        supplier
      );
      let amountOfTokens;
      // If `Eth` is chosen this implies our enter worth is `Eth` which implies our enter quantity can be
      // `_swapAmountWei`, the enter reserve can be the `ethBalance` of the contract and output reserve
      // can be the `Crypto Dev` token reserve
      if (ethSelected) {
        amountOfTokens = await exchangeContract.getAmountOfTokens(
          _swapAmountWei,
          ethBalance,
          reservedCD
        );
      } else {
        // If `Eth` shouldn't be chosen this implies our enter worth is `Crypto Dev` tokens which implies our enter quantity can be
        // `_swapAmountWei`, the enter reserve can be the `Crypto Dev` token reserve of the contract and output reserve
        // can be the `ethBalance`
        amountOfTokens = await exchangeContract.getAmountOfTokens(
          _swapAmountWei,
          reservedCD,
          ethBalance
        );
      }
    
      return amountOfTokens;
    };
    
    /*
      swapTokens: Swaps `swapAmountWei` of Eth/Crypto Dev tokens with `tokenToBeReceivedAfterSwap` quantity of Eth/Crypto Dev tokens.
    */
    export const swapTokens = async (
      signer,
      swapAmountWei,
      tokenToBeReceivedAfterSwap,
      ethSelected
    ) => {
      // Create a brand new occasion of the alternate contract
      const exchangeContract = new Contract(
        EXCHANGE_CONTRACT_ADDRESS,
        EXCHANGE_CONTRACT_ABI,
        signer
      );
      const tokenContract = new Contract(
        TOKEN_CONTRACT_ADDRESS,
        TOKEN_CONTRACT_ABI,
        signer
      );
      let tx;
      // If Eth is chosen name the `ethToCryptoDevToken` operate else
      // name the `cryptoDevTokenToEth` operate from the contract
      // As you possibly can see it's worthwhile to go the `swapAmount` as a price to the operate as a result of
      // it's the ether we're paying to the contract, as a substitute of a price we're passing to the operate
      if (ethSelected) {
        tx = await exchangeContract.ethToCryptoDevToken(
          tokenToBeReceivedAfterSwap,
          {
            worth: swapAmountWei,
          }
        );
      } else {
        // Consumer has to approve `swapAmountWei` for the contract as a result of `Crypto Dev` token
        // is an ERC20
        tx = await tokenContract.approve(
          EXCHANGE_CONTRACT_ADDRESS,
          swapAmountWei.toString()
        );
        await tx.wait();
        // name cryptoDevTokenToEth operate which might soak up `swapAmountWei` of `Crypto Dev` tokens and would
        // ship again `tokenToBeReceivedAfterSwap` quantity of `Eth` to the consumer
        tx = await exchangeContract.cryptoDevTokenToEth(
          swapAmountWei,
          tokenToBeReceivedAfterSwap
        );
      }
      await tx.wait();
    };
    
  • Now its time for the ultimate phases of our app, lets add some code to the pages/index.js file which subsequent already provides you. Exchange all of the contents of the file with the next content material

import { BigNumber, suppliers, utils } from "ethers";
import Head from "subsequent/head";
import React, { useEffect, useRef, useState } from "react";
import Web3Modal from "web3modal";
import kinds from "../kinds/Residence.module.css";
import { addLiquidity, calculateCD } from "../utils/addLiquidity";
import {
  getCDTokensBalance,
  getEtherBalance,
  getLPTokensBalance,
  getReserveOfCDTokens,
} from "../utils/getAmounts";
import {
  getTokensAfterRemove,
  removeLiquidity,
} from "../utils/removeLiquidity";
import { swapTokens, getAmountOfTokensReceivedFromSwap } from "../utils/swap";

export default operate Residence() {
  /** Basic state variables */
  // loading is about to true when the transaction is mining and set to false when
  // the transaction has mined
  const [loading, setLoading] = useState(false);
  // We've two tabs on this dapp, Liquidity Tab and Swap Tab. This variable
  // retains observe of which Tab the consumer is on. Whether it is set to true this implies
  // that the consumer is on `liquidity` tab else he's on `swap` tab
  const [liquidityTab, setLiquidityTab] = useState(true);
  // This variable is the `0` quantity in type of a BigNumber
  const zero = BigNumber.from(0);
  /** Variables to maintain observe of quantity */
  // `ethBalance` retains observe of the quantity of Eth held by the consumer's account
  const [ethBalance, setEtherBalance] = useState(zero);
  // `reservedCD` retains observe of the Crypto Dev tokens Reserve steadiness within the Trade contract
  const [reservedCD, setReservedCD] = useState(zero);
  // Retains observe of the ether steadiness within the contract
  const [etherBalanceContract, setEtherBalanceContract] = useState(zero);
  // cdBalance is the quantity of `CD` tokens assist by the customers account
  const [cdBalance, setCDBalance] = useState(zero);
  // `lpBalance` is the quantity of LP tokens held by the customers account
  const [lpBalance, setLPBalance] = useState(zero);
  /** Variables to maintain observe of liquidity to be added or eliminated */
  // addEther is the quantity of Ether that the consumer desires so as to add to the liquidity
  const [addEther, setAddEther] = useState(zero);
  // addCDTokens retains observe of the quantity of CD tokens that the consumer desires so as to add to the liquidity
  // in case when there isn't a preliminary liquidity and after liquidity will get added it retains observe of the
  // CD tokens that the consumer can add given a certain quantity of ether
  const [addCDTokens, setAddCDTokens] = useState(zero);
  // removeEther is the quantity of `Ether` that might be despatched again to the consumer based mostly on a sure variety of `LP` tokens
  const [removeEther, setRemoveEther] = useState(zero);
  // removeCD is the quantity of `Crypto Dev` tokens that might be despatched again to the consumer based mostly on a sure variety of `LP` tokens
  // that he desires to withdraw
  const [removeCD, setRemoveCD] = useState(zero);
  // quantity of LP tokens that the consumer desires to take away from liquidity
  const [removeLPTokens, setRemoveLPTokens] = useState("0");
  /** Variables to maintain observe of swap performance */
  // Quantity that the consumer desires to swap
  const [swapAmount, setSwapAmount] = useState("");
  // This retains observe of the variety of tokens that the consumer would obtain after a swap completes
  const [tokenToBeReceivedAfterSwap, settokenToBeReceivedAfterSwap] = useState(
    zero
  );
  // Retains observe of whether or not  `Eth` or `Crypto Dev` token is chosen. If `Eth` is chosen it implies that the consumer
  // desires to swap some `Eth` for some `Crypto Dev` tokens and vice versa if `Eth` shouldn't be chosen
  const [ethSelected, setEthSelected] = useState(true);
  /** Pockets connection */
  // Create a reference to the Web3 Modal (used for connecting to Metamask) which persists so long as the web page is open
  const web3ModalRef = useRef();
  // walletConnected maintain observe of whether or not the consumer's pockets is linked or not
  const [walletConnected, setWalletConnected] = useState(false);

  /**
   * getAmounts name numerous capabilities to retrive quantities for ethbalance,
   * LP tokens and so forth
   */
  const getAmounts = async () => {
    strive {
      const supplier = await getProviderOrSigner(false);
      const signer = await getProviderOrSigner(true);
      const handle = await signer.getAddress();
      // get the quantity of eth within the consumer's account
      const _ethBalance = await getEtherBalance(supplier, handle);
      // get the quantity of `Crypto Dev` tokens held by the consumer
      const _cdBalance = await getCDTokensBalance(supplier, handle);
      // get the quantity of `Crypto Dev` LP tokens held by the consumer
      const _lpBalance = await getLPTokensBalance(supplier, handle);
      // will get the quantity of `CD` tokens which can be current within the reserve of the `Trade contract`
      const _reservedCD = await getReserveOfCDTokens(supplier);
      // Get the ether reserves within the contract
      const _ethBalanceContract = await getEtherBalance(supplier, null, true);
      setEtherBalance(_ethBalance);
      setCDBalance(_cdBalance);
      setLPBalance(_lpBalance);
      setReservedCD(_reservedCD);
      setReservedCD(_reservedCD);
      setEtherBalanceContract(_ethBalanceContract);
    } catch (err) {
      console.error(err);
    }
  };

  /**** SWAP FUNCTIONS ****/

  /**
   * swapTokens: Swaps  `swapAmountWei` of Eth/Crypto Dev tokens with `tokenToBeReceivedAfterSwap` quantity of Eth/Crypto Dev tokens.
   */
  const _swapTokens = async () => {
    strive {
      // Convert the quantity entered by the consumer to a BigNumber utilizing the `parseEther` library from `ethers.js`
      const swapAmountWei = utils.parseEther(swapAmount);
      // Verify if the consumer entered zero
      // We're right here utilizing the `eq` technique from BigNumber class in `ethers.js`
      if (!swapAmountWei.eq(zero)) {
        const signer = await getProviderOrSigner(true);
        setLoading(true);
        // Name the swapTokens operate from the `utils` folder
        await swapTokens(
          signer,
          swapAmountWei,
          tokenToBeReceivedAfterSwap,
          ethSelected
        );
        setLoading(false);
        // Get all of the up to date quantities after the swap
        await getAmounts();
        setSwapAmount("");
      }
    } catch (err) {
      console.error(err);
      setLoading(false);
      setSwapAmount("");
    }
  };

  /**
   * _getAmountOfTokensReceivedFromSwap:  Returns the variety of Eth/Crypto Dev tokens that may be obtained 
   * when the consumer swaps `_swapAmountWEI` quantity of Eth/Crypto Dev tokens.
   */
  const _getAmountOfTokensReceivedFromSwap = async (_swapAmount) => {
    strive {
      // Convert the quantity entered by the consumer to a BigNumber utilizing the `parseEther` library from `ethers.js`
      const _swapAmountWEI = utils.parseEther(_swapAmount.toString());
      // Verify if the consumer entered zero
      // We're right here utilizing the `eq` technique from BigNumber class in `ethers.js`
      if (!_swapAmountWEI.eq(zero)) {
        const supplier = await getProviderOrSigner();
        // Get the quantity of ether within the contract
        const _ethBalance = await getEtherBalance(supplier, null, true);
        // Name the `getAmountOfTokensReceivedFromSwap` from the utils folder
        const amountOfTokens = await getAmountOfTokensReceivedFromSwap(
          _swapAmountWEI,
          supplier,
          ethSelected,
          _ethBalance,
          reservedCD
        );
        settokenToBeReceivedAfterSwap(amountOfTokens);
      } else {
        settokenToBeReceivedAfterSwap(zero);
      }
    } catch (err) {
      console.error(err);
    }
  };

  /*** END ***/

  /**** ADD LIQUIDITY FUNCTIONS ****/

  /**
   * _addLiquidity helps add liquidity to the alternate,
   * If the consumer is including preliminary liquidity, consumer decides the ether and CD tokens he desires so as to add
   * to the alternate. If he's including the liquidity after the preliminary liquidity has already been added
   * then we calculate the crypto dev tokens he can add, given the Eth he desires so as to add by protecting the ratios
   * fixed
   */
  const _addLiquidity = async () => {
    strive {
      // Convert the ether quantity entered by the consumer to Bignumber
      const addEtherWei = utils.parseEther(addEther.toString());
      // Verify if the values are zero
      if (!addCDTokens.eq(zero) && !addEtherWei.eq(zero)) {
        const signer = await getProviderOrSigner(true);
        setLoading(true);
        // name the addLiquidity operate from the utils folder
        await addLiquidity(signer, addCDTokens, addEtherWei);
        setLoading(false);
        // Reinitialize the CD tokens
        setAddCDTokens(zero);
        // Get quantities for all values after the liquidity has been added
        await getAmounts();
      } else {
        setAddCDTokens(zero);
      }
    } catch (err) {
      console.error(err);
      setLoading(false);
      setAddCDTokens(zero);
    }
  };

  /**** END ****/

  /**** REMOVE LIQUIDITY FUNCTIONS ****/

  /**
   * _removeLiquidity: Removes the `removeLPTokensWei` quantity of LP tokens from
   * liquidity and in addition the calculated quantity of `ether` and `CD` tokens
   */
  const _removeLiquidity = async () => {
    strive {
      const signer = await getProviderOrSigner(true);
      // Convert the LP tokens entered by the consumer to a BigNumber
      const removeLPTokensWei = utils.parseEther(removeLPTokens);
      setLoading(true);
      // Name the removeLiquidity operate from the `utils` folder
      await removeLiquidity(signer, removeLPTokensWei);
      setLoading(false);
      await getAmounts();
      setRemoveCD(zero);
      setRemoveEther(zero);
    } catch (err) {
      console.error(err);
      setLoading(false);
      setRemoveCD(zero);
      setRemoveEther(zero);
    }
  };

  /**
   * _getTokensAfterRemove: Calculates the quantity of `Ether` and `CD` tokens
   * that might be returned again to consumer after he removes `removeLPTokenWei` quantity
   * of LP tokens from the contract
   */
  const _getTokensAfterRemove = async (_removeLPTokens) => {
    strive {
      const supplier = await getProviderOrSigner();
      // Convert the LP tokens entered by the consumer to a BigNumber
      const removeLPTokenWei = utils.parseEther(_removeLPTokens);
      // Get the Eth reserves inside the alternate contract
      const _ethBalance = await getEtherBalance(supplier, null, true);
      // get the crypto dev token reserves from the contract
      const cryptoDevTokenReserve = await getReserveOfCDTokens(supplier);
      // name the getTokensAfterRemove from the utils folder
      const { _removeEther, _removeCD } = await getTokensAfterRemove(
        supplier,
        removeLPTokenWei,
        _ethBalance,
        cryptoDevTokenReserve
      );
      setRemoveEther(_removeEther);
      setRemoveCD(_removeCD);
    } catch (err) {
      console.error(err);
    }
  };

  /**** END ****/

  /**
   * connectWallet: Connects the MetaMask pockets
   */
  const connectWallet = async () => {
    strive {
      // Get the supplier from web3Modal, which in our case is MetaMask
      // When used for the primary time, it prompts the consumer to attach their pockets
      await getProviderOrSigner();
      setWalletConnected(true);
    } catch (err) {
      console.error(err);
    }
  };

  /**
   * Returns a Supplier or Signer object representing the Ethereum RPC with or
   * with out the signing capabilities of Metamask hooked up
   *
   * A `Supplier` is required to work together with the blockchain - studying
   * transactions, studying balances, studying state, and so forth.
   *
   * A `Signer` is a particular kind of Supplier utilized in case a `write` transaction
   * must be made to the blockchain, which includes the linked account
   * needing to make a digital signature to authorize the transaction being
   * despatched. Metamask exposes a Signer API to permit your web site to request
   * signatures from the consumer utilizing Signer capabilities.
   *
   * @param {*} needSigner - True in the event you want the signer, default false
   * in any other case
   */
  const getProviderOrSigner = async (needSigner = false) => {
    // Hook up with Metamask
    // Since we retailer `web3Modal` as a reference, we have to entry the `present` worth to get entry to the underlying object
    const supplier = await web3ModalRef.present.join();
    const web3Provider = new suppliers.Web3Provider(supplier);

    // If consumer shouldn't be linked to the Rinkeby community, allow them to know and throw an error
    const { chainId } = await web3Provider.getNetwork();
    if (chainId !== 4) {
      window.alert("Change the community to Rinkeby");
      throw new Error("Change community to Rinkeby");
    }

    if (needSigner) {
      const signer = web3Provider.getSigner();
      return signer;
    }
    return web3Provider;
  };

  // useEffects are used to react to modifications in state of the web site
  // The array on the finish of operate name represents what state modifications will set off this impact
  // On this case, every time the worth of `walletConnected` modifications - this impact shall be referred to as
  useEffect(() => {
    // if pockets shouldn't be linked, create a brand new occasion of Web3Modal and join the MetaMask pockets
    if (!walletConnected) {
      // Assign the Web3Modal class to the reference object by setting it is `present` worth
      // The `present` worth is continued all through so long as this web page is open
      web3ModalRef.present = new Web3Modal({
        community: "rinkeby",
        providerOptions: {},
        disableInjectedProvider: false,
      });
      connectWallet();
      getAmounts();
    }
  }, [walletConnected]);

  /*
      renderButton: Returns a button based mostly on the state of the dapp
  */
  const renderButton = () => {
    // If pockets shouldn't be linked, return a button which permits them to attach their wllet
    if (!walletConnected) {
      return (
        <button onClick={connectWallet} className={kinds.button}>
          Join your pockets
        </button>
      );
    }

    // If we're at the moment ready for one thing, return a loading button
    if (loading) {
      return <button className={kinds.button}>Loading...</button>;
    }

    if (liquidityTab) {
      return (
        <div>
          <div className={kinds.description}>
            You have:
            <br />
            {/* Convert the BigNumber to string utilizing the formatEther operate from ethers.js */}
            {utils.formatEther(cdBalance)} Crypto Dev Tokens
            <br />
            {utils.formatEther(ethBalance)} Ether
            <br />
            {utils.formatEther(lpBalance)} Crypto Dev LP tokens
          </div>
          <div>
            {/* If reserved CD is zero, render the state for liquidity zero the place we ask the consumer
            how a lot preliminary liquidity he desires so as to add else simply render the state the place liquidity shouldn't be zero and
            we calculate based mostly on the `Eth` quantity specified by the consumer how a lot `CD` tokens may be added */}
            {utils.parseEther(reservedCD.toString()).eq(zero) ? (
              <div>
                <enter
                  kind="quantity"
                  placeholder="Quantity of Ether"
                  onChange=
                  className={kinds.enter}
                />
                <enter
                  kind="quantity"
                  placeholder="Quantity of CryptoDev tokens"
                  onChange= "0"))
                    )
                  
                  className={kinds.enter}
                />
                <button className={kinds.button1} onClick={_addLiquidity}>
                  Add
                </button>
              </div>
            ) : (
              <div>
                <enter
                  kind="quantity"
                  placeholder="Quantity of Ether"
                  onChange={async (e) =>  "0");
                    // calculate the variety of CD tokens that
                    // may be added given  `e.goal.worth` quantity of Eth
                    const _addCDTokens = await calculateCD(
                      e.goal.worth }
                  className={kinds.enter}
                />
                <div className={kinds.inputDiv}>
                  {/* Convert the BigNumber to string utilizing the formatEther operate from ethers.js */}
                  {`You'll need ${utils.formatEther(addCDTokens)} Crypto Dev
                  Tokens`}
                </div>
                <button className={kinds.button1} onClick={_addLiquidity}>
                  Add
                </button>
              </div>
            )}
            <div>
              <enter
                kind="quantity"
                placeholder="Quantity of LP Tokens"
                onChange={async (e) =>  "0");
                  // Calculate the quantity of Ether and CD tokens that the consumer would obtain
                  // After he removes `e.goal.worth` quantity of `LP` tokens
                  await _getTokensAfterRemove(e.goal.worth }
                className={kinds.enter}
              />
              <div className={kinds.inputDiv}>
                {/* Convert the BigNumber to string utilizing the formatEther operate from ethers.js */}
                {`You're going to get ${utils.formatEther(removeCD)} Crypto
              Dev Tokens and ${utils.formatEther(removeEther)} Eth`}
              </div>
              <button className={kinds.button1} onClick={_removeLiquidity}>
                Take away
              </button>
            </div>
          </div>
        </div>
      );
    } else {
      return (
        <div>
          <enter
            kind="quantity"
            placeholder="Quantity"
            onChange={async (e) => }
            className={kinds.enter}
            worth={swapAmount}
          />
          <choose
            className={kinds.choose}
            identify="dropdown"
            id="dropdown"
            onChange={async () => {
              setEthSelected(!ethSelected);
              // Initialize the values again to zero
              await _getAmountOfTokensReceivedFromSwap(0);
              setSwapAmount("");
            }}
          >
            <possibility worth="eth">Ethereum</possibility>
            <possibility worth="cryptoDevToken">Crypto Dev Token</possibility>
          </choose>
          <br />
          <div className={kinds.inputDiv}>
            {/* Convert the BigNumber to string utilizing the formatEther operate from ethers.js */}
            {ethSelected
              ? `You're going to get ${utils.formatEther(
                  tokenToBeReceivedAfterSwap
                )} Crypto Dev Tokens`
              : `You're going to get ${utils.formatEther(
                  tokenToBeReceivedAfterSwap
                )} Eth`}
          </div>
          <button className={kinds.button1} onClick={_swapTokens}>
            Swap
          </button>
        </div>
      );
    }
  };

  return (
    <div>
      <Head>
        <title>Crypto Devs</title>
        <meta identify="description" content material="Whitelist-Dapp" />
        <hyperlink rel="icon" href="/favicon.ico" />
      </Head>
      <div className={kinds.principal}>
        <div>
          <h1 className={kinds.title}>Welcome to Crypto Devs Trade!</h1>
          <div className={kinds.description}>
            Trade Ethereum &#60;&#62; Crypto Dev Tokens
          </div>
          <div>
            <button
              className={kinds.button}
              onClick={() => {
                setLiquidityTab(!liquidityTab);
              }}
            >
              Liquidity
            </button>
            <button
              className={kinds.button}
              onClick={() => {
                setLiquidityTab(false);
              }}
            >
              Swap
            </button>
          </div>
          {renderButton()}
        </div>
        <div>
          <img className={kinds.picture} src="./cryptodev.svg" />
        </div>
      </div>

      <footer className={kinds.footer}>
        Made with &#10084; by Crypto Devs
      </footer>
    </div>
  );
}
Enter fullscreen mode

Exit fullscreen mode

  • Now in your terminal which is pointing to my-app folder, execute
  npm run dev
Enter fullscreen mode

Exit fullscreen mode

Your Trade dapp ought to now work with out errors 🚀



Push your Code to Github

Be sure you push all of your code to github earlier than shifting to the subsequent step of deployment




Deploying your dApp

We are going to now deploy your dApp, so that everybody can see your web site and you’ll share it with all your LearnWeb3 DAO associates.

  • Go to https://vercel.com/ and check in along with your GitHub
  • Then click on on New Undertaking button after which choose your Defi-Trade dApp repo
  • When configuring your new undertaking, Vercel will mean you can customise your Root Listing
  • Click on Edit subsequent to Root Listing and set it to my-app
  • Click on Deploy
  • Now you possibly can see your deployed web site by going to your dashboard, deciding on your undertaking, and copying the URL from there!

Share your web site in Discord 😀


Image description

This text is delivered to you by LearnWeb3 DAO. A free, complete A to Z blockchain coaching program for builders throughout the globe.

The whole lot from “What’s a Blockchain” to “Hacking good contracts” - and the whole lot in between, but in addition far more!
Be part of us now to begin buidling with 25,000+ builders.

Website
Discord
Twitter



Add a Comment

Your email address will not be published. Required fields are marked *