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
26
27
28
29
contract AccessControl{
event GrandRole(bytes32 indexed role,address indexed account);
event RevokeRole(bytes32 indexed role,address indexed account);
//role=>account=>bool
mapping(bytes32 => mapping(address => bool))public roles;
//字符串的gas消耗比bytes32哈希的消耗要多的多
bytes32 private constant ADMIN=keccak256(abi.encodePacked("ADMIN"));
bytes32 private constant USER=keccak256(abi.encodePacked("USER"));
modifier onlyRole(bytes32 _role){
require(roles[_role][msg.sender],"not dauthorized");
_;
}
constructor(){//给其实账户管理员权限
_grandRole(ADMIN,msg.sender);
}
function _grandRole(bytes32 _role,address _account)internal{//_role为ADMIN与USER
roles[_role][_account]=true;//内部给账户权力的源代码
emit GrandRole(_role,_account);
}
function grandRole(bytes32 _role,address _account)external onlyRole(ADMIN){
_grandRole(_role,_account);//赋予账户权限
}
function revokeRole(bytes32 _role,address _account)external onlyRole(ADMIN){
roles[_role][_account]=false;//撤销账户权限
emit RevokeRole(_role,_account);
}


}

自毁函数

可实现强制发送主币给地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
contract Kill{
constructor()payable{}
function kill()external{
selfdestruct(payable(msg.sender));//必须以payable标记的地址
}
function testCall()external pure returns(uint){
return 123;
}
}
contract Helper{//通过这个合约来调用自毁合约
function getBalance()external view returns(uint){
return address(this).balance;
}
function kill(Kill _kill)external{
_kill.kill();
}
}

小猪存钱罐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
contract PiggyBank{
event Deposit(uint amount);
event Withdraw(uint amount);
address public owner=msg.sender;
receive()external payable{
emit Deposit(msg.value);

}
function withdraw()external {
require(msg.sender==owner,"not owner");
emit Withdraw(address(this).balance);
selfdestruct(payable(msg.sender));
}
}

ERC20

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
interface IERC20{
function totalSupply()external view returns(uint);//代表token总量
function balanceOf(address account)external view returns(uint);//代表某一账户的余额
function transfer(address recipient,uint amount)external returns(bool);//由当前调用者发送
function allowance(address owner,address spender)//查看批准额度
external view returns(uint);
function approve(address spender,uint amount)external returns(bool);//批准账户额度
function transferFrom(address sender,address recipient,uint amount)
external returns(bool);
event Transfer(address indexed from,address indexed to,uint amount);
event Approval(address indexed owner,address indexed spender,uint amount);
}
contract ERC20 is IERC20{
uint public totalSupply;
mapping(address =>uint)public balanceOf;
mapping(address=>mapping(address=>uint))public allowance;
string public name="test";
string public symbol="TEST";
uint public decimals=18;
function transfer(address recipient,uint amount )external returns(bool){
balanceOf[msg.sender]-=amount;
balanceOf[recipient]+=amount;
emit Transfer(msg.sender,recipient,amount);
return true;
}
function approve(address spender,uint amount)external returns(bool){
allowance[msg.sender][spender]=amount;
emit Approval(msg.sender,spender,amount);
return true;
}
function transferFrom(address sender,address recipient,uint amount)
external returns(bool){
allowance[sender][msg.sender]-=amount;
balanceOf[sender]-=amount;
balanceOf[msg.sender]+=amount;
emit Transfer(sender,recipient,amount);
return true;
}

function mint(uint amount)external{
balanceOf[msg.sender]+=amount;
totalSupply+=amount;
emit Transfer(address(0),msg.sender,amount);
}
function burn(uint amount)external{
balanceOf[msg.sender]-=amount;
totalSupply-=amount;
emit Transfer(msg.sender,address(0),amount);
}
}

多签钱包

