solidity基础知识点(二)

事件知识点

1
2
3
4
5
6
7
8
9
10
11
12
contract Event {
event Log(string message,uint val);
event IndexedLog(address indexed sender,uint val);
function example()external{
emit Log("foo",123);
emit IndexedLog(msg.sender,123);
}
event Message(address indexed _from,address indexed _to,string message);
function sendMessage(address _to,string calldata message)external{
emit Message(msg.sender,_to,message);
}
}

继承与多线继承

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
contract A{

function pop() public virtual returns(string memory){//需要被继承的合约要加virtual
return "A";
}
function hip()public virtual returns(string memory){
return "A";
}
function Aa()public pure returns(string memory){
return "A";
}
}
contract B is A{
function pop() public virtual override returns(string memory){//加继承的函数加override
return "B";
}
function hip()public virtual override returns(string memory){
return "B";
}
function Bb()public pure returns(string memory){
return "B";
}
}
contract C is A, B{//多线继承,最基础的放在最前面,然后依次排列
function pop()public pure override(A,B) returns(string memory){
return "C";
}
function hip()public pure override (A,B)returns(string memory){
return "C";
}
}

继承构造函数传入参数的知识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
contract S{
string public name;
constructor(string memory _name){
name=_name;
}
}
contract T{
string public text;
constructor(string memory _text){
text=_text;
}
}
contract U is S("S"),T("t"){//已知输入内容

}
contract V is S ,T{//按照继承的顺序来运行,所以先传入S,后传入T
constructor(string memory _name,string memory _text )S(_name) T(_text){

}
}//构造函数时传入 当然U和V这两种方法是可以合并的

如何调用父级合约

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
contract E{
event Log(string msesage);
function foo()public virtual{
emit Log("E.foo");
}
function bar()public virtual {
emit Log("E.bar");
}
}
contract F is E{
function foo()public virtual override{
emit Log("F.foo");
E.foo();//直接调用v
}
function bar ()public virtual override{
emit Log("F.bar");
super.bar();//super方法调用,自己会寻找父级合约
}
}
contract G is E{
function foo()public virtual override{
emit Log("G.foo");
E.foo();
}
function bar()public virtual override{
emit Log("G.bar");
super.bar();
}
}
contract H is F, G{
function foo()public override(F,G){
F.foo();//直接调用
}
function bar ()public override(F,G){
super.bar();
}
}

可视范围知识点

-private 只能合约内部可见
-internal 只能内部或者被继承的子合约中可见
-public 内外部均可见
-external 只能从外部可见,继承子合约中不可见

1.如果有一个外部合约C调用一个合约A,那么C合约只能看到A合约的public与external合约函数,其他都不行
2.继承只能看到internal和public

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
contract visibility{
uint private x=0;
uint internal y=1;
uint public z=2;
function privateFunc()private pure returns(uint){
return 0;
}
function internalFunc()internal pure returns(uint){
return 100;
}
function publicFunc()public pure returns(uint){
return 200;
}
function externalFunc()external pure returns(uint){
return 300;
}
function examples()external view{
x+y+z;
privateFunc();
internalFunc();
publicFunc();
this.externalFunc();//可以通过这样来访问外部函数。原理:先到合约外部在到内部从而访问external函数。
}
}

不可变量

不可变量immutable用的时候必须赋值

1
2
3
4
5
6
7
8
9
10
11
contract Immutable{
address public immutable owner = msg.sender;//像定义常量一样节约gas费,同样owner永不可变
// constructor(){
// owner=msg.sender;可在构建函数时赋值
//}
uint public x;
function foo()external{
require (msg.sender == owner);
x+=1;
}
}

支付payable属性与回退函数运用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
contract Payable{
address payable public owner;//这样的话就可以发送钱了
constructor(){
owner =payable(msg.sender);
//owner=msg.sender ;这样不行 //因为owner有payable的属性,所以构造函数要有payable的属性
}
function deposit()external payable{}
function getBalance()external view returns(uint){
return address(this).balance;
}
}

//回退函数

