当前位置: 首页 > news >正文

0301-solidity进阶-区块链-web3

文章目录

    • 序言
    • 通过函数发送ETH
    • 通过预言机设定最小额度
    • 通过函数提取合约中的ETH
    • 函数修饰符和时间锁
    • Token和Coin的区别
    • 创建一个Token合约
    • 继承ERC-20合约
    • 部署和验证合约
    • 关于

序言

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

通过函数发送ETH

  • payable关键字
  • 以太坊账户:EOA和合约账户
  • Wei,GWei,Finney和ether
// 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兑换比例,这里需要引入预言机。

  • 预言机(oracle)定义
  • Chainlink技术文档
    • Chainlink喂价文档
    • Chainlink喂价合约地址列表

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

  • 以太币面额转换器
  • 以太币面额
  • Solidity 中如何使用浮点数

代码如下:

// 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; // USDAggregatorV3Interface internal dataFeed;constructor() {// sepolia testnetdataFeed = 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
  • 三种转账方式: transfer, send, call

提取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;}

函数修饰符和时间锁

  • 函数修饰符是什么
  • 怎样开发智能合约中的时间锁
  • Uinx时间戳

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

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

代码如下所示:

    // 合约部署时间uint256 deplomentTimestamp;// 合约锁定时间uint256 lockTime;constructor(uint256 _lockTime) {// sepolia testnetdataFeed = 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和Coin的区别

创建一个Token合约

  • Token介绍
  • Solidity中的继承

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 => uint256mapping(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合约

  • ERC-20标准合约
  • ERC-20标准合约代码
  • virtual和override

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; // USDAggregatorV3Interface 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 testnetdataFeed = 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: MITpragma 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);}}

部署和验证合约

  • 注册从区块链浏览器(Etherscan)账户,并且获取API key
  • 怎样通过 Etherscan 验证智能合约

关于

❓QQ:806797785

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

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

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

[2]remix[CP/OL].

http://www.dtcms.com/a/334018.html

相关文章:

  • 布隆过滤器的原理及使用
  • Kotlin-基础语法练习一
  • Maven私服配置模版
  • Qt 关于QString和std::string数据截断的问题- 遇到\0或者0x00如何处理?
  • 小白学投资理财 第一天
  • 算力与显存、显存带宽的关系
  • 【php反序列化介绍与常见触发方法】
  • Houdini Vop学习笔记
  • 测试工程师的AI转型指南:从工具使用到测试策略重构
  • TDengine IDMP 高级功能(4. 元素引用)
  • OpenAI TTS API + Web 前端 AudioContext 实战方案
  • 【Web后端】Django、flask及其场景——以构建系统原型为例
  • 《深度解构:构建浏览器端Redis控制台的WebSocket协议核心技术》
  • Protues使用说明及Protues与Keil联合仿真实现点亮小灯和流水灯
  • 【洛谷刷题】用C语言和C++做一些入门题,练习洛谷IDE模式:分支机构(一)
  • C#WPF实战出真汁08--【消费开单】--餐桌面板展示
  • C语言基础:(十五)深入理解指针(5)
  • 数据清洗:数据处理的基石
  • 【C++】异常详解(万字解读)
  • 【lubancat】鲁班猫4实现开机后自动播放视频
  • MySQL查询性能慢时索引失效的排查与优化实践
  • AI驱动的性能测试:如何用机器学习预测系统瓶颈?
  • 开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载
  • 储能领域大数据平台的设计中如何使用 Hadoop、Spark、Flink 等组件实现数据采集、清洗、存储及实时 / 离线计算,支持储能系统分析与预测
  • 三天速通 Vue+Flask+SQLite 项目+阿里云轻量应用级服务器【宝塔面板】②
  • IO流与单例模式
  • java项目怎么实现用户行为分析、漏斗转化、数据可视化报表。
  • 奈飞工厂 —— 算法优化实战推荐
  • QT基础入门
  • AI热点周报(8.10~8.16):AI界“冰火两重天“,GPT-5陷入热议,DeepSeek R2模型训练受阻?