接口(interface)
我们知道在Java
里接口
是特殊的抽象类
,限制多于抽象类
,但随着Java
版本的更新,Java
中的接口
是越来越趋于抽象类
了(这样说,可能有点不妥,因为接口
本就是特殊的抽象类
,只是接口
中不能有方法具体实现,而抽象类
还是可以有的!然而随着Java
版本的推移,Java 接口
中可以有方法实现了 )。
虽然在 Solidity 中的接口
跟抽象合约
很类似,但接口
却不能像抽象合约
那样,可以有方法体
函数实现。
接口基本上仅限于合约 ABI 可以表示的内容,并且 ABI 和接口之间的转换不应该丢失任何信息。
Solidity 中的接口
是不能有函数实现
的,且还有以下节点约束:
- 1、不能继承除了接口外的其他合约
- 2、接口中的所有
函数签名
都必须是external
。 - 3、无法声明构造函数。
- 4、不能定义
状态变量
。 - 5、继承接口的合约必须实现接口中声明的函数功能。
就目前来看,接口
有这些限制,但在不久的将来,有可能解除某些限制(本次使用的是Solidity ^0.8.0
)。
使用关键字interface
来声明一个接口。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
interface MyToken {
enum TokenType { Fungible, NonFungible }
struct Coin { string obverse; string reverse; }
function transfer(address recipient, uint amount) external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
interface MyToken {
enum TokenType { Fungible, NonFungible }
struct Coin { string obverse; string reverse; }
//接口中所有 函数签名 隐式标记为 virtual
function transfer(address recipient, uint amount) external;
}
interface MyCoin {
function transfer(address recipient, uint amount) external;
}
//合约实现多接口
contract MyTest is MyToken,MyCoin {
function transfer(address recipient, uint amount) override(MyToken,MyCoin) external{
}
}
//接口继承多接口
interface TestI is MyToken,MyCoin {
function transfer(address recipient, uint amount) override(MyToken,MyCoin) external;
}
虽然接口
有这么多的限制,不能像抽象合约
的那样可以有实现体
,但接口
在 Solidity 中却很重要。它定义了合约的骨架,如合约实现了某些接口(ERC20 或 ERC721),其他Dapp
就可以与我们编写的智能合约进行交互。
我们以IERC721
为例,它定义了3个后续将会讲到的事件(event)
和9个函数(function)
,所有ERC721
标准的NFT都实现了这些函数。
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
IERC721事件
IERC721
包含3个事件,其中Transfer
和Approval
事件在ERC20
中也有。
Transfer
事件:在转账时被释放,记录代币的发出地址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
。
使用接口
假使我们知道一个合约实现了ERC721
接口的话,我们不必去关心它的具体实现功能,却可以与它进行交互。
不知道您有没有玩过加密猫(CryptoKitty )
,这是一款风靡全球的区块链游戏()链游)
,而它便是实现了ERC721
接口。
我可以使用IERC721
接口来查看加密猫(CryptoKitty)
中的余额。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
//导入 IERC721 接口
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract ShowKittyAmount {
// 利用 CryptoKitty 合约地址创建接口合约变量(ETH主网)
IERC721 kitty = IERC721(0xba52c75764d6F594735dc735Be7F1830CDf58dDf);
// 通过接口调用 CryptoKitty 的balanceOf()查询持仓量
function balanceOfKitty(address owner) external view returns (uint256 balance){
return kitty.balanceOf(owner);
}
// 通过接口调用 CryptoKitty 的safeTransferFrom()安全转账
function safeTransferFromKitty(address from, address to, uint256 tokenId) external{
kitty.safeTransferFrom(from, to, tokenId);
}
}
不过在部署时,要选择以太坊主网
,否则查询不出结果!