solidity 基础知识(一)

报错控制的相关知识点

用于检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
contract error{//报错控制
function testrequire(uint i)public pure{
require(i<=10,"i>10");//不满足则报错字符串内容
}//gas费与字符长度有关,报错之后会回滚到报错前的状态

function testrevert(uint i)public pure{
if(i>10){
revert("i>10");
}}
uint public num=123;
function testassert()public view{
assert(num==123);
}
function foo()public{
num+=1;
}//num加1之后就不满足testassert函数,再调用testassert就会报错
error Myerror(address caller,uint i);//自定义错误来节约gas
function testcustom(uint i)public view{
if(i>10){
revert Myerror(msg.sender,i);
}
}


}

函数修改器知识

修饰器(modifier)是 solidity 特有的语法,它就像钢铁侠的智能盔甲,有特定功能,也可以控制访问权限。modifier 的主要使用场景是运行函数前的检查,例如地址,变量,余额等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
contract functionmodifier{//函数修改器
bool public paused;
uint public count;
function setPause(bool _paused)external{
paused=_paused;
}
modifier whenpaused(){
require(!paused,"paused");
_;//表示函数其他代码在哪里运行,既运行到下划线就会跳入函数
}

function inc()external whenpaused{
//require(!paused,"paused");把两个函数相同的require拿出来用函数修改器
count+=1;
}
function dec()external whenpaused{
//require(!paused,"paused");
count-=1;
}


modifier cap(uint x){
require(x<100,"x>=100");
_;
}
function incBy(uint x)external whenpaused cap(x){//先检查第一个是否满足,然后传入入参数到第二个修改器进行判断
count+=x;
}

modifier sandwich(){//三明治修改器
count +=10;
_;
count *=2;
}
function foo()external sandwich{//先加10,再加1,最后乘2;
count+=1;
}
}

构造函数

构造函数(constructor)是一种特殊的函数,每个合约可以定义一个,并在部署合约的时候自动运行一次。它可以用来初始化合约的一些参数,例如初始化合约的 owner 地址

1
2
3
4
5
6
7
8
contract Constructor{
address public owner;
uint public x;
constructor(uint _x){//构造函数一般用于初始化变量,部署前传入_x的值
owner = msg.sender;
x = _x;
}
}

总结1

前面内容的合成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-License-Identifier: HIT
pragma solidity^0.8.0;
contract Ownable{
address public owner;
constructor(){
owner = msg.sender;
}
modifier onlyOwnable(){
require(msg.sender==owner ,"not owner");
_;

}
function setowner(address _newOwner)external onlyOwnable{
require(_newOwner!=address(0),"invalid address");
owner=_newOwner;
}
function onlyOwner()external onlyOwnable{

}
function anyone()external{

}

}

returns的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
contract functionoutput{//返回写法
function returnMany()public pure returns (uint , bool ){
return(1,true);
}
function named()public pure returns(uint x,bool b){
return(1,true);
}
function assigned()public pure returns(uint x,bool b){
x=1;b=true;
}
function destructing()public pure{//接收返回值
uint x;bool b;
( x , b )=returnMany();
(, b)=returnMany();
}
}

数组的知识点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
contract Array{//数组知识点
uint[]public nums=[1,2,3];//动态数组
uint[3]public numsfixed=[4,5,6];//固定数组
uint public x;
uint public len;

function examples()external{
nums.push(4); //[1,2,3,4]
x=nums[1];
nums[2]=777; //[1,2,777,4]
delete nums[1]; //[1,0,777,4]
nums.pop(); //[1,0,777]
len=nums.length; //数组长度
uint []memory a=new uint[](5); //内存中只能固定数组,不能用上面内容
a[1]=123;
}
function returnarray()external view returns(uint[] memory){
return nums; //返回数组所有的元素
}

}

数组的删除函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
contract ArrayShift{
uint []public arr;
function example()public {
arr=[1,2,3];
delete arr[1];//[1,0,3]
}
//[1,2,3]--remove(1)-->[1,3,3]-->[1,3]
function remove(uint _index)public{//闪出数组中的元素
require(_index<arr.length,"index out of bound");
for(uint i=_index;i<arr.length-1;i++){
arr[i]=arr[i+1];
}
arr.pop();

}
}

映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
contract Mapping{
uint bal;
uint bal2;
mapping(address =>uint)public balances;
mapping(address=>mapping(address=>bool))public isFriend;
function examples()external{
balances[msg.sender]=123;
bal =balances[msg.sender];
bal2=balances[address(1)];//默认 0
balances[msg.sender]+=456;
delete balances[msg.sender];//delete并不是删除,而是回归默认值
isFriend[msg.sender][address(this)]=true;
}
}

