web3入门番外篇 | Solidity简明教程

  • netlogs
  • 更新于 2024-08-27 22:22
  • 阅读 666

Solidity简明教程本教程适用于有语言基础,速查或者快速入门。

<!--StartFragment-->

Solidity简明教程

本教程适用于有语言基础,速查或者快速入门。

版权许可标识

// SPDX-License-Identifier: MIT

一般放在文件开头

更多内容请查看https\://learnblockchain.cn/docs/solidity/layout-of-source-files.html

版本

pragma solidity ^0.5.2;

含义是既不允许低于 0.5.2 版本的编译器编译, 也不允许高于(包含) 0.6.0 版本的编译器编译(第二个条件因使用 ^ 被添加)。

pragma solidity >=0.6.12 &lt;0.9.0;

变量

默认值

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

contract DefaultValues {
    bool public b; //false
    uint public u; //0
    int public i; //0
    address public a; //0x0000000(一共40个0,20位16进制数字)
    bytes32 public b32; //0x00000 一共64个0,32位

}

常量

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

contract Constants {
    address public constant MY_ADDRESS = 0x2E35C375782713b7feedEd99B18C2F2728B2566D;
    uint public constant MY_UINT = 123;
}

contract Var {
    address public myaddr = 0x2E35C375782713b7feedEd99B18C2F2728B2566D;
}

constant定义常量

常量和变量消耗的gas不一样,deploy之后点击MY_ADDRESS可以查看gas费用是373

execution cost  373 gas (Cost only applies when called by a contract)

下面这是Var合约内读取myaddr消耗的gas

execution cost  2483 gas (Cost only applies when called by a contract)

结构控制

判断

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract IfElse {
    function example(uint x) external pure returns (uint) {
        if (x &lt; 10) {
            return 1;
        } else if ( x &lt; 20) {
            return 2;
        } else {
            return 3;
        }
    }
    function ternary(uint x) external pure returns (uint) {
        return x &lt; 10 ? 1 : 2;
    }
}

循环

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract ForAndWhile {
    function loops() external pure {
        for (uint i = 0; i &lt; 10; ++i) {
            //code
           if (i == 2)  continue;
            if (i == 4) {
                break;
            }
        }
        int b = 3;
        while(b > 0) {
            --b;
        }
    }
}

和C语言一样,同时要注意控制循环次数,太多的循环会导致gas费用的高昂

Error

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

// require, revert, assert
// - gas refund, state updates are reverted
// 8.0 version 更新了,custom error, save gas
contract Error {
    function testRequire(uint _i) public pure {
        require(_i &lt;= 10, "i > 10");
        //code
    }
    function testRevert(uint _i) public pure {
        if(_i > 10) {
            revert("i > 10");
        }
        //code
    }
    uint public num = 123;
    function testAssert() public view {
        assert(num == 123);
    }

    function foo(uint _i) public {
        num += 1;
        require(_i &lt; 10); //如果i不符合条件,前面+1的num会被回滚,状态不会被更改,gas退还
    }

    function testCustomError1(uint _i) public pure {
        require(_i &lt;= 10, "very long error message xxxxxxxxxxxxxxx");//使用require时,如果error信息很长,会消耗很多gas
    }

    error MyError();

    function testCustomError2(uint _i) public pure {
        if (_i > 10) revert MyError(); //通过这种方式触发自定义报错
    }
    //同时可以:
    error MyError2(address caller, uint i);
    function testCustomError3(uint _i) public view {
        if (_i > 10) revert MyError2(msg.sender, _i); //通过这种方式触发自定义报错
    }
}

函数

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

contract Counter {
    uint public cnt;
    function inc() external {
        cnt += 1;
    }

    function dec() external {
        cnt -= 1;
    }
}

external:外部可视,意思是在合约内部的其他函数不可以调用,只能被外部读取

