solidity8进阶

事件 event

事件是EVM的日志功能之上的抽象。 应用程序可以通过以太坊客户端的RPC接口订阅和监听这些事件

  event Log(address indexed from, uint num);
  //即使只是调用event,也不可以用view prue
  function send(uint _num) external{
    
    
    emit Log(msg.sender, _num);
  }

indexed & anonymous

indexed 修饰事件时:将参数作为 topic 存储。
anonymous 修饰事件时:不把事件签名作为 topic 存储。

  //此事件toppics中应出现 签名、add、_num
  event Log(address indexed add, address sender, uint indexed _num);
  //此事件topics中应出现 add、_num
  event Log1(address indexed add, address sender, uint indexed _num) anonymous;
  
  function send(uint _num) external{
    
    
    emit Log(address(this), msg.sender, _num);
    emit Log1(address(this), msg.sender, _num);
  }

测试结果
在这里插入图片描述

payable

  //被payable修饰的函数可以接收主币
  function tes() public payable {
    
    
    uint value = msg.value;
  }

  function getBalance() public view returns(uint){
    
    
    return address(this).balance;
  }
  //被payable修饰的地址可以接收主币

fallback & receive


  //receive:接收以太函数,一个合约最多有一个receive函数
  //没有function关键字,也没有参数和返回值, 必须是 external可见性 和 payable修饰
  //可以是virtual的,可以被重载也可以有修改器
  receive() external payable{
    
    
  }
  //fallback:回退函数,合约可以最多有一个回退函数,
  //没有function关键字,必须是 external可见性
  //可以是virtual的,可以被重载也可以有修改器modifier
  fallback() external payable{
    
    
  }
  //带参数写法
  fallback(bytes calldata _input) external payable returns(bytes memory _output){
    
    
  }

selfdestruct

selfdestruct(address payable recipient):销毁合约,把合约的剩余资金发送到指定的地址。

pragma solidity ^0.8.0;

contract Mycontract{
    
    
  constructor() payable{
    
    }
  function destroy() external {
    
    
    selfdestruct(payable(msg.sender));
  }
  function get() public pure returns(uint){
    
    
    return 1;
  }
}

在这里插入图片描述
在这里插入图片描述

unchecked

contract Test {
    
    
  
  uint8 public a = type(uint8).max;

  function check() external view returns(uint8 b){
    
    
    b= a + 10;
  }

  function uncheck() external view returns(uint8 b){
    
    
    unchecked{
    
    
      b= a + 10;
    }
  }
}

在这里插入图片描述

发送主币的三种方式:transfer、send、call

transfer:2300 gas,交易失败后会revert
send:2300 gas,交易结果返回布尔值,不会抛异常,继续执行合约代码
call:剩余全部gas发送给_to地址使用,不会抛异常,继续执行合约代码
可以携带数据,返回值为交易结果布尔值和返回数据
如果发送地址是合约,合约有返回值,就会有返回数据

//接收主币合约
contract rec{
    
    

  receive() external payable{
    
    
  }
  //查询合约余额
  function getBal() public view returns(uint) {
    
    
    return address(this).balance;
  }
}
//发送主币合约
contract Mycontract{
    
    
  //部署时向合约发送主币,用于测试
  constructor() payable {
    
    
  }
  //传入rec合约地址 调用后查询rec余额
  function byTransfer(address payable _to) public payable {
    
    
    _to.transfer(1);
  }
  //传入rec合约地址 调用后查询rec余额
  function bySend(address payable _to) public payable {
    
    
    bool res = _to.send(1);
    require(res, "send failed");
  }
  //传入rec合约地址 调用后查询rec余额
  function byCall(address payable _to) public payable {
    
    
    (bool res,) = _to.call{
    
    value: 1}("");
    require(res, "call failed");
  }
}

调用其他合约

引入源码调用

pragma solidity ^0.8.0;
contract TestContract{
    
    

  uint a = 1;
  
  function get() public view returns(uint, uint){
    
    
    return (a, address(this).balance);
  }
  function set(uint _a) public payable{
    
    
    a = _a;
  }
}

