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; // 正常版本
  }
}


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

// 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代码包含两个合约:D 和 Create2。下面是对代码的解释:
- 
合约
D:D是一个简单的合约,包含一个公共的uint类型变量x和一个构造函数。- 构造函数在合约部署时被调用,接受一个参数 
a并将其赋值给变量x。 
 - 
合约
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,可以在不同的情况下创建具有相同构造参数的合约,并且每个合约都会有唯一的地址。这在某些场景下可能是有用的,例如在链上创建合约的预测地址,以便在将来进行交互。

可以看到,用123的byte32数据和salt=777,获取了地址0x023...
然后调用createDsalted方法, 参数为上面的byte32和salt, 最终可以获取到一样的地址.
未解决的问题
在测试自定义错误输出的时候, 反复对比教程发现输出不一样, 我的测试并没有返回error. 换方法名或者加payable都不起作用.


                
            
        
浙公网安备 33010602011771号