# Staker | Web3.0 dApp Dev 0x09 **Published by:** [Web3dAppDevCamp](https://paragraph.com/@apecoder/) **Published on:** 2022-07-19 **URL:** https://paragraph.com/@apecoder/staker-web3-0-dapp-dev-0x09 ## Content 国内用户请加微信:19726581 Authors: Fenix, msfew0x00 GoalBuidl a decentralized Staking dApp. This example also corresponds to Challenge 0x01 in SpeedrunEthereum (an official Ethereum developer challenge):https://speedrunethereum.com/challenge/decentralized-stakingAn example of one of these completions can be found at:Contract: https://rinkeby.etherscan.io/address/0x4707468C95558E9B2F339c2A41DAEF483Ce11104 dApp: https://duckgo_staking.surge.sh/image-202207171211476670x01 What is StakingStaking, which translates to pledge, staking mining, or staking of interest, is the algorithm used to generate new blocks, and is derived from Proof of Stake. If you're familiar with how Bitcoin works, you're probably familiar with Proof of Work (PoW). This mechanism allows transactions to be collected into blocks. These blocks are then linked together to create a blockchain. Specifically, miners compete to solve complex mathematical puzzles, and whoever solves the puzzle first is entitled to add the next block to the blockchain. Proof of workload has proven to be a very powerful mechanism to facilitate consensus in a decentralized manner. The problem is that this mechanism involves a lot of arbitrary computation. Miners are scrambling to solve the puzzle just to maintain network security and nothing else. Moving on to proof of stake, the main idea is that participants can lock in tokens (their "pledged interest") and the protocol will randomly assign rights to one of them for verification of the next block at a given time interval. Usually, the probability of being selected is proportional to the number of tokens: the more tokens locked, the better the chances.Staking & MiningIn the cryptocurrency market, mining has been gradually replaced by staking in recent years, with the benefit of lower power consumption and passive income than the former.0x02 Speed Run Web3Some students may not have experience with web3 related development, so here is a speed run on your behalf to facilitate a quick introduction to the development on Ether. Prerequisitesmetamask WalletEnvironmentnodejsyarngitvscodeTech StackFramework: scaffold-ethFrontend: reactContract Development: hardhatStep 1: Scaffolding https://github.com/scaffold-eth/scaffold-ethgit clone https://github.com/scaffold-eth/scaffold-eth.git cd scaffold-eth yarn install Step 2: Start a local networkcd scaffold-eth yarn chain Step 3: Deploy Smart Contractscd scaffold-eth yarn deploy Step 4: Open the front-end pagecd scaffold-eth yarn start Technology StackTop Level Technology Stacksolidity contract programming languagehardhat local development test chainreact front-endetherseth api sdkantd front-end components uiLibraries & Components & ServicesEth-componentsEth-servicesCommand LineUnderlying Foundationthe graphtenderlyetherscanrpcblocknativeL2/Sidechain ServicesArbitrumOptimismGraph NodeExamplesGeneralsimple daoDiamond StandardMeta-Multi-Sig WalletMinimal ProxyMinimum Viable Payment ChannelPunkWallet.ioPush The Button - Multi-player Turn Based Gameradwallet.ioSignator.ioSimple StreamToken AllocatorStreaming Meta Multi SigDeFiBonding CurverTokensQuadratic FundingUniswapperLenderAave Flash Loans IntroAave ApeDeFi SubgraphsNFTSimple NFTSimple ERC-1155 NFTChainlink VRF NFTMerkle Mint NFTNifty ViewerNFT AuctionNFT Signature Based AuctionSecurityHoneypotRe-entrancy AttackDenial of ServiceInfrastructureChainLinkLayer2Optimism Starter PackOptimism NFTxNFT.io0x03 Staking ContractWe all know that the most important part of dApp development is writing smart contracts, so let's analyze the basic format of a Staking contract.Pledge (stake) a certain amount of tokens (threshold) within a certain time (deadline). After the expiration date, you can transfer (execute) the tokens to another contract, or withdraw (withdraw) the tokens.So we abstracted out three key functions.stake()execute()withdraw()scaffold-eth also provides us with such a scaffold, just pull down the code and we will build on it step by step this time. https://github.com/scaffold-eth/scaffold-eth-challengesgit clone https://github.com/scaffold-eth/scaffold-eth-challenges.git cd scaffold-eth-challenges git checkout challenge-1-decentralized-staking yarn install Then open three terminal windows and execute the following three commands:yarn start yarn deploy --reset 0x04 Live Coding4.1 stakeKey point 1: Stake a certain amount of eth at a time.pragma solidity 0.8.4; import "hardhat/console.sol"; import "./ExampleExternalContract.sol"; contract Staker { mapping(address => uint256) public balances; event Stake(address indexed staker, uint256 amount); function stake() public payable { balances[msg.sender] += msg.value; emit Stake(msg.sender, msg.value); } } Key point 2, deploy the script to remove the constructor's parameters// deploy/01_deploy_staker.js // .... await deploy('Staker', { // Learn more about args here: https://www.npmjs.com/package/hardhat-deploy#deploymentsdeploy from: deployer, // args: [exampleExternalContract.address], log: true, }); //... Key Point 3, Deploymentyarn deploy --reset Key point 4, airdrop some test coinsKey point 5, test staking4.2 executeThe funds raised are transferred to another contract after certain conditions are met.Key point 1, another contractcontract ExampleExternalContract { bool public completed; function complete() public payable { completed = true; } } It's simple, there is a flag to indicate whether it is finished or not.Key point 2, the constructorIn the stake contract, to bring in this contract, there is a constructor.ExampleExternalContract public exampleExternalContract; constructor(address exampleExternalContractAddress) public { exampleExternalContract = ExampleExternalContract( exampleExternalContractAddress ); } Key point 3, initialize when deploying// deploy/01_deploy_staker.js // .... await deploy('Staker', { // Learn more about args here: https://www.npmjs.com/package/hardhat-deploy#deploymentsdeploy from: deployer, args: [exampleExternalContract.address], log: true, }); //... Key Point 4, Staking Limituint256 public constant threshold = 1 ether; Key point 5, transfer to the second contract. function execute() public { uint256 contractBalance = address(this).balance; // check the contract has enough ETH to reach the treshold require(contractBalance >= threshold, "Threshold not reached"); // Execute the external contract, transfer all the balance to the contract // (bool sent, bytes memory data) = exampleExternalContract.complete{value: contractBalance}(); (bool sent, ) = address(exampleExternalContract).call{ value: contractBalance }(abi.encodeWithSignature("complete()")); require(sent, "exampleExternalContract.complete failed"); } The final code is as follows// SPDX-License-Identifier: MIT pragma solidity 0.8.4; import "hardhat/console.sol"; import "./ExampleExternalContract.sol"; contract Staker { ExampleExternalContract public exampleExternalContract; mapping(address => uint256) public balances; uint256 public constant threshold = 1 ether; event Stake(address indexed staker, uint256 amount); constructor(address exampleExternalContractAddress) public { exampleExternalContract = ExampleExternalContract( exampleExternalContractAddress ); } function stake() public payable { balances[msg.sender] += msg.value; emit Stake(msg.sender, msg.value); } function execute() public { uint256 contractBalance = address(this).balance; require(contractBalance >= threshold, "Threshold not reached"); (bool sent, ) = address(exampleExternalContract).call{ value: contractBalance }(abi.encodeWithSignature("complete()")); require(sent, "exampleExternalContract.complete() failed"); } } Deploymentyarn deploy --reset Airdrop test coinsstake some coins reach the limittest execute4.3 withdrawWithdrawing the staked money is relatively simple - just transfer the money out. function withdraw() public { uint256 userBalance = balances[msg.sender]; require(userBalance > 0, "You don't have balance to withdraw"); balances[msg.sender] = 0; (bool sent, ) = msg.sender.call{value: userBalance}(""); require(sent, "Failed to send user balance back to the user"); } The complete code is as follows// SPDX-License-Identifier: MIT pragma solidity 0.8.4; import "hardhat/console.sol"; import "./ExampleExternalContract.sol"; contract Staker { ExampleExternalContract public exampleExternalContract; mapping(address => uint256) public balances; uint256 public constant threshold = 1 ether; uint256 public deadline = block.timestamp + 30 seconds; event Stake(address indexed sender, uint256 amount); constructor(address exampleExternalContractAddress) public { exampleExternalContract = ExampleExternalContract( exampleExternalContractAddress ); } function stake() public payable { balances[msg.sender] += msg.value; emit Stake(msg.sender, msg.value); } function execute() public { uint256 contractBalance = address(this).balance; require(contractBalance >= threshold, "Threshold not reached"); (bool sent, ) = address(exampleExternalContract).call{ value: contractBalance }(abi.encodeWithSignature("complete()")); require(sent, "exampleExternalContract.complete failed"); } function withdraw() public { uint256 userBalance = balances[msg.sender]; require(userBalance > 0, "You don't have balance to withdraw"); balances[msg.sender] = 0; (bool sent, ) = msg.sender.call{value: userBalance}(""); require(sent, "Failed to send user balance back to the user"); } } Deploymentyarn deploy --reset Airdrop test coinsstake some coinstest withdraw4.4 Add time or staking expiration restrictionsThere are two criteria to judge here The first is whether the time has been reached, and the other is whether the pledge has been completed.Whether the first one is completed or not, just go directly to the mark of the other contract modifier stakeNotCompleted() { bool completed = exampleExternalContract.completed(); require(!completed, "staking process is already completed"); _; } Is the second one up to time?The first is to have a deadline variableuint256 public deadline = block.timestamp + 60 seconds; Then there is also a timeLeft function function timeLeft() public view returns (uint256 timeleft) { if (block.timestamp >= deadline) { return 0; } else { return deadline - block.timestamp; } } Next is the deadlineReached function modifier deadlineReached(bool requireReached) { uint256 timeRemaining = timeLeft(); if (requireReached) { require(timeRemaining == 0, "deadline is not reached yet"); } else { require(timeRemaining > 0, "deadline has already reached"); } _; } How to modify these functionsstake function stake() public payable deadlineReached(false) stakeNotCompleted { balances[msg.sender] += msg.value; emit Stake(msg.sender, msg.value); } execute function function execute() public stakeNotCompleted deadlineReached(false) { uint256 contractBalance = address(this).balance; require(contractBalance >= threshold, "Threshold not reached"); (bool sent, ) = address(exampleExternalContract).call{ value: contractBalance }(abi.encodeWithSignature("complete()")); require(sent, "exampleExternalContract.complete() failed"); } withdraw function function withdraw() public deadlineReached(true) stakeNotCompleted { uint256 userBalance = balances[msg.sender]; require(userBalance > 0, "You don't have balance to withdraw"); balances[msg.sender] = 0; (bool sent, ) = msg.sender.call{value: userBalance}(""); require(sent, "Failed to send user balance back to the user"); } Functions that can be called by external contracts receive() external payable { stake(); } The final code is as follows:// SPDX-License-Identifier: MIT pragma solidity 0.8.4; import "hardhat/console.sol"; import "./ExampleExternalContract.sol"; contract Staker { ExampleExternalContract public exampleExternalContract; mapping(address => uint256) public balances; uint256 public constant threshold = 1 ether; event Stake(address indexed staker, uint256 amount); uint256 public deadline = block.timestamp + 60 seconds; constructor(address exampleExternalContractAddress) public { exampleExternalContract = ExampleExternalContract( exampleExternalContractAddress ); } modifier stakeNotCompleted() { bool completed = exampleExternalContract.completed(); require(!completed, "staking process is already completed"); _; } modifier deadlineReached(bool requireReached) { uint256 timeRemaining = timeLeft(); if (requireReached) { require(timeRemaining == 0, "deadline is not reached yet"); } else { require(timeRemaining > 0, "deadline has already reached"); } _; } function timeLeft() public view returns (uint256 timeleft) { if (block.timestamp >= deadline) { return 0; } else { return deadline - block.timestamp; } } function stake() public payable deadlineReached(false) stakeNotCompleted { balances[msg.sender] += msg.value; emit Stake(msg.sender, msg.value); } function execute() public stakeNotCompleted deadlineReached(false) { uint256 contractBalance = address(this).balance; require(contractBalance >= threshold, "Threshold not reached"); (bool sent, ) = address(exampleExternalContract).call{ value: contractBalance }(abi.encodeWithSignature("complete()")); require(sent, "exampleExternalContract.complete() failed"); } function withdraw() public deadlineReached(true) stakeNotCompleted { uint256 userBalance = balances[msg.sender]; require(userBalance > 0, "You don't have balance to withdraw"); balances[msg.sender] = 0; (bool sent, ) = msg.sender.call{value: userBalance}(""); require(sent, "Failed to send user balance back to the user"); } receive() external payable { stake(); } } Deploymentyarn deploy --reset Testing0x05 Related Informationscaffold-ethWeb3-dApp-Camphttps://guoyu.mirror.xyz/RD-xkpoxasAU7x5MIJmiCX4gll3Cs0pAd5iM258S1Eklearnblockchain community ## Publication Information - [Web3dAppDevCamp](https://paragraph.com/@apecoder/): Publication homepage - [All Posts](https://paragraph.com/@apecoder/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@apecoder): Subscribe to updates - [Twitter](https://twitter.com/Web3dAppCamp): Follow on Twitter