public:公开可视,内部外部都可以调用

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract FUnctionOutputs {
    function returnMany() public pure returns (uint, bool) {
        return (1, true);
    }
    function returnManyWithname() public pure returns (uint x, bool b) {
        return (1, true);
    }
    //隐式赋值返回
    function assigned() public pure returns (uint x, bool b) {
        x = 1; 
        b = true;
    }

    //合约内调用
    function otherfunc() public pure {
        (uint a, bool b) = returnMany();
        (, bool _b) = returnManyWithname();
    }
}

函数修改器

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract FunctionModifier {
    bool public paused;
    uint public cnt;

    function setPasuse(bool _paused) external {
        paused = _paused;
    }

    function inc() external {
        require(!paused, "paused");
        cnt += 1;
    }
    function dec() external {
        require(!paused, "paused");
        cnt -= 1;
    }
    //当我们期望合约暂停时cnt不被修改,我们可以在修改的func中添加require,函数修改器支持更强大的操作,
    //有点类似于C语言中,把暂停的判断提炼到一个判断函数内,然后在修改的函数都提前调用一下判断函数
    //写法:
    modifier whenNotPaused() {
        require(!paused, "paused");
        _; //此处代表,使用此修改器的函数的其他的代码在哪里运行
    }

    function new_incOr_dec() external whenNotPaused {
        //cnt += 1; or cnt -= 1;
    }

    //带参数的函数修改器

    modifier cap(uint _x) {
        require(_x &lt; 100, "x >= 100");
        _;
    }
    function incBy(uint _x) external whenNotPaused cap(_x) {
        cnt += _x;
    }

    //sanddwich写法
    //下面相当于把foo中对cnt的一部分操作提到了修改器中
    //cnt 先执行+=10,再返回foo执行+=1,再返回sandwich执行*=2
    modifier sandwich() {
        cnt += 10;
        _;
        cnt *= 2;
    }

    function foo() external sandwich {
        cnt += 1;
    }
}

构造函数

合约部署时被调用一次,之后再也不能被调用

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract Constructor {
    address public owner;
    uint public x;

    constructor(uint _x) {
        owner = msg.sender;
        x = _x;
    }
    //通过传参赋予x值,ctrl+s编译好之后,部署时,Deploy旁边会多一个输入框,代表构造函数的参数列表
}

数组

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract Array {
    uint[] public nums = [1, 2, 3];
    uint[10] public numsFixed= [4, 5, 6];

    function examples() external {
        nums.push(4); //[1, 2, 3, 4]; 适用于不定长数组
        nums[1] = 9; //下标,和C语言一样[1, 9, 3, 4]
        delete nums[1]; //[1, 0, 3, 4] !不能减少数组长度
        nums.pop(); //[1, 0, 3] 类似于stl中的stack,减少了数组长度
        uint len = nums.length; //长度

        //create array in memory
        uint[] memory arr = new uint[](5);
        //在内存中的不允许pop、push
    }

    function returnArray() external view  returns (uint[] memory) {
        return nums;

    }
}

映射

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract Array {
    mapping(address => uint) public balances;
    mapping(address => mapping(address => bool)) public isFriend;

    function examples() external {
        balances[msg.sender] = 123;
        uint bal = balances[msg.sender];
        uint bal2 = balances[address(1)]; // 0
        balances[msg.sender] += 456;

        delete balances[msg.sender]; //0

        isFriend[msg.sender][address(this)] = true;

    }
}

结构体

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

//结合数组和mapping
contract Structs {
    struct Car {
        string model;
        uint year;
    }

    Car public car;
    Car[] public cars;

    mapping(address => Car[]) public carsByOwner;

    function examples() external {
        Car memory toyota = Car("Toyota", 1999);
        Car memory lambo = Car({year: 111, model: "Lamborghini"});
        Car memory zero;
        zero.model = "aaaa";
        zero.year = 1993;

        cars.push(zero);
        cars.push(toyota);
        cars.push(lambo);

        cars.push(Car("Tesla", 1991));

        Car memory temp = cars[0];
        Car storage real = cars[0];
        real.year = 0; //storage带有指针效果
        delete real.year; //恢复cars[0]的成员year的默认值
        delete cars[1]; //默认值全部恢复
    }
}

枚举

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