pragma solidity ^0.8.0;
import './TestContract.sol';
contract Mycontract{
    
    
  //将调用test的set函数并将接收的主币一并发送给test
  function set(address _test, uint _a) public payable{
    
    
    TestContract(_test).set{
    
    value: msg.value}(_a);
  }
  function seta(TestContract _test, uint _a) public payable{
    
    
    _test.set{
    
    value: msg.value}(_a);
  }

  function get(address _test) public view returns(uint a, uint v, uint myV){
    
    
    (a, v) = TestContract(_test).get();
    myV = address(this).balance;
  }
  function geta(TestContract _test) public view returns(uint a, uint v, uint myV){
    
    
    (a, v) = _test.get();
    myV = address(this).balance;
  }
}

调用set发送100wei,调用seta发送200wei,测试结果
在这里插入图片描述

接口合约调用

pragma solidity ^0.8.0;
contract TestContract{
    
    
  uint a = 1;
  function get() external view returns(uint){
    
    
    return a;
  }
  function set(uint _a) external{
    
    
    a = _a;
  }
}
pragma solidity ^0.8.0;

interface ITestContract{
    
    
  function get() external view returns(uint);
  function set(uint _a) external;
}
contract Mycontract{
    
    
  function set(address _test, uint _a) public{
    
    
    ITestContract(_test).set(_a);
  }

  function get(address _test) public view returns(uint a){
    
    
    a = ITestContract(_test).get();
  }
}

在这里插入图片描述

call方法调用

pragma solidity ^0.8.0;
contract TestContract{
    
    
  uint public a = 1;
  uint public bal = 0;
  address public sender;
  
  function set(uint _a) external payable{
    
    
    a = _a;
    bal = address(this).balance;
    sender = msg.sender;
  }
}
pragma solidity ^0.8.0;
contract Mycontract{
    
    
  function set(address _test, uint _a) external payable{
    
    
    (bool success, ) = _test.call(abi.encodeWithSignature("set(uint256)", _a));
    require(success, "call failed");
  }

  function getBal() external view returns(uint b){
    
    
    b = address(this).balance;
  }
}

测试结果:
a变量修改成功,
发送的主币被Mycontract合约接收,
TestContract显示调用者是Mycontract合约

在这里插入图片描述

委托调用

pragma solidity ^0.8.0;
contract TestContract{
    
    
  uint public a;
  uint public bal;
  address public sender;
  
  function set(uint _a) external payable{
    
    
    a = _a;
    bal = msg.value;
    sender = msg.sender;
  }
}
pragma solidity ^0.8.0;
contract Mycontract{
    
    
  uint public a;
  uint public bal;
  address public sender;

  function callSet(address _test, uint _a) external payable{
    
    
    (bool success,) = _test.delegatecall(abi.encodeWithSignature("set(uint256)", _a));
    require(success, "call failed");
  }
}

测试结果:TestContract合约的值并没有变化,余额和调用者信息也是没有的
改变的是Mycontract合约内的变量,主币也被Mycontract接收,显示调用者是函数调用者
相当于Mycontract使用了TestContract合约函数逻辑,但操作的数据是Mycontract本身的
这就要求Mycontract合约内定义的变量要和TestContract合约内保持一致,包括变量顺序

在这里插入图片描述

库合约

pragma solidity ^0.8.0;

library safeMath{
    
    
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    
    
    return a + b;
  }
}

contract Mycontract{
    
    
  //所有uint类型都可以直接使用safeMath库 声明后可直接使用 sub1 方式
  using safeMath for uint;

  function sub(uint _a, uint _b) external pure returns(uint){
    
    
    return safeMath.add(_a, _b);
  }

  function sub1(uint _a, uint _b) external pure returns(uint){
    
    
    return _a.add(_b);
  }
}

在这里插入图片描述

哈希运算

keccak256(bytes memory) returns (bytes32): 计算输入参数的 Keccak-256 哈希
abi.encode(...) returns (bytes memory): ABI - 对给定的参数进行编码
abi.encodePacked(...) returns (bytes memory): 给指定的参数执行 packed encoding , 请注意,这种编码可能会有歧义!(参数和编码可能出现多对一的情况)
pragma solidity ^0.8.0;

