您现在的位置是:首页 >技术交流 >区块链学习(Solidity进阶)【Day11 | 5.26】网站首页技术交流
区块链学习(Solidity进阶)【Day11 | 5.26】
简介区块链学习(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;
}
}
多重继承中的重名
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。