赞
踩
在fund
函数中:
address[] public funders ; //记录发送资金的funder
mapping (address => uint256) public addressToAmountFonded ;// 记录每个地址资金发送的数量
function fund() public payable { require(getConversionRate(msg.value) > minimumUsd,"didn't send enough") ; addressToAmountFonded[msg.sender] += msg.value ; funders.push(msg.sender); }
库
库里面不能有状态变量,只提供函数功能。不能声明任何静态变量,也不能发送eth
可以使getConversionRate成为一个uint256的一个函数,使用uint256.getConversionRate()
我们创建一个PriceConverter.sol作为一个库赋到uint256上面:
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; library PrintConvert{ // 一个库中的所有函数都是internal的 function getPrice() internal view returns (uint256) { AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306); (,int256 price,,,) = priceFeed.latestRoundData(); return uint256(price*1e10); } function getVersion()internal view returns (uint256) { AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306); return priceFeed.version(); } function getConversionRate(uint256 ethAmount) internal view returns (uint256){ uint256 ethPrice = getPrice(); uint256 ethAmountInUsd = (ethAmount * ethPrice)/1e18; return ethAmountInUsd ; } }
在fundMe.sol中加入这个函数:
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import "./PriceConvert.sol"; contract FundMe { using PriceConvert for uint256 ; //最小Usd金额 uint256 public minimumUsd = 50 * 1e18 ; address[] public funders ; //记录发送资金的funder mapping (address => uint256) public addressToAmountFonded ;// 记录每个地址资金发送的数量 function fund() public payable { //msg.value会被传入getConversionRate中 // 会将msg.value作为getConversionRate的第一个参数,第二个参数需要写在getConversionRate括号里面 require(msg.value.getConversionRate() > minimumUsd,"didn't send enough") ; addressToAmountFonded[msg.sender] = msg.value ; funders.push(msg.sender); } }
使用时间最长,最常见的一个库是safeMath.sol,新建一个safeMathTester.sol,使用0.8之前的任意版本
// SPDX-License-Identifier: MIT pragma solidity 0.6.0; contract SafeMathTester{ uint8 public bigNumber = 255; function add()public { bigNumber = bigNumber +1 ; } } //加1的时候bigNumber会被重置为零,无符号整型和整型试运行在unchecked这个概念下的,意味着如何一个数超过了它的上限,就会从最低的数字重新开始 //那么safeMath库的作用就是确保不会在边界的时候绕回去 //如果已经达到了最大值,那么你的交易就会失败
在0.8版本之后,添加了check,会自动检查以确保对变量执行所谓的移除或下限
可以恢复到uncheck版本:
unchecked(bigNumber = bigNumber +1);
,使用unchecked关键字能够减少更多的gas
提取资金
solidity-by-example.org/sending-ether/
// 需要重置funders数组和mapping对应的金额 function withdraw() public { for (uint256 funderIndex = 0 ; funderIndex < funders.length ; funderIndex = funderIndex+1){ address funder = funders[funderIndex] ; addressToAmountFonded[funder] = 0; } // 重置funders数组 funders = new address[](0);//零表示初始只有零个元素 //提取资金的三种方式 // transfer 最简单的一种方式,address(this).balance 获取合约地址原生区块链通证,转移资金 // 要转移到的目标地址 // 该方法gas最大是2300,如果超过直接报错,回滚交易 payable (msg.sender).transfer(address(this).balance); // send gas最大也是2300,会返回是否成功,不会回滚交易,想要回滚交易,就要添加require bool sendSuccess = payable (msg.sender).send(address(this).balance); require(sendSuccess,"send failed"); // call 实际使用的较为底层的命令,可以调用几乎所有solidity的命令,不依赖ABI 最推荐 // dataReturned call允许我们调用不同的函数,函数返回值保存在dataReturned中 回调函数 // callSuccess 函数是否调用成功 // (bool callSuccess , bytes memory dataReturned)payable (msg.sender).call{value: address(this).balance}("");//任何我们想要调用的合约的函数信息,""表示此处留白 (bool callSuccess , ) = payable (msg.sender).call{value: address(this).balance}(""); require(callSuccess,"call failed"); } //任何人都可以从合约中提款,这并不是我们所期待的
构造函数
//部署合约时,立刻调用,成为合约的拥有者 function callMeRightAway() public { } // 但是这样就需要发起两笔交易
//部署合约后立刻调用,通过构造函数设置合约的拥有者,修改withdraw函数使得只有合约拥有者可以调用 address public owner ; constructor() { owner = msg.sender; }
require(msg.sender == owner, "Sender is not owner");
假设合约中有很多函数,都要求只能由合约调用者使用,为了方便,这时候就要使用到修饰器
modifier是一个可以直接在函数声明中添加的关键字
modifier onlyOwner{ // 根据下划线的位置,判断优先执行的代码段 require(msg.sender == owner, "Sender is not owner"); _; }
function withdraw() public onlyOwner{ }
Immutable & Constant
如果我们有只需要设置一次的变量,我们可以使用solidity中的一些工具使其更加节省gas,
uint256 public constant MINIMUM_USD = 50 *1e18;
如果变量是在编译时分配的,就可以添加constant关键字,添加后,minimum不再占用一个存储空间
当view函数被合约调用是消耗Gas的
如果在构造函数中赋值的变量,我们可以声明为immutable
address public immutable i_owner ; constructor() { i_owner = msg.sender; }
更节省gas的原因是变量并没有存储在一个存储槽中,而是直接存储在合约的字节码中
自定义错误类型
还有一种更节省gas的方法,更新我们的require语句,Require语句必须要把"sender is not owner"
存储为一个String字符串数组,错误日志中的每一次字符都要单独存储
error NotOwner(); modifier onlyOwner{ if (msg.sender != i_owner){ revert NotOwner(); } _; }
Receive & Fallback 特殊函数
如果有人未在调用fund函数的情况下向合约发送eth,那么会发生什么呢 ?直接使用钱包进行转账
当人们给这个合约转钱或者调用一个不存在的函数, 仍然可以触发合约中的某些代码
通过Low level interactions
直接向合约发一些数据,可以暂时理解为发送和处理不同函数的方式
当你向合约发送交易时,如果没有指定某个函数,receive函数就会触发
// SPDX-License-Identifier: MIT pragma solidity 0.8.19 ; contract FallBackExample { uint256 public result; // 不用添加recieve,因为Solidity知道这是一个特殊函数 receive() external payable { result = 1 ; } //CALLDATA 中的函数是合约中没有的函数,会调用 fallback() external payable { result = 2 ; } }
改进后: receive() external payable { fund(); } fallback() external payable { fund(); }
其他暂未涉及:
Enums Events Try/Catch Function Selector abi.encode/decode hashing yul / assembly
出现问题解决方式:
ethereum.stackexchange.com
可以使用火山翻译
插件辅助阅读
中文社区:https://ethereum.org/zh/community/online
Stack Exchange Ethereum , 更多的是以解决以太坊和EVM的问题为主
格式化的提出问题,因为提问的格式越好,就要有可能被回答
登录后,通过new discussion
来提出问题
基本形式:
在文本框中粘贴存在问题的智能合约的代码片段,将其格式化,写出错误信息,并在下面写出自己的疑惑
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。