Solidity初学-0.8新特性

https://www.youtube.com/watch?v=xv9OmztShIw&list=PLO5VPQH6OWdVQwpQfw9rZ67O6Pjfo6q-p

helloworld和溢出问题

contract HelloWorld {
  /**
   * @dev Prints Hello World string
   */
  function print() public pure returns (string memory) {
    return "Hello World!";
  }
}
contract SafeMath {
  function testUnderflow() public pure returns(uint) {
    uint x = 0;
    x--;
    return x; // 0.8版本之前跟C一样会返回最大值,之后会报错
  }
  function testUncheckedUnderflow() public pure returns(uint) {
    uint x = 0;
    unchecked {x--;}
    return x; // 正常版本
  }
}

部署SafeMath
测试结果
testUncheckedUnderflow方法它会返回一个非常大的数字,如果没有unchecked代码块的话也就是testUnderflow方法,可以看到右边控制台输出了一个Error occured

0.8新特性:自定义错误

0.8版本除了处理了溢出问题之外,还让开发者可以自定义错误custom error, 相比于直接返回错误信息字符串要好得多. 返回字符串的话, 是要消耗gas的

contract VendingMachine {
  address payable owner = payable(msg.sender);
  function withdraw() public {
    if (msg.sender != owner)
       revert(""); // 2560 gas 并且输入字符串越长它越多
    // 可能是版本问题, 加上这段代码会变成infinite gas
    // owner.transfer(address(this).balance); 
  }
}
contract VendingMachine {
  address payable owner = payable(msg.sender);

  error Unauthorized();

  function withdraw() public {
    if (msg.sender != owner)
      revert Unauthorized(); // 2324 gas, 比上面那种形式少了一些
      // owner.transfer(address(this).balance);
  }
}
contract VendingMachine {
  address payable owner = payable(msg.sender);

  error Unauthorized(address caller);

  function withdraw() public  {
    if (msg.sender != owner)
      revert Unauthorized(msg.sender);
    owner.transfer(address(this).balance);
  }
}

理想效果如下
油管视频截图
0.8版本之后, error也可以放到合约之外去定义, 这样一来各个合约就都能用到自定义error了. 函数也同理. 甚至可以给其它.sol文件引入

// HelloWorld.sol
function helper(uint x) view returns (uint){
  return x * 2;
}
// Import.sol
pragma solidity ^0.8;
import {Unauthorized, helper as h1} from './HelloWorld.sol';
// 注意不能重名
function helper() view returns(uint){
    return 1;
}
contract Import {
    //...
}

Create2

Create2文档解释

// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract D {
    uint public x;
    constructor(uint a) {
        x = a;
    }
}

contract Create2 {
    function getBytes32(uint salt) external pure returns (bytes32) {
        return bytes32(salt);
    }
    function getAddress(bytes32 salt, uint arg) external view returns (address) {
        address addr = address(uint160(uint(keccak256(abi.encodePacked(
            bytes1(0xff),
            address(this),
            salt,
            keccak256(abi.encodePacked(
                type(D).creationCode,
                arg
            ))
        )))));
        return addr;
    }
    address public deployedAddr;
    function createDsalted(bytes32 salt, uint arg) public {
        D d = new D{salt : salt}(arg);
        deployedAddr = address(d);
    }
}

这段Solidity代码包含两个合约:DCreate2。下面是对代码的解释:

  1. 合约 D

    • D 是一个简单的合约,包含一个公共的 uint 类型变量 x 和一个构造函数。
    • 构造函数在合约部署时被调用,接受一个参数 a 并将其赋值给变量 x
  2. 合约 Create2

    • Create2 包含两个外部函数和一个公共的地址变量 deployedAddr

    • 函数 getBytes32

      • 接受一个 uint 类型的参数 salt,并返回它的 bytes32 表示形式。
    • 函数 getAddress

      • 接受一个 bytes32 类型的参数 salt 和一个 uint 类型的参数 arg
      • 使用 keccak256 哈希函数构造一个地址,并返回该地址。
      • 构造地址的方式是利用 CREATE2 EVM 指令,该指令允许在特定地址上创建合约,而不是在交易之后立即创建。这是为了在合约部署时能够预测合约的地址。
      • 构造地址的关键部分是使用 keccak256 对合约创建代码和参数进行哈希,以及指定的 salt。这确保了地址的唯一性。
    • 函数 createDsalted

      • 接受一个 bytes32 类型的参数 salt 和一个 uint 类型的参数 arg
      • 使用 CREATE2 指令在特定地址上创建一个新的 D 合约实例,传递 arg 作为构造函数的参数。
      • 将新创建的合约地址存储在 deployedAddr 变量中。

这个合约的主要目的是演示使用 CREATE2 指令在特定地址上创建合约的过程。通过使用 bytes32 类型的 salt,可以在不同的情况下创建具有相同构造参数的合约,并且每个合约都会有唯一的地址。这在某些场景下可能是有用的,例如在链上创建合约的预测地址,以便在将来进行交互。
Create2使用
可以看到,用123byte32数据和salt=777,获取了地址0x023...
然后调用createDsalted方法, 参数为上面的byte32salt, 最终可以获取到一样的地址.

未解决的问题

在测试自定义错误输出的时候, 反复对比教程发现输出不一样, 我的测试并没有返回error. 换方法名或者加payable都不起作用.
img

posted @ 2023-12-05 13:12  芜光  阅读(51)  评论(0编辑  收藏  举报