0301-solidity进阶-区块链-web3

发布于:2025-08-17 ⋅ 阅读:(15) ⋅ 点赞:(0)

序言

通过一个众筹项目来学习solidity进阶知识。

通过函数发送ETH

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// 1.创建一个收款函数
// 2.记录投资人并且查看
// 3.在锁定期内,达到目标值,生产商可以提款
// 4.在锁定期内,没有达到目标值,投资人可以在锁定期以后退款

contract FundMe {
    mapping (address => uint256) public fundersToAount;

    function fund() external payable {
        fundersToAount[msg.sender] = msg.value;
    }
 }

指fund函数收款0.002ETH,如下图所示:

在这里插入图片描述

通过预言机设定最小额度

限制最小额度,更直观的(容易理解)稳定的是我们自己使用的货币,比如USD或RMB等。那我们需要了解ETH和USD兑换比例,这里需要引入预言机。

-Solidity的数据类型没有double或者floating(小数),如果想要表示带有小数的以太币,把wei当成最小单位,它是ether的10e-18,也就是0.000000000000000001。

代码如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

// 1.创建一个收款函数
// 2.记录投资人并且查看
// 3.在锁定期内,达到目标值,生产商可以提款
// 4.在锁定期内,没有达到目标值,投资人可以在锁定期以后退款

contract FundMe {
    mapping (address => uint256) public fundersToAount;

    uint256 MINIMUM_VALUE = 4 * 10 ** 18; // USD

    AggregatorV3Interface internal dataFeed;

    constructor() {
        // sepolia testnet
        dataFeed = AggregatorV3Interface(
            0x694AA1769357215DE4FAC081bf1f309aDC325306
        );
    }

    function fund() external payable {
        // 控制台打印等价usd值
        uint256 usdValue = convertEthToUsd(msg.value);
        
        require(usdValue >= MINIMUM_VALUE, "cannot be less than the minimum limit");
        fundersToAount[msg.sender] += msg.value;
    }

    /**
     * Returns the latest answer.
     */
    function getChainlinkDataFeedLatestAnswer() public view returns (int) {
        // prettier-ignore
        (
            /* uint80 roundId */,
            int256 answer,
            /*uint256 startedAt*/,
            /*uint256 updatedAt*/,
            /*uint80 answeredInRound*/
        ) = dataFeed.latestRoundData();
        return answer;
    }

    function convertEthToUsd(uint256 ethAmount) internal view  returns (uint256) {
        uint256 ethPrice = uint256(getChainlinkDataFeedLatestAnswer());
        uint256 ethAmountInUsd = (ethPrice * ethAmount) / (10 ** 8);
        return ethAmountInUsd;
    
    }
}

当金额小于最小额度时,如下图所示:

在这里插入图片描述

当金额大于等于最小额度,如下图所示:

在这里插入图片描述

通过函数提取合约中的ETH

提取ETH和退款代码如下所示:

    // 达到目标值,生产商可以提款
    function getFund() external {
        require(owner == msg.sender, "this function can only be called by owner");
        // 判断筹款金额大于等于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) >= TARGET, "Target is not reached.");
        // payable(msg.sender).transfer(balance);
        // bool success = payable(msg.sender).send(balance);
        bool success;
        (success, ) = payable(msg.sender).call{value: balance}("");
        require(success, "transfer failed");
        // 余额清空
        fundersToAount[msg.sender] = 0;
    }

    // 没有达到目标值,投资人可以在锁定期以后退款
    function refund() external {
        // 判断筹款金额小于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) < TARGET, "Target is  reached.");
        uint256 amount = fundersToAount[msg.sender];
        require(amount != 0, "there is not fund for you");
        bool success;
        (success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "transfer failed");
        fundersToAount[msg.sender] = 0;
    }

函数修饰符和时间锁

时间戳和锁定时间相关逻辑:

  • 定时合约部署时间和锁定时间
  • 初始化合约部署时间和锁定时间
  • 在fund中判断合约未到期才可以捐款
  • 在getFund中判断合约到期才可以提款
  • reFund中判断合约到期才可以退款

