23-03-01

First Post:

Last Update:

2023-03-01

多继承

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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/* Graph of inheritance
A
/ \
B C
/ \ /
F D,E

*/
contract A {
//需要被重写的函数加上virtual修饰符
function foo() public pure virtual returns(string memory) {
return "A";
}
}
contract B is A{
function foo() public pure override virtual returns (string memory) {
return "B";
}
}
contract C is A {
function foo() public pure override virtual returns (string memory) {
return "C";
}
}

// Contracts can inherit from multiple parent contracts.
// When a function is called that is defined multiple times in
// different contracts, parent contracts are searched from
// right to left, and in depth-first manner.
//合同可以多重继承
//在不同合约中当函数被定义为多重继承时,父合约会自右向左,按照深度优先搜索


contract D is B, C {
//多继承时候需要给override传入重写合约
function foo() public pure override(B, C) returns (string memory) {
return super.foo();//返回“C”
}
}
contract E is C,B {
function foo() public pure override(C, B) returns (string memory) {
return super.foo();//返回“B”
}
}
contract F is A, B {
//必须把父合同放前面(A是B的父合同)
//此时是孙子合同
//调换A跟B会发生编译错误
function foo() public pure override(B, A) returns (string memory) {
return super.foo();
}
}

不能够调转继承合同的父子顺序

隐藏父合同的状态变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pragma solidity ^0.8.17;
//SPDX-License-Identifier:MIT
contract A {
string public stt = "contract A";
function getName() view public returns(string memory){
return stt;
}
}

//错误写法
// contract B is A {
// string public stt = "contract B";
// }

//正确写法
contract C is A {
constructor() {
stt = "contract C";
}
}

继承的父函数调用

典型错误:没写明override的顺序

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
pragma solidity ^0.8.17;
//SPDX-License-Identifier:MIT
contract A {
event log(string mess);
function foo() public virtual {
emit log("A.foo() is called.");
}
function bar() public virtual {
emit log("A.bar() is called");
}
}
contract B is A{
function foo() override virtual public {
emit log("B.foo() is called");
super.foo();
}
function bar() override virtual public {
emit log("B.bar() is called");
A.bar();
}
}
contract C is A {
function foo() override virtual public {
emit log("C.foo() is called");
super.foo();
}
function bar() override virtual public {
emit log("C.bar() is called");
A.bar();
}
}
contract D is B, C {
function foo() override(B, C) public {
super.foo();//D.foo()->C.foo()->A.foo()错误理解一,错误理解二super会自右向左把自己的父类都调用一遍
//D.foo()->C.foo()->B.foo()->A.foo()
//super就是有父类的时候(指的是最开始调用的合约)会调用父类,没有的时候就调用其爷类
}
function bar() override(B, C) public {//就算override(C, B)也同样是D.bar()->C.bar()->A.bar()
super.bar();//D.bar()->C.bar()->A.bar()
}
}

思考:当为不影响传统意义上的父类,可以采用父类名称的方式去调用函数

接口的五个特点

  • cannot have any functions implemented
  • can inherit from other interfaces
  • all declared functions must be external
  • cannot declare a constructor
  • cannot declare state variables
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
//(例子貌似没太大意义)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Counter {
uint public count;

function increment() external {
count += 1;
}
}

interface ICounter {
function count() external view returns (uint);

function increment() external;
}

contract MyContract {
function incrementCounter(address _counter) external {
ICounter(_counter).increment();
}

function getCount(address _counter) external view returns (uint) {
return ICounter(_counter).count();
}
}

// Uniswap example
interface UniswapV2Factory {
function getPair(
address tokenA,
address tokenB
) external view returns (address pair);
}

interface UniswapV2Pair {
function getReserves()
external
view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

contract UniswapExample {
address private factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address private dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address private weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

function getTokenReserves() external view returns (uint, uint) {
address pair = UniswapV2Factory(factory).getPair(dai, weth);
(uint reserve0, uint reserve1, ) = UniswapV2Pair(pair).getReserves();
return (reserve0, reserve1);
}
}

接口的函数体实现可以通过一个普通的合约继承之后进行重写:

1
2
3
4
5
6
7
8
9
10
11
pragma solidity ^0.8.17;
//SPDX-License-Identifier:MIT
interface abs {
function beforeImpl() external pure returns(uint);

}
contract absImpl is abs {
function beforeImpl() override external pure returns(uint) {
return 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
24
25
26
27
28
29
30
pragma solidity ^0.8.17;
//SPDX-License-Identifier:MIT
contract pt {
address payable owner;//就是合约部署者(由constructor初始化)
//可以写一些事件,方便理解省略了
constructor() payable {
owner = payable(msg.sender);
}
function deposit() public payable onlyOwner{}
modifier onlyOwner {
require(owner == msg.sender);
_;
}
function withDraw(uint _amount) public payable onlyOwner {
owner.transfer(_amount);
//owner是目标账户
}
function transferEther(address payable _to, uint _amount) public payable onlyOwner {
_to.transfer(_amount);
}
function getBalance() public view returns(uint){
return address(this).balance;
}
function distributor(address receiver) public{
if(gasleft()<2300) {
return;
}
payable(receiver).transfer(1 ether);//不用payable也可以这样做
}
}

存的钱会放在合约里面,部署合约的账户就是owner,deposit是(owner->pt[contract])转账的函数,withDraw类似;
transfer参数列表中的address是另一个账户的地址,_amount是转账金额

发送以太币的三种方式

  • (payable)address.transfer()
  • (payable)address.send()
  • .call
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    pragma solidity ^0.8.17;
    //SPDX-License-Identifier:MIT
    contract Recevier {
    constructor() payable {
    }
    function getBalance() public view returns(uint) {
    return address(this).balance;
    }
    }
    contract Sender {
    function sendByTransfer(address payable _to) public payable {
    _to.transfer(msg.value);
    }
    function sendBySend(address payable _to) public payable {
    bool sent = _to.send(msg.value);
    require(sent,"ERROR");
    }
    function sendByCall(address payable _to) public payable {
    (bool success, ) = _to.call{value: msg.value}("");
    require(success,"failed");
    }
    }