多个人同意之后才能转账

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
contract MultiSigWallet{
event Deposit(address indexed sender,uint amount);
event Submit(uint indexed txId);//提交交易申请,txId表示交易数组的索引
event Approve(address indexed owner,uint indexed txId);//多次批准
event Revoke(address indexed owner,uint indexed txId);//撤销批准
event Execute(uint indexed txId);//执行
struct Transaction{//交易的数据
address to;
uint value;
bytes data;
bool executed;//是否被执行过
}
address[]public owners;
mapping(address=>bool)public isOwner;
uint public required;

modifier onlyOwner(){
require(isOwner[msg.sender],"not owner");
_;
}
modifier txExists(uint _txId){//该交易id是否存在,需要存在
require(_txId<transactions.length,"tx does not exist");
_;
}
modifier notApproved(uint _txId){//该批准人是否已经批准,需未批准
require(!approved[_txId][msg.sender],"tx already approved");
_;
}
modifier notExecuted(uint _txId){//该交易是否被执行过,需要未交易
require(!transactions[_txId].executed,"tx already executed");
_;
}

Transaction[]public transactions;//记录所有交易
//某一个交易id下某一个签名人的地址是否批准这次交易

mapping(uint =>mapping(address=>bool))public approved;

constructor(address[]memory _owners,uint _required){
require(_owners.length>0,"owners required");//人的个数要大于零
//最少批准人的个数大于零,且小于总人数
require(_required>0&&_required<=_owners.length,"failed");
for(uint i;i<_owners.length;i++){
address owner=_owners[i];
require(owner!=address(0),"invalid owner");//检查地址是否存在
require(!isOwner[owner],"owner is not unique");//检查是否重复
isOwner[owner]=true;
owners.push(owner);
}
required=_required;

}

receive()external payable{
emit Deposit(msg.sender,msg.value);
}

function submit(address _to,uint _value,bytes calldata _data)
external onlyOwner{//此函数来提交交易数据
transactions.push(Transaction({
to: _to,
value: _value,
data: _data,
executed:false
}));
emit Submit(transactions.length-1);
}

function approve(uint _txId)
external onlyOwner
txExists(_txId)//判断交易id是否存在
notApproved(_txId)//判断这个签名人是否已经批准过了
notExecuted(_txId)//判断这个交易id是否执行过了
{
approved[_txId][msg.sender]=true;
emit Approve(msg.sender,_txId);
}
//用来记录有多少人批准
function _getApproveCount(uint _txId)private view returns(uint count){

for(uint i;i<owners.length;i++){
if(approved[_txId][owners[i]]) {
count+=1;
}
}
}
//用于执行最终业务,既交易
function execute(uint _txId) external txExists(_txId) notExecuted(_txId){
require(_getApproveCount(_txId)>=required,"approvals < required");
Transaction storage transaction=transactions[_txId];

transaction.executed=true;

(bool success,)=transaction.to.call{value:transaction.value}(
transaction.data
);
require(success,"tx failed");
emit Execute(_txId);
}

//批准人在交易前撤销自己原来的批准
function revoke(uint _txId)external onlyOwner txExists(_txId) notExecuted(_txId){
require(approved[_txId][msg.sender],"tx not approved");
approved[_txId][msg.sender]=false;
emit Revoke(msg.sender,_txId);

}


}

函数签名(函数的选择器)

EVM通过函数选择器来调用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
contract FunctionSelector{//获取函数选择器
function getSelector(string calldata _func)external pure returns(bytes4){
//输入的_func为"transfer(address,uint256)"获取oxa9059cbb与下面的一样
//return bytes4(keccak256(bytes(_func)));
return bytes4(bytes(_func));
}
}

contract Receiver{
event Log(bytes data);
function transfer(address _to,uint _amount)external{
emit Log(msg.data);
//msg.data:为下面三行组成
//0xa9059cbb 函数的签名(选择器) [将函数的名称和类型打包后哈希然后取前四个字节]
//0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4 地址参数
//000000000000000000000000000000000000000000000000000000000000000b 数字残数
}
}

荷兰拍卖

随着时间流逝,拍卖价格会越来越低,谁先举牌谁拥有拍卖物品

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
interface IERC721{//这个合约有很多东西
function transferFrom(address _from,address _to,uint _nftId)external;
//ERC721与ERC20都必须在他们的合约中approve才能使用他们的transferFrom方法
}
contract DutchAuction{
uint private constant DURATION=7 days;
IERC721 public immutable nft;
uint public immutable nftId;
address payable public immutable seller;
uint public immutable startingPrice;
uint public immutable startAt;
uint public immutable expiresAt;
uint public immutable discountRate;
constructor(uint _startingPrice,uint _discountRate,address _nft,uint _nftId){
seller=payable(msg.sender);
startingPrice=_startingPrice;
discountRate=_discountRate;//每秒的折扣率
startAt=block.timestamp;
expiresAt=block.timestamp+DURATION;
require(_startingPrice>=_discountRate*DURATION);
nft=IERC721(_nft);
nftId=_nftId;
}
function getPrice()public view returns(uint){
uint timeElapsed=block.timestamp-startAt;
uint discount =discountRate*timeElapsed;
return startingPrice-discount;
}
function buy()external payable{
require(block.timestamp<expiresAt,"auction expired");
uint price= getPrice();
require(msg.value>=price,"ETH < price");
nft.transferFrom(seller,msg.sender,nftId);
uint refund=msg.value-price;
if(refund>0){
payable(msg.sender).transfer(refund);
}
selfdestruct(seller);

}

}

