前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >[区块链]Solidity小白菜系列进阶(二)

[区块链]Solidity小白菜系列进阶(二)

原创
作者头像
荷秋
修改2024-12-27 17:27:38
修改2024-12-27 17:27:38
12810
代码可运行
举报
文章被收录于专栏:开发Notes开发Notes
运行总次数:0
代码可运行

一.存储位置_storage memory calldata

代码语言:javascript
代码运行次数:0
复制
//存储位置
//storage 存储(状态变量)
//memory 内存(局部变量)
//calldata 和memory类似 只能够用在输入的参数中 这样传递的时候不用定义多个memory来消耗gas
contract DataLocations{
    struct MyStruct{
        uint foo;
        string text;
    }

    mapping (address => MyStruct) public myStructs;

    function examples(uint[] calldata y,string calldata s) external returns(uint[] memory){
        myStructs[msg.sender] = MyStruct({foo: 123 , text: "bar"});
        
        MyStruct storage myStruct = myStructs[msg.sender];//像指针一样的传了地址 如果改这个变量里的值 链上的值就会发生改变
        myStruct.text = "foo";

        MyStruct memory readOnly = myStructs[msg.sender];//局部变量改了也不会保存到链上
        readOnly.foo = 456;

        _internal(y);

        uint[] memory memArr = new uint[](3);
        memArr[0] = 222;
        return memArr;

    }

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

简单存储

代码语言:javascript
代码运行次数:0
复制
contract SimpleStorage{
    string public text;

    //输入相同参数的情况下 calldata比memory的gas消耗量要更小
    function set(string calldata _text) external {
        text = _text;

    }

    function get() external view returns (string memory){
        return text;//把状态变量拷贝到内存中然后返回
    }
}

待办列表案例

代码语言:javascript
代码运行次数:0
复制
contract TodoList{
    struct Todo{
        string text;
        bool completed;
    }

    Todo[] public todos;

    function create(string calldata _text) external {
        todos.push(Todo({
            text: _text,
            completed: false
        }));
    }

    function updateText(uint _index,string calldata _text) external {
        todos[_index].text = _text;
        Todo storage todo = todos[_index];
        todo.text = _text;
    }
    //如果需要改很多参数 用storage更节省gas 否则不需要
}

二.事件_event emit

代码语言:javascript
代码运行次数:0
复制
contract Event{
    //事件是一种记录当前智能合约运行状态的方法 并不记录在状态变量中 而是体现在区块链浏览器上或者交易记录中
    //通过事件可以查询改变过的状态

    //声明事件 大写开头 事件报告的数据类型
    event Log(string message, uint val);

    event IndexedLog(address indexed sender,uint val);//indexed 关键字的意义在于提高事件日志的查询效率
    //每个事件最多可以有三个 indexed 参数

    function example() external {
        //触发事件
        emit Log("foo",123);

        emit IndexedLog(msg.sender,789);
    }//改变了链上事件的状态

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

    function sendMessage(address _to, string calldata message) external {
        emit Message(msg.sender, _to, message);
    }//事件的存储更节约gas
}

三.继承

1.合约继承_ is virtual override

代码语言:javascript
代码运行次数:0
复制
//关键词 virtual 标记函数可被重写
//关键词 override 标记函数已被重写 

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";
    } 
}

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

2.继承父级合约构造函数

代码语言:javascript
代码运行次数:0
复制
//继承父级合约构造函数 的两种方式
contract S {
    string public name;

    constructor(string memory _name){
        name = _name;
    }
}

contract T{
    string public text;
    constructor(string memory _text){
        text = _text;
    }
}

contract U is S("s") , T("t"){//在已知给定值的情况下

}

contract V is S,T{
    constructor(string memory _name ,string memory _text) S(_name) T(_text){

    }//赋值根据继承的顺序来 和构造函数后续修饰无关
}

//两种方式混合使用
contract VV is S("s"),T{
    constructor(string memory _text) T(_text){

    }
}

contract VO is S , T{
    //构造函数初始化的顺序根据继承顺序来 基础的被继承合约放前面
    constructor(string memory _name,string memory _text) S(_name) T(_text){

    }
}

3.调用父级合约函数_super

代码语言:javascript
代码运行次数:0
复制
/*
    E
   / \
  F   G
   \ /
    H
*/
contract E{
    event Log(string message);
    function foo() public virtual {
        emit Log("E.foo");
    }
    function bar() public virtual {
        emit Log("E.bar");
    }
}

contract F is E{
    function foo() public virtual override {
        emit Log("F.foo");
        //调用父级合约的两种方式
        E.foo();
    }

    function bar() public virtual override {
        emit Log("F.bar");
        super.bar();
    }
}

