当前位置:   article > 正文

了解区块链技术(Patrick Collins)(四)_智能合约测试 low-level calldata 传金额

智能合约测试 low-level calldata 传金额

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来提出问题

基本形式:

在文本框中粘贴存在问题的智能合约的代码片段,将其格式化,写出错误信息,并在下面写出自己的疑惑

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/998796
推荐阅读
相关标签
  

闽ICP备14008679号