contract Fallback{//有数据则会触发fallback
event Log(string func,address sender,uint value,bytes data);
fallback()external payable{//可接受主币的发送
emit Log("fallback",msg.sender,msg.value,msg.data);
}
receive()external payable{//无数据则会触发receive,如果没有receive则会触发fallback
emit Log("receive",msg.sender,msg.value,"");
}
}

发送以太币三种方法

· transfer 2300 gas,reverts
· sender -2300 gas returns bool
· call - all gas,returns bool and data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
contract SendEther{
constructor()payable{}
receive()external payable{}
function sendtrsndfer(address payable _to)external payable{
_to.transfer(123);//代表123wei
}
function sendsend(address payable _to)external payable{
bool send=_to.send(123);
require(send,"send failed");
}
function sendcall(address payable _to)external payable{
(bool success,)=_to.call{value:123}("");
require(success,"fail");
}
}

制作一个以太坊钱包

1
2
3
4
5
6
7
8
9
10
11
contract EtherWallet{
address payable public owner;
constructor(){
owner=payable(msg.sender);
}
receive()external payable{}
function withdraw(uint _amount)external{
require(msg.sender==owner,"caller is not owner");
owner.transfer(_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
contract callTestContract{
function setx1(address _test,uint _x)external{
TestContract(_test).setx(_x);
}
function setx2(TestContract _test,uint _x)external{
_test.setx(_x);//两种方法都可以
}
function getx(address _test)external view returns(uint){
return TestContract(_test).getx();
}
function setxandReceiveEther(address _test,uint _x)external payable{
TestContract(_test).setxandReceiveEther{value:msg.value}(_x);//传入value的值
}
function getxandValue(address _test)external view returns(uint x,uint value){
(x,value)=TestContract(_test).getxandValue();
}
}
contract TestContract{
uint public x;
uint public value=123;
function setx(uint _x)external{
x=_x;
}
function getx()external view returns(uint){
return x;
}
function setxandReceiveEther(uint _x)external payable{
x=_x;
value=msg.value;
}
function getxandValue()external view returns(uint, uint){
return (x,value);
}
}

接口

当不知到一个合约源代码时可以用接口合约调用其中的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface ICounter{
function count()external view returns(uint);
function inc()external;
}
contract CallInterface{
uint public count;
function examples(address _counter)external{
ICounter(_counter).inc();
count=ICounter(_counter).count();
}
}

//假如不知道Counter合约的源代码
contract Counter{
uint public count;
function inc()external{
count+=1;
}
function dec()external{
count -=1;
}
}

低级调用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
contract TestCall{
string public message;
uint public x;
event Log(string message);
fallback()external payable{
emit Log("fallback was called");
}
function foo(string memory _message,uint _x)external payable returns(bool,uint){
message=_message;
x=_x;
return(true,999);
}
}
contract Call{//用call调用合约中的函数
bytes public data;
function callFoo(address _test)external payable{
(bool success,bytes memory _data)=_test.call{value:111}(abi.encodeWithSignature(
"foo(string,uint256)","call foo",123//需要调用时转钱就加{value:111}(其中可规定gas用量),否则不加
));
require(success,"call failed");
data=_data;
}
function callNO(address _test)external{
(bool success,)= _test.call(abi.encodeWithSignature(
"doesnotexist()"
));
require(success,"call failed");
}

}

委托调用

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
/*
1.直接调用发生的变化
A calls B,sends 100 wei
B calls C,sends 50wei
A --->B --->C
msg.sender=B
msg.value=50
execute code on C state variables
use ETH in C
2.委托调用发生的变化
A calls B,sends 100 wei
Bdelegatecall C
A--->B--->C
msg.sender=A
msg.value=100
execute code on B state variables
use ETH in B
*/
contract TestDelegatecall{//测试合约
uint public num;
address public sender;
uint public value;
function setVars(uint _num)external payable{
num=_num;
sender=msg.sender;
value=msg.value;
}
}
//委托调用合约,只改变委托合约的变量,不改变测试合约变量,通过测试合约改变了委托合约中的状态变量
contract DelegateCall{
uint public num;
address public sender;
uint public value;
function setVars(address _test,uint _num)external payable{
// _test.delegatecall(
// abi.encodeWithSignature("setVars(uint256)",_num)
// );
_test.delegatecall(
(bool success ,bytes memory data)= abi.encodeWithSelect(TestDelegatecall.setVars.selector ,_num)
);
require(success."delegatecall failed");
}
}

用new来创建合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
contract Account{
address public bank;
address public owner;
constructor(address _owner)payable{
bank=msg.sender;
owner=_owner;
}
}
contract AccountFactory{
Account[]public accounts;
function createAccount(address _owner)external payable{
Account account=new Account{value:111}(_owner);//创建合约
accounts.push(account);//将合约添入数组
}
}

库合约(library)

库合约就是自定义函数用来防止重复代码

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
library Math{
function max(uint x,uint y)internal pure returns(uint){
return x>=y?x:y;
}
function find(uint []storage arr,uint x)internal view returns(uint){
for(uint i=0;i<arr.length;i++){
if(arr[i]==x){
return i;
}
}
revert("not found");
}
}
contract Test{
function testMax(uint x,uint y)external pure returns(uint){
return Math.max(x,y);
}
}
contract TestArray{
using Math for uint[];//相当于我们把库运用到数组这个类型
//这个类型就拥有了这个库的所有函数的功能
uint []public arr=[3,2,1];
function testFind()external view returns(uint i){
return Math.find(arr,2);//没有用using的方法
}
}

哈希算法

用于签名获取特定的id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
contract HashFunc{


function encode(string memory text0,string memory text1)external pure returns(bytes memory){
return abi.encode(text0,text1);//返回值会补充0
}
function encodePacked(string memory text0,string memory text1)external pure returns(bytes memory){
return abi.encodePacked(text0,text1);//不会补充零,造成漏洞 "AAA""BB"的返回值与"AA""ABB"一模一样
} //AAA BB表示是传入的text0与text1
function hash(string memory text,uint num,address addr)external pure returns(bytes32){
return keccak256(abi.encodePacked(text,num,addr));//encodePacked这种会造成哈希碰撞
//就是打包之后"AAA""BB"的哈希与"AA""ABB"一样
}
}
//encodePacked漏洞解决:在两个text之间uint一个数,这样就解决了

在智能合约中验证签名

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
/*
四步骤:
1.message to sign
2.hash(message)
3.sign(hash(message),private key) | offchain(链下完成)
4.ecrecover(hash(message),signature)==signer 恢复签名,得出签名人的地址
*/
contract VerifySig{//签名的验证
function verify(address _signer,string memory _message,bytes memory _sig)
external pure returns(bool) //_sig 签名结果
{
bytes32 messageHash=getMessageHash(_message);
bytes32 ethSignedMessageHash=getEthSignedMessageHash(messageHash);
return recover(ethSignedMessageHash,_sig)==_signer;
}
function getMessageHash(string memory _message)public pure returns(bytes32){
return keccak256(abi.encodePacked(_message));//第一次加密

}
function getEthSignedMessageHash(bytes32 _messageHash)public pure returns(bytes32){//第二次加密
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32",_messageHash));//两次哈希且增加字符串更安全
}

function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", toString(s.length), s));
}

function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
function recover(bytes32 _ethSignedMessageHash,bytes memory _sig)
public pure returns(address)
{//得到签名人地址
(bytes32 r,bytes32 s,uint8 v)=_split(_sig);
return ecrecover(_ethSignedMessageHash,v,r,s);
}
function _split(bytes memory _sig)internal pure//分割
returns(bytes32 r,bytes32 s,uint8 v)//32+32+1=65位的长度
{//将加密信息分割成r s v
require(_sig.length==65,"invalid length");
assembly {//用内联汇编进行分割_sig(只能)
r:=mload(add(_sig,32))
s:=mload(add(_sig,64))
v:=byte(0,mload(add(_sig,96)))
}
}

function toString(uint256 value) internal pure returns (string memory) {

if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);}

function get(string memory _message)public pure returns(bytes memory){
//return keccak256(abi.encodePacked(_message));//第一次加密
return abi.encodePacked(_message);
return keccak256(abi.)
}}