<<WTF-Solidity>>学习笔记(part 9-12)
part 9
这一讲,我们介绍Solidity中和常量相关的两个关键字,constant
(常量)和immutable
(不变量)。状态变量声明这两个关键字之后,不能在初始化后更改数值。这样做的好处是提升合约的安全性并节省gas
。
另外,只有数值变量可以声明constant
和immutable
;string
和bytes
可以声明为constant
,但不能为immutable
。
constant和immutable
constant
constant
变量必须在声明的时候初始化,之后再也不能改变。尝试改变的话,编译不通过。
// constant变量必须在声明的时候初始化,之后不能改变 uint256 constant CONSTANT_NUM = 10; string constant CONSTANT_STRING = "0xAA"; bytes constant CONSTANT_BYTES = "WTF"; address constant CONSTANT_ADDRESS = 0x0000000000000000000000000000000000000000;
immutable
immutable
变量可以在声明时或构造函数中初始化,因此更加灵活。在Solidity v0.8.21
以后,immutable
变量不需要显式初始化,未显式初始化的immutable
变量将使用数值类型的初始值(见 8. 变量初始值)。反之,则需要显式初始化。 若immutable
变量既在声明时初始化,又在constructor中初始化,会使用constructor初始化的值。
part 10:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract InsertionSort {
// if else
function ifElseTest(uint256 _number) public pure returns(bool){
if(_number == 0){
return(true);
}else{
return(false);
}
}
// for loop
function forLoopTest() public pure returns(uint256){
uint sum = 0;
for(uint i = 0; i < 10; i++){
sum += i;
}
return(sum);
}
// while
function whileTest() public pure returns(uint256){
uint sum = 0;
uint i = 0;
while(i < 10){
sum += i;
i++;
}
return(sum);
}
// do-while
function doWhileTest() public pure returns(uint256){
uint sum = 0;
uint i = 0;
do{
sum += i;
i++;
}while(i < 10);
return(sum);
}
// 三元运算符 ternary/conditional operator
function ternaryTest(uint256 x, uint256 y) public pure returns(uint256){
// return the max of x and y
return x >= y ? x: y;
}
// 插入排序 错误版
function insertionSortWrong(uint[] memory a) public pure returns(uint[] memory) {
for (uint i = 1;i < a.length;i++){
uint temp = a[i];
uint j=i-1;
while( (j >= 0) && (temp < a[j])){
a[j+1] = a[j];
j--;
}
a[j+1] = temp;
}
return(a);
}
// 插入排序 正确版
function insertionSort(uint[] memory a) public pure returns(uint[] memory) {
// note that uint can not take negative value
for (uint i = 1;i < a.length;i++){
uint temp = a[i];
uint j=i;
while( (j >= 1) && (temp < a[j-1])){
a[j] = a[j-1];
j--;
}
a[j] = temp;
}
return(a);
}
}
part 11 :
构造函数
构造函数(constructor
)是一种特殊的函数,每个合约可以定义一个,并在部署合约的时候自动运行一次。它可以用来初始化合约的一些参数,例如初始化合约的owner
地址:
address owner; // 定义owner变量 // 构造函数 constructor(address initialOwner) { owner = initialOwner; // 在部署合约的时候,将owner设置为传入的initialOwner地址 }
注意:构造函数在不同的Solidity版本中的语法并不一致,在Solidity 0.4.22之前,构造函数不使用 constructor
而是使用与合约名同名的函数作为构造函数而使用,由于这种旧写法容易使开发者在书写时发生疏漏(例如合约名叫 Parents
,构造函数名写成 parents
),使得构造函数变成普通函数,引发漏洞,所以0.4.22版本及之后,采用了全新的 constructor
写法。
修饰器
修饰器(modifier
)是Solidity
特有的语法,类似于面向对象编程中的装饰器(decorator
),声明函数拥有的特性,并减少代码冗余。它就像钢铁侠的智能盔甲,穿上它的函数会带有某些特定的行为。modifier
的主要使用场景是运行函数前的检查,例如地址,变量,余额等。
part 12:
这一讲,我们用转账ERC20代币为例来介绍Solidity
中的事件(event
)。
事件
Solidity
中的事件(event
)是EVM
上日志的抽象,它具有两个特点:
- 响应:应用程序(ethers.js)可以通过
RPC
接口订阅和监听这些事件,并在前端做响应。 - 经济:事件是
EVM
上比较经济的存储数据的方式,每个大概消耗2,000gas
;相比之下,链上存储一个新变量至少需要20,000gas
。
声明事件
事件的声明由event
关键字开头,接着是事件名称,括号里面写好事件需要记录的变量类型和变量名。以ERC20
代币合约的Transfer
事件为例:
event Transfer(address indexed from, address indexed to, uint256 value);
我们可以看到,Transfer
事件共记录了3个变量from
,to
和value
,分别对应代币的转账地址,接收地址和转账数量,其中from
和to
前面带有indexed
关键字,他们会保存在以太坊虚拟机日志的topics
中,方便之后检索。
释放事件
我们可以在函数里释放事件。在下面的例子中,每次用_transfer()
函数进行转账操作的时候,都会释放Transfer
事件,并记录相应的变量。
// 定义_transfer函数,执行转账逻辑 function _transfer( address from, address to, uint256 amount ) external { _balances[from] = 10000000; // 给转账地址一些初始代币 _balances[from] -= amount; // from地址减去转账数量 _balances[to] += amount; // to地址加上转账数量 // 释放事件 emit Transfer(from, to, amount);
Event.sol 在remix上的测试结果