//结合数组和mapping
contract Enums {
    enum Status {
        None,
        Pending,
        Shipped,
        Completed,
        Rejected,
        Canceled
    }

    Status public status;

    struct Order {
        address buyer;
        Status status;
    }

    Order[] public orders;

    function get() external view returns (Status) {
        return status;
    }

    function set(Status _status) external {
        status = _status;
    }

    function ship() external {
        status = Status.Shipped;
    }

    function reset() external {
        delete status; //枚举类型的默认值是第一个字段,本例为"None"
    }
}

小结

管理员合约

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract Owner {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(owner == msg.sender, "no owner");
        _;
    }

    function setOwner(address _newowner) external onlyOwner {
        require(_newowner != address(0), "no 0 address");
        owner = _newowner;
    }

    function ownerUse() external onlyOwner {
        //code
    }

    function anyoneUse() external {
        //code
    }
}

<!--StartFragment-->

存储位置

memory 内存的存储类型,局部变量,生命周期随着作用域结束而结束

storage 状态变量的存储类型,有点像引用或者指针

calldata 只能用在参数中,如果使用calldata,会节约gas

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

contract DataLocations {
    struct Mystruct {
        uint foo;
        string text;
    }

    mapping(address => Mystruct) public mys;

    function examples(uint[] calldata y, string calldata s) external returns (uint[] memory) {
        mys[msg.sender] = Mystruct({foo: 123, text: "bar"});
         Mystruct storage p = mys[msg.sender]; //此处也可以直接mys[msg.sender].text进行修改,当修改量少时消耗较少的gas,修改项多时使用storage
         p.text = "new_text"; //mys[msg.sender].text is: new_text

         Mystruct memory readonly = mys[msg.sender];
         readonly.foo = 444; //but ..mys[msg.sender].foo is: 123

         _internal(y); 
         //1. 如果_internal函数的参数列表中的y是memory类型,那么这里调用的时候会进行一次拷贝,消耗gas
         //如果使用calldata那么可以直接传递过去,不会发生拷贝

         uint[] memory memArr = new uint[](5);
         memArr[0] = 1;
         return memArr;
    }

    function _internal(uint[] calldata y) private {
        uint x = y[0];
    }

}

事件

记录当前contract状态的方法,不会记录在状态变量之中,而是体现在区块链浏览器上或者交易记录中的log

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

contract Event {
    event Log(string message, uint val);
    event IndexLog(address indexed sender, uint val, uint a, uint b); //添加indexed 链外可以搜索查询

    function examples() external {
        emit Log("hahaha", 1234);
        emit IndexLog(msg.sender, 22, 22, 22); //在链外通过工具可以查询该地址的一些事件

    }

    event Message(address indexed _from, address indexed _to, string message);

    function sendMessage(address _to, string calldata message) external {
        emit Message(msg.sender, _to, message);
    }
}

继承那些事

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

contract A {
    function foo() public pure virtual returns (string memory) {
        return "A";
    }
    function bar() public pure virtual returns (string memory) {
        return "A";
    }
    function baz() public pure returns (string memory) {
        return "A";
    }
}

contract B is A { //继承写法
    function foo() public pure override returns (string memory) {
        return "B";
    }
    function bar() public pure virtual override returns (string memory) {
        return "A";
    }
}

contract C is B {
    function bar() public pure override returns (string memory) {
        return "C";
    }
}

多线继承

优先写比较基类的继承关系,

X基类,Y is X

Z is X, Y

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

contract X {
    function foo() public pure virtual returns (string memory) {
        return "X";
    }
    function bar() public pure virtual returns (string memory) {
        return "X";
    }
    function x() public pure returns (string memory) {
        return "X";
    }
}

contract Y is X { //继承写法
    function foo() public pure virtual override returns (string memory) {
        return "Y";
    }
    function bar() public pure virtual override returns (string memory) {
        return "Y";
    }
    function y() public pure returns (string memory) {
        return "Y";
    }
}

contract Z is X, Y {
    function foo() public pure override(X, Y) returns (string memory) {
        return "Z";
    }

    function bar() public pure override(X, Y) returns (string memory) {
        return "Z";
    }
}