contract G is E{
    function foo() public virtual override {
        emit Log("G.foo");
        //调用父级合约的两种方式
        E.foo();
    }

    function bar() public virtual override {
        emit Log("G.bar");
        super.bar();
    }
}

contract H is F, G{
    function foo() public override (F, G){
        F.foo();//指定父F
    }

    function bar() public override (F, G){
        super.bar();//所有父
    }
}

4.可视范围

public   公开 合约内部和被继承的子合约中 以及外部可见

private  私有 合约内部可见

external 外部 仅在合约外部调用可见 内部不可见

internal 内部 合约的内部和被继承的子合约中可见

使用this可以访问外部函数 但是太消耗gas不建议使用

代码语言:javascript
代码运行次数:0
复制
contract SimpleStorage{
    string public text;

    //输入相同参数的情况下 calldata比memory的gas消耗量要更小
    function set(string calldata _text) external {
        text = _text;

    }

    function get() external view returns (string memory){
        return text;//把状态变量拷贝到内存中然后返回
    }
}

四.不可变量_immutable

代码语言:javascript
代码运行次数:0
复制
//不可变量
contract Immutable{
    // address public immutable owner = msg.sender;
    address public immutable owner;
    //常量需要一开始就赋值定值
    //不可变量赋值一次就不可变了 使用这种方式更节省gas
    //但是必须在定义的时候赋值 要么就在构造函数中赋值 不然就报错 部署合约的时候就必须得赋值
    constructor(){
        owner = msg.sender;
    }

    uint public x;
    function foo() external {
        require(msg.sender == owner);
        x += 1;
    }

五.支付ETH_payable

代码语言:javascript
代码运行次数:0
复制
//payable关键字表示可以接收以太坊主币 部署后红色按钮
contract Payable{
    address payable public  owner;

    constructor(){
        owner = payable (msg.sender); //需要转换成payable类型的
    }

    function deposit() external payable {

    }

    //显示当前余额
    function getBalance() external view returns (uint){
        return address(this).balance;
    }
}

六.回退函数_fallback receive

代码语言:javascript
代码运行次数:0
复制
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, "");
    }
    //fallback和receive的逻辑关系 看以下伪代码
    // if(msg.data为空){
    //     if(receive()方法存在){
    //         执行receive()方法
    //     }else{
    //         执行fallback()方法
    //     }
    // }else{
    //     执行fallback()方法
    // }
}

七.发送ETH_transfer send call

代码语言:javascript
代码运行次数:0
复制
//三种方式发送ETH
//1.transfer 2300gas 失败会reverts
//2.send 2300gas 返回bool标记是否成功
//3.call all gas 返回bool和data
contract SendEther{
    //存入主币的两种方式
    constructor() payable {

    }
    receive() external payable { 

    }

    function sendViaTransfer(address payable _to) external payable{
        _to.transfer(123);
    }

    function sendViaSend(address payable _to) external payable {
        bool sent = _to.send(123);
        require(sent,"send failed");
    }

    function sendViaCall(address payable _to) external payable {
        (bool success, ) = _to.call{value: 123}("");
        require(success,"call failed");
    }
}

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

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

八.钱包合约_payable

代码语言:javascript
代码运行次数:0
复制
//钱包合约
contract EtherWallet{
    address payable  public owner;

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

    receive() external payable { }//接受到主币的发送

    function withdraw(uint _amount) external {
        require(payable(msg.sender) == owner,"caller is not owner");
        payable (msg.sender).transfer(_amount);
    }

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

九.调用其他合约

代码语言:javascript
代码运行次数:0
复制
//调用其他合约
contract CallTestContract{
    function setX1(address _test,uint _x) external {
        //调用其他合约的方法的方式
        TestContract(_test).setX(_x);//把另一个合约当做类型 然后传入地址
    }
    function setX2(TestContract _test,uint _x) external {
        //调用其他合约的方法的方式
        _test.setX(_x);//使用合约对象调用
    }
    function getX(address _test) external view returns (uint) {
        uint x = TestContract(_test).getX();
        return x;
    }
    function setXandSendEther(address _test,uint _x) external payable  {
        TestContract(_test).setXandReceiveEther{value: msg.value}(_x);//传递主币{value: msg.value}
    }
    function getXandValue(address _test) external view returns (uint,uint) {
        (uint x,uint value) = TestContract(_test).getXandValue();
        return (x,value);
    }
}

十.接口_interface

代码语言:javascript
代码运行次数:0
复制
//接口
interface ICounter {
    //定义方法但不实现
    function count() external view returns (uint);

    function inc() external ;
}

contract CallInterface{
    uint public count;
    function examples (address _counter) external {
        ICounter(_counter).inc();
        count = ICounter(_counter).count();
    }
}

//假如这个合约我们不清楚里面的内容 或者内容过多 就可以在其他地方先部署 然后通过接口方法来调用
contract Couter{
    uint public count;

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

十一.低级call_call

代码语言:javascript
代码运行次数:0
复制
//低级调用另一个合约
contract TestCall{
    string public message;
    uint public x;

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

}

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

十二.委托调用

代码语言:javascript
代码运行次数:0
复制
//委托调用
contract TestDelegateCall{
    uint public num;
    address public sender;
    uint public value;

    function setVars(uint _num) external payable {
        num = _num;
        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(TestDelegateCall.setVars.selector, _num)
        );
        require(success,"delegatecall failed");

    } 
}
//两个局部变量需要一一对应 后续加可行

十三.工厂合约_new

代码语言:javascript
代码运行次数:0
复制
//工厂合约
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: 111}(_owner);
        accounts.push(account);
    }
}