映射与数组的结合,高价值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
contract iterableMapping{//将数组与映射结合
mapping(address=>uint)public balances;
mapping(address=>bool)public inserted;
address[]public keys;
function set(address _key,uint _val)external{
balances[_key]=_val;
if(!inserted[_key]){
inserted[_key]=true;
keys.push(_key);//增加地址到数组,从而实现遍历
}
}
function getsize()external view returns(uint){
return keys.length;
}
function first()external view returns(uint){
return balances[keys[0]];//第一个地址的余额
}
function last()external view returns(uint){
return balances[keys[keys.length-1]];//最后一个地址的余额
}
function get(uint i)external view returns(uint){
return balances[keys[i]];//任意一个地址的余额
}

结构体的定义方法与数组组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
contract Struct{
struct Car{
string model;
uint year;
address owner;
}
Car public car;
Car[] public cars;//可通过这里查看数组
mapping(address =>Car[])public carsByOwner;
function examples()external{
Car memory toyota=Car("Toyota",1990,msg.sender);
Car memory lambo=Car({year:1980,model:"Lamborghini",owner:msg.sender});//顺序可换换
Car memory tesla;
tesla.model="Tesla";
tesla.year=2010;
tesla.owner=msg.sender;
cars.push(toyota);
cars.push(lambo);
cars.push(tesla);
cars.push(Car("Ferrari",2020,msg.sender));
Car storage _car=cars[0];
_car.year=1999;
delete _car.owner;
delete cars[1];
}
}

枚举的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
contract Enum{
enum Status{
None,
Pending,
Shipped,
Completed,
Rejected,
Canceled
}//分别对应 0,1,2,3,4,5
Status public status;
struct Order{
address buyer;
Status status;
}
Order[]public orders;
function get(Status _status) external returns(Status){//输入枚举的序号,得到枚举的序号
status=_status;
return status;
}
function ship()external{
status=Status.Shipped;//调用后得到2;
}
function reset()external{
delete status;//删除,变成默认值0
}
}

通过内联汇编部署合约(懵点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
contract TextContract1{
address public owner=msg.sender;
function setOwner(address _owner)public{
require(msg.sender==owner,"not owner");
owner=_owner;
}
}

contract TestContract2{
address public owner=msg.sender;
uint public value=msg.value;
uint public x;
uint public y;
constructor(uint _x,uint _y){
x=_x;
y=_y;
}
}
//代理合约,用来部署合约1和2
contract Proxy{
event Deploy(address);
function deploy(bytes memory _code)external payable returns(address addr){//隐式返回:returns后面定义了名称,自动返回
//获得部署合约的地址
assembly{//内联汇编
//create(v,p,n)
//v代表部署合约发送的币的数量 通过callvalue获取
//p代表内存中机器码开始的位置 通过add(_code,0x20)跳跃
//n代表机器码内存的大小 通过mload(_code)获取
addr :=create(callvalue(),add(_code,0x20),mload(_code))//msg.value在这里不行

}
require(addr!=address(0),"deploy failed"); //判断是否部署成功
emit Deploy(addr);
}
//通过date来呼叫另一个合约中的函数
function execute(address _target,bytes memory _date)external payable{
(bool success, )=_target.call{value:msg.value}(_date);
require(success,"failed");
}
}


//助手合约,用来得到合约1和2或者合约中函数的code
contract Helper{
//获取呼叫合约1的代码
function getBytecode1()external pure returns(bytes memory){
bytes memory bytecode=type(TestContract1).creationCode;//特定方法type(合约名称).creationCode
return bytecode;
没有构造函数时用此方法获得bytecode;

}
//获取呼叫合约2的代码
function getBytecode2(uint _x,uint _y)external pure returns(bytes memory){
bytes memory bytecode=type(TestContract2).creationCode;//特定方法
return abi.encodePacked(bytecode,abi.encode(_x,_y)); //通过将传入的参数与bytecode打包生成新的bytecode
}
//获取呼叫合约1中setOwner的所需要的date
function getCalldate(address _owner)external pure returns(bytes memory){//_owner为传入合约1中函数的参数
return abi.encodeWithSignature("setOwner(address)",_owner);//特定方法
}
}

用create2来构造函数(可提前预测生成的地址)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract DeployWithCreate2{
address public owner;
constructor(address _owner){
owner=_owner;
}
}

contract Create2Factory{
event Deploy(address addr);
function deploy(uint _salt)external{
DeployWithCreate2 _contract=new DeployWithCreate2{
salt:bytes32(_salt)
}(msg.sender);//create2方法相对于原来的方法只需加个大括号
emit Deploy(address(_contract));
}

//计算合约地址的方法
function getAddress(bytes memory bytecode,uint _salt)public view returns(address)
{
bytes32 hash=keccak256(abi.encodePacked(
bytes1(0xff),address(this),_salt,keccak256(bytecode)
));
return address(uint160(uint(hash)));

}
//计算bytecode
function getBytecode(address _owner)public pure returns(bytes memory){
bytes memory bytecode=type(DeployWithCreate2).creationCode;
return abi.encodePacked(bytecode,abi.encode(_owner));
}
}