了解过 NFT 合约的同学应该注意到,每个 NFT 合约都实现了 supportsInterface 方法,但是每个合约中的具体方法实现又不尽相同,那么在实际开发过程中,究竟应该怎么实现这个方法呢,这篇文章就来讲讲这个内容
ERC165的具体内容这里就不再多说了,不了解的同学可以 Google 一下
首先我们需要知道计算一个接口的 interfaceId 都有几种方法。
假设对于如下接口:
interface IExample {
function foo() external view returns (uint);
function bar() external view returns (bool);
function baz() external view returns (bytes32);
}
那么可以这样计算它的 interfaceId
function calcInterfaceId1() public view returns (bytes4) {
return bytes4(keccak256("foo()"))
^ bytes4(keccak256("bar()"))
^ bytes4(keccak256("baz()"));
}
function calcInterfaceId2() public view returns (bytes4) {
IExample i;
return i.foo.selector ^ i.bar.selector ^ i.baz.selector;
}
function calcInterfaceId3() public view returns (bytes4) {
return type(IExample).interfaceId;
}
这三种方法都可以用来计算 interfaceId,结果相同,都是 0x9bb235aa。
接下来,我们看看实际开发中都是怎么实现 supportsInterface 方法的。
来看几个例子:
// 合约继承了 ERC721,实现了 IERC2981接口
contract InvisibleFriends is ERC721, IERC2981, Ownable, ReentrancyGuard {
}
// override 后面包括 ERC721 和 IERC2981 的最上层基类 IERC165
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, IERC165)
returns (bool)
{
// 需要显式声明 IERC2981
return
interfaceId == type(IERC2981).interfaceId ||
super.supportsInterface(interfaceId);
}
合约实现的基类中,ERC721 和 IERC2981 中都包括 IERC165,因此需要显式重写 supportsInterface 方法。override后面的括号需要包含 ERC721,由于 IERC2981 是接口,因此要包含 IERC2981 的最上层基类 IERC165,同时需要在方法实现中显示声明 IERC2981 接口本身。
// 合约继承了 ERC721,ERC721Enumerable
contract mfers is ERC721, ERC721Enumerable, Ownable {
}
// override 后面包括 ERC721 和 ERC721Enumerable
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721, ERC721Enumerable)
returns (bool)
{
// 无需显式声明接口
return super.supportsInterface(interfaceId);
}
合约实现的基类中,ERC721 和 ERC721Enumerable 中都包括 IERC165。因此需要显式重写 supportsInterface 方法。而由于两者均非接口,因此直接在 override 后面声明。在方法实现中无需显式声明。
// 合约继承了 ERC721
contract TastyBones is Ownable, ERC721A, ReentrancyGuard {
}
// 没有显式实现 supportsInterface 方法
合约实现的基类中,只有 ERC721A 一个合约包括 IERC165 接口,因此合约的最终实现无需显式实现 supportsInterface 方法。
这三个合约基本上代表了几种具体实现方式,总结一下:
如果合约继承的基类中只有一个合约包含 IERC165,即没有冲突,那么无需显式实现
如果基类中存在 IERC165 冲突,且均非接口,需要显式实现,override 之后需要包括这些基类名,方法实现中无需显式包括合约
如果基类中存在 IERC165 冲突,且存在接口,需要显式实现,override 之后需要包括合约以及接口的最上层基类 IERC165,方法实现中需要显式包括继承的接口
了解过 NFT 合约的同学应该注意到,每个 NFT 合约都实现了 supportsInterface 方法,但是每个合约中的具体方法实现又不尽相同,那么在实际开发过程中,究竟应该怎么实现这个方法呢,这篇文章就来讲讲这个内容
ERC165的具体内容这里就不再多说了,不了解的同学可以 Google 一下
首先我们需要知道计算一个接口的 interfaceId 都有几种方法。
假设对于如下接口:
interface IExample {
function foo() external view returns (uint);
function bar() external view returns (bool);
function baz() external view returns (bytes32);
}
那么可以这样计算它的 interfaceId
function calcInterfaceId1() public view returns (bytes4) {
return bytes4(keccak256("foo()"))
^ bytes4(keccak256("bar()"))
^ bytes4(keccak256("baz()"));
}
function calcInterfaceId2() public view returns (bytes4) {
IExample i;
return i.foo.selector ^ i.bar.selector ^ i.baz.selector;
}
function calcInterfaceId3() public view returns (bytes4) {
return type(IExample).interfaceId;
}
这三种方法都可以用来计算 interfaceId,结果相同,都是 0x9bb235aa。
接下来,我们看看实际开发中都是怎么实现 supportsInterface 方法的。
来看几个例子:
// 合约继承了 ERC721,实现了 IERC2981接口
contract InvisibleFriends is ERC721, IERC2981, Ownable, ReentrancyGuard {
}
// override 后面包括 ERC721 和 IERC2981 的最上层基类 IERC165
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, IERC165)
returns (bool)
{
// 需要显式声明 IERC2981
return
interfaceId == type(IERC2981).interfaceId ||
super.supportsInterface(interfaceId);
}
合约实现的基类中,ERC721 和 IERC2981 中都包括 IERC165,因此需要显式重写 supportsInterface 方法。override后面的括号需要包含 ERC721,由于 IERC2981 是接口,因此要包含 IERC2981 的最上层基类 IERC165,同时需要在方法实现中显示声明 IERC2981 接口本身。
// 合约继承了 ERC721,ERC721Enumerable
contract mfers is ERC721, ERC721Enumerable, Ownable {
}
// override 后面包括 ERC721 和 ERC721Enumerable
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721, ERC721Enumerable)
returns (bool)
{
// 无需显式声明接口
return super.supportsInterface(interfaceId);
}
合约实现的基类中,ERC721 和 ERC721Enumerable 中都包括 IERC165。因此需要显式重写 supportsInterface 方法。而由于两者均非接口,因此直接在 override 后面声明。在方法实现中无需显式声明。
// 合约继承了 ERC721
contract TastyBones is Ownable, ERC721A, ReentrancyGuard {
}
// 没有显式实现 supportsInterface 方法
合约实现的基类中,只有 ERC721A 一个合约包括 IERC165 接口,因此合约的最终实现无需显式实现 supportsInterface 方法。
这三个合约基本上代表了几种具体实现方式,总结一下:
如果合约继承的基类中只有一个合约包含 IERC165,即没有冲突,那么无需显式实现
如果基类中存在 IERC165 冲突,且均非接口,需要显式实现,override 之后需要包括这些基类名,方法实现中无需显式包括合约
如果基类中存在 IERC165 冲突,且存在接口,需要显式实现,override 之后需要包括合约以及接口的最上层基类 IERC165,方法实现中需要显式包括继承的接口
Smart Contract Developer
Smart Contract Developer

Subscribe to xyyme.eth

Subscribe to xyyme.eth
<100 subscribers
<100 subscribers
EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
CREATE2 操作码使用方法详解
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。 那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:endowment(创建合约时往合约中打的 ETH 数量)memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )memory_length(代码长度,一般固定为 mload(bytecode) )salt(随机数盐)这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:bytes32 salt = keccak256(abi.encodePacked(token0, token1)); create2 还有一个优点,相...
EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
CREATE2 操作码使用方法详解
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。 那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:endowment(创建合约时往合约中打的 ETH 数量)memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )memory_length(代码长度,一般固定为 mload(bytecode) )salt(随机数盐)这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:bytes32 salt = keccak256(abi.encodePacked(token0, token1)); create2 还有一个优点,相...
Share Dialog
Share Dialog
No activity yet