白天搞智能合约,晚上撸前端代码,只要咖啡还续着,凌晨照样在线!会说 Solidity、PHP、Node.js,还有 Vue 和 HTML 的“方言”,代码不怕我不写,就怕写完跑太快。Bug?那都是小场面,一出手就搞定。我的宗旨是——交付准时,调试无敌,客户满意才是真理!
12. 识别ERC721合约
我们介绍如何用ether.js识别一个合约是否为ERC721标准。
ERC721
ERC721是以太坊上流行的非同质化代币(NFT)标准。在做NFT相关产品时,我们需要筛选出符合ERC721标准的合约。例如Opensea,他会自动识别ERC721,并爬下它的名称、代号、metadata等数据用于展示。要识别ERC721,我们先要理解ERC165。
ERC165
通过ERC165标准,智能合约可以声明它支持的接口,供其他合约检查。因此,我们可以通过ERC165来检查一个智能合约是不是支持了ERC721的接口。
IERC165接口合约只声明了一个supportsInterface函数,输入要查询的interfaceId接口id(类型为bytes4),若合约实现了该接口id,则返回true;反之,则返回false:
interface IERC165 {
/**
* @dev 如果合约实现了查询的`interfaceId`,则返回true
* 规则详见:https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
*
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
ERC721合约中会实现IERC165接口合约的supportsInterface函数,并且当查询0x80ac58cd(ERC721接口id)时返回true。
function supportsInterface(bytes4 interfaceId)
external
pure
override
returns (bool)
{
return
interfaceId == type(IERC721).interfaceId
}
识别ERC721
创建
provider,连接以太坊主网。const ALCHEMY_MAINNET_URL = 'https://eth-mainnet.g.alchemy.com/v2/oKmOQKbneVkxgHZfibs-iFhIlIAl6HDN'; const provider = new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);创建
ERC721合约实例,在abi接口中,我们声明要使用的name(),symbol(),和supportsInterface()函数即可。这里我们用的BAYC的合约地址。// 合约abi const abiERC721 = [ "function name() view returns (string)", "function symbol() view returns (string)", "function supportsInterface(bytes4) public view returns(bool)", ]; // ERC721的合约地址,这里用的BAYC const addressBAYC = "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d" // 创建ERC721合约实例 const contractERC721 = new ethers.Contract(addressBAYC, abiERC721, provider)读取合约的链上信息:名称和代号。
// 1. 读取ERC721合约的链上信息 const nameERC721 = await contractERC721.name() const symbolERC721 = await contractERC721.symbol() console.log("\n1. 读取ERC721合约信息") console.log(`合约地址: ${addressBAYC}`) console.log(`名称: ${nameERC721}`) console.log(`代号: ${symbolERC721}`)
利用
ERC165的supportsInterface()函数,识别合约是否为ERC721标准。如果是,则返回true;反之,则报错或返回false。注意此处的代码中的
selectorERC721常量被提取出main函数// 2. 利用ERC165的supportsInterface,确定合约是否为ERC721标准 // ERC721接口的ERC165 identifier const selectorERC721 = "0x80ac58cd" const isERC721 = await contractERC721.supportsInterface(selectorERC721) console.log("\n2. 利用ERC165的supportsInterface,确定合约是否为ERC721标准") console.log(`合约是否为ERC721标准: ${isERC721}`)
完整代码
import { ethers } from "ethers";
const ALCHEMY_MAINNET_URL = 'https://eth-mainnet.g.alchemy.com/v2/oKmOQKbneVkxgHZfibs-iFhIlIAl6HDN';
const provider = new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
// 合约abi
const abiERC721 = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function supportsInterface(bytes4) public view returns(bool)",
];
// ERC721的合约地址,这里用的BAYC
const addressBAYC = "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"
// 创建ERC721合约实例
const contractERC721 = new ethers.Contract(addressBAYC, abiERC721, provider)
// ERC721接口的ERC165 identifier
const selectorERC721 = "0x80ac58cd"
const main = async () => {
try {
// 1. 读取ERC721合约的链上信息
const nameERC721 = await contractERC721.name()
const symbolERC721 = await contractERC721.symbol()
console.log("\n1. 读取ERC721合约信息")
console.log(`合约地址: ${addressBAYC}`)
console.log(`名称: ${nameERC721}`)
console.log(`代号: ${symbolERC721}`)
// 2. 利用ERC165的supportsInterface,确定合约是否为ERC721标准
const isERC721 = await contractERC721.supportsInterface(selectorERC721)
console.log("\n2. 利用ERC165的supportsInterface,确定合约是否为ERC721标准")
console.log(`合约是否为ERC721标准: ${isERC721}`)
}catch (e) {
// 如果不是ERC721,则会报错
console.log(e);
}
}
main()
