MCPcopy Index your code
hub / github.com/LearnWeb3DAO/Defi-Exchange

github.com/LearnWeb3DAO/Defi-Exchange @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
23 symbols 57 edges 14 files 0 documented · 0%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Build your own Decentralized Exchange like Uniswap

Now its time for you to launch a DeFi Exchange for your Crypto Dev tokens


Requirements

  • Build an exhange with only one asset pair (Eth / Crypto Dev)
  • Your Decentralized Exchange should take a fees of 1% on swaps
  • When user adds liquidity, they should be given Crypto Dev LP tokens (Liquidity Provider tokens)
  • CD LP tokens should be given propotional to the Ether user is willing to add to the liquidity

Lets start building 🚀


Prerequisites


Smart Contract

To build the smart contract we would be using Hardhat. Hardhat is an Ethereum development environment and framework designed for full stack development in Solidity. In simple words you can write your smart contract, deploy them, run tests, and debug your code.

  • To setup a Hardhat project, Open up a terminal and execute these commands

bash mkdir hardhat-tutorial cd hardhat-tutorial npm init --yes npm install --save-dev hardhat

  • In the same directory where you installed Hardhat run:

bash npx hardhat

  • Select Create a Javascript project
  • Press enter for the already specified Hardhat Project root
  • Press enter for the question on if you want to add a .gitignore
  • Press enter for Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)?

  • Now you have a hardhat project ready to go!

  • If you are on Windows, please do this extra step and install these libraries as well :)

bash npm install --save-dev @nomicfoundation/hardhat-toolbox

