Solidity学习笔记2


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内置的值类型,比如uintaddress等,不能用自定义的结构体,而_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(不变量),只有数值变量可以声明constantimmutablestringbytes可以声明为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可以调用,别人调用就会报错。这也是最常用的控制智能合约权限的方法。


文章作者: ye
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ye !
  目录