调用父级合约的构造函数

contract X {
    function foo() public pure virtual returns (string memory) {
        return "X";
    }
    function bar() public pure virtual returns (string memory) {
        return "X";
    }
    function x() public pure returns (string memory) {
        return "X";
    }
}

contract Y is X {
    X.foo(); //1
    super.foo(); //2 多重继承时,父类的func都会被执行
}

可视范围

  • private 内部可见
  • internal 内部inside和继承child范围可见
  • public 内部inside和外部可见
  • external  仅仅外部可见

不可变常量

关键词:immutable

第一次定义后,就不可被改变

一般用于你不知道其初始化,但是又是常量的情况。第一次赋值后变为常量。

接受ETH

关键词:payable

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract Payable {
    address payable public owner; //可发送ETH主币

    constructor() {
        owner = payable(msg.sender); //使用时要给带有payable属性
    }

    function deposit() external payable { //函数可接受ETH主币的传入

    }

    function getBalance() external view returns (uint) {
        return address(this).balance;
    }
}

回退函数

当你调用合约中不存在的函数时,或者向合约发送主币的时候,都会调用回退函数

当你期望发送主币的时候触发fallback,那么你需要给fallback方法加上payable属性,

solidity8.0以上,新增receive用来只接受主币

触发逻辑:当合约收到主币时,判断是否调用了数据,也就是msg.data是否为空,如果没有调用数据,执行fallback。如果为空,判断receive是否存在,存在调用receive,调用receive,如果不为空,不存在调用fallback

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract Fallback {
    event Log(string func, address sender, uint value, bytes data);
    fallback() external payable {
        emit Log("fallback", msg.sender, msg.value, msg.data);
    }
    receive() external payable {
        emit Log("receive", msg.sender, msg.value, "");
     }
}

发送ETH

transfer 消耗2300gas,gas耗费完或者其他异常情况,revert

send 消耗2300gas,返回bool值

call 会把剩余的gas都发送过去

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;
//transfer  2300gas revert
//send    2300 gas, return bool
//call   all gas, returns bool and data
contract SendETH {
    constructor() payable {

    }
    receive() external payable {

    }

    //三种发送ETH主币的方法
    function Sendbytransfer(address payable _to) external payable {
        _to.transfer(123);
    }
    function Sendbysend(address payable _to) external payable {
        bool sent = _to.send(123);
        require(sent, "send failed");
    }
    function Sendbycall(address payable _to) external payable returns (bytes memory) {
        (bool success, bytes memory data) = _to.call{value: 123}("");
        require(success, "send failed");
        return data;
    }

}

//接受ETH的合约,即上面的发送合约发送ETH到该地址

contract Ethreceive {
    event Log(uint amount, uint gas);

    receive() external payable {
        emit Log(msg.value, gasleft());
    }
}

小结:钱包合约

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract EthWallet {
    address payable public owner;

    constructor() payable {
        owner = payable(msg.sender);
    }

    receive() external payable { }

    function withdraw(uint _amount) external {
        require(msg.sender == owner, "no owner");

        owner.transfer(_amount); //两种效果一样,但是msg.sender在内存,节约gas
        //payable(msg.sender).transfer(_amount);
    }
    function getBalance() external view returns (uint) {
        return address(this).balance;
    }
    function getOwnBalance() external view returns (uint) {
        return msg.sender.balance;
    }
}

调用其他合约

区分两种不同的调用方式,以及可以在调用的时候发送主币

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract CallTest {
    function setOtherContract_X(address _test, uint _x) external {
        Test(_test).setX(_x);
    }

    function getOtherContract_X(Test _test) external view returns (uint) {
        return _test.getX(); //直接将另一个合约名字作为类型传参,然后调用也可
    }

    function setOtherContract_XandETH(address _test, uint _x) external payable {
        Test(_test).setXandReceiveETH{value: msg.value}(_x);
    }

    function getOtherContract_XandETH(Test _test) external view returns (uint, uint) {
        return _test.getXandETH(); 
    }

}
contract Test {
    uint public x;
    uint public value;

    function setX(uint _x) external {
        x = _x;
    }
    function getX() external view returns (uint) {
        return x;
    }

    function setXandReceiveETH(uint _x) external payable {
        x = _x;
        value = msg.value;
    }
    function getXandETH() external view returns (uint, uint) {
        return (x, value);
    }

}

