11.1 抽象合约
抽象合约的形式为:
abstract contract A {
function add(uint _a, uint _b) public pure virtual returns (uint);
}
- 抽象合约在声明时需要在前面加上关键字
abstract
。 - 如果合约中最少存在一个未实现函数,则合约必须声明为抽象合约,否则编译不通过。
- 为实现的函数必须加上
virtual
关键字,以便子合约重写,否则编译不通过。 - 未实现函数以
;
替换代码块。 - 继承抽象合约的子合约,如果没有全部实现父合约中所有未实现方法,则必须也声明为抽象合约,否则编译不通过:
abstract contract B is A {}
- 子合约实现了所有未实现函数,则无须声明为抽象合约:
contract C is A {
function add(uint _a, uint _b) public pure override virtual returns (uint) {
return _a + _b;
}
11.2 接口
接口类似于抽象合约,但它不实现任何功能。
11.2.1 定义一个接口
一个计算器,包含加和减两个未实现函数:
interface Calculator {
function add(uint _x, uint _y) external returns (uint);
function sub(uint _x, uint _y) external returns (uint);
}
说明:
- 接口定义类似于合约,只不过使用
interface
作为关键词。 - 接口中的所有函数都必须为未实现函数。
- 接口中不能包含状态变量。
- 接口中的未实现函数必须为
external
。
11.2.2 接口的继承
继承Calculator接口,添加两个事件。
interface CalculatorEvent is Calculator {
event Add(uint x, uint y, uint z);
event Sub(uint x, uint y, uint z);
}
11.2.3 合约实现接口
如果未完全实现接口中所有的未实现函数,则合约必须声明为抽象合约:
abstract contract A is CalculatorEvent {
function add(uint _x, uint _y) external returns (uint) {
uint r = _x + _y;
emit Add(_x, _y, r);
return r;
}
}
实现了所有的未实现函数,则无须添加abstract
关键字:
contract C is A{
function sub(uint _x, uint _y) external returns (uint) {
uint r = _x - _y;
emit Add(_x, _y, r);
return r;
}
}
11.2.4 接口的使用
如果我们知道一个合约实现了CalculatorEvent接口,我们不需要知道它具体代码实现,就可以与它交互。例如:
contract D {
CalculatorEvent sss = CalculatorEvent(0xAb0b06f11dFC8FD18ef6b8eC4A8dEA026A136506);
function add(uint _x, uint _y) public returns (uint) {
return sss.add(_x, _y);
}
}
11.2.5 接口的重要性
虽然接口不实现任何功能,但它非常重要。接口是智能合约的骨架,定义了合约的功能以及如何触发它们:如果智能合约实现了某种接口(比如ERC20
或ERC721
),其他Dapps和智能合约就知道如何与它交互。因为接口提供了两个重要的信息:
-
合约里每个函数的
bytes4
选择器,以及函数签名函数名(每个参数类型)
。 -
接口id(更多信息见EIP165)
另外,接口与合约ABI
(Application Binary Interface)等价,可以相互转换:编译接口可以得到合约的ABI
,利用abi-to-sol工具也可以将ABI json
文件转换为接口sol
文件。
网友评论