您现在的位置是:首页 >技术交流 >区块链学习(Solidity进阶)【Day11 | 5.26】网站首页技术交流

区块链学习(Solidity进阶)【Day11 | 5.26】

BingjieDreams 2024-07-15 12:01:02
简介区块链学习(Solidity进阶)【Day11 | 5.26】

事件 Event

  • Event 事件是以太坊虚拟机(EVM)日志基础设置提供的一个便利接口
    • 当被发送事件(调用)时,会触发参数存储到交易的日志中。
    • 这些日志与合约地址关联,并记录到区块链中
  • 区块链是打包一系列交易的区块组成的链条,每个交易“收据”会包含0到多个日志记录
    • 日志表明着智能合约所触发的事件

语法

  • 使用 event 关键字定义一个事件 event
event EventName(<parameter list>);
  • 使用 emit 关键字来触发一个事件 Event
emit EventName(<parameter list>);

示例

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

contract Test{
    // 定义 event
    event Log(string, uint);

    function operations() external{
        // 触发 event
        emit Log("Info", 10);
    }
}

Event indexed

  • 事件 Event 还有一种特殊形式 event indexed,也就是索引事件,语法如下:
event EventName(TypeName indexed varibleName....);
  • 事件中 indexed 标记过的参数,可以在链外进行搜索查询
    • 一个事件中 indexed 标记过的参数最多有3个
    • 记录区块链的日志,可以使用状态变量,也可以使用事件 Event
      • 但 Event 使用的 gas 费用比状态变量更低
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Envent{
    // 定义 event
    event Log(address indexed, string);

    function operations() external{
        // 触发 event
        emit Log(msg.sender,"Info");
    }
}