bash npm install @openzeppelin/contracts

  • Create a new file inside the contracts directory called Exchange.sol. In this tutorial we would cover each part of the contract seperately

  • First lets start by importing ERC20.sol

    • We imported ERC20.sol because our Exchange needs to mint and create Crypto Dev LP tokens thats why it needs to inherit ERC20.sol

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

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

    contract Exchange is ERC20 { } ```

  • Now lets create a constructor for our contract

    • It takes the address of the _CryptoDevToken that you deployed in the ICO tutorial as an input param
    • It then checks if the address is a null address
    • After all the checks, it assigns the value to the input param to the cryptoDevTokenAddress variable
    • Constructor also sets the name and symbol for our Crypto Dev LP token

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

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

    contract Exchange is ERC20 {

      address public cryptoDevTokenAddress;
    
      // Exchange is inheriting ERC20, because our exchange would keep track of Crypto Dev LP tokens
      constructor(address _CryptoDevtoken) ERC20("CryptoDev LP Token", "CDLP") {
          require(_CryptoDevtoken != address(0), "Token address passed is a null address");
          cryptoDevTokenAddress = _CryptoDevtoken;
      }
    

    } ```

  • Time to create a function to get reserves of the Eth and Crypto Dev tokens held by the contract.

    • Eth reserve as we all know would be equal to the balance of the contract and can be found using address(this).balance so lets just create a function only for getting reserves of the Crypto Dev tokens
    • We know that the Crypto Dev Token contract that we deployed is an ERC20
    • So we can just call the balanceOf to check the balance of CryptoDev Tokens that the contract address holds

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

  • Time to create an addLiquidity function which would add liquidity in the form of Ether and Crypto Dev tokens to our contract

    • If cryptoDevTokenReserve is zero it means that it is the first time someone is adding Crypto Dev tokens and Ether to the contract
    • When the user is adding initial liquidity we dont have to maintain any ratio because we dont have any liquidity. So we accept any amount of tokens that user has sent with the initial call
    • If cryptoDevTokenReserve is not zero, then we have to make sure that when someone adds the liquidity it doesnt impact the price which the market currently has
    • To ensure this, we maintain a ratio which has to remain constant
    • Ratio is (cryptoDevTokenAmount user can add/cryptoDevTokenReserve in the contract) = (Eth Sent by the user/Eth Reserve in the contract)
    • This ratio decides how much Crypto Dev tokens user can supply given a certain amount of Eth
    • When user adds liquidity, we need to provide him with some LP tokens because we need to keep track of the amount of liquiidty he has supplied to the contract
    • The amount of LP tokens that get minted to the user are propotional to the Eth supplied by the user
    • In the initial liquidity case, when there is no liquidity: The amount of LP tokens that would be minted to the user is equal to the Eth balance of the contract (because balance is equal to the Eth sent by the user in the addLiquidity call)
    • When there is already liquidity in the contract, the amount of LP tokens that get minted is based on a ratio.
    • The ratio is (LP tokens to be sent to the user (liquidity) / totalSupply of LP tokens in contract) = (Eth sent by the user) / (Eth reserve in the contract)

    go /** * @dev Adds liquidity to the exchange. */ function addLiquidity(uint _amount) public payable returns (uint) { uint liquidity; uint ethBalance = address(this).balance; uint cryptoDevTokenReserve = getReserve(); ERC20 cryptoDevToken = ERC20(cryptoDevTokenAddress); /* If the reserve is empty, intake any user supplied value for `Ether` and `Crypto Dev` tokens because there is no ratio currently */ if(cryptoDevTokenReserve == 0) { // Transfer the `cryptoDevToken` from the user's account to the contract cryptoDevToken.transferFrom(msg.sender, address(this), _amount); // Take the current ethBalance and mint `ethBalance` amount of LP tokens to the user. // `liquidity` provided is equal to `ethBalance` because this is the first time user // is adding `Eth` to the contract, so whatever `Eth` contract has is equal to the one supplied // by the user in the current `addLiquidity` call // `liquidity` tokens that need to be minted to the user on `addLiquidity` call should always be proportional // to the Eth specified by the user liquidity = ethBalance; _mint(msg.sender, liquidity); // _mint is ERC20.sol smart contract function to mint ERC20 tokens } else { /* If the reserve is not empty, intake any user supplied value for `Ether` and determine according to the ratio how many `Crypto Dev` tokens need to be supplied to prevent any large price impacts because of the additional liquidity */ // EthReserve should be the current ethBalance subtracted by the value of ether sent by the user // in the current `addLiquidity` call uint ethReserve = ethBalance - msg.value; // Ratio should always be maintained so that there are no major price impacts when adding liquidity // Ratio here is -> (cryptoDevTokenAmount user can add/cryptoDevTokenReserve in the contract) = (Eth Sent by the user/Eth Reserve in the contract); // So doing some maths, (cryptoDevTokenAmount user can add) = (Eth Sent by the user * cryptoDevTokenReserve /Eth Reserve); uint cryptoDevTokenAmount = (msg.value * cryptoDevTokenReserve)/(ethReserve); require(_amount >= cryptoDevTokenAmount, "Amount of tokens sent is less than the minimum tokens required"); // transfer only (cryptoDevTokenAmount user can add) amount of `Crypto Dev tokens` from users account // to the contract cryptoDevToken.transferFrom(msg.sender, address(this), cryptoDevTokenAmount); // The amount of LP tokens that would be sent to the user should be propotional to the liquidity of // ether added by the user // Ratio here to be maintained is -> // (LP tokens to be sent to the user (liquidity)/ totalSupply of LP tokens in contract) = (Eth sent by the user)/(Eth reserve in the contract) // by some maths -> liquidity = (totalSupply of LP tokens in contract * (Eth sent by the user))/(Eth reserve in the contract) liquidity = (totalSupply() * msg.value)/ ethReserve; _mint(msg.sender, liquidity); } return liquidity; }

  • Now lets create a function for removing liquidity from the contract.

    • The amount of ether that would be sent back to the user would be based on a ratio
    • Ratio is (Eth sent back to the user) / (current Eth reserve) = (amount of LP tokens that user wants to withdraw) / (total supply of LP tokens)
    • The amount of Crypto Dev tokens that would be sent back to the user would also be based on a ratio
    • Ratio is (Crypto Dev sent back to the user) / (current Crypto Dev token reserve) = (amount of LP tokens that user wants to withdraw) / (total supply of LP tokens)
    • The amount of LP tokens that user would use to remove liquidity would be burnt

    go /** * @dev Returns the amount Eth/Crypto Dev tokens that would be returned to the user * in the swap */ function removeLiquidity(uint _amount) public returns (uint , uint) { require(_amount > 0, "_amount should be greater than zero"); uint ethReserve = address(this).balance; uint _totalSupply = totalSupply(); // The amount of Eth that would be sent back to the user is based // on a ratio // Ratio is -> (Eth sent back to the user) / (current Eth reserve) // = (amount of LP tokens that user wants to withdraw) / (total supply of LP tokens) // Then by some maths -> (Eth sent back to the user) // = (current Eth reserve * amount of LP tokens that user wants to withdraw) / (total supply of LP tokens) uint ethAmount = (ethReserve * _amount)/ _totalSupply; // The amount of Crypto Dev token that would be sent back to the user is based // on a ratio // Ratio is -> (Crypto Dev sent back to the user) / (current Crypto Dev token reserve) // = (amount of LP tokens that user wants to withdraw) / (total supply of LP tokens) // Then by some maths -> (Crypto Dev sent back to the user) // = (current Crypto Dev token reserve * amount of LP tokens that user wants to withdraw) / (total supply of LP tokens) uint cryptoDevTokenAmount = (getReserve() * _amount)/ _totalSupply; // Burn the sent LP tokens from the user's wallet because they are already sent to // remove liquidity _burn(msg.sender, _amount); // Transfer `ethAmount` of Eth from user's wallet to the contract payable(msg.sender).transfer(ethAmount); // Transfer `cryptoDevTokenAmount` of Crypto Dev tokens from the user's wallet to the contract ERC20(cryptoDevTokenAddress).transfer(msg.sender, cryptoDevTokenAmount); return (ethAmount, cryptoDevTokenAmount); }

    • Next lets implement the swap functionality
    • Swap would go two ways. One way would be Eth to Crypto Dev tokens and other would be Crypto Dev to Eth
    • Its important for us to determine: Given x amount of Eth/Crypto Dev token sent by the user, how many Eth/Crypto Dev tokens would he receive from the swap?
    • So let's create a function which calculates this:

    • We will charge 1%. This means the amount of input tokens with fees would equal Input amount with fees = (input amount - (1*(input amount)/100)) = ((input amount)*99)/100

    • We need to follow the concept of XY = K curve
    • We need to make sure (x + Δx) * (y - Δy) = x * y, so the final formula is Δy = (y * Δx) / (x + Δx);
    • Δy in our case is tokens to be received, Δx = ((input amount)*99)/100, x = Input Reserve, y = Output Reserve
    • Input Reserve and Ouput Reserve would depend on which swap we are implementing. Eth to Crypto Dev token or vice versa

      ```go /* * @dev Returns the amount Eth/Crypto Dev tokens that would be returned to the user * in the swap / function getAmountOfTokens( uint256 inputAmount, uint256 inputReserve, uint256 outputReserve ) public pure returns (uint256) { require(inputReserve > 0 && outputReserve > 0, "invalid reserves"); // We are cha

Core symbols most depended-on inside this repo

getProviderOrSigner
called by 8
my-app/pages/index.js
getEtherBalance
called by 4
my-app/utils/getAmounts.js
getAmounts
called by 4
my-app/pages/index.js
getReserveOfCDTokens
called by 2
my-app/utils/getAmounts.js
main
called by 1
hardhat-tutorial/scripts/sample-script.js
main
called by 1
hardhat-tutorial/scripts/deploy.js
addLiquidity
called by 1
my-app/utils/addLiquidity.js
getCDTokensBalance
called by 1
my-app/utils/getAmounts.js

Shape

Function 23

Languages

TypeScript100%

Modules by API surface

my-app/pages/index.js9 symbols
my-app/utils/getAmounts.js4 symbols
my-app/utils/swap.js2 symbols
my-app/utils/removeLiquidity.js2 symbols
my-app/utils/addLiquidity.js2 symbols
my-app/pages/api/hello.js1 symbols
my-app/pages/_app.js1 symbols
hardhat-tutorial/scripts/sample-script.js1 symbols
hardhat-tutorial/scripts/deploy.js1 symbols

For agents

$ claude mcp add Defi-Exchange \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact