文章目录
- solidity 精简笔记
-
- 一、三行框架
- 二、数值类型 bool、uint、int、address、bytes32
- 三、函数 function、函数名(传入参数)、可见性、gas 节省支付性、returns (参数类型 参数名)
- 四、函数输出
- 五、变量存储属性(storage/memory/calldata) 与作用域分类(状态变量、局部变量、全局变量)
- 六、数组(定长、变长;length、push()、push(x)、pop() )、 结构体
- 七、映射类型 mapping
- 八、变量初始值与 delete
- 九、常数属性 constant 与 immutable
- 十、控制流(if、for、while、?:),用solidity实现插入排序
- 十一、构造函数 constructor 和修饰器 modifier
- 十二、事件 event
- 十三、继承:简单继承、多重继承以及修饰器(modifier)和构造函数(constructor)的继承
- 十四、抽象合约 abstract 与接口 interface
- 十五、异常:error、require 与 assert
solidity 精简笔记
一、三行框架
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract 合约名{
...;
}
二、数值类型 bool、uint、int、address、bytes32
1.布尔型 bool
// 布尔型变量
bool public _bool = true;
2.正整数 uint、整数 int
// 正整数、整数定义
uint public _uint = 1; // 正整数
uint256 public _number = 20231024; // 256位正整数
int public _int = -1; // 整数,包括负数
// 整数运算
uint256 public _number2 = 2**2; // 指数
uint256 public _number3 = 7 % 2; // 取余数
bool public _numberbool = _number2 > _number3; // 比大小
3.地址类型 address
// 地址定义
address public _address = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
address payable public _address1 = payable(_address);
// payable address,可以转账、查余额
// balance
uint256 public balance = _address1.balance;
// transfer
address payable addr;
addr.transfer(1);//合约向addr转账1wei
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);// 向 x 转账 10?
4.定长字节数组 bytes32
一个字节占两位,字节乘2是位数(16进制)
// 固定长度的字节数组
bytes32 public _byte32 = "MiniSolidity";
bytes1 public _byte = _byte32[0]; //存储_byte32数组的第一个字节,即0x4d (0x是象征意义,代表十六进制)
三、函数 function、函数名(传入参数)、可见性、gas 节省支付性、returns (参数类型 参数名)
function 函数名(传入参数) 可见性 [gas 节省支付性] [returns(参数类型 参数名)]
//可见性:internal|external|public|private
//gas 节省支付性:pure|view|payable
中括号部分可省略不写。
四、函数输出
主要还是用命名式返回,直接 returns(参数类型 参数名)
// 命名式返回
function returnNamed() public pure returns(uint256 _number, bool _bool, uint256[3] memory _array){
_number = 2;
_bool = false;
_array = [uint256(3),2,1];
}
五、变量存储属性(storage/memory/calldata) 与作用域分类(状态变量、局部变量、全局变量)
//变量存储属性的用法:
//变量类型 变量属性 变量名
uint[] x = [1,2,3]; //变量属性默认是storage
uint[] storage xStorage = x;
uint[] memory xMemory = x;
1.变量存储位置
storage
是默认属性,存在链上消耗gas
多memory
和calldata
存在内存消耗gas
少memory
的变量可修改calldata
的变量不可修改
2.赋值修改新变量会不会影响原变量
合约的状态变量与函数里的本地变量相互赋值时,会相互影响的情况只有两种:
storage
赋值给storage
memory
赋值给memory
3.变量按作用域分类
-
状态变量:合约内、函数外定义
存储在链上
随意使用
-
局部变量:函数内定义
存储在内存,
gas
费低只能在当前函数内使用
-
全局变量:都是预留关键字
随意使用
六、数组(定长、变长;length、push()、push(x)、pop() )、 结构体
// 定长数组:变量类型[长度] 数组名
uint[8] array1;
bytes1[5] array2;
address[100] array3;
// 变长数组:变量类型[] 数组名
uint[] array4;
bytes1[] array5;
address[] array6;
bytes array7; //bytes是数组,但是后面不用加[]
// memory 变长数组需要定义时赋值或者 new 变量类型[](长度)
uint[] memory array8 = new uint[](5);
bytes memory array9 = new bytes(9);
//函数传入返回数组时,需要注意位数统一,可以直接强制转换位数,例如 uint(第一个数组元素)
[uint(1), 2, 3]
//如果创建的是动态数组,则需要一个一个元素的赋值
uint[] memory x = new uint[](3);
x[0] = 1;
x[1] = 3;
x[2] = 4;
//数组.成员函数 只有 length 是所有数组都有的,push()、push(x)、pop() 只在变长数组和bytes数组中才有
//push()是末尾添一个0,push(x)是末尾添一个x,pop()是删除尾元素
// 结构体
struct Student{
uint256 id;
uint256 score;
}
Student student; // 定义一个Student类型的变量
// 结构体赋值:变量名.内部成员名 = xx;
function initStudent2() external{
student.id = 1;
student.score = 80;
}
七、映射类型 mapping
用途:通过一个人的id
来查询他的钱包地址。
//mapping 映射实例
mapping(uint => address) public idToAddress; // id映射到地址
mapping(address => address) public swapPair; // 币对的映射,地址到地址
//使用规则:
1.自定义的结构体类型不能映射别人,只能被映射
2.映射的存储位置只能是 storage (默认链上存储)
3.映射声明为 public,则会获得 getter 函数
4.新增键值对语法:_Var[_Key] = _Value
function writeMap (uint _Key, address _Value) public{
idToAddress[_Key] = _Value;
}
八、变量初始值与 delete
1.值类型初始值
可以用public
变量的getter
函数验证注释写的初始值是否正确:
bool public _bool; // false
string public _string; // ""
int public _int; // 0
uint public _uint; // 0
address public _address; // 0x0000000000000000000000000000000000000000
enum ActionSet { Buy, Hold, Sell}
ActionSet public _enum; // 第一个元素 0
function fi() internal{} // internal空白方程
function fe() external{} // external空白方程
2.引用类型初始值
可以用public
变量的getter
函数验证注释写的初始值是否正确:
// Reference Types
uint[8] public _staticArray; // 所有成员设为其默认值的静态数组[0,0,0,0,0,0,0,0]
uint[] public _dynamicArray; // `[]`
mapping(uint => address) public _mapping; // 所有元素都为其默认值的mapping
// 所有成员设为其默认值的结构体 0, 0
struct Student{
uint256 id;
uint256 score;
}
Student public student;
3.delete 操作符
delete a
会让变量a
的值变为初始值。
// delete操作符
bool public _bool2 = true;
function d() external {
delete _bool2; // delete 会让_bool2变为默认值,false
}
九、常数属性 constant 与 immutable
这两个常数属性的意义是节省gas
的同时提升合约安全性。
immutable
更灵活,但只能作用于数值变量。
1.constant:定义时就初始化
// constant变量必须在声明的时候初始化,之后不能改变
uint256 constant CONSTANT_NUM = 10;
string constant CONSTANT_STRING = "0xAA";
bytes constant CONSTANT_BYTES = "WTF";
address constant CONSTANT_ADDRESS = 0x0000000000000000000000000000000000000000;
2.immutable:定义、全局变量、构造函数初始化
// immutable变量可以在constructor里初始化,之后不能改变
uint256 public immutable IMMUTABLE_NUM = 9999999999;
address public immutable IMMUTABLE_ADDRESS;
uint256 public immutable IMMUTABLE_BLOCK;
uint256 public immutable IMMUTABLE_TEST;
// 利用constructor初始化immutable变量
constructor(){
IMMUTABLE_ADDRESS = address(this);
IMMUTABLE_BLOCK = block.number;
IMMUTABLE_TEST = test();
}
function test() public pure returns(uint256){
uint256 what = 9;
return(what);
}
十、控制流(if、for、while、?:),用solidity实现插入排序
//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(sum);
}
//while
function whileTest() public pure returns(uint256){
uint sum = 0;
uint i = 0;
while(i < 10){
sum += i;
i++;
}
return(sum);
}
//?:(条件? 条件为真的输出:条件为假的输出)
function ternaryTest(uint256 x, uint256 y) public pure returns(uint256){
// return the max of x and y
return x >= y ? x: y;
}
十一、构造函数 constructor 和修饰器 modifier
1.构造函数 constructor:部署地址
//初始化合约的 owner 地址
address owner; // 定义owner变量
// 构造函数
constructor() {
owner = msg.sender; // 在部署合约的时候,将owner设置为部署者的地址
}
2.修饰器 modifier:检查地址控制合约权限
十二、事件 event
Solidity
中的事件(event
)是EVM
上日志的抽象,它具有响应和经济两个特点:
- 响应:应用程序(
ether.js
)可以通过RPC
接口订阅和监听这些事件,并在前端做响应。 - 经济:链上存储数据比存储事件的成本高。事件是
EVM
上比较经济的存储数据的方式,每个大概消耗2,000gas
;相比之下,链上存储一个新变量至少需要20,000gas
。
可以使用emit关键字来释放一个事件。
十三、继承:简单继承、多重继承以及修饰器(modifier)和构造函数(constructor)的继承
//多重继承:is , , , ...
contract Erzi is Yeye, Baba{
// 继承两个function: hip()和pop(),输出值为Erzi。
function hip() public virtual override(Yeye, Baba){
emit Log("Erzi");
}
function pop() public virtual override(Yeye, Baba) {
emit Log("Erzi");
}
//调用父合约中的函数:
//父合约名.函数名()
function callParent() public{
Yeye.pop();
}
//super.函数名()
function callParentSuper() public{
// 将调用最近的父合约函数,Baba.pop()
super.pop();
}
十四、抽象合约 abstract 与接口 interface
1.抽象合约:有未实现的函数
//合约 contract 前面加上 abstract,未实现的函数 属性要加上 virtual ,并且以 ; 结尾
abstract contract InsertionSort{
function insertionSort(uint[] memory a) public pure virtual returns(uint[] memory);
}
2.接口:定义合约功能,以及如何触发它们
接口类似于抽象合约,但它不实现任何功能。接口是智能合约的骨架,定义了合约的功能以及如何触发它们。
十五、异常:error、require 与 assert
写智能合约经常会出bug
,solidity
中的异常命令可以帮助我们debug
。
首选 error 搭配 revert。