前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >币安链BSC众筹系统开发技术详细分析

币安链BSC众筹系统开发技术详细分析

原创
作者头像
I357O98O7I8
发布2022-08-09 17:07:01
8200
发布2022-08-09 17:07:01
举报
文章被收录于专栏:dapp系统开发dapp系统开发

在一定时间(deadline)内,质押(stake)一定数量(threshold)的代币。到期之后可以将代币转入(execute)到另一个合约,也可以将代币提取出来(withdraw)。

所以我们抽像出来了三个关键函数:

  1. stake()
  2. execute()
  3. withdraw()

scaffold-eth也为我们提供了这样的一个脚手架,只需要把代码拉下来,我们本次就在这个基础上逐步来实现。

代码语言:javascript
复制
git clone https://github.com/scaffold-eth/scaffold-eth-challenges.git
cd scaffold-eth-challenges
git checkout challenge-1-decentralized-staking
yarn install

然后开三个终端窗口,执行如下三个命令:

代码语言:javascript
复制
yarn chain
代码语言:javascript
复制
yarn start
代码语言:javascript
复制
yarn deploy --reset

0x04 Live Coding

4.1 stake

  • 关键点1,每次质押一定数量的eth。
代码语言:javascript
复制
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);
    }
}
  • 关键点2,部署脚本移除构造函数的参数
代码语言:javascript
复制
// 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,
  });

//...
  • 关键点3,部署
代码语言:javascript
复制
yarn deploy --reset
  • 关键点4,空投一些测试币
  • 关键点5,测试stake

4.2 execute

筹集到的资金,在满足一定条件之后,转移给另一个合约中。

  • 关键点1,另一个合约
代码语言:javascript
复制
contract ExampleExternalContract {

  bool public completed;

  function complete() public payable {
    completed = true;
  }

}

很简单,有一个flag代表是否已经结束了。

  • 关键点2,构造函数

在stake合约中,要把这个合约引入进来,同时要有一个构造函数

代码语言:javascript
复制
ExampleExternalContract public exampleExternalContract;

constructor(address exampleExternalContractAddress) public {
        exampleExternalContract = ExampleExternalContract(
            exampleExternalContractAddress
        );
}
  • 关键点3,部署的时候要初始化
代码语言:javascript
复制
// 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,
  });

//...
  • 关键点4,质押上限
代码语言:javascript
复制
uint256 public constant threshold = 1 ether;
  • 关键点5,向第二个合约转账。
代码语言:javascript
复制
    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");
    }
  • 最终代码如下
代码语言:javascript
复制
// 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");
    }
}
  • 部署
代码语言:javascript
复制
yarn deploy --reset
  • 空投测试币
  • stake 一些币到达上限
  • 测试 execute

4.3 withdraw

将质押的钱提取出来,这个比较简单,就是将钱转移出来即可。

代码语言:javascript
复制
    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");
    }
  • 完整代码如下
代码语言:javascript
复制
// 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");
    }
}
  • 部署
代码语言:javascript
复制
yarn deploy --reset
  • 空投测试币
  • stake一些币
  • 测试withdraw

4.4 加上时间或质押到期限制

这里有两个判断的标准

首先是是否到达时间,另一个就是质押是否已经完成。

  • 第一个是否完成,直接去看另一个合约的标志即可
代码语言:javascript
复制
		modifier stakeNotCompleted() {
        bool completed = exampleExternalContract.completed();
        require(!completed, "staking process is already completed");
        _;
    }
  • 第二个是否到时间了 uint256 public deadline = block.timestamp + 60 seconds; function timeLeft() public view returns (uint256 timeleft) { if (block.timestamp >= deadline) { return 0; } else { return deadline - block.timestamp; } } 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"); } _; }
    • 接着是deadlineReached函数
    • 然后还要有一个timeLeft函数
    • 首先要有一个deadline变量
  • 如何修饰这些函数 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"); }
    • withdraw函数
    • execute函数
    • stake
  • 可以被外部合约调用的函数
代码语言:javascript
复制
	receive() external payable {
        stake();
    }

最终代码如下:

代码语言:javascript
复制
// 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();
    }
}
  • 部署
代码语言:javascript
复制
yarn deploy --reset
  • 测试

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x04 Live Coding
    • 4.1 stake
      • 4.2 execute
        • 4.3 withdraw
          • 4.4 加上时间或质押到期限制
          相关产品与服务
          网站建设
          网站建设(Website Design Service,WDS),是帮助您快速搭建企业网站的服务。通过自助模板建站工具及专业设计服务,无需了解代码技术,即可自由拖拽模块,可视化完成网站管理。全功能管理后台操作方便,一次更新,数据多端同步,省时省心。使用网站建设服务,您无需维持技术和设计师团队,即可快速实现网站上线,达到企业数字化转型的目的。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档