ryan

ERC721合约标准

15 次阅读

ERC721 是一个以太坊智能合约标准,用来定义每个代币都是唯一的、不可互换的资产。这和常见的代币标准 ERC20(同质化代币) 相对,ERC20的每个 token 是可互换、可拆分的,类似法币.

ERC721核心特性

ERC721 是一个 NFT 标准,必须实现以下接口:

功能函数说明
查询余额balanceOf(address owner)获取某个地址拥有的 NFT 数量
查询拥有者ownerOf(uint256 tokenId)获取某个 token 的拥有者
授权转移approve(address to, uint256 tokenId)授权他人管理自己的某个 NFT
查看授权getApproved(uint256 tokenId)查看某个 token 的授权地址
批量授权setApprovalForAll(address operator, bool approved)批量授权管理所有 NFT
检查批量授权isApprovedForAll(address owner, address operator)检查是否授权了某人管理所有 NFT
转移 NFTtransferFrom(address from, address to, uint256 tokenId)转账(不校验接收者)
安全转账safeTransferFrom(…)转账 + 检查接收者是否能接收 ERC721

事件支持

标准中规定必须发出以下事件:


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 name() external view returns (string memory); 
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);


安全接收检查(必需)


function onERC721Received(...) external returns (bytes4);

防止 NFT 转账到不会处理的合约地址,避免被锁死。

https://docs.openzeppelin.com/contracts/5.x/wizard上新建一个ERC721合约,我们通过编写单元测试脚本来测试NFT标准接口.


describe("test MyNFT contract", function () {
  let myNftContract: any;
  let deployer: any;
  let user: any;
  let user2: any;
    beforeEach(async function () {
        await deployments.fixture(["MyNFT"]);
        const { firstAccount,secondAccount,thirdAccount } = await getNamedAccounts();
        deployer = firstAccount;
        user = secondAccount;
        user2 = thirdAccount;
        const deployment = await deployments.get("MyNFT");
        myNftContract = await ethers.getContractAt("MyNFT", deployment.address);
    })
})

查询余额 balanceOf(address owner)

   
 // 查询 某个账户 balanceOf
    it("should get the correct balanceOf", async function () {
       
      const uri = "ipfs://QmS4ghgMgfFvqPjB4WKXHaN15ZyT4K4JY";
      const tx = await myNftContract.safeMint(deployer, uri);
      await tx.wait();

      const uri1 = "ipfs://QmS4ghgMgfFvqPjB4WKXHaN15ZyT4K4JY";
      const tx1 = await myNftContract.safeMint(deployer, uri1);
      await tx1.wait();
      const balance = await myNftContract.balanceOf(deployer);
      expect(balance).to.equal(2);
    })

查询拥有者ownerOf(uint256 tokenId)

  
  it("should get the correct ownerOf", async function () {
        const uri = "ipfs://QmS4ghgMgfFvqPjB4WKXHaN15ZyT4K4JYZ";
        const tx = await myNftContract.safeMint(user, uri);
        await tx.wait();
        const owner = await myNftContract.ownerOf(0);
        await expect(owner).to.equal(user);
    })

授权转移 approve(address to, uint256 tokenId)


// approve
    it("should get the correct approve", async function () {
        const uri = "ipfs://QmS4ghgMgfFvqPjB4WKXHaN15ZyT4K4JYZ";
        await myNftContract.safeMint(deployer, uri);
        await myNftContract.approve(user, 0);
        const approved = await myNftContract.getApproved(0);
        await expect(approved).to.equal(user);
        // test user 转移nft
        const etherUser = await ethers.getSigner(user);
        const tx3 = await myNftContract.connect(etherUser).transferFrom(deployer, user2, 0);
        await tx3.wait();
        const owner = await myNftContract.ownerOf(0);
        await expect(owner).to.equal(user2);
    })

setApprovalForAll

批量授权管理所有 NFT

  
  // setApprovalForAll
    it('should setApprovalForAll', async function () {
        const uri = "ipfs://QmS4ghgMgfFvqPjB4WKXHaN15ZyT4K4J";
        // 铸造3个NFT
        await myNftContract.safeMint(deployer, uri);
        await myNftContract.safeMint(deployer, uri);
        await myNftContract.safeMint(deployer, uri);

        // 授权 user 管理他所有的NFT
        const tx = await myNftContract.setApprovalForAll(user, true);
        await tx.wait();
        // 验证授权关系
        const approved = await myNftContract.isApprovedForAll(deployer, user);
        await expect(approved).to.be.true;

        // user 转移 tokenId 0 给 user2
        const etherUser = await ethers.getSigner(user);
        const tx2 = await myNftContract.connect(etherUser).transferFrom(deployer, user2, 0);
        await tx2.wait();

        // user 转移tokenId 1 给 user2
        const tx3 = await myNftContract.connect(etherUser).transferFrom(deployer, user2, 1);
        await tx3.wait();
        // 验证转移结果
        const owner0 = await myNftContract.ownerOf(0);
        const owner1 = await myNftContract.ownerOf(1);
         const owner2 = await myNftContract.ownerOf(2);
        await expect(owner0).to.equal(user2);
        await expect(owner1).to.equal(user2);
        await expect(owner2).to.equal(deployer);
    })

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注