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


1% on swapsCrypto Dev LP tokens (Liquidity Provider tokens)Ether user is willing to add to the liquidityLets start building 🚀
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.
bash
mkdir hardhat-tutorial
cd hardhat-tutorial
npm init --yes
npm install --save-dev hardhat
bash
npx hardhat
Create a Javascript projectHardhat Project root.gitignorePress 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
@openzeppelin/contracts as we would be importing Openzeppelin's ERC20 Contract in our Exchange contractbash
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
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
_CryptoDevToken that you deployed in the ICO tutorial as an input paramcryptoDevTokenAddress variablename 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.
address(this).balance so lets just create a function only for getting reserves of the Crypto Dev tokensCrypto Dev Token contract that we deployed is an ERC20balanceOf to check the balance of CryptoDev Tokens
that the contract address holdsgo
/**
* @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
cryptoDevTokenReserve is zero it means that it is the first time someone is adding Crypto Dev tokens and Ether to the contractcryptoDevTokenReserve 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(cryptoDevTokenAmount user can add/cryptoDevTokenReserve in the contract) = (Eth Sent by the user/Eth Reserve in the contract)Crypto Dev tokens user can supply given a certain amount of EthLP tokens because we need to keep track of the amount of liquiidty he has supplied to the contractLP tokens that get minted to the user are propotional to the Eth supplied by the userLP 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)LP tokens that get minted is based on a ratio.(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.
(Eth sent back to the user) / (current Eth reserve) = (amount of LP tokens that user wants to withdraw) / (total supply of LP tokens)(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)LP tokens that user would use to remove liquidity would be burntgo
/**
* @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);
}
Eth to Crypto Dev tokens and other would be Crypto Dev to Ethx 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
XY = K curve(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 ReserveInput 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
$ claude mcp add Defi-Exchange \
-- python -m otcore.mcp_server <graph>