contract Mycontract{
    
    
  
  function encode(string memory _a, string memory _b) public pure returns(bytes memory){
    
    
    return abi.encode(_a, _b);
  }
  function encode1(string memory _a, string memory _b) public pure returns(bytes memory){
    
    
    return abi.encode(_a, _b);
  }

  function encodePacked(string memory _a, string memory _b) public pure returns(bytes memory){
    
    
    return abi.encodePacked(_a, _b);
  }
  function encodePacked1(string memory _a, string memory _b) public pure returns(bytes memory){
    
    
    return abi.encodePacked(_a, _b);
  }

  function hash(string memory _a, string memory _b) public pure returns(bytes32){
    
    
    return keccak256(abi.encode(_a, _b));
  }
  function hash1(string memory _a, string memory _b) public pure returns(bytes32){
    
    
    return keccak256(abi.encode(_a, _b));
  }

  function hashPacked(string memory _a, string memory _b) public pure returns(bytes32){
    
    
    return keccak256(abi.encodePacked(_a, _b));
  }
  function hashPacked1(string memory _a, string memory _b) public pure returns(bytes32){
    
    
    return keccak256(abi.encodePacked(_a, _b));
  }
}

测试结果:encodePacked不会补0,会出现不同入参编码相同的情况
在这里插入图片描述

函数签名

pragma solidity ^0.8.0;

contract Mycontract{
    
    

  event Log(bytes data);
  
  function getSign(string memory _t) public pure returns(bytes4){
    
    
    return bytes4(keccak256(bytes(_t)));
  }

  function transfer(address _to, uint _num) external{
    
    
    emit Log(msg.data);
  }
}

在这里插入图片描述

create2部署合约

pragma solidity ^0.8.0;

contract Mycontract{
    
    
  address public owner;
  constructor(address _owner){
    
    
    owner = _owner;
  }
}

contract Create2Factory{
    
    
  event Deploy(address add);

  function deploy(uint _salt) external {
    
    
    Mycontract _contract = new Mycontract{
    
    
      salt : bytes32(_salt)
    }(msg.sender);
    emit Deploy(address(_contract));
  }

  function get(bytes memory bytecode, uint _salt) external view returns(address){
    
    
    bytes32 hash = keccak256(abi.encodePacked(
      bytes1(0xff), address(this), _salt, keccak256(bytecode)
    ));

    return address(uint160(uint(hash)));
  }

  function getByteCode(address _owner) external pure returns(bytes memory){
    
    
    bytes memory bytecode = type(Mycontract).creationCode;
    return abi.encodePacked(bytecode, abi.encode(_owner));
  }
}

在这里插入图片描述

MultiCall批量调用

call调用中,要注意msg.sender、msg.value,如果函数中有相关的逻辑,会出现问题

pragma solidity ^0.8.0;

contract Mycontract{
    
    
  
  function fun1() external view returns (address, uint){
    
    
    return(msg.sender, 1);
  }
  function fun2() external view returns (address, uint){
    
    
    return(msg.sender, 2);
  }
  function getdata1() external pure returns(bytes memory) {
    
    
    return abi.encodeWithSelector(this.fun1.selector);
  }
  function getdata2() external pure returns(bytes memory) {
    
    
    return abi.encodeWithSelector(this.fun2.selector);
  }
}

contract MultiCall{
    
    

  function call(address[] calldata adds, bytes[] calldata data) external view returns(bytes[] memory){
    
    
    
    require(adds.length == data.length, "params exception");
    bytes[] memory res = new bytes[](adds.length);
    for(uint a = 0; a < adds.length; a++){
    
    
      (bool success, bytes memory result) = adds[a].staticcall(data[a]);
      require(success, "call failed");
      res[a] = result;
    }
    return res;
  }

在这里插入图片描述

批量委托调用

批量委托调用中,如果多次调用有payable接收主币的函数,会出现计算问题

pragma solidity ^0.8.0;

contract Mycontract{
    
    

  event Log(address caller, uint i);

  //由于委托调用只是使用目标合约的函数,并不会改变其他合约的变量,
  //所有必须address(this).delegatecall才是合理的
  function delegatecall(bytes[] calldata data) external payable returns(bytes[] memory){
    
    
    bytes[] memory res = new bytes[](data.length);
    for(uint a = 0; a < data.length; a++){
    
    
      (bool success, bytes memory result) = address(this).delegatecall(data[a]);
      require(success, "call failed");
      res[a] = result;
    }
    return res;
  }
  
  function fun1(uint x) external{
    
    
    emit Log(msg.sender, x++);
  }
  function fun2() external returns (uint){
    
    
    emit Log(msg.sender, 666);
  }
  function getdata1(uint x) external pure returns(bytes memory) {
    
    
    return abi.encodeWithSelector(this.fun1.selector, x);
  }
  function getdata2() external pure returns(bytes memory) {
    
    
    return abi.encodeWithSelector(this.fun2.selector);
  }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42704356/article/details/125880873
今日推荐