十四.库合约_library

代码语言:javascript
代码运行次数:0
复制
//库合约
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 < arr.length ; i++) {
            if(arr[i] == x){
                return i;
            }
        }
        revert("not found");
    }
}
contract TestArray{
    using ArrayLib for uint[];//用于为特定类型添加库函数
    uint[] public arr = [3,2,1];

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

十五.哈希运算_keccak256

代码语言:javascript
代码运行次数:0
复制
//哈希运算
contract HashFunc{
    function hash (string memory text,uint num ,address addr) external pure returns (bytes32){//hash值特定类型
        //打包形式1 有点压缩
        //keccak256(abi.encodePacked(text,num,addr)) //不会补0
        // return keccak256(abi.encode(text,num,addr));//会补0
        return keccak256(abi.encodePacked(text,num,addr));
        //打包形式应该选一样的 不然哈希碰撞几率大
    }
}

十六.验证签名

代码语言:javascript
代码运行次数:0
复制
//验证签名
contract VerifySig{
    function verify(address _signer, string memory _message, bytes memory _sig) 
    external pure returns (bool){
        bytes32 messageHash = getMessageHash(_message);
        bytes32 ethSingedMessageHash = getEthSignedMessageHash(messageHash);

        return recover(ethsignedMessageHash,_sig) == _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(
            "\x19Etherum Signed Message:\n32",
            _messageHash
        ));
    }

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

    function _split(bytes memory _sig) internal pure 
    returns (bytes32 r,bytes32 s ,uint8 v){

    }
}

十七.权限控制合约

代码语言:javascript
代码运行次数:0
复制
//权限控制合约 多种角色身份
contract AccessControl{
    event GrantRole(bytes32 indexed role, address indexed account);
    event RevokeRole(bytes32 indexed role, address indexed account);

    //角色->账户->布尔值
    mapping (bytes32 => mapping (address => bool)) public roles;

    //采用哈希值存储更合适
    bytes32 private constant ADMIN = keccak256(abi.encodePacked("ADMIN"));
    bytes32 private constant USER = keccak256(abi.encodePacked("USER"));

    modifier onlyRole(bytes32 _role){
        require(roles[_role][msg.sender],"not authorized");
        _;
    }

    constructor(){
        _grantRole(ADMIN, msg.sender);
    }

    //升级函数 给账户赋予权限
    function _grantRole(bytes32 _role,address _account) internal{
        roles[_role][_account] = true;
        emit GrantRole(_role, _account);
    }

    function grantRole(bytes32 _role,address _account) external onlyRole(ADMIN) {
        roles[_role][_account] = true;
        emit GrantRole(_role, _account);
    }
}

十八.自毁合约_selfdestruct

代码语言:javascript
代码运行次数:0
复制
//自毁合约
//1.删除合约
//2.发送剩余所有eth
contract Kill{
    constructor() payable {

    }
    function kill() external {
        selfdestruct(payable(msg.sender));
    }

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.存储位置_storage memory calldata
  • 二.事件_event emit
  • 三.继承
    • 1.合约继承_ is virtual override
    • 2.继承父级合约构造函数
    • 3.调用父级合约函数_super
    • 4.可视范围
  • 四.不可变量_immutable
  • 五.支付ETH_payable
  • 六.回退函数_fallback receive
  • 七.发送ETH_transfer send call
  • 八.钱包合约_payable
  • 九.调用其他合约
  • 十.接口_interface
  • 十一.低级call_call
  • 十二.委托调用
  • 十三.工厂合约_new
  • 十四.库合约_library
  • 十五.哈希运算_keccak256
  • 十六.验证签名
  • 十七.权限控制合约
  • 十八.自毁合约_selfdestruct
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档