接口合约

当我们不知道另一个合约的源码,或者另一个合约源码很长,可以通过接口方法来调用

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

//此为外部其他的合约,我们不知道其源码,但是我们知道它实现了Icounter接口
contract Count {
    uint public count;

    function inc() external {
        count += 1;
    }
    function dec() external {
        count -= 1;
    }

}

interface Icounter {
    function count() external view returns (uint); //外部合约Count中有count变量,所以这也可以是一个接口
    function inc() external;
}

contract CallInterface {
    uint public count;
    function example(address _cnt) external {
        Icounter(_cnt).inc();
        count = Icounter(_cnt).count();
    }
}

call调用合约

call合约中使用:

_test.call{value: 111}(abi.encodeWithSignature());来调用另一个合约的函数,依靠abi.encodeWithSignature来实现。

其中value: 111表示在调用是发送主币,也可以带有gasvalue: 111, gas:5000但是要确保gas够用。

当Test合约的fallback不存在时,使用call调用不存在的函数时,即下文callnoexist会失败,原理见:回退函数

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract Test {
    string public message;
    uint public x;

    event Log(string message);

    fallback() external payable {
        emit Log("fallback was called");
    }

    function foo(string memory _message, uint _x) external payable returns (bool, uint) {
        message = _message;
        x = _x;
        return (true, 11);
    }
}

contract Call {
    bytes public data;
    function callFoo(address _test) external payable {
        (bool success, bytes memory _data) = _test.call{value: 111}(abi.encodeWithSignature(
            "foo(string, uint256", "call foo", 123
        ));
        require(success, "call failed");
        data = _data;
    }

    function callnoexist(address  _test) external {
        (bool success, ) = _test.call(abi.encodeWithSignature("noexist"));
        require(success, "call failed");
    }
}

委托调用

传统调用:

A call B, send 100wei
B call C, send 50wei

在C的视角
msg.sender = B;
msg.value = 50;
可能发生的状态变量也是在C上,ETH主币也会留在C中

而委托调用:delegatecall

A call B, send 100wei
B delegatecall C

此时,在C的视角
msg.sender = A;
msg.value = 100;
100个ETH主币保存在B,状态也是B中的状态变量发生改变。

人话: 合约X委托调用合约Y的函数,相当于使用了Y的函数作用于自身,在下面的函数中,DelegateCall委托调用了Test中的setVars方法,作用于DelegateCall合约自身。发生改变的也是DelegateCall的状态变量。

tip:两个合约的状态变量等布局要保持一致,不然会发生奇怪的错误,本质上像是内存布局的调用,假设DelegateCall的状态变量和Test的前面的状态变量一样,Test后面新增几个新的状态变量,就不会发生错误。

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract Test {
    uint public num;
    address public sender;
    uint public value;

    function setVars(uint _num) external payable {
        num = _num*2;
        sender = msg.sender;
        value = msg.value;
    }
}

contract DelegateCall {
    uint public num;
    address public sender;
    uint public value;

    function setVars(address _test, uint _num) external payable {
        //_test.delegatecall (
        //    abi.encodeWithSignature("setVars(uint256)", _num)
        //);

        //两种方法效果一样

        (bool success, bytes memory _data) = _test.delegatecall (
            abi.encodeWithSelector(Test.setVars.selector, _num)
        );
        require(success, "delegatecall failed");
    }

}

工厂合约

可以使用一个合约,创建另一个合约,同时也是可以添加payable方法,使用value来给合约发送ETH主币。

在ide测试使用的时候,deploy工厂之后,生成了一个account地址,可以通过deploy按钮下面的At address来直接生成对应的Account合约,然后就可以查看其内容了。

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract Account {
    address public bank;
    address public owner;

    constructor(address _owner) payable {
        bank = msg.sender;
        owner = _owner;
    }
}