代码如下所示:

    // 合约部署时间
    uint256 deplomentTimestamp;
    // 合约锁定时间
    uint256 lockTime;

    constructor(uint256 _lockTime) {
        // sepolia testnet
        dataFeed = AggregatorV3Interface(
            0x694AA1769357215DE4FAC081bf1f309aDC325306
        );
        owner = msg.sender;
        deplomentTimestamp = block.timestamp;
        lockTime = _lockTime;
    }

    function fund() external payable {
         // 判断锁定时间

        require( (block.timestamp < deplomentTimestamp + lockTime), "the lock time is over");
        // 控制台打印等价usd值
        uint256 usdValue = convertEthToUsd(msg.value);
        
        require(usdValue >= MINIMUM_VALUE, "cannot be less than the minimum limit");
        fundersToAount[msg.sender] += msg.value;
    }
        // 达到目标值,生产商可以提款
    function getFund() external {
        // 判断锁定时间
        require( (block.timestamp > deplomentTimestamp + lockTime), "the lock time is not  over");
        require(owner == msg.sender, "this function can only be called by owner");
        // 判断筹款金额大于等于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) >= TARGET, "Target is not reached.");
        // payable(msg.sender).transfer(balance);
        // bool success = payable(msg.sender).send(balance);
        bool success;
        (success, ) = payable(msg.sender).call{value: balance}("");
        require(success, "transfer failed");
        // 余额清空
        fundersToAount[msg.sender] = 0;
    }
    
    // 没有达到目标值,投资人可以在锁定期以后退款
    function refund() external {
        // 判断锁定时间
        require( (block.timestamp > deplomentTimestamp + lockTime), "the lock time is not  over");
        // 判断筹款金额小于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) < TARGET, "Target is  reached.");
        uint256 amount = fundersToAount[msg.sender];
        require(amount != 0, "there is not fund for you");
        bool success;
        (success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "transfer failed");
        fundersToAount[msg.sender] = 0;
    }

通过上述代码,我们注意到判定锁定时间和判断是否合约所有者多处用到,为实现代码复用,这里引入modified(修改器)。代码如下所示:

		
    // 达到目标值,生产商可以提款
    function getFund() external windowClosed onlyOwner{    
        // 判断筹款金额大于等于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) >= TARGET, "Target is not reached.");
        // payable(msg.sender).transfer(balance);
        // bool success = payable(msg.sender).send(balance);
        bool success;
        (success, ) = payable(msg.sender).call{value: balance}("");
        require(success, "transfer failed");
        // 余额清空
        fundersToAount[msg.sender] = 0;
    }

    // 没有达到目标值,投资人可以在锁定期以后退款
    function refund() external windowClosed{
        // 判断筹款金额小于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) < TARGET, "Target is  reached.");
        uint256 amount = fundersToAount[msg.sender];
        require(amount != 0, "there is not fund for you");
        bool success;
        (success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "transfer failed");
        fundersToAount[msg.sender] = 0;
    }
    
    modifier windowClosed() {
        // 判断锁定时间
        require( (block.timestamp > deplomentTimestamp + lockTime), "the lock time is not  over");
        _;
    }

    modifier onlyOwner() {
        require(owner == msg.sender, "this function can only be called by owner");
        _;
    }

Token和Coin的区别

创建一个Token合约

FundToken代码如下所示:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract FundToken {
    // 1. 通证的名称
    string public tokenName;
    // 2. 通证的简称
    string public tokenSymbol;
    // 3. 通证的发行数量
    uint256 public totalSupply;
    // 4. owner地址
    address public owner;
    // 5. balance address => uint256
    mapping(address => uint256) public balances;

    constructor(string memory _tokenName, string memory _tokenSymbol) {
        tokenName = _tokenName;
        tokenSymbol = _tokenSymbol;
        owner = msg.sender;
    }

    // mint: 获取通证
    function mint(uint256 _amount) public {
        balances[msg.sender] += _amount;
        totalSupply += _amount;
    }
    // transfer:transfer 通证
    function transfer(address payee, uint256 amount) public {
        // 验证数量
        require(balances[msg.sender] >= amount, "You don't have enough balance to transfer");
        balances[msg.sender] -= amount;
       balances[payee] += amount;
    }

    // balanceOf:查看某一个地址的通证数量
    function balanceOf(address _address) public view returns (uint256) {
        return balances[_address];
    }
}

继承ERC-20合约

FundMe.sol 代码如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";

// 1.创建一个收款函数
// 2.记录投资人并且查看
// 3.在锁定期内,达到目标值,生产商可以提款
// 4.在锁定期内,没有达到目标值,投资人可以在锁定期以后退款