英式拍卖

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
62
63
64
65
66
67
68
69
70
71
72
73
interface IERC721{
function transferFrom(address from,address to,uint nftId)external;
}
contract EnglishAuction{
event Bid(address indexed sender ,uint amount);
event Withdraw(address indexed bidder,uint amount);
event End(address highestBidder,uint amount);

IERC721 public immutable nft;
uint public immutable nftId;

address payable public immutable seller;
uint32 public endAt;
bool public started;
bool public ended;

address public highestBidder;
uint public highestBid;
mapping(address =>uint)public bids;//记录失败投标人投入的钱


constructor(address _nft,uint _nftId,uint _startingBid){
nft=IERC721(_nft);
nftId=_nftId;
seller=payable(msg.sender);
highestBid=_startingBid;//以起初价格来初始化
}

function start()external{//开始拍卖
require(msg.sender==seller,"not seller");
require(!started,"started");

started=true;
endAt=uint32(block.timestamp+60);//六十秒结束
nft.transferFrom(seller,address(this),nftId);

}
function bid()external payable{//参加的拍卖者再次竞标
require(started,"not started");
require(block.timestamp<endAt,"ended");
require(msg.value>highestBid,"value < hightest bid");

if(highestBidder!=address(0)){
bids[highestBidder]+=highestBid;
}

highestBid=msg.value;
highestBidder=msg.sender;

emit Bid(msg.sender,msg.value);
}
function withdraw()external{//失败的选手可在此提取原来竞拍投入的钱
uint bal=bids[msg.sender];
bids[msg.sender]=0;
payable(msg.sender).transfer(bal);
emit Withdraw(msg.sender,bal);
}

function end()external{//结束拍卖,并将nftId转入最高价的人
require(started,"not started");
require(!ended,"ended");
require(block.timestamp>=endAt,"not ended");

ended=true;
if(highestBidder!=address(0)){//如果有人参加
nft.transferFrom(address(this),highestBidder,nftId);
seller.transfer(highestBid);
}else{//没人参加highestBidders的地址为0
nft.transferFrom(address(this),seller,nftId);
}
emit End(highestBidder,highestBid);
}
}

众筹合约

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
contract CrowdFund{
event Launch(uint id,address indexed creator,uint goal,uint32 startAt,uint32 endAt);
event Cancel(uint id);
event Pledge(uint indexed id,address indexed caller,uint amount );
event Unpledge(uint indexed id,address indexed caller,uint amount);
event Claim(uint id);
event Refund(uint indexed id,address indexed caller,uint amount);
struct Campaign{
address creator;
uint goal;
uint pledged;
uint32 startAt;
uint32 endAt;
bool claimed;//false筹款未被领走,true筹款被领走

}
IERC20 public immutable token;
uint public count;//多少个筹款活动
mapping(uint =>Campaign) public campaigns;//每个合约的映射
mapping(uint=>mapping(address=>uint))public pledgeAmount;//参与人参与第几个合约的数量

constructor(address _token){
token=IERC20(_token);
}

function launch(uint _goal,uint32 _startAt,uint32 _endAt)external{//
require(startAt>=block.timestamp,"start at < now");
require(_endAt>=_startAt,"end at < start at");
require (_endAt<=block.timestamp+90 days,"end at >max duration");
count+=1;
campaigns[count]=Campaign({
creator:msg.sender,
goal:_goal,
pledged:0,
startAt:_startAt,
endAt:_endAt,
claimed:false
});

emit Launch(count, msg.sender,_goal,_startAt,_endAt);

}
function cancel(uint _id)external{//开始前取消众筹
Campaign memory campaign=campaigns[id];
require(msg.sender==campaign.creator,"not creator");
require(block.timestamp<campaign.startAt,"started");
delete campaigns[id];
emit Cancel(_id);

}
function pledge(uint _id,uint _amount)external{//参与众筹
Campaign storage campaign=campaigns[_id];
require(block.timestamp>=campaign.startAt,"not started");
require(block.timestamp<=campaign.endAt,"ended");
campaign.pledged+=_amount;
pledgedAmount[_id][msg.sender]+=_amount;
token.transferFrom(msg.sender,address(this),_amount);
emit pledge(_id,msg.sender,_amount);

}
function unpledge(uint _id,uint _amount)external{//撤消众筹
Campaign storage campaign=campaigns[_id];
require(block.timestamp<=campaign.endAt,"ended");
require(pledgeAmount[_id][msg.sender]>=_amount);
campaign.pledged-=_amount;
pledgeAmount[_id][msg.sender]-=_amount;

token.transfer(msg.sender,_amount);
emit Unpledge(_id,msg.sender,_amount);

}
function claim(uint _id)external{
Campaign storage campaign=campaigns[_id];
require(msg.sender==campaign.creator,"not creator");
require(block.timestamp>campaign.endAt,"not ended");
require(campaign.pledged>=campaign.goal,"pledged<goal");
require(!campaign.claimed,"claimed");
campaign.claimed=true;
token.transferFrom(msg.sender,campaign.pledged);
emit Claim(_id);

}
function refund(uint _id)external{//未达到目标就会失败,用户可以取走自己的钱
Campaign storage campaign=campaigns[_id];
require(block.timestamp>campaign.endAt,"not ended");
require(campaign.pledged<campaign.goal,"pledged>goal");

uint bal=pledgeAmount[_id][msg.sender];
pledgeAmount[_id][msg.sender]=0;
token.transfer(msg.sender,bal);
emit Refund(_id,msg.sender,bal);

}
}