contract AccountFactory {
    Account[] public accounts;
    function createAccount(address _owner) external payable {
        Account account = new Account{value: 123}(_owner);
        accounts.push(account);
    }
}

库合约

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

library Math {
    function max(uint x, uint y) internal pure returns (uint) {
        return x >= y ? x : y;
    }
}

contract Test {
    function testMax(uint x, uint y) external pure returns (uint) {
        return Math.max(x, y);
    }
}

library ArrayLib {
    function find(uint[] storage arr, uint x) internal view returns (uint) {
        for(uint i = 0; i &lt; arr.length; i++) {
            if (arr[i] == x) {
                return i;
            }
        }
        revert("not found");
    }
}

contract TestArray {
    uint[] public arr = [3, 2, 1];

    function testFind() external view returns (uint i) {
        return ArrayLib.find(arr, 2);
    }
}

//更方便的写法
contract TestArray2 {
    using ArrayLib for uint[];
    uint[] public arr = [3, 2, 1];

    function testFind() external view returns (uint i) {
        return arr.find(2);
    }
}

hash运算

keccak256

tip:在做hash运算进行编码时,使用abi.encodexxxx编码时,不同的编码方式出来的结果不一致

image-20240630194319168

可以看到一个会补0,另一个不会补0。

那么使用encodePacked"AAAA", "BBB" "AAA", "ABBB"编码,出来的结果会是一样的。如果在这个的基础上再次进行hash运算,就会导致hash碰撞。所以编码时比较好的方式是采用abi.encode,或者在要编码的字符串之前添加uint等方式来隔开。

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract HashFunc {
    function hash(string memory text, uint num, address addr) external pure returns (bytes32) {
        return keccak256(abi.encodePacked(text, num, addr));
    }

    function encode(string memory text1, string memory text2) external pure returns (bytes memory) {
        return abi.encode(text1, text2);
    }
    function encodePacked(string memory text1, string memory text2) external pure returns (bytes memory) {
        return abi.encodePacked(text1, text2);
    }
}

验证签名

对一个消息签名分为四步

  1. 将消息签名,message to sign
  2. hash(message)
  3. sign(hash(message), private key) | offchain  把消息和私钥签名,在链下完成
  4. 恢复签名 ecrecover(hash(message), signature) == signer ,然后验证signer和你期望的是否一致
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract Verifysign {
    function verify(address _signer, string memory _message, bytes memory _sign) external pure returns (bool) {
        bytes32 messageHash = getMessageHash(_message);
        bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
        return recover(ethSignedMessageHash, _sign) == _signer;
    }
    function getMessageHash(string memory _message) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_message));
    }
    function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            _messageHash)); //进行两次hash的原因可能是在数学界一次hash已经有被破解的可能性了
    }

    function recover(bytes32 _ethSignedMessageHash, bytes memory _sign) public pure returns (address) {
        (bytes32 r, bytes32 s, uint8 v) = _split(_sign);
        return ecrecover(_ethSignedMessageHash, v, r, s);
    }

    function _split(bytes memory _sign) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
        require(_sign.length == 65, "invalid signature length");

        assembly {
            r := mload(add(_sign, 32))
            s := mload(add(_sign, 64))
            v := byte(0, mload(add(_sign, 96)))
        }

    }

}

小结:权限控制合约

自毁合约

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.2;

contract Killer {
    constructor() payable {}

    function kill() external {
        selfdestruct(payable(msg.sender)); //像该地址强制发送ETH,即使你是不接受ETH主币的合约,也会给你
    }

    function testCall() external pure returns (uint) {
        return 123;
    }
}

contract Helper {
    function getBalance() external view returns (uint) {
        return address(this).balance;
    }

    function kill(Killer _kill) external {
        _kill.kill();
    }
}

ERCP20标准合约

ERC20标准包含了一组接口IERC20,只要你的代码实现了全部接口,就代表你满足了ERC20标准

<!--EndFragment-->

在线IDE

https\://remix.ethereum.org/

<!--EndFragment-->

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
netlogs
netlogs
0x2E35...566D
colorepistle@gmail.com