timezone |
---|
Asia/Shanghai |
- 自我介绍
后端开发,寻求新挑战
- 你认为你会完成本次残酷学习吗?
稳稳的
0: 主版本号
8: 次版本号
21: 补丁版本号
指定编译器版本的编译指令,含义: version >=0.8.21 && version <0.9.0
另外的表达方式 : pragma solidity >=0.8.21<0.9.0;
- 值类型
包括布尔型(bool)、整型(int)、地址类型(address)、定长字节数组(bytes1)、枚举(enum)
地址类型分为普通地址和payable addressd,地址长度32bytes(字节),十六进制数就是64位
payable address存在transfer和send两个成员方法,用于接收和转账?todo
- 引用类型
数组(array) 不定长字节数组(bytes) 结构体(struct)
引用类型变量较为复杂,在使用时需要声明数据存储的位置
数据存储位置分类:
1. storage
数据存储在链上,可以认为是整个合约的全局参数
2. memory
函数的入参和临时变量,一般使用memory,存储在内存中,不上链
3. calldata
存储在内存中,不上链。他的特点是不可变更,多用于函数参数
- 映射类型
哈希表(map)
// 含义就是 key为int类型,value为address类型的hash表
mapping(uint => address) public map1;
- 权限关键字
pure | view | payable
pure: 表示不对链上状态进行读写
view: 表示对链上状态的读
无:变更链上状态
payable: 附带payable修饰的函数,能够在执行时向合约中转入eth
逻辑:
分为内外两个循环,外循环从index=1开始逐个获取元素,内循环将arr[index] 和 arr[<index]的数据进行比较。将大于arr[index]的数据往后排,一直找到小于它的数,将arr[index]放在它后面
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
contract Test{
function insertSort(uint[] memory arr) pure public returns(uint[] memory) {
for (uint i=1; i<arr.length; i++)
{
uint key = arr[i];
uint j = i;
while( j >=1 && key < arr[j-1]){
arr[j] = arr[j-1];
j--;
//需要注意j的类型为无符号整数uint,所以当j--的行为导致j<0时会报错,因此将j的下限设为1
}
arr[j] = key;
}
return arr;
}
}
可以将其理解为函数的前置的判断条件
//状态变量
address owner;
//修饰器
modifier onlyOwner {
require(msg.sender == owner);//需要满足这个条件
_;//条件满足后继续执行,有点像委托
}
event Transfer(address indexed from,address indexed to,uint256 value);
作用:
- 事件能够被前端ether.js订阅
- 事件的消耗(2000gas)要比链上存储一个变量要少(20000gas),但是这个作用性要存疑
indexed修饰的变量,能够被保存在虚拟机日志的topics中,方便查询
日志结构
- topics
topics数组的长度要求<=4,且indexed修饰的参数<=3
数组的首位存储 keccak("Transfer(address,address,uint256)")的结果
- data
不带indexed修饰的参数值
各种开发语言都有继承这一特性。学习时只要注意solidity语言与其他语言的区别即可。
描述继承关系关键词: is
抽象关键词: virtual
子类重写关键词: overrid
- 函数继承
大部分高级语言都只允许继承一个类,允许继承多个接口。对solidity语言来说,它允许继承多个合约。要求越底层、兼容性越高的要排在前面。
比如:
contract child is grandPa,father{
function say() public override{
}
}
-
修饰器继承
-
构造函数继承
// 构造函数的继承
abstract contract A {
uint public a;
constructor(uint _a) {
a = _a;
}
}
//方式1
contract B is A(3){
}
//方式2
contract C is A {
constructor(uint _C) A(_C){
}
}
- 调用父合约的参数
//方式1:通过super关键词,使用当前的最靠近的父级的方法
function callP() public{
super.test();
}
//方法2: 直接制定继承的父级
function callP1() public {
Papa.test();
}
- 钻石继承
/* 继承树:
God
/ \
Adam Eve
\ /
people
*/
contract people is Adam, Eve {
function foo() public override(Adam, Eve) {
super.foo();
}
function bar() public override(Adam, Eve) {
super.bar();
}
}
//执行的顺序将会是:
//people eve adam god
//且god只会在结尾被调用一次
合约中存在未实现的函数。
抽象合约中,是可以存在已经实现的函数的,且不需要子合约实现。如果没有被virtual修饰,子合约实现会编译报错。如Hi()。
在solidity中,如果要对abstract合约的部分function进行实现,必须要预先设为virtual,且自合约必须要经过override的修饰。
abstract contract Father {
function Say() public pure virtual returns(uint res){
res = 1;
}
function Hi() public pure returns(uint){
return 1;
}
function Ha() public pure virtual returns(uint);
}
contract Test is Father{
function Say() public pure override returns(uint res){
res = 2;
}
function Ha() public pure override returns(uint){
return 0;
}
}
规则:
- 不能包含状态变量
- 不能包含构造函数
- 不能继承除接口外的其他合约
- 所有函数都必须是external且不能有函数体
- 继承接口的非抽象合约必须实现接口定义的所有功能
观察erc20的接口,发现事件的定义也是在接口的书写范围内的。
抛出异常的三种方式: error require assert
- error
需要配合revert(回退)使用。
error TransferNotOwner(address sender);//自定义error
function TransferOwner(uint256 tokenId,address newOwner) public {
if(owners[tokenId]!= msg.sender){
revert TransferNotOwner(msg.sender);
}
owners[tokenId] = newOwner;
}
- require
缺点是errormessage的长度会影响gas
require(condition,"error messager");
- assert
开发人员测试使用。不会记录errormessage。
gas消耗: error < assert < require
函数重载,即函数名称相同,但是入参不同的
function saySomething() public pure returns(string memory){
return("Nothing");
}
function saySomething(string memory something) public pure returns(string memory){
return(something);
}
高级语言都会有的东西。solidity的库合约看起来是已经部署在线上的,可以直接引用。
特点:
- 不能存在状态变量
- 不能够继承或被继承
- 不能接收以太币
- 不可以被销毁
使用方式:
- using for语法
//library
library Strings {
}
// 利用using for指令
using Strings for uint256;
function getString1(uint256 _number) public pure returns(string memory){
// 库合约中的函数会自动添加为uint256型变量的成员
return _number.toHexString();
}
- 直接库合约名称调用
这个有点像静态对象函数直接调用
// 直接通过库合约名调用
function getString2(uint256 _number) public pure returns(string memory){
return Strings.toHexString(_number);
}
关键词 import
导入方式:
- 通过文件的相对位置导入
- 通过全局符号,导入合约特定的全局符号
- 通过网址引入
- 通过npm的目录导入
//1
import './3_Ballot.sol';
//2
import {Ballot} from './3_Ballot.sol';
//3
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol';
//4
import '@openzeppelin/contracts/access/Ownable.sol';
接下来就可以很简单的像使用库一样,通过using for语句或者直接静态函数引用的方式来使用。