Solidity笔记
原项目参考:https://github.com/AmazingAng/WTF-Solidity
7、映射类型
7.1、Mapping
- 通过键值对(key-value),
mappong(_KeyType => _ValueType)
mapping(uint => address) public idToAddress; // id映射到地址
mapping(address => address) public swapPair; // 币对映射
7.2、映射规则
- 规则1:映射的
_KeyType
只能选择Solidity内置的值类型,比如uint
,address
等,不能用自定义的结构体,而_ValutType
可以用自定义的类型。
struct Student {
uint256 id;
uint256 score;
}
mapping(Student => uint) public testVar; // 报错
- 规则2:映射的存储位置必须是
storage
,因此可以用于合约的状态变量,函数中的storage
变量,和library
函数的参数。不能用于public
函数的参数或返回结果中,因为mapping
记录的是一种关系。 - 规则3:如果映射声明为
public
,Solidity会自动创建getter
,可以通过_Key
来查询对应的Value
。 - 规则4:给映射新增的键值对的语法为
_Var[_Key]=_Value
,其中_Var
是映射变量名。
function writeMap(uint _Key, address _Value) public {
idToAddress[_Key] = _Value;
}
7.3、映射原理
- 原理1:映射不存储任何键(
Key
)的咨询,也没有length的咨询。 - 原理2:映射使用
keccak256(abi.encodePacked(key, slot))
当成offset存取value,其中slot
是映射变量定义锁在的插槽位置。 - 原理3:因为Ethereum会定义所有未使用的空间为0,所以未赋值(
Value
)的键(Key
)初始值都是各个type的默认值,如uint的默认值是0。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Mappinggg {
mapping(uint => address) public idToAddress;
mapping(address => address) public swapPair;
function writeMap(uint _Key, address _Value) public {
idToAddress[_Key] = _Value;
}
}
8、变量初始值
8.1、值类型初始值
boolean
:false
string
:""
int
:0
uint
:0
enum
: 枚举中的第一个元素address
:0x0000000000000000000000000000000000000000
(或address(0)
)function
internal
: 空白函数external
: 空白函数
// 用public变量的getter函数可以验证
bool public _bool;
string public _string;
int public _int;
uint public _uint;
address public _address;
enum ActionSet { Buy, Hold, Sell }
ActionSet public _enum;
function fi() internal{}
function fo() external{}
8.2、引用类型初始值
- 映射
mapping
: 所有元素都为其默认值的mapping
- 结构体
struct
: 所有成员设为其默认值的结构体 - 数组
array
- 动态数组:
[]
- 静态数组(定长): 所有成员设为其默认值的静态数组
- 动态数组:
// 可用public的getter验证
uint[8] public _staticArray;
uint[] public _dynamicArray;
mapping(uint => address) public _mapping;
struct Student {
uint256 id;
uint256 score;
}
Student public student;
8.3、delete操作符
// delete会让变量变为初始值
bool public _bool2 = true;
function d() external {
delete _bool2;
}
9、常量
constant
(常量)和immutable
(不变量),只有数值变量可以声明constant
和immutable
;string
和bytes
可以声明为constant
,但不能为immutable
。
9.1、constant
constant
变量必须在声明的时候初始化,之后再也不能改变。尝试改变的话,编译不通过。
uint256 constant CONSTANT_NUM = 10;
string constant CONSTANT_STRING = "0xAA";
bytes constant CONSTANT_BYTES = "WTF";
address constant CONSTANT_ADDRESS = 0x0000000000000000000000000000000000000000;
9.2、immutable
immutable
变量可以在声明时或构造函数中初始化,因此更加灵活。
uint256 public immutable IMMUTABLE_NUM = 99999999;
address public immutable IMMUTABLE_ADDRESS;
uint256 public immutable IMMUTABLE_BLOCK;
uint256 public immutable IMMUTABLE_TEST;
// 可以使用全局变量,或者自定义函数给immutable初始化
constructor() {
IMMUTABLE_ADDRESS = address(this);
IMMUTABLE_BLOCK = block.number;
IMMUTABLE_TEST = test();
}
function test() public pure returns(uint256) {
uint256 what = 9;
return(what);
}
10、控制流
// if-else
function ifElseTest(uint256 _number) public pure returns(bool) {
if (_number == 0) {
return(true);
} else {
return(false);
}
}
// for
function forLoopTest() public pure returns(uint256) {
uint sum = 0;
for (uint i = 0; i < 10; ++i) {
sum +=i;
}
return(num);
}
// while
function whileTest() public pure returns(uint256) {
uint sum = 0;
uint i = 0;
do {
sum += i;
++i;
} while (i <10);
return(sum);
}
// 三元运算
function ternartTest(uint256 x, uint256 y) public pure returns(uint256) {
return x >= y ? x: y;
}
插入排序InsertionSort
// 注意Solidity的uint是无法取到负数的,因此运算时要注意防止负数的出现
function insertionSort(uint[] memory a) public pure returns(uint[] memory) {
for (uint i = 1; i < a.length; ++i) {
uint temp = a[i];
uint j = i; // 注意这里不能i-1,否则循环后会出现j<0导致报错
while ((j >= 1) && (temp < a[j - 1])) {
a[j] = a[j - 1];
--j;
}
a[j] = temp;
}
return(a);
}
11、构造函数和修饰器
11.1、构造函数(constructor)
- 一种特殊的函数,每个合约可以定义一个,并在部署合约的时候自动运行一次。它可以用来初始化合约的一些参数,例如初始化合约的
owner
地址
address public owner;
constructor() {
owner = msg.sender;
}
注意⚠️:构造函数在不同的solidity版本中的语法并不一致,在Solidity 0.4.22之前,构造函数不使用 constructor
而是使用与合约名同名的函数作为构造函数而使用,由于这种旧写法容易使开发者在书写时发生疏漏(例如合约名叫 Parents
,构造函数名写成 parents
),使得构造函数变成普通函数,引发漏洞,所以0.4.22版本及之后,采用了全新的 constructor
写法。
11.2、修饰器(modifier)
- 类似于面向对象编程中的
decorator
,声明函数拥有的特性,并减少代码冗余,主要使用场景是运行函数前的检查,例如地址,变量,余额等。
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner); // 检查调用者是否为owner地址
_; // 如果是的话,继续运行函数主体;否则报错并revert交易
}
// 带有onlyOwner修饰符的函数只能被owner地址调用
function changeOwner(address _newOwener) external onlyOwner {
owner = _newOwner;
}
定义了一个changeOwner
函数,运行他可以改变合约的owner
,但是由于onlyOwner
修饰符的存在,只有原先的owner
可以调用,别人调用就会报错。这也是最常用的控制智能合约权限的方法。