python |
Pythonタグが付けられた新着投稿 - Qiita |
パナソニックグループプログラミングコンテスト2022(ABC251) A~C問題 ものすごく丁寧でわかりやすい解説 python 灰色~茶色コーダー向け #AtCoder |
https://qiita.com/sano192/items/d56403a5b0ed8188571f
|
atcoder |
2022-05-15 17:36:54 |
Ruby |
Rubyタグが付けられた新着投稿 - Qiita |
Ruby on Railsでアプリを作る準備をする |
https://qiita.com/snuf/items/bdb7a9d5560fd395f64b
|
geminsta |
2022-05-15 17:40:44 |
AWS |
AWSタグが付けられた新着投稿 - Qiita |
【初心者】特徴量エンジニアリング(ダミー変数[One-Hotエンコーディング])について調べてみた |
https://qiita.com/zumax/items/de5cef1ac4a281400764
|
onehot |
2022-05-15 17:14:09 |
Docker |
dockerタグが付けられた新着投稿 - Qiita |
symfony を Dockerで利用する |
https://qiita.com/idani/items/73fb7041aa84ea60a778
|
usingd |
2022-05-15 17:52:21 |
golang |
Goタグが付けられた新着投稿 - Qiita |
カスタムプロバイダを自作したらTerraformの仕組みをよく理解できた |
https://qiita.com/tutu/items/b81c498388fc05dcf845
|
conohavps |
2022-05-15 17:47:19 |
Ruby |
Railsタグが付けられた新着投稿 - Qiita |
Ruby on Railsでアプリを作る準備をする |
https://qiita.com/snuf/items/bdb7a9d5560fd395f64b
|
geminsta |
2022-05-15 17:40:44 |
海外TECH |
DEV Community |
React v18: useTransition hook — Why??? |
https://dev.to/sameer1612/react-v18-usetransition-hook-why-3bml
|
React v useTransition hook ーWhy React v introduced the useTransition hook which may seem like just another hook but let s look into the use and the indications it lays down for the future Long long back React hinted about the concept of concurrent mode whose implementation was a mystery in itself However the goal was to draw a line between the slow intensive operations and much needed faster UI interactions in complex applications One practical problem that I landed into as a fresher was while building a search component which used to fetch recommendations from the backend on every keypress I refactored it with the debounce mechanism to avoid hitting the backend too much In short debouncing means that you only perform a certain action in fixed intervals of time In my case hit the API after seconds of the key press and cancel the ongoing API call if another keypress was registered then If we reflect then we can understand that the solution was to move heavy API operations out of the main typing flow in the search box Had it been sequential it would feel very sluggish to type in the input field Conceptually react did the same thing with this hook It allows you to move your heavy logic out of the main flow into the startTransition method which works independently of the typing flow This results in splitting the work into high and low priority queues This is an “apparent performance gain and shouldn t be confused with some automatic optimization of application from react side The speedup is for the end user s experience whereas for react amount of work done is the same Do note that it s not skipping any operations in between i e calculating UI based on the state of input at the time of rendering it s just changing the priority of rendering multiples table and the input process Let s see it in action now In our demo application we are going to print multiples of the number being typed into the input This calculation is a fairly heavy operation which will slow down our application Putting the useTransition hook to use now for generating multiples It provides a boolean flag to know if the process is completed or not and a startTranistion function which wraps the intensive process The key point to notice is the dissociation in rendering between input and the multiples table Conclusion The example I took maybe an overkill to demonstrate the use of this hook but do share where you find it more fitting This hook is not something that we ll need to use in our day to day work but is a welcomed approach for user ended performance tuning It s totally possible to replicate this behaviour without using this hook but seeing task prioritization in React indicates good progress in concurrency and can help developers build a more refined UX To Connect LinkedIn ️Follow on Medium |
2022-05-15 08:25:08 |
海外TECH |
DEV Community |
Build your own Decentralized Exchange! Use the xy=k AMM curve on Ethereum using Solidity, ethers.js, Next.js, and Web3Modal |
https://dev.to/learnweb3/build-your-own-decentralized-exchange-use-the-xyk-amm-curve-on-ethereum-using-solidity-ethersjs-nextjs-and-web3modal-3ncn
|
Build your own Decentralized Exchange Use the xy k AMM curve on Ethereum using Solidity ethers js Next js and WebModal DeFi ExchangeNow its time for you to launch a DeFi Exchange for your Crypto Dev tokens RequirementsBuild an exhange with only one asset pair Eth lt gt Crypto Dev Your Decentralized Exchange should take a fees of on swapsWhen 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 liquidityLets start building PrerequisitesYou have completed the ICO tutorialYou have completed the Defi Exchange Theory TutorialYou have completed the Mixed Topics Tutorial Smart ContractTo 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 mkdir hardhat tutorial cd hardhat tutorial npm init yes npm install save dev hardhatIn the same directory where you installed Hardhat run npx hardhatSelect Create a basic sample projectPress enter for the already specified Hardhat Project rootPress enter for the question on if you want to add a gitignorePress enter for Do you want to install this sample project s dependencies with npm nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethers Now you have a hardhat project ready to go If you are not on mac please do this extra step and install these libraries as well npm install save dev nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethersIn the same terminal now install openzeppelin contracts as we would be importing Openzeppelin s ERC Contract in our Exchange contract npm install openzeppelin contractsCreate a new file inside the contracts directory called Exchange sol In this tutorial we would cover each part of the contract seperatelyFirst lets start by importing ERC solWe imported ERC sol because our Exchange needs to mint and create Crypto Dev LP tokens thats why it needs to inherit ERC sol SPDX License Identifier MIT pragma solidity import openzeppelin contracts token ERC ERC sol contract Exchange is ERC Now lets create a constructor for our contractIt takes the address of the CryptoDevToken that you deployed in the ICO tutorial as an input paramIt then checks if the address is a null addressAfter all the checks it assigns the value to the input param to the cryptoDevTokenAddress variableConstructor also sets the name and symbol for our Crypto Dev LP token SPDX License Identifier MIT pragma solidity import openzeppelin contracts token ERC ERC sol contract Exchange is ERC address public cryptoDevTokenAddress Exchange is inheriting ERC becase our exchange would keep track of Crypto Dev LP tokens constructor address CryptoDevtoken ERC CryptoDev LP Token CDLP require CryptoDevtoken address Token address passed is a null address cryptoDevTokenAddress CryptoDevtoken Time to create a function to get reserves of the Eth and Crypto Dev tokens heldby 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 tokensWe know that the Crypto Dev Token contract that we deployed is an ERCSo we can just call the balanceOf to check the balance of CryptoDev Tokensthat the contract address holds dev Returns the amount of Crypto Dev Tokens held by the contract function getReserve public view returns uint return ERC 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 contractIf cryptoDevTokenReserve is zero it means that it is the first time someone is adding Crypto Dev tokens and Ether to the contractWhen the user is adding initial liquidity we dont have to maintain any ratio becausewe dont have any liquidity So we accept any amount of tokens that user has sent with the initial callIf 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 hasTo ensure this we maintain a ratio which has to remain constantRatio 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 EthWhen 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 contractThe amount of LP tokens that get minted to the user are propotional to the Eth supplied by the userIn the inital 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 dev Adds liquidity to the exchange function addLiquidity uint amount public payable returns uint uint liquidity uint ethBalance address this balance uint cryptoDevTokenReserve getReserve ERC cryptoDevToken ERC 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 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 ERC sol smart contract function to mint ERC 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 gt 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 gt 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 gt 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 gt 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 ratioRatio 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 ratioRatio 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 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 gt 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 gt 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 gt 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 gt 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 gt 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 ERC cryptoDevTokenAddress transfer msg sender cryptoDevTokenAmount return ethAmount cryptoDevTokenAmount Next lets implement the swap functionalitySwap would go two ways One way would be Eth to Crypto Dev tokens and other would be Crypto Dev to EthIts 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 This means the amount of input tokens with fees would equal Input amount with fees input amount input amount input amount We need to follow the concept of XY K curveWe 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 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 dev Returns the amount Eth Crypto Dev tokens that would be returned to the user in the swap function getAmountOfTokens uint inputAmount uint inputReserve uint outputReserve public pure returns uint require inputReserve gt amp amp outputReserve gt invalid reserves We are charging a fee of Input amount with fee input amount input amount input amount uint inputAmountWithFee inputAmount Because 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 x inputReserve y outputReserve So by putting the values in the formulae you can get the numerator and denominator uint numerator inputAmountWithFee outputReserve uint denominator inputReserve inputAmountWithFee return numerator denominator Now lets implement a function to swap Ether for Crypto Dev tokens dev Swaps Eth for CryptoDev Tokens function ethToCryptoDevToken uint minTokens public payable uint tokenReserve getReserve call the getAmountOfTokens to get the amount of Crypto Dev tokens that would be returned to the user after the swap Notice that the inputReserve we are sending is equal to address this balance msg value instead of just address this balance because address this balance already contains the msg value user has sent in the given call so we need to subtract it to get the actual input reserve uint tokensBought getAmountOfTokens msg value address this balance msg value tokenReserve require tokensBought gt minTokens insufficient output amount Transfer the Crypto Dev tokens to the user ERC cryptoDevTokenAddress transfer msg sender tokensBought Now lets implement a function to swap Crypto Dev tokens to Ether dev Swaps CryptoDev Tokens for Eth function cryptoDevTokenToEth uint tokensSold uint minEth public uint tokenReserve getReserve call the getAmountOfTokens to get the amount of Eth that would be returned to the user after the swap uint ethBought getAmountOfTokens tokensSold tokenReserve address this balance require ethBought gt minEth insufficient output amount Transfer Crypto Dev tokens from the user s address to the contract ERC cryptoDevTokenAddress transferFrom msg sender address this tokensSold send the ethBought to the user from the contract payable msg sender transfer ethBought Your final contract should look like this SPDX License Identifier MITpragma solidity import openzeppelin contracts token ERC ERC sol contract Exchange is ERC address public cryptoDevTokenAddress Exchange is inheriting ERC becase our exchange would keep track of Crypto Dev LP tokens constructor address CryptoDevtoken ERC CryptoDev LP Token CDLP require CryptoDevtoken address Token address passed is a null address cryptoDevTokenAddress CryptoDevtoken dev Returns the amount of Crypto Dev Tokens held by the contract function getReserve public view returns uint return ERC cryptoDevTokenAddress balanceOf address this dev Adds liquidity to the exchange function addLiquidity uint amount public payable returns uint uint liquidity uint ethBalance address this balance uint cryptoDevTokenReserve getReserve ERC cryptoDevToken ERC 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 Transfer the cryptoDevToken address 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 shouls always be proportional to the Eth specified by the user liquidity ethBalance mint msg sender liquidity 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 Ration here is gt 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 gt 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 gt 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 gt 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 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 gt 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 gt 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 gt 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 gt 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 gt 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 ERC cryptoDevTokenAddress transfer msg sender cryptoDevTokenAmount return ethAmount cryptoDevTokenAmount dev Returns the amount Eth Crypto Dev tokens that would be returned to the user in the swap function getAmountOfTokens uint inputAmount uint inputReserve uint outputReserve public pure returns uint require inputReserve gt amp amp outputReserve gt invalid reserves We are charging a fee of Input amount with fee input amount input amount input amount uint inputAmountWithFee inputAmount Because 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 x inputReserve y outputReserve So by putting the values in the formulae you can get the numerator and denominator uint numerator inputAmountWithFee outputReserve uint denominator inputReserve inputAmountWithFee return numerator denominator dev Swaps Eth for CryptoDev Tokens function ethToCryptoDevToken uint minTokens public payable uint tokenReserve getReserve call the getAmountOfTokens to get the amount of Crypto Dev tokens that would be returned to the user after the swap Notice that the inputReserve we are sending is equal to address this balance msg value instead of just address this balance because address this balance already contains the msg value user has sent in the given call so we need to subtract it to get the actual input reserve uint tokensBought getAmountOfTokens msg value address this balance msg value tokenReserve require tokensBought gt minTokens insufficient output amount Transfer the Crypto Dev tokens to the user ERC cryptoDevTokenAddress transfer msg sender tokensBought dev Swaps CryptoDev Tokens for Eth function cryptoDevTokenToEth uint tokensSold uint minEth public uint tokenReserve getReserve call the getAmountOfTokens to get the amount of Eth that would be returned to the user after the swap uint ethBought getAmountOfTokens tokensSold tokenReserve address this balance require ethBought gt minEth insufficient output amount Transfer Crypto Dev tokens from the user s address to the contract ERC cryptoDevTokenAddress transferFrom msg sender address this tokensSold send the ethBought to the user from the contract payable msg sender transfer ethBought Now we would install dotenv package to be able to import the env file and use it in our config Open up a terminal pointing at hardhat tutorial directory and execute this command npm install dotenvNow create a env file in the hardhat tutorial folder and add the following lines use the instructions in the comments to get your Alchemy API Key URL and Rinkeby Private Key Make sure that the account from which you get your Rinkeby private key is funded with Rinkeby Ether Go to sign up create a new App in its dashboard and select the network as Rinkeby and replace add the alchemy key url here with its key url ALCHEMY API KEY URL add the alchemy key url here Replace this private key with your RINKEBY account private key To export your private key from Metamask open Metamask and go to Account Details gt Export Private Key Be aware of NEVER putting real Ether into testing accounts RINKEBY PRIVATE KEY add the rinkeby private key here Lets also create a constants folder to keep track of any constants we have Under the hardhat tutorial folder create a new folder named constants and under the constants folder create a new file index jsInside the index js file add the following lines of code Remember to replace ADDRESS OF CRYPTO DEV TOKEN with the contract address of the Crypto Dev token contract that you deployed in the ICO tutorial const CRYPTO DEV TOKEN CONTRACT ADDRESS ADDRESS OF CRYPTO DEV TOKEN module exports CRYPTO DEV TOKEN CONTRACT ADDRESS Lets deploy the contract to rinkeby network Create a new file named deploy js under the scripts folderNow we would 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 function main const cryptoDevTokenAddress CRYPTO DEV TOKEN CONTRACT ADDRESS A ContractFactory in ethers js is an abstraction used to deploy new smart contracts so exchangeContract here is a factory for instances of our Exchange contract const exchangeContract await ethers getContractFactory Exchange here we deploy the contract const deployedExchangeContract await exchangeContract deploy cryptoDevTokenAddress await deployedExchangeContract deployed print the address of the deployed contract console log Exchange Contract Address deployedExchangeContract address Call the main function and catch if there is any error main then gt process exit catch error gt console error error process exit Now open the hardhat config js file we would add the rinkeby network here so that we can deploy our contract to rinkeby Replace all the lines in the hardhart config js file with the given below lines require nomiclabs hardhat waffle require dotenv config path env const ALCHEMY API KEY URL process env ALCHEMY API KEY URL const RINKEBY PRIVATE KEY process env RINKEBY PRIVATE KEY module exports solidity networks rinkeby url ALCHEMY API KEY URL accounts RINKEBY PRIVATE KEY Compile the contract open up a terminal pointing at hardhat tutorial directory and execute this command npx hardhat compileTo deploy open up a terminal pointing athardhat tutorial directory and execute this command npx hardhat run scripts deploy js network rinkebySave the Exchange Contract Address that was printed on your terminal in your notepad you would need it further down in the tutorial WebsiteTo develop the website we would be using React and Next Js React is a javascript framework which is used to make websites and Next Js is built on top of React First You would need to create a new next app Your folder structure should look something like DeFi Exchange hardhat tutorial my appTo create this my app in the terminal point to DeFi Exchange folder and type npx create next app latestand press enter for all the questionsNow to run the app execute these commands in the terminal cd my app npm run devNow go to http localhost your app should be running Now lets install WebModal library WebModal is an easy to use library to help developers add support for multiple providers in their apps with a simple customizable configuration By default WebModal Library supports injected providers like Metamask Dapper Gnosis Safe Frame Web Browsers etc You can also easily configure the library to support Portis Fortmatic Squarelink Torus Authereum D CENT Wallet and Arkane Open up a terminal pointing atmy app directory and execute this command npm install webmodalIn the same terminal also install ethers js npm i ethersIn your public folder download this image and rename it to cryptodev svg Now go to styles folder and replace all the contents of Home modules css file with the following code this would add some styling to your dapp main min height vh display flex flex direction row justify content center align items center font family Courier New Courier monospace footer display flex padding rem border top px solid eaeaea justify content center align items center image width height margin left input width px height padding margin box shadow px px rgba border radius px title font size rem margin rem description line height margin font size rem button border radius px background color purple border none color ffffff font size px padding px width px cursor pointer margin inputDiv width px height padding margin border lightslategray box shadow px px rgba border radius px select border radius px font size px padding px width px cursor pointer margin button border radius px background color blue border none color ffffff font size px padding px width px cursor pointer margin media max width px main width flex direction column justify content center align items center Now lets create a constants folder to keep track of any constants we might have Create a constants folder under my app folder and inside the constants folder create a file names index jsPaste the following code Replace ABI CRYPTO DEV TOKEN CONTRACT with the abi of the Crypto Dev token contract that you deployed in the ICO tutorial Replace ADDRESS OF CRYPTO DEV TOKEN CONTRACT with the address of the Crypto Dev token contract that you deployed in the ICO tutorialReplace ABI EXCHANGE CONTRACT with the abi of the Exchange Contract To get the abi for your contract go to your hardhat tutorial artifacts contracts Exchange sol folder and from your Exchange json file get the array marked under the abi key Replace ADDRESS EXCHANGE CONTRACT with the address of the exchange contract that you deployed above and saved to your notepadexport 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 would create some utility files which would help us to better interact with the contract Create a utils folder inside the my app folder and inside the folder create files addLiquidity js removeLiquidity js getAmounts js and swap jsLets start by writing some code in getAmounts js This file is used to retrieve balances and reserves for assets import Contract from ethers import EXCHANGE CONTRACT ABI EXCHANGE CONTRACT ADDRESS TOKEN CONTRACT ABI TOKEN CONTRACT ADDRESS from constants getEtherBalance Retrieves the ether balance of the user or the contract export const getEtherBalance async provider address contract false gt try If the caller has set the contract boolean to true retrieve the balance of ether in the exchange contract if it is set to false retrieve the balance of the user s address if contract const balance await provider getBalance EXCHANGE CONTRACT ADDRESS return balance else const balance await provider getBalance address return balance catch err console error err return getCDTokensBalance Retrieves the Crypto Dev tokens in the account of the provided address export const getCDTokensBalance async provider address gt try const tokenContract new Contract TOKEN CONTRACT ADDRESS TOKEN CONTRACT ABI provider const balanceOfCryptoDevTokens await tokenContract balanceOf address return balanceOfCryptoDevTokens catch err console error err getLPTokensBalance Retrieves the amount of LP tokens in the account of the provided address export const getLPTokensBalance async provider address gt try const exchangeContract new Contract EXCHANGE CONTRACT ADDRESS EXCHANGE CONTRACT ABI provider const balanceOfLPTokens await exchangeContract balanceOf address return balanceOfLPTokens catch err console error err getReserveOfCDTokens Retrieves the amount of CD tokens in the exchange contract address export const getReserveOfCDTokens async provider gt try const exchangeContract new Contract EXCHANGE CONTRACT ADDRESS EXCHANGE CONTRACT ABI provider const reserve await exchangeContract getReserve return reserve catch err console error err Lets now write some code for addLiquidity js addLiquidity js has two functions addLiquidity and calculateCDaddLiquidity is used to call the addLiquidity function in the contract to add liquidityIt also gets the Crypto Dev tokens approved for the contract by the user The reason why Crypto Dev tokens need approval is because they are an ERC token For the contract to withdraw an ERC from a user s account it needs the approval from the user s accountcalculateCD tells you for a given amount of Eth how many Crypto Dev tokens can be added to the liquidityWe calculate this by maintaining a ratio The ratio we follow is amount of Crypto Dev tokens to be added Crypto Dev tokens balance Eth that would be added Eth reserve in the contract So by maths we get amount of Crypto Dev tokens to be added Eth that would be added Crypto Dev tokens balance Eth reserve in the contract The ratio is needed so that adding liquidity doesn t largely impact the priceNote tx wait means we are waiting for the transaction to get minedimport 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 exchange If the user is adding initial liquidity user decides the ether and CD tokens he wants to add to the exchange If he is adding the liquidity after the initial liquidity has already been added then we calculate the Crypto Dev tokens he can add given the Eth he wants to add by keeping the ratios constant export const addLiquidity async signer addCDAmountWei addEtherAmountWei gt try create a new instance of the token contract const tokenContract new Contract TOKEN CONTRACT ADDRESS TOKEN CONTRACT ABI signer create a new instance of the exchange contract const exchangeContract new Contract EXCHANGE CONTRACT ADDRESS EXCHANGE CONTRACT ABI signer Because CD tokens are an ERC user would need to give the contract allowance to take the required number 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 in the liquidity tx await exchangeContract addLiquidity addCDAmountWei value addEtherAmountWei await tx wait catch err console error err calculateCD calculates the CD tokens that need to be added to the liquidity given addEtherAmountWei amount of ether export const calculateCD async addEther etherBalanceContract cdTokenReserve gt addEther is a string we need to convert it to a Bignumber before we can do our calculations We do that using the parseEther function from ethers js const addEtherAmountWei utils parseEther addEther Ratio needs to be maintained when we add liquidty We need to let the user know for a specific amount of ether how many CD tokens he can add so that the price impact is not large The ratio we follow is amount of Crypto Dev tokens to be added Crypto Dev tokens balance Eth that would be added Eth reserve in the contract So by maths we get amount of Crypto Dev tokens to be added Eth that would be added Crypto Dev tokens balance Eth reserve in the contract const cryptoDevTokenAmount addEtherAmountWei mul cdTokenReserve div etherBalanceContract return cryptoDevTokenAmount Now add some code to removeLiquidity jsWe have two functions here One is removeLiquidity and the other is getTokensAfterRemoveremoveLiquidity calls the removeLiquidity function from the contract to remove the amount of LP tokens specified by the usergetTokensAfterRemove calulates the amount of Ether and CD tokensthat would be sent back to the user after he removes a certain amount of LPtokens from the poolThe amount of Eth that would be sent back to the user after he withdraws the LP token is calculated based on a ratio Ratio is gt amount of Eth that would be sent back to the user Eth reserve LP tokens withdrawn total supply of LP tokens By some maths we get gt amount of Eth that would be sent back to the user Eth Reserve LP tokens withdrawn total supply of LP tokens Similarly we also maintain a ratio for the CD tokens so here in our caseRatio is gt amount of CD tokens sent back to the user CD Token reserve LP tokens withdrawn total supply of LP tokens Then amount of CD tokens sent back to the user CD token reserve LP tokens withdrawn total supply of LP tokens import Contract providers utils BigNumber from ethers import EXCHANGE CONTRACT ABI EXCHANGE CONTRACT ADDRESS from constants removeLiquidity Removes the removeLPTokensWei amount of LP tokens from liquidity and also the calculated amount of ether and CD tokens export const removeLiquidity async signer removeLPTokensWei gt Create a new instance of the exchange contract const exchangeContract new Contract EXCHANGE CONTRACT ADDRESS EXCHANGE CONTRACT ABI signer const tx await exchangeContract removeLiquidity removeLPTokensWei await tx wait getTokensAfterRemove Calculates the amount of Eth and CD tokens that would be returned back to user after he removes removeLPTokenWei amount of LP tokens from the contract export const getTokensAfterRemove async provider removeLPTokenWei ethBalance cryptoDevTokenReserve gt try Create a new instance of the exchange contract const exchangeContract new Contract EXCHANGE CONTRACT ADDRESS EXCHANGE CONTRACT ABI provider Get the total supply of Crypto Dev LP tokens const totalSupply await exchangeContract totalSupply Here we are using the BigNumber methods of multiplication and division The amount of Eth that would be sent back to the user after he withdraws the LP token is calculated based on a ratio Ratio is gt amount of Eth that would be sent back to the user Eth reserve LP tokens withdrawn total supply of LP tokens By some maths we get gt amount of Eth that would be sent back to the user Eth Reserve LP tokens withdrawn total supply of LP tokens Similarly we also maintain a ratio for the CD tokens so here in our case Ratio is gt amount of CD tokens sent back to the user CD Token reserve LP tokens withdrawn total supply of LP tokens Then amount of CD tokens sent back to the user CD token reserve LP tokens withdrawn total supply 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 s time to write code for swap js our last utils fileIt has two functions getAmountOfTokenReceivedFromSwap and swapTokensswapTokens swaps certain amount of Eth Crypto Dev tokens with Crypto Dev Eth tokensIf Eth has been selected by the user from the UI it means that the user has Eth and he wants to swap it for a certain amount of Crypto Dev tokensIn this case we call the ethToCryptoDevToken function Note that Eth is sent as a value in the function because the user is paying this Eth to the contract Eth sent is not an input param value in this caseOn the other hand if Eth is not selected this means that the user wants to swap Crypto Dev tokens for EthHere we call the cryptoDevTokenToEthgetAmountOfTokensReceivedFromSwap is a function which calculates given a certain amount of Eth Crypto Dev tokens how many Eth Crypto Dev tokens would be sent back to the userIf Eth is selected it calls the getAmountOfTokens from the contract which takes in an input reserve and an output reserve Here input reserve would be the Eth balance of the contract and output reserve would be the Crypto Dev token reserve Opposite would be true if Eth is not selectedimport Contract from ethers import EXCHANGE CONTRACT ABI EXCHANGE CONTRACT ADDRESS TOKEN CONTRACT ABI TOKEN CONTRACT ADDRESS from constants getAmountOfTokensReceivedFromSwap Returns the number of Eth Crypto Dev tokens that can be received when the user swaps swapAmountWei amount of Eth Crypto Dev tokens export const getAmountOfTokensReceivedFromSwap async swapAmountWei provider ethSelected ethBalance reservedCD gt Create a new instance of the exchange contract const exchangeContract new Contract EXCHANGE CONTRACT ADDRESS EXCHANGE CONTRACT ABI provider let amountOfTokens If Eth is selected this means our input value is Eth which means our input amount would be swapAmountWei the input reserve would be the ethBalance of the contract and output reserve would be the Crypto Dev token reserve if ethSelected amountOfTokens await exchangeContract getAmountOfTokens swapAmountWei ethBalance reservedCD else If Eth is not selected this means our input value is Crypto Dev tokens which means our input amount would be swapAmountWei the input reserve would be the Crypto Dev token reserve of the contract and output reserve would be the ethBalance amountOfTokens await exchangeContract getAmountOfTokens swapAmountWei reservedCD ethBalance return amountOfTokens swapTokens Swaps swapAmountWei of Eth Crypto Dev tokens with tokenToBeReceivedAfterSwap amount of Eth Crypto Dev tokens export const swapTokens async signer swapAmountWei tokenToBeReceivedAfterSwap ethSelected gt Create a new instance of the exchange 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 selected call the ethToCryptoDevToken function else call the cryptoDevTokenToEth function from the contract As you can see you need to pass the swapAmount as a value to the function because it is the ether we are paying to the contract instead of a value we are passing to the function if ethSelected tx await exchangeContract ethToCryptoDevToken tokenToBeReceivedAfterSwap value swapAmountWei else User has to approve swapAmountWei for the contract because Crypto Dev token is an ERC tx await tokenContract approve EXCHANGE CONTRACT ADDRESS swapAmountWei toString await tx wait call cryptoDevTokenToEth function which would take in swapAmountWei of Crypto Dev tokens and would send back tokenToBeReceivedAfterSwap amount of Eth to the user tx await exchangeContract cryptoDevTokenToEth swapAmountWei tokenToBeReceivedAfterSwap await tx wait Now its time for the final stages of our app lets add some code to the pages index js file which next already gives you Replace all the contents of the file with the following contentimport BigNumber providers utils from ethers import Head from next head import React useEffect useRef useState from react import WebModal from webmodal import styles from styles Home 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 function Home General state variables loading is set to true when the transaction is mining and set to false when the transaction has mined const loading setLoading useState false We have two tabs in this dapp Liquidity Tab and Swap Tab This variable keeps track of which Tab the user is on If it is set to true this means that the user is on liquidity tab else he is on swap tab const liquidityTab setLiquidityTab useState true This variable is the number in form of a BigNumber const zero BigNumber from Variables to keep track of amount ethBalance keeps track of the amount of Eth held by the user s account const ethBalance setEtherBalance useState zero reservedCD keeps track of the Crypto Dev tokens Reserve balance in the Exchange contract const reservedCD setReservedCD useState zero Keeps track of the ether balance in the contract const etherBalanceContract setEtherBalanceContract useState zero cdBalance is the amount of CD tokens help by the users account const cdBalance setCDBalance useState zero lpBalance is the amount of LP tokens held by the users account const lpBalance setLPBalance useState zero Variables to keep track of liquidity to be added or removed addEther is the amount of Ether that the user wants to add to the liquidity const addEther setAddEther useState zero addCDTokens keeps track of the amount of CD tokens that the user wants to add to the liquidity in case when there is no initial liquidity and after liquidity gets added it keeps track of the CD tokens that the user can add given a certain amount of ether const addCDTokens setAddCDTokens useState zero removeEther is the amount of Ether that would be sent back to the user based on a certain number of LP tokens const removeEther setRemoveEther useState zero removeCD is the amount of Crypto Dev tokens that would be sent back to the user based on a certain number of LP tokens that he wants to withdraw const removeCD setRemoveCD useState zero amount of LP tokens that the user wants to remove from liquidity const removeLPTokens setRemoveLPTokens useState Variables to keep track of swap functionality Amount that the user wants to swap const swapAmount setSwapAmount useState This keeps track of the number of tokens that the user would receive after a swap completes const tokenToBeReceivedAfterSwap settokenToBeReceivedAfterSwap useState zero Keeps track of whether Eth or Crypto Dev token is selected If Eth is selected it means that the user wants to swap some Eth for some Crypto Dev tokens and vice versa if Eth is not selected const ethSelected setEthSelected useState true Wallet connection Create a reference to the Web Modal used for connecting to Metamask which persists as long as the page is open const webModalRef useRef walletConnected keep track of whether the user s wallet is connected or not const walletConnected setWalletConnected useState false getAmounts call various functions to retrive amounts for ethbalance LP tokens etc const getAmounts async gt try const provider await getProviderOrSigner false const signer await getProviderOrSigner true const address await signer getAddress get the amount of eth in the user s account const ethBalance await getEtherBalance provider address get the amount of Crypto Dev tokens held by the user const cdBalance await getCDTokensBalance provider address get the amount of Crypto Dev LP tokens held by the user const lpBalance await getLPTokensBalance provider address gets the amount of CD tokens that are present in the reserve of the Exchange contract const reservedCD await getReserveOfCDTokens provider Get the ether reserves in the contract const ethBalanceContract await getEtherBalance provider 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 amount of Eth Crypto Dev tokens const swapTokens async gt try Convert the amount entered by the user to a BigNumber using the parseEther library from ethers js const swapAmountWei utils parseEther swapAmount Check if the user entered zero We are here using the eq method from BigNumber class in ethers js if swapAmountWei eq zero const signer await getProviderOrSigner true setLoading true Call the swapTokens function from the utils folder await swapTokens signer swapAmountWei tokenToBeReceivedAfterSwap ethSelected setLoading false Get all the updated amounts after the swap await getAmounts setSwapAmount catch err console error err setLoading false setSwapAmount getAmountOfTokensReceivedFromSwap Returns the number of Eth Crypto Dev tokens that can be received when the user swaps swapAmountWEI amount of Eth Crypto Dev tokens const getAmountOfTokensReceivedFromSwap async swapAmount gt try Convert the amount entered by the user to a BigNumber using the parseEther library from ethers js const swapAmountWEI utils parseEther swapAmount toString Check if the user entered zero We are here using the eq method from BigNumber class in ethers js if swapAmountWEI eq zero const provider await getProviderOrSigner Get the amount of ether in the contract const ethBalance await getEtherBalance provider null true Call the getAmountOfTokensReceivedFromSwap from the utils folder const amountOfTokens await getAmountOfTokensReceivedFromSwap swapAmountWEI provider ethSelected ethBalance reservedCD settokenToBeReceivedAfterSwap amountOfTokens else settokenToBeReceivedAfterSwap zero catch err console error err END ADD LIQUIDITY FUNCTIONS addLiquidity helps add liquidity to the exchange If the user is adding initial liquidity user decides the ether and CD tokens he wants to add to the exchange If he is adding the liquidity after the initial liquidity has already been added then we calculate the crypto dev tokens he can add given the Eth he wants to add by keeping the ratios constant const addLiquidity async gt try Convert the ether amount entered by the user to Bignumber const addEtherWei utils parseEther addEther toString Check if the values are zero if addCDTokens eq zero amp amp addEtherWei eq zero const signer await getProviderOrSigner true setLoading true call the addLiquidity function from the utils folder await addLiquidity signer addCDTokens addEtherWei setLoading false Reinitialize the CD tokens setAddCDTokens zero Get amounts 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 amount of LP tokens from liquidity and also the calculated amount of ether and CD tokens const removeLiquidity async gt try const signer await getProviderOrSigner true Convert the LP tokens entered by the user to a BigNumber const removeLPTokensWei utils parseEther removeLPTokens setLoading true Call the removeLiquidity function 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 amount of Ether and CD tokens that would be returned back to user after he removes removeLPTokenWei amount of LP tokens from the contract const getTokensAfterRemove async removeLPTokens gt try const provider await getProviderOrSigner Convert the LP tokens entered by the user to a BigNumber const removeLPTokenWei utils parseEther removeLPTokens Get the Eth reserves within the exchange contract const ethBalance await getEtherBalance provider null true get the crypto dev token reserves from the contract const cryptoDevTokenReserve await getReserveOfCDTokens provider call the getTokensAfterRemove from the utils folder const removeEther removeCD await getTokensAfterRemove provider removeLPTokenWei ethBalance cryptoDevTokenReserve setRemoveEther removeEther setRemoveCD removeCD catch err console error err END connectWallet Connects the MetaMask wallet const connectWallet async gt try Get the provider from webModal which in our case is MetaMask When used for the first time it prompts the user to connect their wallet await getProviderOrSigner setWalletConnected true catch err console error err Returns a Provider or Signer object representing the Ethereum RPC with or without the signing capabilities of Metamask attached A Provider is needed to interact with the blockchain reading transactions reading balances reading state etc A Signer is a special type of Provider used in case a write transaction needs to be made to the blockchain which involves the connected account needing to make a digital signature to authorize the transaction being sent Metamask exposes a Signer API to allow your website to request signatures from the user using Signer functions param needSigner True if you need the signer default false otherwise const getProviderOrSigner async needSigner false gt Connect to Metamask Since we store webModal as a reference we need to access the current value to get access to the underlying object const provider await webModalRef current connect const webProvider new providers WebProvider provider If user is not connected to the Rinkeby network let them know and throw an error const chainId await webProvider getNetwork if chainId window alert Change the network to Rinkeby throw new Error Change network to Rinkeby if needSigner const signer webProvider getSigner return signer return webProvider useEffects are used to react to changes in state of the website The array at the end of function call represents what state changes will trigger this effect In this case whenever the value of walletConnected changes this effect will be called useEffect gt if wallet is not connected create a new instance of WebModal and connect the MetaMask wallet if walletConnected Assign the WebModal class to the reference object by setting it s current value The current value is persisted throughout as long as this page is open webModalRef current new WebModal network rinkeby providerOptions disableInjectedProvider false connectWallet getAmounts walletConnected renderButton Returns a button based on the state of the dapp const renderButton gt If wallet is not connected return a button which allows them to connect their wllet if walletConnected return lt button onClick connectWallet className styles button gt Connect your wallet lt button gt If we are currently waiting for something return a loading button if loading return lt button className styles button gt Loading lt button gt if liquidityTab return lt div gt lt div className styles description gt You have lt br gt Convert the BigNumber to string using the formatEther function from ethers js utils formatEther cdBalance Crypto Dev Tokens lt br gt utils formatEther ethBalance Ether lt br gt utils formatEther lpBalance Crypto Dev LP tokens lt div gt lt div gt If reserved CD is zero render the state for liquidity zero where we ask the user how much initial liquidity he wants to add else just render the state where liquidity is not zero and we calculate based on the Eth amount specified by the user how much CD tokens can be added utils parseEther reservedCD toString eq zero lt div gt lt input type number placeholder Amount of Ether onChange e gt setAddEther e target value className styles input gt lt input type number placeholder Amount of CryptoDev tokens onChange e gt setAddCDTokens BigNumber from utils parseEther e target value className styles input gt lt button className styles button onClick addLiquidity gt Add lt button gt lt div gt lt div gt lt input type number placeholder Amount of Ether onChange async e gt setAddEther e target value calculate the number of CD tokens that can be added given e target value amount of Eth const addCDTokens await calculateCD e target value etherBalanceContract reservedCD setAddCDTokens addCDTokens className styles input gt lt div className styles inputDiv gt Convert the BigNumber to string using the formatEther function from ethers js You will need utils formatEther addCDTokens Crypto Dev Tokens lt div gt lt button className styles button onClick addLiquidity gt Add lt button gt lt div gt lt div gt lt input type number placeholder Amount of LP Tokens onChange async e gt setRemoveLPTokens e target value Calculate the amount of Ether and CD tokens that the user would receive After he removes e target value amount of LP tokens await getTokensAfterRemove e target value className styles input gt lt div className styles inputDiv gt Convert the BigNumber to string using the formatEther function from ethers js You will get utils formatEther removeCD Crypto Dev Tokens and utils formatEther removeEther Eth lt div gt lt button className styles button onClick removeLiquidity gt Remove lt button gt lt div gt lt div gt lt div gt else return lt div gt lt input type number placeholder Amount onChange async e gt setSwapAmount e target value Calculate the amount of tokens user would receive after the swap await getAmountOfTokensReceivedFromSwap e target value className styles input value swapAmount gt lt select className styles select name dropdown id dropdown onChange async gt setEthSelected ethSelected Initialize the values back to zero await getAmountOfTokensReceivedFromSwap setSwapAmount gt lt option value eth gt Ethereum lt option gt lt option value cryptoDevToken gt Crypto Dev Token lt option gt lt select gt lt br gt lt div className styles inputDiv gt Convert the BigNumber to string using the formatEther function from ethers js ethSelected You will get utils formatEther tokenToBeReceivedAfterSwap Crypto Dev Tokens You will get utils formatEther tokenToBeReceivedAfterSwap Eth lt div gt lt button className styles button onClick swapTokens gt Swap lt button gt lt div gt return lt div gt lt Head gt lt title gt Crypto Devs lt title gt lt meta name description content Whitelist Dapp gt lt link rel icon href favicon ico gt lt Head gt lt div className styles main gt lt div gt lt h className styles title gt Welcome to Crypto Devs Exchange lt h gt lt div className styles description gt Exchange Ethereum amp amp Crypto Dev Tokens lt div gt lt div gt lt button className styles button onClick gt setLiquidityTab liquidityTab gt Liquidity lt button gt lt button className styles button onClick gt setLiquidityTab false gt Swap lt button gt lt div gt renderButton lt div gt lt div gt lt img className styles image src cryptodev svg gt lt div gt lt div gt lt footer className styles footer gt Made with amp by Crypto Devs lt footer gt lt div gt Now in your terminal which is pointing to my app folder execute npm run devYour Exchange dapp should now work without errors Push your Code to GithubMake sure you push all your code to github before moving to the next step of deployment Deploying your dAppWe will now deploy your dApp so that everyone can see your website and you can share it with all of your LearnWeb DAO friends Go to and sign in with your GitHubThen click on New Project button and then select your Defi Exchange dApp repoWhen configuring your new project Vercel will allow you to customize your Root DirectoryClick Edit next to Root Directory and set it to my appClick DeployNow you can see your deployed website by going to your dashboard selecting your project and copying the URL from there Share your website in Discord DThis article is brought to you by LearnWeb DAO A free comprehensive A to Z blockchain training program for developers across the globe Everything from What is a Blockchain to Hacking smart contracts and everything in between but also much more Join us now to start buidling with builders WebsiteDiscordTwitter |
2022-05-15 08:20:37 |
海外TECH |
DEV Community |
How do DEX's work? Understand Uniswap v1 by deep diving into the math and code. Learn the xy=k AMM curve. |
https://dev.to/learnweb3/how-do-dexs-work-understand-uniswap-v1-by-deep-diving-into-the-math-and-code-learn-the-xyk-amm-curve-46hb
|
How do DEX x s work Understand Uniswap v by deep diving into the math and code Learn the xy k AMM curve DeFi Exchange TheoryMost of us are used to buying crypto through centralized exchanges Binance Coinbase etc Often times we resolve to the same platforms when trading between different cryptocurrencies However centralized exchanges are rife with problems They can get hacked and lose all their user s money or worse yet the company behind the exchange can close up shop and run away with all the money This may seem extreme but this is not fiction Mt GoxMt Gox was the leading Bitcoin exchange from At it s peak it was responsible for of all Bitcoin transactions In early the company reported they were missing hundreds of thousands of Bitcoin and declared bankruptcy Today those lost Bitcoin are worth billions of dollars In the following years Mt Gox faced several lawsuits some of which are still going on years later You can read more about what happened with Mt Gox here QuadrigaCXQuadrigaCX a centralized exchange based in Canada went under in The founder of Quadriga mysteriously died and all the crypto on the platform disappeared with him Users reported almost million in lost funds The Ontario Securities Commision conducted thorough research into the activities of the company and declared that the founder of Quadriga was simply a fraud You can read more about what happened with Quadriga hereThis is not an exhaustive list by any means but it gives you an idea Web has a common sayingNot your keys not your coinswhich means that if you don t own your private keys and instead trust a centralized exchange to manage them for you you don t really own your cryptocurrency coins This is true The Birth of Decentralized ExchangesThe idea of a decentralized exchange is simple allow users to trade their crypto directly on chain through smart contracts without giving up control of their private keys While it sounds simple the reality is much more complicated In short decentralized exchanges are a beautiful piece of mathematics and software combined together We hope by the end of this article you will share the same feeling The birth of modern decentralized exchanges was primarily led by Uniswap Not only is Uniswap the leading decentralized exchange on Ethereum it is THE leading dApp on Ethereum in general After Vitalik Buterin posted a blog post on Path Independence in Hayden Adams was inspired to try to implement Vitalik s ideas in what eventually became Uniswap After spending over a year working on the code Hayden finally announced and launched Uniswap in November You can read more about the history of Uniswap in this blog post by the founder In this article we will attempt to go over the mathematics that allow for Uniswap to exist and function and hope to give you an insight into why it s so amazing Why is it complicated You might be wondering why can t we just recreate a centralized exchange on chain Well you can but it s not good enough Centralized exchanges typically work on an order book system Alice puts up a listing saying she is willing to sell of TokenA for of TokenB and the listing is added to the order book At some point if Bob comes along and says he wants to buy of TokenA for of TokenB their orders are matched together and the trade is executed Order book based exchanges were attempted on Ethereum with the most significant example being xProject but due to the high gas required for all the storage and matching algorithms it was challenging to attract users There was need for a new approach a way to allow users to swap between any two tokens arbitrarily without needing an orderbook Additionally cookie points if users could actually earn money by using Uniswap Uniswap V V VAs of January three versions of Uniswap have been launched The first version was launched in November and it allowed only swaps between ether and a token Chained swaps were also possible to allow token token swaps Chained swaps would allow for a TokenA lt gt TokenB swap by first swapping one of them for ETH and then swapping the ETH for the second token V was launched in March and it was a huge improvement of V that allowed direct swaps between any ERC tokens as well as chained swaps between any pairs V was launched in May and it significantly improved capital efficiency which allowed liquidity providers to remove a bigger portion of their liquidity from pools and still keep getting the same rewards or squeeze the capital in smaller price ranges and get up to x of profits For the purposes of this tutorial we will be focusing on the design of Uniswap V and in the following level we will actually implement a slightly simplified version of it that allows swapping between ether and a token Market MakersUniswap is an Automated Market Maker Let s try to understand what that means Market Makers are entities that provide liquidity assets to trading markets In non orderbook systems liquidity is what allows trading to be possible That means if you want to sell BTC to buy ETH the exchange must have an ETH balance you can purchase from in exchange for BTC Some trading pairs have very high liquidity eg BTC lt gt ETH trading pair but some have extremely low or no liquidity at all eg scam tokens or newly created tokens A DEX must have enough liquidity to function and serve as an alternative to centralized exchanges One way to get that liquidity is that the developers or investors put in their own money and become market makers However this is not realistic as they would need a huge amount of money to provide enough liquidity for all possible trading pairs Moreover this hurts decentralization as the developers investors would hold all the power in the market Another way which Uniswap implemented was to let anyone be a market maker and this is what makes Uniswap an automated market maker Any user can deposit funds to a specific trading pair and add liquidity and in exchange earn money for doing so through trading fees taken from the users Functional RequirementsConsidering what we have learnt we need to allow for the following functionality at least to build an automated market maker Anyone can add liquidity to become a liquidity providerLiquidity providers can remove their liquidity and get back their crypto whenever they wantUsers can swap between assets present in the trading pool assuming there is enough liquidityUsers are charged a small trading fees that gets distributed amongst the liquidity providers so they can earn for providing liquidity XY KAt the core of Uniswap is one math function x y kAssume we have a trading pair for ETH lt gt LW Tokenx reserve balance of ETH in the trading pooly reserve balance of LW Token in the trading poolk a constantThis formula is responsible for calculating prices deciding how much LW Token would be received in exchange for a certain amount of ETH or vice versa NOTE It doesn t matter if we use x to represent the reserve of ETH or LW Token as long as y represents the opposite The formula states that k is a constant no matter what the reserves x and y are Every swap made increases the reserve of either ETH or LW Token and decreases the reserve of the other Let s try to write that as a formula x Δx y Δy kwhere Δx is the amount being provided by the user for sale and Δy is the amount the user is receiving from the DEX in exchange for Δx Since k is a constant we can compare the above two formulas to get x y x Δx y Δy Now before a swap occurs we know the values of x y and Δx given by user We are interested in calculating Δy which is the amount of ETH or LW Token the user will receive We can simplify the above equation to solve for Δy and we get the following formula Δy y Δx x Δx Let s try to code this up in Solidity function calculateOutputAmount uint inputAmount uint inputReserve uint outputReserve private pure returns uint uint outputAmount outputReserve inputAmount inputReserve inputAmount return outputAmount Assume we have ETH and LW Token in the contract What would happen if I want to swap ETH for LW Tokens Let s do the math inputAmount ETHinputReserve ETHoutputReserve LW Tokens gt outputAmount LW TokensWhat would happen if instead I wanted to swap LW Tokens for ETH inputAmount LW TokensinputReserve LW TokensoutputReserve ETH gt outputAmount ETHThese amounts are very close to the ratio of tokens present in the contract reserves but slightly smaller Why The product formula we use for price calculations is actually a hyperbola The hyperbola never intersects at x or y this means that neither of the reserves will ever be just as a product of trading Reserves are infinite SlippageSince we don t get tokens in the exact ratio of reserves this leads to an interesting implication of the math The price function causes slippage in the price The bigger the amount of tokens being traded relative to their reserve values the lower the price would be Let s say I wanted to try to drain out the entire pool and sell ETH inputAmount ETHinputReserve ETHoutputReserve LW Tokens gt outputAmount LW TokensAs you can see when we re trying to drain out the pool we re only getting close to a half of what we expect Some may see this as a flaw of automated market makers that follow x y k but it s actually not It is the same mechanism that protects pools from being completely drained This also aligns with the law of supply and demand the higher the demand relative to the supply the more costly it is to buy that supply Who sets the initial price When a new cryptocurrency is created there is no liquidity for trading pairs involving that token As such there is no way to calculate the price for it Therefore the first person adding liquidity to the pool gets to set a price Adding liquidity involves adding tokens from both sides of the trading pair you cannot add liquidity for just one side When the first person adds liquidity it creates a reserve balance and sets the initial x and y values From that point onward we can do price calculations when swapping between tokens A simple implementation of the addLiquidity function in Solidity would look something like this function addLiquidity uint tokenAmount public payable IERC token IERC tokenAddress token transferFrom msg sender address this tokenAmount This function accepts ETH and a token from the user However this implementation is incomplete A second person can come along and add liquidity in a completely different ratio of reserves which would massively affect price calculations We do not want to allow for such price manipulations and we want prices on the decentralized exchange to be as close to those on centralized exchanges as possible So we must ensure that anyone adding additional liquidity to the pool is doing so in the same proportion as that already established in the pool We only want to allow arbitrary ratios when the pool is completely empty This leads to an implementation that looks like this function addLiquidity uint tokenAmount public payable assuming a hypothetical function that returns the balance of the token in the contract if getReserve IERC token IERC tokenAddress token transferFrom msg sender address this tokenAmount else uint ethReserve address this balance msg value uint tokenReserve getReserve uint proportionalTokenAmount msg value tokenReserve ethReserve require tokenAmount gt proportionalTokenAmount incorrect ratio of tokens provided IERC token IERC tokenAddress token transferFrom msg sender address this proportionalTokenAmount LP TokensSo far we have discussed how to add liquidity and how to do price calculations for swaps But what if a liquidity provider wants to withdraw their liquidity from the pool We need a way to reward the liquidity providers for their tokens as without them other users would not have been able to perform swaps Nobody would put tokens in a third party contract if they are not getting something out of it The only good solution for this is to collect a small fee on each token swap and distribute the fees amongst the liquidity providers based on how much liquidity they provided If someone provided of the pool s liquidity they should receive of the fees Makes sense There is a quite elegant solution to do this Liquidity Provider Tokens LP Tokens LP Tokens work as shares You get LP tokens in exchange for your liquidityAmount of tokens you get is proportional to your share of the liquidity in the poolFees are distributed proportional to how many LP tokens you ownLP tokens can be exchanged back for the liquidity earned feesBut there are additional requirements Issued shares must always be correct When someone else deposits or removes liquidity after you your shares should remain and maintain correct values Writing data to the chain can be expensive gas fees we want to reduce the maintainence costs of LP tokens as much as possible Imagine we issue a lot of tokens say a few billion The first time someone adds liquidity they own of the liquidity in the pool So do we give them all few billion tokens This leads to the problem that when a second person adds liquidity the shares need to be recalculated which is expensive due to gas fees Alternatively if we choose to distribute only a portion of the tokens initially we risk hitting the limit which will also eventually force us to recalculate existing shares The only good solution seems to have no supply limit at all and mint new tokens whenever new liquidity is added This allows for infinite growth and if we do the math carefully we can make sure issued shares remain correct whenever liquidity is added or removed So how do we calculate the amount of LP tokens to be minted when liquidity is added Uniswap V calculates the amount proportionate to the ETH reserve The following equation shows how the amount of new LP tokens is calculated depending on how much ETH is deposited amountMinted totalAmount ethDeposited ethReserve We can update addLiquidity function to mint LP tokens when liquidity is added function addLiquidity uint tokenAmount public payable if getReserve uint liquidity address this balance mint msg sender liquidity else uint liquidity totalSupply msg value ethReserve mint msg sender liquidity Now we have LP tokens we can also use them to calculate how much underlying tokens to return when someone wants to withdraw their liquidity in exchange for their LP tokens We don t need to remember how much they originally deposited Since LP tokens are proportional to amount of ETH deposited we can rearrange the above formula to calculate the amount of ETH to return and proportionately calculate the amount of tokens to return FeesNow to collect fees on swaps and distribute them amongst liquidity providers we need to think about a couple of things Do we collect fees in ETH or tokens Do we pay rewards in ETH or tokens How do we collect the fees from each swap How to distribute the fees amongst all liquidity providers These may seem difficult questions to answer but we actually have everything we need to answer them Traders are already sending ether tokens to the contract Instead of asking for an explicit fee we can just deduct some amount from the ether tokens they are sending We can just add the fees to the reserve balance This means over time the reserves will grow We can collect fees in the currency of the asset being deposited by the trader Liquidity providers thus get a balanced amount of ether and tokens proportional to their share of LP tokens Uniswap takes a fees from each swap Let s say we take to keep things simple Adding fees to the contract is as simple as making a few edits to our price calculation formula We had outputAmount outputReserve inputAmount inputReserve inputAmount Now outputAmountWithFees outputAmountBut Solidity does not support floating point operations So for Solidity we rewrite the formula as such outputAmountWithFees outputAmount Congratulations This was a big tutorial with a lot of condensed information Congratulations on making it this far While the math and the ideas can be a little tricky to initially understand we hope that going by going through the material and asking questions on Discord you can develop an appreciation for how beautifully architected all of this is See you in the next level where we will actually implement the full contract along with a website for the DEX This article is brought to you by LearnWeb DAO A free comprehensive A to Z blockchain training program for developers across the globe Everything from What is a Blockchain to Hacking smart contracts and everything in between but also much more Join us now to start buidling with builders WebsiteDiscordTwitter |
2022-05-15 08:19:37 |
海外TECH |
DEV Community |
Build an on-chain DAO for your NFT holders on Ethereum using Solidity, Next.js, ethers.js, Web3Modal |
https://dev.to/learnweb3/build-an-on-chain-dao-for-your-nft-holders-on-ethereum-using-solidity-nextjs-ethersjs-web3modal-481a
|
Build an on chain DAO for your NFT holders on Ethereum using Solidity Next js ethers js WebModal Building a DAO What is a DAO DAO stands for Decentralized Autonomous Organization You can think of DAOs as analogous to companies in the real world Essentially DAOs allow for members to create and vote on governance decisions In traditional companies when a decision needs to be made the board of directors or executives of the company are in charge of making that decision In a DAO however this process is democratized and any member can create a proposal and all other members can vote on it Each proposal created has a deadline for voting and after the deadline the decision is made in favour of the voting outcome YES or NO Membership in DAOs is typically restricted either by ownership of ERC tokens or by ownership of NFTs Examples of DAOs where membership and voting power is proportional to how many tokens you own include Uniswap and ENS Examples of DAOs where they are based on NFTs include Meebits DAO Building our DAOYou want to launch a DAO for holders of your CryptoDevs NFTs From the ETH that was gained through the ICO you built up a DAO Treasury The DAO now has a lot of ETH but currently does nothing with it You want to allow your NFT holders to create and vote on proposals to use that ETH for purchasing other NFTs from an NFT marketplace and speculate on price Maybe in the future when you sell the NFT back you split the profits among all members of the DAO RequirementsAnyone with a CryptoDevs NFT can create a proposal to purchase a different NFT from an NFT marketplaceEveryone with a CryptoDevs NFT can vote for or against the active proposalsEach NFT counts as one vote for each proposalVoter cannot vote multiple times on the same proposal with the same NFTIf majority of the voters vote for the proposal by the deadline the NFT purchase is automatically executed What we will makeTo be able to purchase NFTs automatically when a proposal is passed you need an on chain NFT marketplace that you can call a purchase function on There exist a lot of NFT marketplaces out there but to avoid overcomplicating things we will create a simplified fake NFT marketplace for this tutorial as the focus is on the DAO We will also make the actual DAO smart contract using Hardhat We will make the website using Next js to allow users to create and vote on proposals PrerequisitesYou have completed the NFT Collection TutorialYou must have some ETH to give to the DAO Treasury BUIDL IT Smart Contract DevelopmentWe will start off with first creating the smart contracts We will be making two smart contracts FakeNFTMarketplace solCryptoDevsDAO solTo do so we will use the Hardhat development framework we have been using for the last few tutorials Create a folder for this project named DAO Tutorial and open up a Terminal window in that folder Setup a new hardhat project by running the following commands in your terminal mkdir hardhat tutorial cd hardhat tutorial npm init yes npm install save dev hardhatNow that you have installed Hardhat we can setup a project Execute the following command in your terminal In the same directory where you installed Hardhat run npx hardhatSelect Create a basic sample projectPress enter for the already specified Hardhat Project rootPress enter for the question on if you want to add a gitignorePress enter for Do you want to install this sample project s dependencies with npm nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethers Now you have a hardhat project ready to go If you are not on mac please do this extra step and install these libraries as well npm install save dev nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethersand press Enter for all the questions Choose the Create a basic sample project option Now let s install the openzeppelin contracts package from NPM as we will be using OpenZeppelin s Ownable Contract for the DAO contract npm install openzeppelin contractsFirst let s make a simple Fake NFT Marketplace Create a file named FakeNFTMarketplace sol under the contracts directory within hardhat tutorial and add the following code SPDX License Identifier MIT pragma solidity contract FakeNFTMarketplace dev Maintain a mapping of Fake TokenID to Owner addresses mapping uint gt address public tokens dev Set the purchase price for each Fake NFT uint nftPrice ether dev purchase accepts ETH and marks the owner of the given tokenId as the caller address param tokenId the fake NFT token Id to purchase function purchase uint tokenId external payable require msg value nftPrice This NFT costs ether tokens tokenId msg sender dev getPrice returns the price of one NFT function getPrice external view returns uint return nftPrice dev available checks whether the given tokenId has already been sold or not param tokenId the tokenId to check for function available uint tokenId external view returns bool address x This is the default value for addresses in Solidity if tokens tokenId address return true return false The FakeNFTMarketplace exposes some basic functions that we will be using from the DAO contract to purchase NFTs if a proposal is passed A real NFT marketplace would be more complicated as not all NFTs have the same price Let s make sure everything compiles before we start writing the DAO Contract Run the following command inside the hardhat tutorial folder from your Terminal npx hardhat compileand make sure there are no compilation errors Now we will start writing the CryptoDevsDAO contract Since this is mostly a completely custom contract and relatively more complicated than what we have done so far we will explain this one bit by bit First let s write the boilerplate code for the contract Create a new file named CryptoDevsDAO sol under the contracts directory in hardhat tutorial and add the following code to it SPDX License Identifier MIT pragma solidity import openzeppelin contracts access Ownable sol We will add the Interfaces here contract CryptoDevsDAO is Ownable We will write contract code here Now we will need to call functions on the FakeNFTMarketplace contract and your previously deployed CryptoDevs NFT contract Recall from the Advanced Solidity Topics tutorial that we need to provide an interface for those contracts so this contract knows which functions are available to call and what they take as parameters and what they return Add the following two interfaces to your code by adding the following code Interface for the FakeNFTMarketplace interface IFakeNFTMarketplace dev getPrice returns the price of an NFT from the FakeNFTMarketplace return Returns the price in Wei for an NFT function getPrice external view returns uint dev available returns whether or not the given tokenId has already been purchased return Returns a boolean value true if available false if not function available uint tokenId external view returns bool dev purchase purchases an NFT from the FakeNFTMarketplace param tokenId the fake NFT tokenID to purchase function purchase uint tokenId external payable Minimal interface for CryptoDevsNFT containing only two functions that we are interested in interface ICryptoDevsNFT dev balanceOf returns the number of NFTs owned by the given address param owner address to fetch number of NFTs for return Returns the number of NFTs owned function balanceOf address owner external view returns uint dev tokenOfOwnerByIndex returns a tokenID at given index for owner param owner address to fetch the NFT TokenID for param index index of NFT in owned tokens array to fetch return Returns the TokenID of the NFT function tokenOfOwnerByIndex address owner uint index external view returns uint Now let s think about what functionality we need in the DAO contract Store created proposals in contract stateAllow holders of the CryptoDevs NFT to create new proposalsAllow holders of the CryptoDevs NFT to vote on proposals given they haven t already voted and that the proposal hasn t passed it s deadline yetAllow holders of the CryptoDevs NFT to execute a proposal after it s deadline has been exceeded triggering an NFT purchase in case it passedLet s start off by creating a struct representing a Proposal In your contract add the following code Create a struct named Proposal containing all relevant information struct Proposal nftTokenId the tokenID of the NFT to purchase from FakeNFTMarketplace if the proposal passes uint nftTokenId deadline the UNIX timestamp until which this proposal is active Proposal can be executed after the deadline has been exceeded uint deadline yayVotes number of yay votes for this proposal uint yayVotes nayVotes number of nay votes for this proposal uint nayVotes executed whether or not this proposal has been executed yet Cannot be executed before the deadline has been exceeded bool executed voters a mapping of CryptoDevsNFT tokenIDs to booleans indicating whether that NFT has already been used to cast a vote or not mapping uint gt bool voters Let s also create a mapping from Proposal IDs to Proposals to hold all created proposals and a counter to count the number of proposals that exist Create a mapping of ID to Proposal mapping uint gt Proposal public proposals Number of proposals that have been created uint public numProposals Now since we will be calling functions on the FakeNFTMarketplace and CryptoDevsNFT contract let s initialize variables for those contracts IFakeNFTMarketplace nftMarketplace ICryptoDevsNFT cryptoDevsNFT Create a constructor function that will initialize those contract variables and also accept an ETH deposit from the deployer to fill the DAO ETH treasury In the background since we imported the Ownable contract this will also set the contract deployer as the owner of this contract Create a payable constructor which initializes the contract instances for FakeNFTMarketplace and CryptoDevsNFT The payable allows this constructor to accept an ETH deposit when it is being deployed constructor address nftMarketplace address cryptoDevsNFT payable nftMarketplace IFakeNFTMarketplace nftMarketplace cryptoDevsNFT ICryptoDevsNFT cryptoDevsNFT Now since we want pretty much all of our other functions to only be called by those who own NFTs from the CryptoDevs NFT contract we will create a modifier to avoid duplicating code Create a modifier which only allows a function to be called by someone who owns at least CryptoDevsNFT modifier nftHolderOnly require cryptoDevsNFT balanceOf msg sender gt NOT A DAO MEMBER We now have enough to write our createProposal function which will allow members to create new proposals dev createProposal allows a CryptoDevsNFT holder to create a new proposal in the DAO param nftTokenId the tokenID of the NFT to be purchased from FakeNFTMarketplace if this proposal passes return Returns the proposal index for the newly created proposal function createProposal uint nftTokenId external nftHolderOnly returns uint require nftMarketplace available nftTokenId NFT NOT FOR SALE Proposal storage proposal proposals numProposals proposal nftTokenId nftTokenId Set the proposal s voting deadline to be current time minutes proposal deadline block timestamp minutes numProposals return numProposals Now to vote on a proposal we want to add an additional restriction that the proposal being voted on must not have had it s deadline exceeded To do so we will create a second modifier Create a modifier which only allows a function to be called if the given proposal s deadline has not been exceeded yet modifier activeProposalOnly uint proposalIndex require proposals proposalIndex deadline gt block timestamp DEADLINE EXCEEDED Note how this modifier takes a parameter Additionally since a vote can only be one of two values YAY or NAY we can create an enum representing possible options Create an enum named Vote containing possible options for a vote enum Vote YAY YAY NAY NAY Let s write the voteOnProposal function dev voteOnProposal allows a CryptoDevsNFT holder to cast their vote on an active proposal param proposalIndex the index of the proposal to vote on in the proposals array param vote the type of vote they want to cast function voteOnProposal uint proposalIndex Vote vote external nftHolderOnly activeProposalOnly proposalIndex Proposal storage proposal proposals proposalIndex uint voterNFTBalance cryptoDevsNFT balanceOf msg sender uint numVotes Calculate how many NFTs are owned by the voter that haven t already been used for voting on this proposal for uint i i lt voterNFTBalance i uint tokenId cryptoDevsNFT tokenOfOwnerByIndex msg sender i if proposal voters tokenId false numVotes proposal voters tokenId true require numVotes gt ALREADY VOTED if vote Vote YAY proposal yayVotes numVotes else proposal nayVotes numVotes We re almost done To execute a proposal whose deadline has exceeded we will create our final modifier Create a modifier which only allows a function to be called if the given proposals deadline HAS been exceeded and if the proposal has not yet been executed modifier inactiveProposalOnly uint proposalIndex require proposals proposalIndex deadline lt block timestamp DEADLINE NOT EXCEEDED require proposals proposalIndex executed false PROPOSAL ALREADY EXECUTED Note this modifier also takes a parameter Let s write the code for executeProposal dev executeProposal allows any CryptoDevsNFT holder to execute a proposal after it s deadline has been exceeded param proposalIndex the index of the proposal to execute in the proposals array function executeProposal uint proposalIndex external nftHolderOnly inactiveProposalOnly proposalIndex Proposal storage proposal proposals proposalIndex If the proposal has more YAY votes than NAY votes purchase the NFT from the FakeNFTMarketplace if proposal yayVotes gt proposal nayVotes uint nftPrice nftMarketplace getPrice require address this balance gt nftPrice NOT ENOUGH FUNDS nftMarketplace purchase value nftPrice proposal nftTokenId proposal executed true We have at this point implemented all the core functionality However there are a couple of additional features we could and should implement Allow the contract owner to withdraw the ETH from the DAO if neededAllow the contract to accept further ETH depositsThe Ownable contract we inherit from contains a modifier onlyOwner which restricts a function to only be able to be called by the contract owner Let s implement withdrawEther using that modifier dev withdrawEther allows the contract owner deployer to withdraw the ETH from the contract function withdrawEther external onlyOwner payable owner transfer address this balance This will transfer the entire ETH balance of the contract to the owner addressFinally to allow for adding more ETH deposits to the DAO treasury we need to add some special functions Normally contract addresses cannot accept ETH sent to them unless it was through a payable function But we don t want users to call functions just to deposit money they should be able to tranfer ETH directly from their wallet For that let s add these two functions The following two functions allow the contract to accept ETH deposits directly from a wallet without calling a function receive external payable fallback external payable Smart Contract DeploymentNow that we have written both our contracts let s deploy them to the Rinkeby Testnet Ensure you have some ETH on the Rinkeby Testnet Install the dotenv package from NPM to be able to use environment variables specified in env files in the hardhat config js Execute the following command in your Terminal in the hardhat tutorial directory npm install dotenvNow create a env file in the hardhat tutorial directory and set the following two environment variables Follow the instructions to get their values Make sure the Rinkeby private key you use is has ETH on the Rinkeby Testnet Go to sign up create a new App in its dashboard and select the network as Rinkeby and replace add the alchemy key url here with its key url ALCHEMY API KEY URL add the alchemy key url here Replace this private key with your RINKEBY account private key To export your private key from Metamask open Metamask and go to Account Details gt Export Private Key Be aware of NEVER putting real Ether into testing accounts RINKEBY PRIVATE KEY add the rinkeby private key here Now let s write a deployment script to automatically deploy both our contracts for us Create a new file named deploy js under hardhat tutorial scripts and add the following code const ethers require hardhat const CRYPTODEVS NFT CONTRACT ADDRESS require constants async function main Deploy the FakeNFTMarketplace contract first const FakeNFTMarketplace await ethers getContractFactory FakeNFTMarketplace const fakeNftMarketplace await FakeNFTMarketplace deploy await fakeNftMarketplace deployed console log FakeNFTMarketplace deployed to fakeNftMarketplace address Now deploy the CryptoDevsDAO contract const CryptoDevsDAO await ethers getContractFactory CryptoDevsDAO const cryptoDevsDAO await CryptoDevsDAO deploy fakeNftMarketplace address CRYPTODEVS NFT CONTRACT ADDRESS This assumes your account has at least ETH in it s account Change this value as you want value ethers utils parseEther await cryptoDevsDAO deployed console log CryptoDevsDAO deployed to cryptoDevsDAO address main then gt process exit catch error gt console error error process exit As you may have noticed deploy js imports a variable called CRYPTODEVS NFT CONTRACT ADDRESS from a file named constants Let s make that Create a new file named constants js in the hardhat tutorial directory Replace the value with your NFT contract address const CRYPTODEVS NFT CONTRACT ADDRESS YOUR CRYPTODEVS NFT CONTRACT ADDRESS HERE module exports CRYPTODEVS NFT CONTRACT ADDRESS Now let s add the Rinkeby Network to your Hardhat Config so we can deploy to Rinkeby Open your hardhat config js file and replace it with the following require nomiclabs hardhat waffle require dotenv config path env const ALCHEMY API KEY URL process env ALCHEMY API KEY URL const RINKEBY PRIVATE KEY process env RINKEBY PRIVATE KEY module exports solidity networks rinkeby url ALCHEMY API KEY URL accounts RINKEBY PRIVATE KEY Let s make sure everything compiles before proceeding Execute the following command from your Terminal within the hardhat tutorial folder npx hardhat compileand make sure there are no compilation errors If you do face compilation errors try comparing your code against the final version present hereLet s deploy Execute the following command in your Terminal from the hardhat tutorial directory npx hardhat run scripts deploy js network rinkebySave the FakeNFTMarketplace and CryptoDevsDAO contract addresses that get printed in your Terminal You will need those later Frontend DevelopmentWhew So much coding We ve successfully developed and deployed our contracts to the Rinkeby Testnet Now it s time to build the Frontend interface so users can create and vote on proposals from the website To develop the website we will be using Next js as we have so far which is a meta framework built on top of React Let s get started by creating a new next app Your folder structure should look like this after setting up the next app DAO Tutorial hardhat tutorial my appTo create my app execute the following command in your Terminal within the DAO Tutorial directory npx create next app latestand press Enter for all the question prompts This should create the my app folder and setup a basic Next js project Let s see if everything works Run the following in your Terminal cd my app npm run devYour website should be up and running at http localhost However this is a basic starter Next js project and we need to add code for it to do what we want Let s install the webmodal and ethers library WebModal will allow us to support connecting to wallets in the browser and Ethers will be used to interact with the blockchain Run this in your Terminal from the my app directory npm install webmodal ethersDownload and save the following file as svg in my app public cryptodevs We will display this image on the webpage NOTE You need to create the cryptodevs folder inside public Download ImageAdd the following CSS styles in my app styles Home modules css main min height vh display flex flex direction row justify content center align items center font family Courier New Courier monospace footer display flex padding rem border top px solid eaeaea justify content center align items center image width height margin left title font size rem margin rem description line height margin rem font size rem button border radius px background color blue border none color ffffff font size px padding px width px cursor pointer margin right button border radius px background color indigo border none color ffffff font size px padding px cursor pointer margin right margin top rem proposalCard width fit content margin top rem border black px solid flex flex direction column container margin top rem flex flex justify content space between media max width px main width flex direction column justify content center align items center The website also needs to read write data from two smart contracts CryptoDevsDAO and CryptoDevsNFT Let s store their contract addresses and ABIs in a constants file Create a constants js file in the my app directory export const CRYPTODEVS DAO CONTRACT ADDRESS export const CRYPTODEVS NFT CONTRACT ADDRESS export const CRYPTODEVS DAO ABI export const CRYPTODEVS NFT ABI Replace the contract address and ABI values with your relevant contract addresses and ABIs Now for the actual cool website code Open up my app pages index js and write the following code Explanation of the code can be found in the comments import Contract providers from ethers import formatEther from ethers lib utils import Head from next head import useEffect useRef useState from react import WebModal from webmodal import CRYPTODEVS DAO ABI CRYPTODEVS DAO CONTRACT ADDRESS CRYPTODEVS NFT ABI CRYPTODEVS NFT CONTRACT ADDRESS from constants import styles from styles Home module css export default function Home ETH Balance of the DAO contract const treasuryBalance setTreasuryBalance useState Number of proposals created in the DAO const numProposals setNumProposals useState Array of all proposals created in the DAO const proposals setProposals useState User s balance of CryptoDevs NFTs const nftBalance setNftBalance useState Fake NFT Token ID to purchase Used when creating a proposal const fakeNftTokenId setFakeNftTokenId useState One of Create Proposal or View Proposals const selectedTab setSelectedTab useState True if waiting for a transaction to be mined false otherwise const loading setLoading useState false True if user has connected their wallet false otherwise const walletConnected setWalletConnected useState false const webModalRef useRef Helper function to connect wallet const connectWallet async gt try await getProviderOrSigner setWalletConnected true catch error console error error Reads the ETH balance of the DAO contract and sets the treasuryBalance state variable const getDAOTreasuryBalance async gt try const provider await getProviderOrSigner const balance await provider getBalance CRYPTODEVS DAO CONTRACT ADDRESS setTreasuryBalance balance toString catch error console error error Reads the number of proposals in the DAO contract and sets the numProposals state variable const getNumProposalsInDAO async gt try const provider await getProviderOrSigner const contract getDaoContractInstance provider const daoNumProposals await contract numProposals setNumProposals daoNumProposals toString catch error console error error Reads the balance of the user s CryptoDevs NFTs and sets the nftBalance state variable const getUserNFTBalance async gt try const signer await getProviderOrSigner true const nftContract getCryptodevsNFTContractInstance signer const balance await nftContract balanceOf signer getAddress setNftBalance parseInt balance toString catch error console error error Calls the createProposal function in the contract using the tokenId from fakeNftTokenId const createProposal async gt try const signer await getProviderOrSigner true const daoContract getDaoContractInstance signer const txn await daoContract createProposal fakeNftTokenId setLoading true await txn wait await getNumProposalsInDAO setLoading false catch error console error error window alert error data message Helper function to fetch and parse one proposal from the DAO contract Given the Proposal ID and converts the returned data into a Javascript object with values we can use const fetchProposalById async id gt try const provider await getProviderOrSigner const daoContract getDaoContractInstance provider const proposal await daoContract proposals id const parsedProposal proposalId id nftTokenId proposal nftTokenId toString deadline new Date parseInt proposal deadline toString yayVotes proposal yayVotes toString nayVotes proposal nayVotes toString executed proposal executed return parsedProposal catch error console error error Runs a loop numProposals times to fetch all proposals in the DAO and sets the proposals state variable const fetchAllProposals async gt try const proposals for let i i lt numProposals i const proposal await fetchProposalById i proposals push proposal setProposals proposals return proposals catch error console error error Calls the voteOnProposal function in the contract using the passed proposal ID and Vote const voteOnProposal async proposalId vote gt try const signer await getProviderOrSigner true const daoContract getDaoContractInstance signer let vote vote YAY const txn await daoContract voteOnProposal proposalId vote setLoading true await txn wait setLoading false await fetchAllProposals catch error console error error window alert error data message Calls the executeProposal function in the contract using the passed proposal ID const executeProposal async proposalId gt try const signer await getProviderOrSigner true const daoContract getDaoContractInstance signer const txn await daoContract executeProposal proposalId setLoading true await txn wait setLoading false await fetchAllProposals catch error console error error window alert error data message Helper function to fetch a Provider Signer instance from Metamask const getProviderOrSigner async needSigner false gt const provider await webModalRef current connect const webProvider new providers WebProvider provider const chainId await webProvider getNetwork if chainId window alert Please switch to the Rinkeby network throw new Error Please switch to the Rinkeby network if needSigner const signer webProvider getSigner return signer return webProvider Helper function to return a DAO Contract instance given a Provider Signer const getDaoContractInstance providerOrSigner gt return new Contract CRYPTODEVS DAO CONTRACT ADDRESS CRYPTODEVS DAO ABI providerOrSigner Helper function to return a CryptoDevs NFT Contract instance given a Provider Signer const getCryptodevsNFTContractInstance providerOrSigner gt return new Contract CRYPTODEVS NFT CONTRACT ADDRESS CRYPTODEVS NFT ABI providerOrSigner piece of code that runs everytime the value of walletConnected changes so when a wallet connects or disconnects Prompts user to connect wallet if not connected and then calls helper functions to fetch the DAO Treasury Balance User NFT Balance and Number of Proposals in the DAO useEffect gt if walletConnected webModalRef current new WebModal network rinkeby providerOptions disableInjectedProvider false connectWallet then gt getDAOTreasuryBalance getUserNFTBalance getNumProposalsInDAO walletConnected Piece of code that runs everytime the value of selectedTab changes Used to re fetch all proposals in the DAO when user switches to the View Proposals tab useEffect gt if selectedTab View Proposals fetchAllProposals selectedTab Render the contents of the appropriate tab based on selectedTab function renderTabs if selectedTab Create Proposal return renderCreateProposalTab else if selectedTab View Proposals return renderViewProposalsTab return null Renders the Create Proposal tab content function renderCreateProposalTab if loading return lt div className styles description gt Loading Waiting for transaction lt div gt else if nftBalance return lt div className styles description gt You do not own any CryptoDevs NFTs lt br gt lt b gt You cannot create or vote on proposals lt b gt lt div gt else return lt div className styles container gt lt label gt Fake NFT Token ID to Purchase lt label gt lt input placeholder type number onChange e gt setFakeNftTokenId e target value gt lt button className styles button onClick createProposal gt Create lt button gt lt div gt Renders the View Proposals tab content function renderViewProposalsTab if loading return lt div className styles description gt Loading Waiting for transaction lt div gt else if proposals length return lt div className styles description gt No proposals have been created lt div gt else return lt div gt proposals map p index gt lt div key index className styles proposalCard gt lt p gt Proposal ID p proposalId lt p gt lt p gt Fake NFT to Purchase p nftTokenId lt p gt lt p gt Deadline p deadline toLocaleString lt p gt lt p gt Yay Votes p yayVotes lt p gt lt p gt Nay Votes p nayVotes lt p gt lt p gt Executed p executed toString lt p gt p deadline getTime gt Date now amp amp p executed lt div className styles flex gt lt button className styles button onClick gt voteOnProposal p proposalId YAY gt Vote YAY lt button gt lt button className styles button onClick gt voteOnProposal p proposalId NAY gt Vote NAY lt button gt lt div gt p deadline getTime lt Date now amp amp p executed lt div className styles flex gt lt button className styles button onClick gt executeProposal p proposalId gt Execute Proposal p yayVotes gt p nayVotes YAY NAY lt button gt lt div gt lt div className styles description gt Proposal Executed lt div gt lt div gt lt div gt return lt div gt lt Head gt lt title gt CryptoDevs DAO lt title gt lt meta name description content CryptoDevs DAO gt lt link rel icon href favicon ico gt lt Head gt lt div className styles main gt lt div gt lt h className styles title gt Welcome to Crypto Devs lt h gt lt div className styles description gt Welcome to the DAO lt div gt lt div className styles description gt Your CryptoDevs NFT Balance nftBalance lt br gt Treasury Balance formatEther treasuryBalance ETH lt br gt Total Number of Proposals numProposals lt div gt lt div className styles flex gt lt button className styles button onClick gt setSelectedTab Create Proposal gt Create Proposal lt button gt lt button className styles button onClick gt setSelectedTab View Proposals gt View Proposals lt button gt lt div gt renderTabs lt div gt lt div gt lt img className styles image src cryptodevs svg gt lt div gt lt div gt lt footer className styles footer gt Made with amp by Crypto Devs lt footer gt lt div gt Let s run it In your terminal from the my app directory execute npm run devto see your website in action It should look like the screenshot at the beginning of this tutorial Congratulations Your CryptoDevs DAO website should now be working TestingCreate a couple of proposalsTry voting YAY on one and NAY on the otherWait for minutes for their deadlines to passExecute both of them Watch the balance of the DAO Treasury go down by ETH due to the proposal which passed as it bought an NFT upon execution Push to GithubMake sure to push all this code to Github before proceeding to the next step Website DeploymentWhat good is a website if you cannot share it with others Let s work on deploying your dApp to the world so you can share it with all your LearnWebDAO frens Go to Vercel Dashboard and sign in with your GitHub account Click on the New Project button and select your DAO Tutorial repo When configuring your new project Vercel will allow you to customize your Root DirectorySince our Next js application is within a subfolder of the repo we need to modify it Click Edit next to Root Directory and set it to my app Select the framework as Next jsClick DeployNow you can see your deployed website by going to your Vercel Dashboard selecting your project and copying the domain from there CONGRATULATIONS You re all done Hopefully you enjoyed this tutorial Don t forget to share your DAO website in the showcase channel on Discord DThis article is brought to you by LearnWeb DAO A free comprehensive A to Z blockchain training program for developers across the globe Everything from What is a Blockchain to Hacking smart contracts and everything in between but also much more Join us now to start buidling with builders WebsiteDiscordTwitter |
2022-05-15 08:17:51 |
海外TECH |
DEV Community |
Launch an ICO on Ethereum using Solidity, Next.js, ethers.js, Web3Modal. Airdrop free tokens to NFT holders. |
https://dev.to/learnweb3/launch-an-ico-on-ethereum-using-solidity-nextjs-ethersjs-web3modal-airdrop-free-tokens-to-nft-holders-4l3c
|
Launch an ICO on Ethereum using Solidity Next js ethers js WebModal Airdrop free tokens to NFT holders ICO Initial Coin Offering Now its time for you to launch a token for Crypto Devs Let s call the token Crypto Dev Token Prefer a Video If you would rather learn from a video we have a recording available of this tutorial on our YouTube Watch the video by clicking on the screenshot below or go ahead and read the tutorial Build RequirementsThere should be a max of CD tokens Every Crypto Dev NFT holder should get tokens for free but they would have to pay the gas fees The price of one CD at the time of ICO should be etherThere should be a website which users can visit for the ICO Let s start building PrerequisitesYou must have completed the NFT Collection tutorial TheoryWhat is an ERC ERC is a technical standard it is used for all smart contracts on the Ethereum blockchain for token implementation and provides a list of rules that all Ethereum based tokens must follow Please look at all the ERC functions before moving ahead Build Smart ContractTo 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 mkdir hardhat tutorial cd hardhat tutorial npm init yes npm install save dev hardhatIn the same directory where you installed Hardhat run npx hardhatSelect Create a basic sample projectPress enter for the already specified Hardhat Project rootPress enter for the question on if you want to add a gitignorePress enter for Do you want to install this sample project s dependencies with npm nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethers Now you have a hardhat project ready to go If you are not on mac please do this extra step and install these libraries as well npm install save dev nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethersIn the same terminal now install openzeppelin contracts as we would be importing Openzeppelin s ERC Contract and Openzeppelin s Ownable Contract in our CryptoDevToken contract npm install openzeppelin contractsWe will need to call the CryptoDevs Contract that you deployed for your previous level to check for owners of CryptoDev NFT s As we only need to call tokenOfOwnerByIndex and balanceOf methods we can create an interface for CryptoDevs contract with only these two functions This way we would save gas as we would not need to inherit and deploy the entire CryptoDevs Contract but only a part of it Create a new file inside the contracts directory and call it ICryptoDevs sol and add the following lines SPDX License Identifier MIT pragma solidity interface ICryptoDevs dev Returns a token ID owned by owner at a given index of its token list Use along with balanceOf to enumerate all of owner s tokens function tokenOfOwnerByIndex address owner uint index external view returns uint tokenId dev Returns the number of tokens in owner s account function balanceOf address owner external view returns uint balance Create a new file inside the contracts directory and call it CryptoDevToken sol and add the following lines SPDX License Identifier MIT pragma solidity import openzeppelin contracts token ERC ERC sol import openzeppelin contracts access Ownable sol import ICryptoDevs sol contract CryptoDevToken is ERC Ownable Price of one Crypto Dev token uint public constant tokenPrice ether Each NFT would give the user tokens It needs to be represented as as ERC tokens are represented by the smallest denomination possible for the token By default ERC tokens have the smallest denomination of This means having a balance of is actually equal to tokens Owning full token is equivalent to owning tokens when you account for the decimal places More information on this can be found in the Freshman Track Cryptocurrency tutorial uint public constant tokensPerNFT the max total supply is for Crypto Dev Tokens uint public constant maxTotalSupply CryptoDevsNFT contract instance ICryptoDevs CryptoDevsNFT Mapping to keep track of which tokenIds have been claimed mapping uint gt bool public tokenIdsClaimed constructor address cryptoDevsContract ERC Crypto Dev Token CD CryptoDevsNFT ICryptoDevs cryptoDevsContract dev Mints amount number of CryptoDevTokens Requirements msg value should be equal or greater than the tokenPrice amount function mint uint amount public payable the value of ether that should be equal or greater than tokenPrice amount uint requiredAmount tokenPrice amount require msg value gt requiredAmount Ether sent is incorrect total tokens amount lt otherwise revert the transaction uint amountWithDecimals amount require totalSupply amountWithDecimals lt maxTotalSupply Exceeds the max total supply available call the internal function from Openzeppelin s ERC contract mint msg sender amountWithDecimals dev Mints tokens based on the number of NFT s held by the sender Requirements balance of Crypto Dev NFT s owned by the sender should be greater than Tokens should have not been claimed for all the NFTs owned by the sender function claim public address sender msg sender Get the number of CryptoDev NFT s held by a given sender address uint balance CryptoDevsNFT balanceOf sender If the balance is zero revert the transaction require balance gt You dont own any Crypto Dev NFT s amount keeps track of number of unclaimed tokenIds uint amount loop over the balance and get the token ID owned by sender at a given index of its token list for uint i i lt balance i uint tokenId CryptoDevsNFT tokenOfOwnerByIndex sender i if the tokenId has not been claimed increase the amount if tokenIdsClaimed tokenId amount tokenIdsClaimed tokenId true If all the token Ids have been claimed revert the transaction require amount gt You have already claimed all the tokens call the internal function from Openzeppelin s ERC contract Mint amount tokens for each NFT mint msg sender amount tokensPerNFT Function to receive Ether msg data must be empty receive external payable Fallback function is called when msg data is not empty fallback external payable Now we would install dotenv package to be able to import the env file and use it in our config Open up a terminal pointing athardhat tutorial directory and execute this command npm install dotenvNow create a env file in the hardhat tutorial folder and add the following lines use the instructions in the comments to get your Alchemy API Key URL and RINKEBY Private Key Make sure that the account from which you get your rinkeby private key is funded with Rinkeby Ether Go to sign up create a new App in its dashboard and select the network as Rinkeby and replace add the alchemy key url here with its key url ALCHEMY API KEY URL add the alchemy key url here Replace this private key with your RINKEBY account private key To export your private key from Metamask open Metamask and go to Account Details gt Export Private Key Be aware of NEVER putting real Ether into testing accounts RINKEBY PRIVATE KEY add the rinkeby private key here Lets deploy the contract to rinkeby network Create a new file named deploy js under the scripts folderNow we would write some code to deploy the contract in deploy js file const ethers require hardhat require dotenv config path env const CRYPTO DEVS NFT CONTRACT ADDRESS require constants async function main Address of the Crypto Devs NFT contract that you deployed in the previous module const cryptoDevsNFTContract CRYPTO DEVS NFT CONTRACT ADDRESS A ContractFactory in ethers js is an abstraction used to deploy new smart contracts so cryptoDevsTokenContract here is a factory for instances of our CryptoDevToken contract const cryptoDevsTokenContract await ethers getContractFactory CryptoDevToken deploy the contract const deployedCryptoDevsTokenContract await cryptoDevsTokenContract deploy cryptoDevsNFTContract print the address of the deployed contract console log Crypto Devs Token Contract Address deployedCryptoDevsTokenContract address Call the main function and catch if there is any error main then gt process exit catch error gt console error error process exit You would see that the deploy js file requires a constant Let s create a constants folder under hardhat tutorial folder Inside the constants folder create a new file named index js and add the following lines to it Replace address of the nft contract with the address of the CryptoDevs sol that you deployed in the previous module NFT Collection Address of the NFT Contract that you deployedconst CRYPTO DEVS NFT CONTRACT ADDRESS address of the nft contract module exports CRYPTO DEVS NFT CONTRACT ADDRESS Now open the hardhat config js file we would add the rinkeby network here so that we can deploy our contract to rinkeby Replace all the lines in the hardhat config js file with the given below lines require nomiclabs hardhat waffle require dotenv config path env const ALCHEMY API KEY URL process env ALCHEMY API KEY URL const RINKEBY PRIVATE KEY process env RINKEBY PRIVATE KEY module exports solidity networks rinkeby url ALCHEMY API KEY URL accounts RINKEBY PRIVATE KEY Compile the contract open up a terminal pointing athardhat tutorial directory and execute this command npx hardhat compileTo deploy open up a terminal pointing athardhat tutorial directory and execute this command npx hardhat run scripts deploy js network rinkebySave the CryptoDevToken Contract Address that was printed on your terminal in your notepad you would need it futher down in the tutorial WebsiteTo develop the website we would be using React and Next Js React is a javascript framework which is used to make websites and Next Js is built on top of React First You would need to create a new next app Your folder structure should look something like ICO hardhat tutorial my appTo create this my app in the terminal point to ICO folder and type npx create next app latestand press enter for all the questionsNow to run the app execute these commands in the terminal cd my app npm run devNow go to http localhost your app should be running Now lets install WebModal library WebModal is an easy to use library to help developers add support for multiple providers in their apps with a simple customizable configuration By default WebModal Library supports injected providers like Metamask Dapper Gnosis Safe Frame Web Browsers etc You can also easily configure the library to support Portis Fortmatic Squarelink Torus Authereum D CENT Wallet and Arkane Open up a terminal pointing atmy app directory and execute this command npm install webmodalIn the same terminal also install ethers js npm i ethersIn your public folder download the following image Make sure that the name of the downloaded image is svgNow go to styles folder and replace all the contents of Home modules css file with the following code this would add some styling to your dapp main min height vh display flex flex direction row justify content center align items center font family Courier New Courier monospace footer display flex padding rem border top px solid eaeaea justify content center align items center image width height margin left input width px height padding margin bottom box shadow px px rgba border radius px title font size rem margin rem description line height margin rem font size rem button border radius px background color blue border none color ffffff font size px padding px width px cursor pointer margin bottom media max width px main width flex direction column justify content center align items center Open you index js file under the pages folder and paste the following code explanation of the code can be found in the comments import BigNumber Contract providers utils from ethers import Head from next head import React useEffect useRef useState from react import WebModal from webmodal import NFT CONTRACT ABI NFT CONTRACT ADDRESS TOKEN CONTRACT ABI TOKEN CONTRACT ADDRESS from constants import styles from styles Home module css export default function Home Create a BigNumber const zero BigNumber from walletConnected keeps track of whether the user s wallet is connected or not const walletConnected setWalletConnected useState false loading is set to true when we are waiting for a transaction to get mined const loading setLoading useState false tokensToBeClaimed keeps track of the number of tokens that can be claimed based on the Crypto Dev NFT s held by the user for which they havent claimed the tokens const tokensToBeClaimed setTokensToBeClaimed useState zero balanceOfCryptoDevTokens keeps track of number of Crypto Dev tokens owned by an address const balanceOfCryptoDevTokens setBalanceOfCryptoDevTokens useState zero amount of the tokens that the user wants to mint const tokenAmount setTokenAmount useState zero tokensMinted is the total number of tokens that have been minted till now out of max total supply const tokensMinted setTokensMinted useState zero Create a reference to the Web Modal used for connecting to Metamask which persists as long as the page is open const webModalRef useRef getTokensToBeClaimed checks the balance of tokens that can be claimed by the user const getTokensToBeClaimed async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner Create an instance of NFT Contract const nftContract new Contract NFT CONTRACT ADDRESS NFT CONTRACT ABI provider Create an instance of tokenContract const tokenContract new Contract TOKEN CONTRACT ADDRESS TOKEN CONTRACT ABI provider We will get the signer now to extract the address of the currently connected MetaMask account const signer await getProviderOrSigner true Get the address associated to the signer which is connected to MetaMask const address await signer getAddress call the balanceOf from the NFT contract to get the number of NFT s held by the user const balance await nftContract balanceOf address balance is a Big number and thus we would compare it with Big number zero if balance zero setTokensToBeClaimed zero else amount keeps track of the number of unclaimed tokens var amount For all the NFT s check if the tokens have already been claimed Only increase the amount if the tokens have not been claimed for a an NFT for a given tokenId for var i i lt balance i const tokenId await nftContract tokenOfOwnerByIndex address i const claimed await tokenContract tokenIdsClaimed tokenId if claimed amount tokensToBeClaimed has been initialized to a Big Number thus we would convert amount to a big number and then set its value setTokensToBeClaimed BigNumber from amount catch err console error err setTokensToBeClaimed zero getBalanceOfCryptoDevTokens checks the balance of Crypto Dev Tokens s held by an address const getBalanceOfCryptoDevTokens async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner Create an instace of token contract const tokenContract new Contract TOKEN CONTRACT ADDRESS TOKEN CONTRACT ABI provider We will get the signer now to extract the address of the currently connected MetaMask account const signer await getProviderOrSigner true Get the address associated to the signer which is connected to MetaMask const address await signer getAddress call the balanceOf from the token contract to get the number of tokens held by the user const balance await tokenContract balanceOf address balance is already a big number so we dont need to convert it before setting it setBalanceOfCryptoDevTokens balance catch err console error err setBalanceOfCryptoDevTokens zero mintCryptoDevToken mints amount number of tokens to a given address const mintCryptoDevToken async amount gt try We need a Signer here since this is a write transaction Create an instance of tokenContract const signer await getProviderOrSigner true Create an instance of tokenContract const tokenContract new Contract TOKEN CONTRACT ADDRESS TOKEN CONTRACT ABI signer Each token is of ether The value we need to send is amount const value amount const tx await tokenContract mint amount value signifies the cost of one crypto dev token which is eth We are parsing string to ether using the utils library from ethers js value utils parseEther value toString setLoading true wait for the transaction to get mined await tx wait setLoading false window alert Sucessfully minted Crypto Dev Tokens await getBalanceOfCryptoDevTokens await getTotalTokensMinted await getTokensToBeClaimed catch err console error err claimCryptoDevTokens Helps the user claim Crypto Dev Tokens const claimCryptoDevTokens async gt try We need a Signer here since this is a write transaction Create an instance of tokenContract const signer await getProviderOrSigner true Create an instance of tokenContract const tokenContract new Contract TOKEN CONTRACT ADDRESS TOKEN CONTRACT ABI signer const tx await tokenContract claim setLoading true wait for the transaction to get mined await tx wait setLoading false window alert Sucessfully claimed Crypto Dev Tokens await getBalanceOfCryptoDevTokens await getTotalTokensMinted await getTokensToBeClaimed catch err console error err getTotalTokensMinted Retrieves how many tokens have been minted till now out of the total supply const getTotalTokensMinted async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner Create an instance of token contract const tokenContract new Contract TOKEN CONTRACT ADDRESS TOKEN CONTRACT ABI provider Get all the tokens that have been minted const tokensMinted await tokenContract totalSupply setTokensMinted tokensMinted catch err console error err Returns a Provider or Signer object representing the Ethereum RPC with or without the signing capabilities of metamask attached A Provider is needed to interact with the blockchain reading transactions reading balances reading state etc A Signer is a special type of Provider used in case a write transaction needs to be made to the blockchain which involves the connected account needing to make a digital signature to authorize the transaction being sent Metamask exposes a Signer API to allow your website to request signatures from the user using Signer functions param needSigner True if you need the signer default false otherwise const getProviderOrSigner async needSigner false gt Connect to Metamask Since we store webModal as a reference we need to access the current value to get access to the underlying object const provider await webModalRef current connect const webProvider new providers WebProvider provider If user is not connected to the Rinkeby network let them know and throw an error const chainId await webProvider getNetwork if chainId window alert Change the network to Rinkeby throw new Error Change network to Rinkeby if needSigner const signer webProvider getSigner return signer return webProvider connectWallet Connects the MetaMask wallet const connectWallet async gt try Get the provider from webModal which in our case is MetaMask When used for the first time it prompts the user to connect their wallet await getProviderOrSigner setWalletConnected true catch err console error err useEffects are used to react to changes in state of the website The array at the end of function call represents what state changes will trigger this effect In this case whenever the value of walletConnected changes this effect will be called useEffect gt if wallet is not connected create a new instance of WebModal and connect the MetaMask wallet if walletConnected Assign the WebModal class to the reference object by setting it s current value The current value is persisted throughout as long as this page is open webModalRef current new WebModal network rinkeby providerOptions disableInjectedProvider false connectWallet getTotalTokensMinted getBalanceOfCryptoDevTokens getTokensToBeClaimed walletConnected renderButton Returns a button based on the state of the dapp const renderButton gt If we are currently waiting for something return a loading button if loading return lt div gt lt button className styles button gt Loading lt button gt lt div gt If tokens to be claimed are greater than Return a claim button if tokensToBeClaimed gt return lt div gt lt div className styles description gt tokensToBeClaimed Tokens can be claimed lt div gt lt button className styles button onClick claimCryptoDevTokens gt Claim Tokens lt button gt lt div gt If user doesn t have any tokens to claim show the mint button return lt div style display flex col gt lt div gt lt input type number placeholder Amount of Tokens BigNumber from converts the e target value to a BigNumber onChange e gt setTokenAmount BigNumber from e target value className styles input gt lt div gt lt button className styles button disabled tokenAmount gt onClick gt mintCryptoDevToken tokenAmount gt Mint Tokens lt button gt lt div gt return lt div gt lt Head gt lt title gt Crypto Devs lt title gt lt meta name description content ICO Dapp gt lt link rel icon href favicon ico gt lt Head gt lt div className styles main gt lt div gt lt h className styles title gt Welcome to Crypto Devs ICO lt h gt lt div className styles description gt You can claim or mint Crypto Dev tokens here lt div gt walletConnected lt div gt lt div className styles description gt Format Ether helps us in converting a BigNumber to string You have minted utils formatEther balanceOfCryptoDevTokens Crypto Dev Tokens lt div gt lt div className styles description gt Format Ether helps us in converting a BigNumber to string Overall utils formatEther tokensMinted have been minted lt div gt renderButton lt div gt lt button onClick connectWallet className styles button gt Connect your wallet lt button gt lt div gt lt div gt lt img className styles image src svg gt lt div gt lt div gt lt footer className styles footer gt Made with amp by Crypto Devs lt footer gt lt div gt Now create a new folder under the my app folder and name it constants In the constants folder create a file index js and paste the following code Replace abi of your nft contract with the abi of the NFT contract that you deployed in the last tutorial Replace address of your nft contract with the address of the NFT contract that you deployed in your previous tutorial Replace abi of your token contract by the abi of the token contract To get the abi of the Token contract go to hardhat tutorial artifacts contracts CryptoDevToken sol and then fromCryptoDevToken json file get the array marked under the abi key Replace address of your token contract with the address of the token contract that you saved to your notepad early on in the tutorial export const NFT CONTRACT ABI abi of your nft contract export const NFT CONTRACT ADDRESS address of your nft contract export const TOKEN CONTRACT ABI abi of your token contract export const TOKEN CONTRACT ADDRESS address of your token contract Now in your terminal which is pointing to my app folder execute npm run devYour ICO dapp should now work without errors Push to GithubMake sure to push all this code to Github before proceeding to the next step Deploying your dAppWe will now deploy your dApp so that everyone can see your website and you can share it with all of your LearnWeb DAO friends Go to and sign in with your GitHubThen click on New Project button and then select your ICO dApp repoWhen configuring your new project Vercel will allow you to customize your Root DirectoryClick Edit next to Root Directory and set it to my appSelect the Framework Preset as Next jsClick DeployNow you can see your deployed website by going to your dashboard selecting your project and copying the URL from there CONGRATULATIONS You re all done Hopefully you enjoyed this tutorial Don t forget to share your ICO website in the showcase channel on Discord DThis article is brought to you by LearnWeb DAO A free comprehensive A to Z blockchain training program for developers across the globe Everything from What is a Blockchain to Hacking smart contracts and everything in between but also much more Join us now to start buidling with builders WebsiteDiscordTwitter |
2022-05-15 08:16:23 |
海外TECH |
DEV Community |
Build an entire NFT Collection on Ethereum using Solidity, Next.js, Web3Modal, Ethers.js |
https://dev.to/learnweb3/build-an-entire-nft-collection-on-ethereum-using-solidity-nextjs-web3modal-ethersjs-37d6
|
Build an entire NFT Collection on Ethereum using Solidity Next js WebModal Ethers js NFT CollectionNow its time for you to launch your own NFT collection Crypto Devs RequirementsThere should only exist Crypto Dev NFT s and each one of them should be unique User s should be able to mint only NFT with one transaction Whitelisted users should have a min presale period before the actual sale where they are guaranteed NFT per transaction There should be a website for your NFT Collection Lets start building PrerequisitesYou should have completed the Whitelist dApp tutorial TheoryWhat is a Non Fungible Token Fungible means to be the same or interchangeable eg Eth is fungible With this in mind NFTs are unique each one is different Every single token has unique characteristics and values They are all distinguishable from one another and are not interchangeable eg Unique ArtWhat is ERC ERC is an open standard that describes how to build Non Fungible tokens on EVM Ethereum Virtual Machine compatible blockchains it is a standard interface for Non Fungible tokens it has a set of rules which make it easy to work with NFTs Before moving ahead have a look at all the functions supported by ERC Build Prefer a Video If you would rather learn from a video we have a recording available of this tutorial on our YouTube Watch the video by clicking on the screenshot below or go ahead and read the tutorial Smart ContractWe would also be using Ownable sol from Openzeppelin which helps you manage the Ownership of a contractBy default the owner of an Ownable contract is the account that deployed it which is usually exactly what you want Ownable also lets you transferOwnership from the owner account to a new one andrenounceOwnership for the owner to relinquish this administrative privilege a common pattern after an initial stage with centralized administration is over We would also be using an extension of ERC known as ERC EnumerableERC Enumerable is helps you to keep track of all the tokenIds in the contract and also the tokensIds held by an address for a given contract Please have a look at the functions it implements before moving aheadTo 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 mkdir hardhat tutorial cd hardhat tutorial npm init yes npm install save dev hardhatIn the same directory where you installed Hardhat run npx hardhatSelect Create a basic sample projectPress enter for the already specified Hardhat Project rootPress enter for the question on if you want to add a gitignorePress enter for Do you want to install this sample project s dependencies with npm nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethers Now you have a hardhat project ready to go If you are not on mac please do this extra step and install these libraries as well npm install save dev nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethersand press enter for all the questions In the same terminal now install openzeppelin contracts as we would be importing Openzeppelin s ERCEnumerable Contract in our CryptoDevs contract npm install openzeppelin contractsWe will need to call the Whitelist Contract that you deployed for your previous level to check for addresses that were whitelisted and give them presale access As we only need to call mapping address gt bool public whitelistedAddresses We can create an interface for Whitelist contract with a function only for this mapping this way we would save gas as we would not need to inherit and deploy the entire Whitelist Contract but only a part of it Create a new file inside the contracts directory and call it IWhitelist sol SPDX License Identifier MIT pragma solidity interface IWhitelist function whitelistedAddresses address external view returns bool Now lets create a new file inside the contracts directory and call it CryptoDevs sol SPDX License Identifier MIT pragma solidity import openzeppelin contracts token ERC extensions ERCEnumerable sol import openzeppelin contracts access Ownable sol import IWhitelist sol contract CryptoDevs is ERCEnumerable Ownable dev baseTokenURI for computing tokenURI If set the resulting URI for each token will be the concatenation of the baseURI and the tokenId string baseTokenURI price is the price of one Crypto Dev NFT uint public price ether paused is used to pause the contract in case of an emergency bool public paused max number of CryptoDevs uint public maxTokenIds total number of tokenIds minted uint public tokenIds Whitelist contract instance IWhitelist whitelist boolean to keep track of whether presale started or not bool public presaleStarted timestamp for when presale would end uint public presaleEnded modifier onlyWhenNotPaused require paused Contract currently paused dev ERC constructor takes in a name and a symbol to the token collection name in our case is Crypto Devs and symbol is CD Constructor for Crypto Devs takes in the baseURI to set baseTokenURI for the collection It also initializes an instance of whitelist interface constructor string memory baseURI address whitelistContract ERC Crypto Devs CD baseTokenURI baseURI whitelist IWhitelist whitelistContract dev startPresale starts a presale for the whitelisted addresses function startPresale public onlyOwner presaleStarted true Set presaleEnded time as current timestamp minutes Solidity has cool syntax for timestamps seconds minutes hours days years presaleEnded block timestamp minutes dev presaleMint allows a user to mint one NFT per transaction during the presale function presaleMint public payable onlyWhenNotPaused require presaleStarted amp amp block timestamp lt presaleEnded Presale is not running require whitelist whitelistedAddresses msg sender You are not whitelisted require tokenIds lt maxTokenIds Exceeded maximum Crypto Devs supply require msg value gt price Ether sent is not correct tokenIds safeMint is a safer version of the mint function as it ensures that if the address being minted to is a contract then it knows how to deal with ERC tokens If the address being minted to is not a contract it works the same way as mint safeMint msg sender tokenIds dev mint allows a user to mint NFT per transaction after the presale has ended function mint public payable onlyWhenNotPaused require presaleStarted amp amp block timestamp gt presaleEnded Presale has not ended yet require tokenIds lt maxTokenIds Exceed maximum Crypto Devs supply require msg value gt price Ether sent is not correct tokenIds safeMint msg sender tokenIds dev baseURI overides the Openzeppelin s ERC implementation which by default returned an empty string for the baseURI function baseURI internal view virtual override returns string memory return baseTokenURI dev setPaused makes the contract paused or unpaused function setPaused bool val public onlyOwner paused val dev withdraw sends all the ether in the contract to the owner of the contract function withdraw public onlyOwner address owner owner uint amount address this balance bool sent owner call value amount require sent Failed to send Ether Function to receive Ether msg data must be empty receive external payable Fallback function is called when msg data is not empty fallback external payable Now we would install dotenv package to be able to import the env file and use it in our config Open up a terminal pointing athardhat tutorial directory and execute this command npm install dotenvNow create a env file in the hardhat tutorial folder and add the following lines use the instructions in the comments to get your Alchemy API Key URL and RINKEBY Private Key Make sure that the account from which you get your rinkeby private key is funded with Rinkeby Ether Go to sign up create a new App in its dashboard and select the network as Rinkeby and replace add the alchemy key url here with its key url ALCHEMY API KEY URL add the alchemy key url here Replace this private key with your RINKEBY account private key To export your private key from Metamask open Metamask and go to Account Details gt Export Private Key Be aware of NEVER putting real Ether into testing accounts RINKEBY PRIVATE KEY add the rinkeby private key here Lets deploy the contract to rinkeby network Create a new file named deploy js under the scripts folderNow we would write some code to deploy the contract in deploy js file const ethers require hardhat require dotenv config path env const WHITELIST CONTRACT ADDRESS METADATA URL require constants async function main Address of the whitelist contract that you deployed in the previous module const whitelistContract WHITELIST CONTRACT ADDRESS URL from where we can extract the metadata for a Crypto Dev NFT const metadataURL METADATA URL A ContractFactory in ethers js is an abstraction used to deploy new smart contracts so cryptoDevsContract here is a factory for instances of our CryptoDevs contract const cryptoDevsContract await ethers getContractFactory CryptoDevs deploy the contract const deployedCryptoDevsContract await cryptoDevsContract deploy metadataURL whitelistContract print the address of the deployed contract console log Crypto Devs Contract Address deployedCryptoDevsContract address Call the main function and catch if there is any error main then gt process exit catch error gt console error error process exit As you can read deploy js requires some constants Lets create a folder named constants under the hardhat tutorial folderNow add an index js file inside the constants folder and add the following lines to the file Replace address of the whitelist contract with the address of the whitelist contract that you deployed in the previous tutorial For Metadata URL just copy the sample one that has been provided We would replace this further down in the tutorial Address of the Whitelist Contract that you deployed const WHITELIST CONTRACT ADDRESS address of the whitelist contract URL to extract Metadata for a Crypto Dev NFT const METADATA URL module exports WHITELIST CONTRACT ADDRESS METADATA URL Now open the hardhat config js file we would add the rinkeby network here so that we can deploy our contract to rinkeby Replace all the lines in the hardhart config js file with the given below lines require nomiclabs hardhat waffle require dotenv config path env const ALCHEMY API KEY URL process env ALCHEMY API KEY URL const RINKEBY PRIVATE KEY process env RINKEBY PRIVATE KEY module exports solidity networks rinkeby url ALCHEMY API KEY URL accounts RINKEBY PRIVATE KEY Compile the contract open up a terminal pointing athardhat tutorial directory and execute this command npx hardhat compileTo deploy open up a terminal pointing athardhat tutorial directory and execute this command npx hardhat run scripts deploy js network rinkebySave the Crypto Devs Contract Address that was printed on your terminal in your notepad you would need it futher down in the tutorial WebsiteTo develop the website we would be using React and Next Js React is a javascript framework which is used to make websites and Next Js is built on top of React First You would need to create a new next app Your folder structure should look something like NFT Collection hardhat tutorial my appTo create this my app in the terminal point to NFT Collection folder and type npx create next app latestand press enter for all the questionsNow to run the app execute these commands in the terminal cd my app npm run devNow go to http localhost your app should be running Now lets install WebModal library WebModal is an easy to use library to help developers add support for multiple providers in their apps with a simple customizable configuration By default WebModal Library supports injected providers like Metamask Dapper Gnosis Safe Frame Web Browsers etc You can also easily configure the library to support Portis Fortmatic Squarelink Torus Authereum D CENT Wallet and Arkane Open up a terminal pointing atmy app directory and execute this command npm install webmodalIn the same terminal also install ethers js npm install ethersIn your public folder download this folder and all the images in it Make sure that the name of the downloaded folder is cryptodevsNow go to styles folder and replace all the contents of Home modules css file with the following code this would add some styling to your dapp main min height vh display flex flex direction row justify content center align items center font family Courier New Courier monospace footer display flex padding rem border top px solid eaeaea justify content center align items center image width height margin left title font size rem margin rem description line height margin rem font size rem button border radius px background color blue border none color ffffff font size px padding px width px cursor pointer margin bottom media max width px main width flex direction column justify content center align items center Open you index js file under the pages folder and paste the following code explanation of the code can be found in the comments import Contract providers utils from ethers import Head from next head import React useEffect useRef useState from react import WebModal from webmodal import abi NFT CONTRACT ADDRESS from constants import styles from styles Home module css export default function Home walletConnected keep track of whether the user s wallet is connected or not const walletConnected setWalletConnected useState false presaleStarted keeps track of whether the presale has started or not const presaleStarted setPresaleStarted useState false presaleEnded keeps track of whether the presale ended const presaleEnded setPresaleEnded useState false loading is set to true when we are waiting for a transaction to get mined const loading setLoading useState false checks if the currently connected MetaMask wallet is the owner of the contract const isOwner setIsOwner useState false tokenIdsMinted keeps track of the number of tokenIds that have been minted const tokenIdsMinted setTokenIdsMinted useState Create a reference to the Web Modal used for connecting to Metamask which persists as long as the page is open const webModalRef useRef presaleMint Mint an NFT during the presale const presaleMint async gt try We need a Signer here since this is a write transaction const signer await getProviderOrSigner true Create a new instance of the Contract with a Signer which allows update methods const whitelistContract new Contract NFT CONTRACT ADDRESS abi signer call the presaleMint from the contract only whitelisted addresses would be able to mint const tx await whitelistContract presaleMint value signifies the cost of one crypto dev which is eth We are parsing string to ether using the utils library from ethers js value utils parseEther setLoading true wait for the transaction to get mined await tx wait setLoading false window alert You successfully minted a Crypto Dev catch err console error err publicMint Mint an NFT after the presale const publicMint async gt try We need a Signer here since this is a write transaction const signer await getProviderOrSigner true Create a new instance of the Contract with a Signer which allows update methods const whitelistContract new Contract NFT CONTRACT ADDRESS abi signer call the mint from the contract to mint the Crypto Dev const tx await whitelistContract mint value signifies the cost of one crypto dev which is eth We are parsing string to ether using the utils library from ethers js value utils parseEther setLoading true wait for the transaction to get mined await tx wait setLoading false window alert You successfully minted a Crypto Dev catch err console error err connectWallet Connects the MetaMask wallet const connectWallet async gt try Get the provider from webModal which in our case is MetaMask When used for the first time it prompts the user to connect their wallet await getProviderOrSigner setWalletConnected true catch err console error err startPresale starts the presale for the NFT Collection const startPresale async gt try We need a Signer here since this is a write transaction const signer await getProviderOrSigner true Create a new instance of the Contract with a Signer which allows update methods const whitelistContract new Contract NFT CONTRACT ADDRESS abi signer call the startPresale from the contract const tx await whitelistContract startPresale setLoading true wait for the transaction to get mined await tx wait setLoading false set the presale started to true await checkIfPresaleStarted catch err console error err checkIfPresaleStarted checks if the presale has started by quering the presaleStarted variable in the contract const checkIfPresaleStarted async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner We connect to the Contract using a Provider so we will only have read only access to the Contract const nftContract new Contract NFT CONTRACT ADDRESS abi provider call the presaleStarted from the contract const presaleStarted await nftContract presaleStarted if presaleStarted await getOwner setPresaleStarted presaleStarted return presaleStarted catch err console error err return false checkIfPresaleEnded checks if the presale has ended by quering the presaleEnded variable in the contract const checkIfPresaleEnded async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner We connect to the Contract using a Provider so we will only have read only access to the Contract const nftContract new Contract NFT CONTRACT ADDRESS abi provider call the presaleEnded from the contract const presaleEnded await nftContract presaleEnded presaleEnded is a Big Number so we are using the lt less than function instead of lt Date now returns the current time in seconds We compare if the presaleEnded timestamp is less than the current time which means presale has ended const hasEnded presaleEnded lt Math floor Date now if hasEnded setPresaleEnded true else setPresaleEnded false return hasEnded catch err console error err return false getOwner calls the contract to retrieve the owner const getOwner async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner We connect to the Contract using a Provider so we will only have read only access to the Contract const nftContract new Contract NFT CONTRACT ADDRESS abi provider call the owner function from the contract const owner await nftContract owner We will get the signer now to extract the address of the currently connected MetaMask account const signer await getProviderOrSigner true Get the address associated to the signer which is connected to MetaMask const address await signer getAddress if address toLowerCase owner toLowerCase setIsOwner true catch err console error err message getTokenIdsMinted gets the number of tokenIds that have been minted const getTokenIdsMinted async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner We connect to the Contract using a Provider so we will only have read only access to the Contract const nftContract new Contract NFT CONTRACT ADDRESS abi provider call the tokenIds from the contract const tokenIds await nftContract tokenIds tokenIds is a Big Number We need to convert the Big Number to a string setTokenIdsMinted tokenIds toString catch err console error err Returns a Provider or Signer object representing the Ethereum RPC with or without the signing capabilities of metamask attached A Provider is needed to interact with the blockchain reading transactions reading balances reading state etc A Signer is a special type of Provider used in case a write transaction needs to be made to the blockchain which involves the connected account needing to make a digital signature to authorize the transaction being sent Metamask exposes a Signer API to allow your website to request signatures from the user using Signer functions param needSigner True if you need the signer default false otherwise const getProviderOrSigner async needSigner false gt Connect to Metamask Since we store webModal as a reference we need to access the current value to get access to the underlying object const provider await webModalRef current connect const webProvider new providers WebProvider provider If user is not connected to the Rinkeby network let them know and throw an error const chainId await webProvider getNetwork if chainId window alert Change the network to Rinkeby throw new Error Change network to Rinkeby if needSigner const signer webProvider getSigner return signer return webProvider useEffects are used to react to changes in state of the website The array at the end of function call represents what state changes will trigger this effect In this case whenever the value of walletConnected changes this effect will be called useEffect gt if wallet is not connected create a new instance of WebModal and connect the MetaMask wallet if walletConnected Assign the WebModal class to the reference object by setting it s current value The current value is persisted throughout as long as this page is open webModalRef current new WebModal network rinkeby providerOptions disableInjectedProvider false connectWallet Check if presale has started and ended const presaleStarted checkIfPresaleStarted if presaleStarted checkIfPresaleEnded getTokenIdsMinted Set an interval which gets called every seconds to check presale has ended const presaleEndedInterval setInterval async function const presaleStarted await checkIfPresaleStarted if presaleStarted const presaleEnded await checkIfPresaleEnded if presaleEnded clearInterval presaleEndedInterval set an interval to get the number of token Ids minted every seconds setInterval async function await getTokenIdsMinted walletConnected renderButton Returns a button based on the state of the dapp const renderButton gt If wallet is not connected return a button which allows them to connect their wllet if walletConnected return lt button onClick connectWallet className styles button gt Connect your wallet lt button gt If we are currently waiting for something return a loading button if loading return lt button className styles button gt Loading lt button gt If connected user is the owner and presale hasnt started yet allow them to start the presale if isOwner amp amp presaleStarted return lt button className styles button onClick startPresale gt Start Presale lt button gt If connected user is not the owner but presale hasn t started yet tell them that if presaleStarted return lt div gt lt div className styles description gt Presale hasnt started lt div gt lt div gt If presale started but hasn t ended yet allow for minting during the presale period if presaleStarted amp amp presaleEnded return lt div gt lt div className styles description gt Presale has started If your address is whitelisted Mint a Crypto Dev lt div gt lt button className styles button onClick presaleMint gt Presale Mint lt button gt lt div gt If presale started and has ended its time for public minting if presaleStarted amp amp presaleEnded return lt button className styles button onClick publicMint gt Public Mint lt button gt return lt div gt lt Head gt lt title gt Crypto Devs lt title gt lt meta name description content Whitelist Dapp gt lt link rel icon href favicon ico gt lt Head gt lt div className styles main gt lt div gt lt h className styles title gt Welcome to Crypto Devs lt h gt lt div className styles description gt Its an NFT collection for developers in Crypto lt div gt lt div className styles description gt tokenIdsMinted have been minted lt div gt renderButton lt div gt lt div gt lt img className styles image src cryptodevs svg gt lt div gt lt div gt lt footer className styles footer gt Made with amp by Crypto Devs lt footer gt lt div gt Now create a new folder under the my app folder and name it constants In the constants folder create a file index js and paste the following code Replace addres of your NFT contract with the address of the CryptoDevs contract that you deployed and saved to your notepad Replace your abi with the abi of your CryptoDevs Contract To get the abi for your contract go to your hardhat tutorial artifacts contracts CryptoDevs sol folder and from your CryptoDevs json file get the array marked under the abi key export const abi your abi export const NFT CONTRACT ADDRESS address of your NFT contract Now in your terminal which is pointing to my app folder execute npm run devYour Crypto Devs NFT dapp should now work without errors Push to githubMake sure before proceeding you have pushed all your code to github Deploying your dAppWe will now deploy your dApp so that everyone can see your website and you can share it with all of your LearnWeb DAO friends Go to and sign in with your GitHubThen click on New Project button and then select your NFT Collection repoWhen configuring your new project Vercel will allow you to customize your Root DirectoryClick Edit next to Root Directory and set it to my appSelect the Framework as Next jsClick DeployNow you can see your deployed website by going to your dashboard selecting your project and copying the domain from there Save the domain on notepad you would need it later View your Collection on OpenseaNow lets make your collection is available on OpenseaTo make the collection available on Opensea we would need to create a metadata endpoint This endpoint would return the metadata for an NFT given its tokenId Open your my app folder and underpages api folder create a new file named tokenId js Make sure the name has the brackets as well Adding the brackets helps create dynamic routes in next jsAdd the following lines to tokenId js file Read about adding API routes in next js here export default function handler req res get the tokenId from the query params const tokenId req query tokenId As all the images are uploaded on github we can extract the images from github directly const image url The api is sending back metadata for a Crypto Dev To make our collection compatible with Opensea we need to follow some Metadata standards when sending back the response from the api More info can be found here res status json name Crypto Dev tokenId description Crypto Dev is a collection of developers in crypto image image url tokenId svg Now you have an api route which Opensea can call to retrieve the metadata for the NFTLets deploy a new Crypto Devs contract with this new api route as your METADATA URLOpen your hardhat tutorial constants folder and inside your index js file replace with the domain which you saved to notepad and add api to its end Save the file and open up a new terminal pointing to hardhat tutorial folder and deploy a new contract npx hardhat run scripts deploy js network rinkebySave the new NFT contract address to a notepad Open up my app constants folder and inside the index js file replace the old NFT contract address with the new onePush all the code to github and wait for vercel to deploy the new code After vercel has deployed your code open up your website and mint an NFTAfter your transaction gets successful In your browser open up this link by replacing your nft contract address with the address of your NFT contract Your NFT is now available on Opensea Share your Opensea link with everyone on discord and spread happiness This article is brought to you by LearnWeb DAO A free comprehensive A to Z blockchain training program for developers across the globe Everything from What is a Blockchain to Hacking smart contracts and everything in between but also much more Join us now to start buidling with builders WebsiteDiscordTwitter |
2022-05-15 08:14:30 |
海外TECH |
DEV Community |
Build a simple whitelist dApp using Solidity, Next.js, ethers.js on Ethereum |
https://dev.to/learnweb3/build-a-simple-whitelist-dapp-using-solidity-nextjs-ethersjs-on-ethereum-4c21
|
Build a simple whitelist dApp using Solidity Next js ethers js on Ethereum Whitelist DappYou are launching your NFT collection named Crypto Devs You want to give your early supporters access to a whitelist for your collection so here you are creating a whitelist dapp for Crypto Devs RequirementsWhitelist access should be given to the first users for free who want to get in There should be a website where people can go and enter into the whitelist Lets start building PrerequisitesYou can write code in JavaScript Beginner Track Level Have set up a Metamask Wallet Beginner Track Level Your computer has Node js installed If not download from here Prefer a Video If you would rather learn from a video we have a recording available of this tutorial on our YouTube Watch the video by clicking on the screenshot below or go ahead and read the tutorial Build Smart ContractTo build the smart contract we will 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 First you need to create a Whitelist Daap folder where the Hardhat project and your Next js app will later goOpen up a terminal and execute these commands mkdir Whitelist Dapp cd Whitelist DappThen in Whitelist Daap folder you will set up Hardhat project mkdir hardhat tutorial cd hardhat tutorial npm init yes npm install save dev hardhatIn the same directory where you installed Hardhat run npx hardhatSelect Create a basic sample projectPress enter for the already specified Hardhat Project rootPress enter for the question on if you want to add a gitignorePress enter for Do you want to install this sample project s dependencies with npm nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethers Now you have a hardhat project ready to go If you are not on mac please do this extra step and install these libraries as well npm install save dev nomiclabs hardhat waffle ethereum waffle chai nomiclabs hardhat ethers ethersStart by creating a new file inside the contracts directory called Whitelist sol SPDX License Identifier Unlicense pragma solidity contract Whitelist Max number of whitelisted addresses allowed uint public maxWhitelistedAddresses Create a mapping of whitelistedAddresses if an address is whitelisted we would set it to true it is false by default for all other addresses mapping address gt bool public whitelistedAddresses numAddressesWhitelisted would be used to keep track of how many addresses have been whitelisted NOTE Don t change this variable name as it will be part of verification uint public numAddressesWhitelisted Setting the Max number of whitelisted addresses User will put the value at the time of deployment constructor uint maxWhitelistedAddresses maxWhitelistedAddresses maxWhitelistedAddresses addAddressToWhitelist This function adds the address of the sender to the whitelist function addAddressToWhitelist public check if the user has already been whitelisted require whitelistedAddresses msg sender Sender has already been whitelisted check if the numAddressesWhitelisted lt maxWhitelistedAddresses if not then throw an error require numAddressesWhitelisted lt maxWhitelistedAddresses More addresses cant be added limit reached Add the address which called the function to the whitelistedAddress array whitelistedAddresses msg sender true Increase the number of whitelisted addresses numAddressesWhitelisted Lets deploy the contract to rinkeby network Create a new file named deploy js under the scripts folderNow we will write some code to deploy the contract in deploy js file const ethers require hardhat async function main A ContractFactory in ethers js is an abstraction used to deploy new smart contracts so whitelistContract here is a factory for instances of our Whitelist contract const whitelistContract await ethers getContractFactory Whitelist here we deploy the contract const deployedWhitelistContract await whitelistContract deploy is the Maximum number of whitelisted addresses allowed Wait for it to finish deploying await deployedWhitelistContract deployed print the address of the deployed contract console log Whitelist Contract Address deployedWhitelistContract address Call the main function and catch if there is any error main then gt process exit catch error gt console error error process exit Now create a env file in the hardhat tutorial folder and add the following lines use the instructions in the comments to get your Alchemy API Key URL and RINKEBY Private Key Make sure that the account from which you get your rinkeby private key is funded with Rinkeby Ether Go to sign up create a new App in its dashboard and select the network as Rinkeby and replace add the alchemy key url here with its key url ALCHEMY API KEY URL add the alchemy key url here Replace this private key with your RINKEBY account private key To export your private key from Metamask open Metamask and go to Account Details gt Export Private Key Be aware of NEVER putting real Ether into testing accounts RINKEBY PRIVATE KEY add the rinkeby private key here Now we will install dotenv package to be able to import the env file and use it in our config Open up a terminal pointing athardhat tutorial directory and execute this command npm install dotenvNow open the hardhat config js file we would add the rinkeby network here so that we can deploy our contract to rinkeby Replace all the lines in the hardhar config js file with the given below lines require nomiclabs hardhat waffle require dotenv config path env const ALCHEMY API KEY URL process env ALCHEMY API KEY URL const RINKEBY PRIVATE KEY process env RINKEBY PRIVATE KEY module exports solidity networks rinkeby url ALCHEMY API KEY URL accounts RINKEBY PRIVATE KEY Compile the contract open up a terminal pointing athardhat tutorial directory and execute this command npx hardhat compileTo deploy open up a terminal pointing athardhat tutorial directory and execute this command npx hardhat run scripts deploy js network rinkebySave the Whitelist Contract Address that was printed on your terminal in your notepad you would need it futher down in the tutorial WebsiteTo develop the website we will use React and Next Js React is a javascript framework used to make websites and Next js is a React framework that also allows writing backend APIs code along with the frontend so you don t need two separate frontend and backend services First You will need to create a new next app Your folder structure should look something like Whitelist Dapp hardhat tutorial my appTo create this next app in the terminal point to Whitelist Dapp folder and type npx create next app latestand press enter for all the questionsNow to run the app execute these commands in the terminal cd my app npm run devNow go to http localhost your app should be running Now lets install WebModal library WebModal is an easy to use library to help developers easily allow their users to connect to your dApps with all sorts of different wallets By default WebModal Library supports injected providers like Metamask Dapper Gnosis Safe Frame Web Browsers etc and WalletConnect You can also easily configure the library to support Portis Fortmatic Squarelink Torus Authereum D CENT Wallet and Arkane Open up a terminal pointing atmy app directory and execute this command npm install webmodalIn the same terminal also install ethers js npm install ethersIn your my app public folder download this image and rename it to crypto devs svgNow go to styles folder and replace all the contents of Home modules css file with the following code this would add some styling to your dapp main min height vh display flex flex direction row justify content center align items center font family Courier New Courier monospace footer display flex padding rem border top px solid eaeaea justify content center align items center image width height margin left title font size rem margin rem description line height margin rem font size rem button border radius px background color blue border none color ffffff font size px padding px width px cursor pointer margin bottom media max width px main width flex direction column justify content center align items center Open your index js file under the pages folder and paste the following code explanation of the code can be found in the comments Make sure you read about React and React Hooks React Hooks Tutorial if you are not familiar with them import Head from next head import styles from styles Home module css import WebModal from webmodal import providers Contract from ethers import useEffect useRef useState from react import WHITELIST CONTRACT ADDRESS abi from constants export default function Home walletConnected keep track of whether the user s wallet is connected or not const walletConnected setWalletConnected useState false joinedWhitelist keeps track of whether the current metamask address has joined the Whitelist or not const joinedWhitelist setJoinedWhitelist useState false loading is set to true when we are waiting for a transaction to get mined const loading setLoading useState false numberOfWhitelisted tracks the number of addresses s whitelisted const numberOfWhitelisted setNumberOfWhitelisted useState Create a reference to the Web Modal used for connecting to Metamask which persists as long as the page is open const webModalRef useRef Returns a Provider or Signer object representing the Ethereum RPC with or without the signing capabilities of metamask attached A Provider is needed to interact with the blockchain reading transactions reading balances reading state etc A Signer is a special type of Provider used in case a write transaction needs to be made to the blockchain which involves the connected account needing to make a digital signature to authorize the transaction being sent Metamask exposes a Signer API to allow your website to request signatures from the user using Signer functions param needSigner True if you need the signer default false otherwise const getProviderOrSigner async needSigner false gt Connect to Metamask Since we store webModal as a reference we need to access the current value to get access to the underlying object const provider await webModalRef current connect const webProvider new providers WebProvider provider If user is not connected to the Rinkeby network let them know and throw an error const chainId await webProvider getNetwork if chainId window alert Change the network to Rinkeby throw new Error Change network to Rinkeby if needSigner const signer webProvider getSigner return signer return webProvider addAddressToWhitelist Adds the current connected address to the whitelist const addAddressToWhitelist async gt try We need a Signer here since this is a write transaction const signer await getProviderOrSigner true Create a new instance of the Contract with a Signer which allows update methods const whitelistContract new Contract WHITELIST CONTRACT ADDRESS abi signer call the addAddressToWhitelist from the contract const tx await whitelistContract addAddressToWhitelist setLoading true wait for the transaction to get mined await tx wait setLoading false get the updated number of addresses in the whitelist await getNumberOfWhitelisted setJoinedWhitelist true catch err console error err getNumberOfWhitelisted gets the number of whitelisted addresses const getNumberOfWhitelisted async gt try Get the provider from webModal which in our case is MetaMask No need for the Signer here as we are only reading state from the blockchain const provider await getProviderOrSigner We connect to the Contract using a Provider so we will only have read only access to the Contract const whitelistContract new Contract WHITELIST CONTRACT ADDRESS abi provider call the numAddressesWhitelisted from the contract const numberOfWhitelisted await whitelistContract numAddressesWhitelisted setNumberOfWhitelisted numberOfWhitelisted catch err console error err checkIfAddressInWhitelist Checks if the address is in whitelist const checkIfAddressInWhitelist async gt try We will need the signer later to get the user s address Even though it is a read transaction since Signers are just special kinds of Providers We can use it in it s place const signer await getProviderOrSigner true const whitelistContract new Contract WHITELIST CONTRACT ADDRESS abi signer Get the address associated to the signer which is connected to MetaMask const address await signer getAddress call the whitelistedAddresses from the contract const joinedWhitelist await whitelistContract whitelistedAddresses address setJoinedWhitelist joinedWhitelist catch err console error err connectWallet Connects the MetaMask wallet const connectWallet async gt try Get the provider from webModal which in our case is MetaMask When used for the first time it prompts the user to connect their wallet await getProviderOrSigner setWalletConnected true checkIfAddressInWhitelist getNumberOfWhitelisted catch err console error err renderButton Returns a button based on the state of the dapp const renderButton gt if walletConnected if joinedWhitelist return lt div className styles description gt Thanks for joining the Whitelist lt div gt else if loading return lt button className styles button gt Loading lt button gt else return lt button onClick addAddressToWhitelist className styles button gt Join the Whitelist lt button gt else return lt button onClick connectWallet className styles button gt Connect your wallet lt button gt useEffects are used to react to changes in state of the website The array at the end of function call represents what state changes will trigger this effect In this case whenever the value of walletConnected changes this effect will be called useEffect gt if wallet is not connected create a new instance of WebModal and connect the MetaMask wallet if walletConnected Assign the WebModal class to the reference object by setting it s current value The current value is persisted throughout as long as this page is open webModalRef current new WebModal network rinkeby providerOptions disableInjectedProvider false connectWallet walletConnected return lt div gt lt Head gt lt title gt Whitelist Dapp lt title gt lt meta name description content Whitelist Dapp gt lt link rel icon href favicon ico gt lt Head gt lt div className styles main gt lt div gt lt h className styles title gt Welcome to Crypto Devs lt h gt lt div className styles description gt Its an NFT collection for developers in Crypto lt div gt lt div className styles description gt numberOfWhitelisted have already joined the Whitelist lt div gt renderButton lt div gt lt div gt lt img className styles image src crypto devs svg gt lt div gt lt div gt lt footer className styles footer gt Made with amp by Crypto Devs lt footer gt lt div gt Now create a new folder under the my app folder and name it constants In the constants folder create a file index js and paste the following code Replace YOUR WHITELIST CONTRACT ADDRESS with the address of the whitelist contract that you deployed Replace YOUR ABI with the ABI of your Whitelist Contract To get the ABI for your contract go to your hardhat tutorial artifacts contracts Whitelist sol folder and from your Whitelist json file get the array marked under the abi key it will be a huge array close to lines if not more export const abi YOUR ABI export const WHITELIST CONTRACT ADDRESS YOUR WHITELIST CONTRACT ADDRESS Now in your terminal which is pointing to my app folder execute npm run devYour whitelist dapp should now work without errors Push to githubMake sure before proceeding you have pushed all your code to github Deploying your dAppWe will now deploy your dApp so that everyone can see your website and you can share it with all of your LearnWeb DAO friends Go to Vercel and sign in with your GitHubThen click on New Project button and then select your Whitelist dApp repoWhen configuring your new project Vercel will allow you to customize your Root DirectoryClick Edit next to Root Directory and set it to my appSelect the Framework as Next jsClick DeployNow you can see your deployed website by going to your dashboard selecting your project and copying the URL from there Share your website in Discord DThis article is brought to you by LearnWeb DAO A free comprehensive A to Z blockchain training program for developers across the globe Everything from What is a Blockchain to Hacking smart contracts and everything in between but also much more Join us now to start buidling with builders WebsiteDiscordTwitter |
2022-05-15 08:13:00 |
海外TECH |
DEV Community |
What are Providers and Signers? What is an ABI? What are BigNumbers? How do contracts deal with ERC-20 tokens? |
https://dev.to/learnweb3/what-are-providers-and-signers-what-is-an-abi-what-are-bignumbers-how-do-contracts-deal-with-erc-20-tokens-4g03
|
What are Providers and Signers What is an ABI What are BigNumbers How do contracts deal with ERC tokens Sophomore Mixed TopicsThere are a few topics that we wanted to provide some information on but weren t detailed enough to deserve their own levels This level is meant to group together a bunch of separate topics like that and go over some things that will be helpful to keep in mind Table of ContentsProviders and SignersBigNumbersABIReact HooksERC Approval Flow Providers and SignersWhen building interfaces to smart contracts you will commonly come across these two terms Provider and Signer While you will develop a much better understanding of them when you actually start using them we will try to provide a brief explanation of what they mean We know that to read or write data to the blockchain we need to be communicating through an Ethereum node The node contains the blockchain state allowing us to read data It can also broadcast transactions to miners allowing us to write data Notice how the node only needs to broadcast transactions in case you want to write data to the blockchain Since if you re just reading data that already exists miners don t need to do anything about it they ve already done their work A Provider is an Ethereum node connection that allows you to read data from its state You will use a Provider to do things like calling read only functions in smart contracts fetching balances of accounts fetching transaction details etc A Signer is an Ethereum node connection that allows you to write data to the blockchain You will use a Signer to do things like calling write functions in smart contracts transferring ETH between accounts etc To do so the Signer needs access to a Private Key it can use for making transactions on behalf of an account Additionally a Signer can do everything a Provider can You can do both reading and writing using a Signer but a Provider is only good for reading data Wallets like Metamask by default inject a Provider into your browser Therefore dApps can use your Metamask Provider to read values from the blockchain network your wallet is connected to However sometimes you want the users to make transactions not just read data Of course Metamask can t just go around sharing your private keys with random websites that would be crazy For this Metamask also allows websites to request a Signer So when a dApp attempts to send a transaction to the blockchain the Metamask window pops up asking the user to confirm the action BigNumbersWhen learning Solidity we have been reading about and using uint a lot uint has a range from to Therefore the maximum number that can be held by the uint datatype is astronomically large Specifically the maximum value for uint is For comparison one million is Clearly uint can hold insanely large numbers But this presents a problem We usually build interfaces for smart contracts in Javascript Javascript s number data type has a significantly smaller upper limit Specifically the maximum value of a numeric type that Javascript can hold is just That is not even close to what uint can hold So let s say we use Javascript to call a function on a smart contract that returns a uint If that number is greater than Javascript s maximum numeric value which is quite definitely possible then what happens Well as it turns out Javascript can not support that Therefore we have to use a special type called a BigNumber Libraries used for interacting with Ethereum nodes ethers js and web js both come with support for BigNumbers BigNumber is a custom class library written in Javascript that introduces it s own functions for mathematical functions add sub mul div etc BigNumber has a significantly larger capacity for numbers than what Javascript can natively support When we write code in the following levels you will come across mathematical operations being done by calling functions like add and mul instead of the typical and operators that we know this is because when we are working with BigNumbers we need to use it s mathematical functions As to what would happen if we were to try and do this with Javascript numbers well we would very easily overflow or underflow This means our calculations would be entirely incorrect and undefined So just keep this in mind ABIABI stands for Application Binary Interface It is one of the more tricky things to understand when working with Ethereum but we will try to explain it as best as we can In Freshman tutorials and in tutorials you will come across further you will be using ABIs heavily When Solidity code is compiled it is compiled down to bytecode which is essentially binary It contains no record of function names that existed in the contract what parameters they contain and what values they return However if you want to call a Solidity function from a web app you need a way to call the correct bytecode in the contract To do this you need a way to convert human readable function names and parameters into bytecode and back ABIs help us achieve exactly that When you compile your Solidity code an ABI is automatically generated by the compiler It contains rules and metadata about functions present in the contract which help in doing the proper data conversion back and forth So when you want to call a contract you need its address of course but you also need to provide its ABI Libraries like ethers js use the ABI to encode and decode the human readable functions into bytecode and back when communicating with an Ethereum node and calling functions in smart contracts React HooksIn Sophomore tutorials and beyond we will be developing websites using React and Next js Oftentimes we will be using some React specific features to help in writing clean code A really important feature of React is React Hooks We will not try to reinvent the wheel and teach you in detail what React Hooks are as there are a lot of other resources for that but we will list the things that are relevant so you can focus on that React comes inbuilt with hooks and developers can create their own hooks as well For our purposes we will focus on that we use regularly and the rest you can learn if you want Read up on these three hooks and understand what they are used for useStateuseEffectuseRef ERC Approval FlowIn the past we learnt about payable functions which allow smart contracts to accept an ETH payment when a function is called This is really useful if you want to charge users in ETH in exchange for something for example an NFT sale But what if you want to use something other than ETH for payment What if you wanted to use your own Cryptocurrency that you deployed for payment Things get a little tricky here Since ETH is the native currency of Ethereum and the ERC standard was introduced much later after Ethereum was invented they don t behave exactly the same way Specifically accepting payments in ERC tokens is not as simple as just making a function payable in Solidity The payable keyword is only good for ETH payments If you wanted to use your own ERC cryptocurrency the flow to do that is a little more complicated First let s think about this a little bit Okay so you cannot send ERC tokens along with a function call like you can do with ETHPerhaps the smart contract can somehow pull tokens from the function caller s account But that would mean I can code a smart contract that steals everyone s tokens if someone makes a transaction with my contractSo we need a safer way to pull tokens out of someone s accountHere is where the Approve and Transfer flow comes in The ERC standard comes with the concept of Allowance Let s try to think about this with the help of an example Alice wants to sell her NFTAlice wants to accept payment for her NFT in her own cryptocurrency AliceCoinAlice s NFT costs AliceCoinBob owns AliceCoinBob wants to buy Alice s NFTBob needs a way to call a function on Alice s NFT smart contract which would take the Alicecoin payment and send him his NFTSince smart contracts cannot accept Alicecoin as payment directly Alice has coded up the ERC Approval and Transfer flow in her NFT contractAlicecoin is an ERC token ERC comes inbuilt with a few functions that relate to the Allowance concept approve address spender uint amount This allows a user to approve a different address to spend up to amount tokens on their behalf i e This function provides an Allowance to the spender of up to amount transferFrom address from address to uint amount Allows a user to transfer amount token from from to to If the user calling the function is the same as the from address tokens are removed from the user s balance If the user is someone other than the from address the from address must have in the past given the user allowance to spend amount tokens using the approve function Continuing with the example now Bob gives Alice s NFT contract the allowance to spend up to of his Alicecoin using the approve functionBob calls the function to purchase Alice s NFT on her NFT contractThe purchase function internally calls transferFrom on Alicecoin and transfers Alicecoin from Bob s account to Alice s accountSince the contract was given the allowance to spend up to Alicecoin by Bob earlier this action is permittedAlice therefore receives her Alicecoin and Bob receives his NFTOk so what does this mean for us Well note how Bob had to give approval to the contract so the contract could pull Bob s tokens from his account Therefore Bob essentially had to do two transactions to replicate the behaviour of what could be done in one transaction if payment was being accepted in ETH Transaction Give allowance to the contractTransaction Call the contract function which internally uses the allowance to transfer Bob s tokens to a different addressSo if you were building a dApp where you needed users to pay your smart contract using an ERC token you would also need to make them do both transactions Simply calling your contract function without first having your users provide allowance to your contract will cause the function call to fail We will come across a use case of this flow when building the DeFi Exchange in the final level of Sophomore track Since an exchange involves being able to convert one token of another you need to call a function on the exchange smart contract which takes in one token and gives you another To take in your token to swap the exchange contract requires approval to pull the token from your account So keep this flow in mind if you re wondering why swapping on the exchange can use two transactions instead of one This article is brought to you by LearnWeb DAO A free comprehensive A to Z blockchain training program for developers across the globe Everything from What is a Blockchain to Hacking smart contracts and everything in between but also much more Join us now to start buidling with builders WebsiteDiscordTwitter |
2022-05-15 08:11:02 |
海外ニュース |
Japan Times latest articles |
Okinawa marks 50 years since reversion from U.S. rule |
https://www.japantimes.co.jp/news/2022/05/15/national/okinawa-reversion-anniversary/
|
Okinawa marks years since reversion from U S ruleOkinawa continues to shoulder the bulk of U S bases in the country despite its long stated hope of becoming a peaceful island free of military bases |
2022-05-15 17:33:08 |
ニュース |
BBC News - Home |
Nigeria's Kaduna train attack: Pregnant woman freed |
https://www.bbc.co.uk/news/world-africa-61455262?at_medium=RSS&at_campaign=KARANGA
|
compassionate |
2022-05-15 08:29:50 |
サブカルネタ |
ラーブロ |
雅楽@あざみ野 |
http://ra-blog.net/modules/rssc/single_feed.php?fid=199150
|
北大西洋条約機構 |
2022-05-15 09:09:26 |
北海道 |
北海道新聞 |
春の高校野球支部予選・5月15日の試合結果 |
https://www.hokkaido-np.co.jp/article/680959/
|
立命館慶祥 |
2022-05-15 17:07:30 |
北海道 |
北海道新聞 |
北海道内2488人感染 2日連続3千人下回る 死亡3人 新型コロナ |
https://www.hokkaido-np.co.jp/article/681003/
|
北海道内 |
2022-05-15 17:11:58 |
北海道 |
北海道新聞 |
パラ陸上、兎沢がアジア新で優勝 ジャパンパラ最終日 |
https://www.hokkaido-np.co.jp/article/681011/
|
陸上 |
2022-05-15 17:06:00 |
北海道 |
北海道新聞 |
今平周吾、逆転で6勝目 ダイヤモンド杯最終日 |
https://www.hokkaido-np.co.jp/article/681010/
|
今平周吾 |
2022-05-15 17:06:00 |
コメント
コメントを投稿