原文:《Solidity 极简入门: 14.抽象合约和接口》

我最近在重新学 solidity,巩固一下细节,也写一个「Solidity 极简入门」,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。

所有代码和教程开源在 github: github.com/AmazingAng/WTFSolidity

这一讲,我们用 ERC721 的接口合约为例介绍 solidity 中的抽象合约(abstract)和接口(interface),帮助大家更好的理解 ERC721 标准。

抽象合约

如果一个智能合约里至少有一个未实现的函数,即某个函数缺少 {} 中的内容,则必须将该合约标为 abstract,不然编译会报错;另外,未实现的函数需要加 virtual,以便子合约重写。拿我们之前的插入排序合约为例,如果我们还没想好具体怎么实现插入排序函数,那么可以把合约标为 abstract,之后让别人补写上。

Solidity极简入门第十四讲:抽象合约和接口

接口

接口类似于抽象合约,但它不实现任何功能。接口的规则:

- 不能包含状态变量

- 不能包含构造函数

- 不能继承除接口外的其他合约

- 所有函数都必须是 external 且不能有函数体

- 继承接口的合约必须实现接口定义的所有功能

虽然接口不实现任何功能,但它非常重要。接口是智能合约的骨架,定义了合约的功能以及如何触发它们:如果智能合约实现了某种接口(比如 ERC20 或 ERC721),其他 Dapps 和智能合约就知道如何与它交互。因为接口提供了两个重要的信息:

- 合约里每个函数的 bytes4 选择器,以及基于它们的函数签名函数名 (每个参数类型)。

- 接口 id(更多信息见EIP165)

另外,接口与合约 ABI(Application Binary Interface)等价,可以相互转换:编译接口可以得到合约的 ABI,利用abi-to-sol 工具也可以将 ABI json 文件转换为接口 sol 文件。

我们以 ERC721 接口合约 IERC721 为例,它定义了 3 个 event 和 9 个 function,所有 ERC721 标准的 NFT 都实现了这些函数。我们可以看到,接口和常规合约的区别在于每个函数都以;代替函数体 { } 结尾。

Solidity极简入门第十四讲:抽象合约和接口

IERC721 事件

IERC721 包含 3 个事件,其中 Transfer 和 Approval 事件在 ERC20 中也有。

- Transfer 事件:在转账时被释放,记录Token的发出地址 from,接收地址 to 和 tokenid。

- Approval 事件:在授权时释放,记录授权地址 owner,被授权地址 approved 和 tokenid`。

- ApprovalForAll 事件:在批量授权时释放,记录批量授权的发出地址 owner,被授权地址 operator 和授权与否的 approved。

IERC721 函数

balanceOf:返回某地址的 NFT 持有量 balance。

ownerOf:返回某 tokenId 的主人 owner。

transferFrom:普通转账,参数为转出地址 from,接收地址 to 和 tokenId。

safeTransferFrom:安全转账(如果接收方是合约地址,会要求实现 ERC721Receiver 接口)。参数为转出地址 from,接收地址 to 和 tokenId。

approve:授权另一个地址使用你的 NFT。参数为被授权地址 approve 和 tokenId。

getApproved:查询 tokenId 被批准给了哪个地址。

setApprovalForAll:将自己持有的该系列 NFT 批量授权给某个地址 operator。

isApprovedForAll:查询某地址的 NFT 是否批量授权给了另一个 operator 地址。

safeTransferFrom:安全转账的重载函数,参数里面包含了 data。

什么时候使用接口?

如果我们知道一个合约实现了 IERC721 接口,我们不需要知道它具体代码实现,就可以与它交互。

无聊猿 BAYC 属于 ERC721 Token,实现了 IERC721 接口的功能。我们不需要知道它的源代码,只需知道它的合约地址,用 IERC721 接口就可以与它交互,比如用 balanceOf() 来查询某个地址的 BAYC 余额,用 safeTransferFrom() 来转账 BAYC。

Solidity极简入门第十四讲:抽象合约和接口

总结

这一讲,我介绍了`solidity`中的抽象合约(`abstract`)和接口(`interface`),他们都可以写模版并且减少代码冗余。我们还讲了`ERC721`接口合约`IERC721`,以及如何利用它与无聊猿`BAYC`合约进行交互。