contract FundMe {
    // 筹款人 => 筹款金额
    mapping (address => uint256) public fundersToAount;

    // 最小筹款金额
    uint256 constant MINIMUM_VALUE = 4 * 10 ** 18; // USD

    AggregatorV3Interface internal dataFeed;

    // 目标金额
    uint256 constant TARGET = 10 * 10 ** 18;

    // 合约所有者
    address public  owner;

    // 合约部署时间
    uint256 deplomentTimestamp;
    // 合约锁定时间
    uint256 lockTime;

    address erc20Addr;

    bool public getFundSuccess = false;

    constructor(uint256 _lockTime) {
        // sepolia testnet
        dataFeed = AggregatorV3Interface(
            0x694AA1769357215DE4FAC081bf1f309aDC325306
        );
        owner = msg.sender;
        deplomentTimestamp = block.timestamp;
        lockTime = _lockTime;
    }

    function fund() external payable {
         // 判断锁定时间
        require( (block.timestamp < deplomentTimestamp + lockTime), "the lock time is over");
        // 控制台打印等价usd值
        uint256 usdValue = convertEthToUsd(msg.value);
        
        require(usdValue >= MINIMUM_VALUE, "cannot be less than the minimum limit");
        fundersToAount[msg.sender] += msg.value;
    }

    /**
     * Returns the latest answer.
     */
    function getChainlinkDataFeedLatestAnswer() public view returns (int) {
        // prettier-ignore
        (
            /* uint80 roundId */,
            int256 answer,
            /*uint256 startedAt*/,
            /*uint256 updatedAt*/,
            /*uint80 answeredInRound*/
        ) = dataFeed.latestRoundData();
        return answer;
    }

    function convertEthToUsd(uint256 ethAmount) internal view  returns (uint256) {
        uint256 ethPrice = uint256(getChainlinkDataFeedLatestAnswer());
        uint256 ethAmountInUsd = (ethPrice * ethAmount) / (10 ** 8);
        return ethAmountInUsd;
    
    }

    // 判断合约所有者可以提取
    function transferOwnship(address newOwner) public {
        require(owner == msg.sender, "this function can only be called by owner");
        owner = newOwner;
    }

    // 达到目标值,生产商可以提款
    function getFund() external windowClosed onlyOwner{    
        // 判断筹款金额大于等于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) >= TARGET, "Target is not reached.");
        // payable(msg.sender).transfer(balance);
        // bool success = payable(msg.sender).send(balance);
        bool success;
        (success, ) = payable(msg.sender).call{value: balance}("");
        require(success, "transfer failed");
        // 余额清空
        fundersToAount[msg.sender] = 0;
        getFundSuccess = true;
    }

    // 没有达到目标值,投资人可以在锁定期以后退款
    function refund() external windowClosed{
        // 判断筹款金额小于目标金额
        uint256 balance = address(this).balance;
        require(convertEthToUsd(balance) < TARGET, "Target is  reached.");
        uint256 amount = fundersToAount[msg.sender];
        require(amount != 0, "there is not fund for you");
        bool success;
        (success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "transfer failed");
        fundersToAount[msg.sender] = 0;
    }

    function setFunderToAmount(address funder, uint256 amountToUpdate) external  {
        require(msg.sender == erc20Addr, "You don't have permission to call this funciton.");
        fundersToAount[funder] = amountToUpdate;
    }

    function setErc20Addr(address _erc20Addr) public onlyOwner {
        erc20Addr = _erc20Addr;
    }

    modifier windowClosed() {
        // 判断锁定时间
        require( (block.timestamp > deplomentTimestamp + lockTime), "the lock time is not  over");
        _;
    }

    modifier onlyOwner() {
        require(owner == msg.sender, "this function can only be called by owner");
        _;
    }
}

FundTokenERC20.sol代码如下所示:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

// FundMe
// 1. 让FundMe的参与者,基于mapping领取相应数量的通证
// 2. 让FundMe的参与者,transfer通证
// 3. 在使用完成以后,需要burn通证


import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {FundMe} from "./FundMe.sol";

contract FundTokenERC20 is ERC20 {
    FundMe fundMe;

    constructor(address fundMeAddr) ERC20("FundTokenERC20", "FT") {
        fundMe = FundMe(fundMeAddr);
    }

    function mint(uint256 amountToMint) public {
        require(fundMe.fundersToAount(msg.sender) >= amountToMint, "You cannot mint this many tokens.");
        require(fundMe.getFundSuccess(), "The fundme is not complete yet.");
        _mint(msg.sender, amountToMint);
        fundMe.setFunderToAmount(msg.sender, fundMe.fundersToAount(msg.sender)-amountToMint);
    }

    function claim(uint256 amountToClaim) public {
        require(balanceOf(msg.sender) >= amountToClaim, "You dont have enough ERC20 tokens.");
        require(fundMe.getFundSuccess(), "The fundme is not complete yet.");
        _burn(msg.sender, amountToClaim);
    }

}

部署和验证合约

关于

❓QQ:806797785

⭐️仓库地址:https://gitee.com/gaogzhen

⭐️仓库地址:https://github.com/gaogzhen

[1]Web3教程:ERC20,NFT,Hardhat,CCIP跨链[CP/OL].

[2]remix[CP/OL].


网站公告

今日签到

点亮在社区的每一天
去签到