Multi call多重调用

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
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TestMultiCall{
function func1()external view returns(uint,uint){
return(1,block.timestamp);
}
function func2()external view returns(uint,uint){
return(2,block.timestamp);
}
function getData1()external pure returns(bytes memory){
//abi.encodeWithSignature("func1()")
return abi.encodeWithSelector(this.func1.selector);
}
function getData2()external pure returns(bytes memory){
//abi.encodeWithSignature("func2()")
return abi.encodeWithSelector(this.func2.selector);
}

}
contract MultiCall{
function fun(address addr)external returns( uint,uint){
(uint x,uint y)=TestMultiCall(addr).func1();
(uint z,uint t)=TestMultiCall(addr).func2();
return (y,t);
}
}
//通过data调用
contract MultiCall2{
function multicall(address[]calldata targets,bytes[]memory data)
external view returns(bytes[] memory)
{
require(targets.length==data.length,"target!=data");
bytes[]memory results=new bytes[](data.length);
for(uint i;i<targets.length;i++){
(bool success,bytes memory result)=targets[i].staticcall(data[i]);//静态调用,因为有view
require(success,"call failed");
results[i]=result;
}
return results;
//返回的data是bytes类型的,需要解码。
}
}

多重委托调用(mint处内涵漏洞!!!)

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
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Helper{

function getData1(uint x,uint y)external pure returns(bytes memory){
//abi.encodeWithSignature("func1()")
return abi.encodeWithSelector(TestMultiDelegatecall.func1.selector,x,y);
}

function getData2()external pure returns(bytes memory){
//abi.encodeWithSignature("func2()")
return abi.encodeWithSelector(TestMultiDelegatecall.func2.selector);
}

function getMintData()external pure returns(bytes memory){
return abi.encodeWithSelector(TestMultiDelegatecall.mint.selector);
}
}

contract MultiDelegatecall{
function multicallDelegatecall(bytes[]memory data)
external payable returns(bytes[] memory)
{
bytes[]memory results=new bytes[](data.length);
for(uint i;i<data.length;i++){
(bool ok,bytes memory result)=address(this).delegatecall(data[i]);
require(ok,"call failed");
results[i]=result;
}
return results;
}
}
contract TestMultiDelegatecall is MultiDelegatecall{
mapping(address=>uint)public balanceOf;
event Log(address caller,string func,uint i);

function func1(uint x,uint y)external{
emit Log(msg.sender,"func1",x+y);
}

function func2()external returns(uint){
emit Log(msg.sender,"func2",2);
return 111;
}

function mint()external payable{
balanceOf[msg.sender]+=msg.value;
}
//如果在本合约中多重委托调用mint,则输入一次msg.value账户的balanceOf会增加n次
//n就是多重调用的次数

}

abi解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract AbiDecode{
struct MyStruct{
string name;
uint[2] nums;
}
function encode(uint x,address addr,uint[]calldata arr,MyStruct calldata myStruct)
external pure returns(bytes memory){
return abi.encode(x,addr,arr,myStruct);
}

//解码函数
function decode(bytes calldata data)external pure returns(
uint x,address addr,uint[]memory arr,MyStruct memory myStruct
)
{
(x,addr,arr,myStruct)=abi.decode(data,(uint,address,uint[],MyStruct));
}
}