不可变量 immutable

  • immutable 是另外一种常量的表达方式,
  • 与常量类似,但是不必硬编码,可以在构造函数时传值,部署后无法改变。
  • immutable 不可变量同样不会占用状态变量存储空间
    • 在部署时,变量的值会被追加的运行时字节码中
    • 因此它比使用状态变量便宜的多,也同样带来了更多的安全性。
  • 最常见的如 ERC20 代币用来指示小数位置的 decimals 变量
  • 需要在创建合约时指定值,使用 immutable, 例如:保存创建者地址、关联合约地址等
  • immutable 不可变量的三种赋值方式:
    • 需要注意,immutable 只能在状态变量声明和构造函数中赋值,其他位置不允许
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Immutable{
    address public immutable owner = msg.sender;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Immutable {
   address public immutable owner;

   constructor() {
      owner = msg.sender;
   }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Immutable {
   address public immutable owner;

   constructor(address _owner) {
      owner = _owner;
   }
}

log 日志

  • 调试时可以通过打印 Log 的方式,查看合约运行过程中的数据
  • 可以使用以下方法:
    • 在合约中创建一个 event,命名为 Log
    • 在想要打印日志的地方调用事件 emit Log(...),即可查看
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SolidityTest{
    // 定义事件
    event Log(address);

    constructor(){
        // 调用事件
        emit Log(msg.sender);
        emit Log(address(this));
    }
}

/*  Log(msg.sender) 在日志中输出了合约部署者的地址。
    Log(address(this)) 在日志中输出了合约地址。 */

查看合约在部署时的日志结果

[
    {
        "from": "0xE3Ca443c9fd7AF40A2B5a95d43207E763e56005F",
        "topic": "0x2c2ecbc2212ac38c2f9ec89aa5fcef7f532a5db24dbf7cad1f48bc82843b7428",
        "event": "log",
        "args": {
            // 合约部署者的地址
            "0": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
        }
    },
    {
        "from": "0xE3Ca443c9fd7AF40A2B5a95d43207E763e56005F",
        "topic": "0x2c2ecbc2212ac38c2f9ec89aa5fcef7f532a5db24dbf7cad1f48bc82843b7428",
        "event": "log",
        "args": {
            // 合约地址
            "0": "0xE3Ca443c9fd7AF40A2B5a95d43207E763e56005F"
        }
    }
]

合约继承

  • 通过关键字 is 来实现

继承示例

  • 合约 Employee 继承了合约 Person,继承了状态变量 name、age 和方法 getSalary
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Person{
    string public name;
    uint public age;
    function getSalary() external pure returns(uint){
        return 0;
    }
}
contract Employee is Person{
}

Virtual 和 override

  • 父合约可以使用 virtual 关键字声明一个虚函数

  • 子合约使用 override 关键字来覆盖父合约的方法

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

contract Person{
    string public name;
    uint public age;
    function getSalary() external pure virtual returns(uint){
        return 1;
    }
}
contract Employee is Person{
    function getSalary() external pure override returns(uint){
        return 300;
    }
}

/*  子合约 Employee 的 uint 方法覆盖了父合约 Person 的 getSalary 方法,调用子合约 Employee getSalary 方法,输出结果为 3000。 */
  • 如果合约 Manager 又继承了 Employee,而且还需要覆盖 getSalary 方法,那么需要如下写法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Person{
  string public name;
  uint public age;
  function getSalary() external pure virtual returns(uint){
    return 0;
  }
}
contract Employee is Person{
  function getSalary() external pure virtual override returns(uint){
    return 3000;
  }
}

contract getsex is Employee{
  function getSalary() external pure override returns(uint){
    return 20000;
  }
}

abstract

  • solidity 还允许在基类中只声明函数原型,没有实现,而在派生类中再去实现。

  • solidity 使用 abstract 关键字来标记基类。

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

abstract contract Employee {
    function getSalary() public  pure virtual returns(int);
}

contract Manager is Employee {
    function getSalary() public  pure override returns(int){
        return 20000;
    }
}

抽象合约 abstract 的作用是将函数定义和具体实现分离,实现解耦、可拓展性,其使用规则为:

  • 当合约中有未实现的函数时,则合约必须修饰为abstract
  •  当合约继承的base合约中有构造函数,但是当前合约并没有对其进行传参时
    • 则必须修饰为abstract
  • abstract合约中未实现的函数必须在子合约中实现
    • 即所有在abstract中定义的函数都必须有实现
  • abstract合约不能单独部署,必须被继承后才能部署
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract Animal{
    string public species;
    constructor(string memory _base){
        species = _base;
    }
}

abstract contract Feline{
    uint public num;
    function utterance()public pure virtual returns(bytes32);
    
    function base(uint _num)public returns(uint, string memory) {
        num = _num;
        return(num,"hello");
    }
}

// 由于 Animal 中的构造函数没有进行初始化,所以必须修饰为 abstract
abstract contract Cat1 is Feline,Animal{
    function utterance()public pure override returns(bytes32) {
        return "miaow";
    }
}

contract Cat2 is Feline,Animal("Animal"){
    function utterance() public pure override returns(bytes32){
        return "miaow";
    }
}

子类访问父类权限

  • 子类访问父类权限修饰符包括:public、internal、private
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract A{
    uint stateVar;
    function somePublicFun()public{}
    function someInternalFun() internal{}
    function somePrivateFun() private{}
}

contract B is A{
    function call()public{
        // 访问父类的 public 方法
        somePublicFun();

        // 访问父类的状态变量(状态变量默认是 internal 权限)
        stateVar = 10;

        // 访问父类的 internal 方法
        someInternalFun();

        // 不能访问 private
        // somePrivateFun();
    }
}

传参数到父类

  • 直接传递
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Base{
    uint a;
    constructor(uint _a){
        a = _a;
    }
}

contract Derive is Base(1){
    function getBasePara()external view returns(uint) {
        return a;
    }
}
  • 根据输入值传递
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Base{
    uint a;
    constructor(uint _a){
    a = _a;
    }
}
contract T is Base{
    constructor(uint _a)Base(_a){}
    function getBasePara()external view returns(uint) {
        return a;
    }
}

多重继承中的重名

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。