您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# ERC721藏品合约怎么实现
## 目录
1. [ERC721标准概述](#1-erc721标准概述)
2. [开发环境准备](#2-开发环境准备)
3. [基础合约实现](#3-基础合约实现)
4. [元数据扩展实现](#4-元数据扩展实现)
5. [枚举功能实现](#5-枚举功能实现)
6. [高级功能扩展](#6-高级功能扩展)
7. [安全注意事项](#7-安全注意事项)
8. [部署与测试](#8-部署与测试)
9. [最佳实践建议](#9-最佳实践建议)
10. [总结与展望](#10-总结与展望)
## 1. ERC721标准概述
### 1.1 什么是ERC721
ERC721是以太坊上用于非同质化代币(NFT)的标准接口,由William Entriken等人于2018年提出。与ERC20代币不同,每个ERC721代币都是独一无二的,具有独特性。
### 1.2 核心功能要求
- 所有权追踪:记录每个代币的所有者
- 转账功能:`safeTransferFrom`和`transferFrom`
- 授权管理:`approve`和`setApprovalForAll`
- 余额查询:`balanceOf`
- 所有者查询:`ownerOf`
### 1.3 可选扩展
- 元数据扩展(ERC721Metadata)
- 枚举扩展(ERC721Enumerable)
- 接收安全扩展(ERC721TokenReceiver)
## 2. 开发环境准备
### 2.1 工具链安装
```bash
npm install -g truffle
npm install @openzeppelin/contracts
npm install @truffle/hdwallet-provider
mkdir nft-project && cd nft-project
truffle init
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract BasicNFT is ERC721 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("BasicNFT", "BNFT") {}
function mint(address to) public returns (uint256) {
_tokenIds.increment();
uint256 newTokenId = _tokenIds.current();
_mint(to, newTokenId);
return newTokenId;
}
}
_mint()
: 内部铸造函数_burn()
: 内部销毁函数_safeTransfer()
: 带安全检查的转账_exists()
: 检查代币是否存在function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
contract MetadataNFT is ERC721 {
string private _baseTokenURI;
constructor(string memory baseURI) ERC721("MetadataNFT", "MNFT") {
_baseTokenURI = baseURI;
}
function _baseURI() internal view virtual override returns (string memory) {
return _baseTokenURI;
}
}
function setBaseURI(string memory baseURI) public onlyOwner {
_baseTokenURI = baseURI;
}
// 示例URI: "ipfs://QmXo...Xo/"
mapping(uint256 => string) private _tokenURIs;
function setTokenURI(uint256 tokenId, string memory _tokenURI) public {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
return bytes(_tokenURIs[tokenId]).length > 0
? _tokenURIs[tokenId]
: super.tokenURI(tokenId);
}
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
contract EnumerableNFT is ERC721Enumerable {
constructor() ERC721("EnumerableNFT", "ENFT") {}
function mint(address to) public returns (uint256) {
uint256 tokenId = totalSupply() + 1;
_mint(to, tokenId);
return tokenId;
}
}
totalSupply()
: 返回已铸造总量tokenOfOwnerByIndex()
: 分页查询用户代币tokenByIndex()
: 全局代币枚举function getTokensByOwner(address owner, uint256 page, uint256 pageSize)
public
view
returns (uint256[] memory)
{
uint256 balance = balanceOf(owner);
uint256 start = page * pageSize;
require(start < balance, "Invalid page");
uint256 end = (start + pageSize) > balance
? balance
: start + pageSize;
uint256[] memory tokens = new uint256[](end - start);
for (uint256 i = start; i < end; i++) {
tokens[i - start] = tokenOfOwnerByIndex(owner, i);
}
return tokens;
}
interface IERC2981 {
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
}
contract RoyaltyNFT is ERC721, IERC2981 {
uint256 private _royaltyBasisPoints = 1000; // 10%
function royaltyInfo(uint256, uint256 salePrice)
external
view
override
returns (address, uint256)
{
return (owner(), (salePrice * _royaltyBasisPoints) / 10000);
}
}
function batchMint(address[] memory recipients) public {
for (uint256 i = 0; i < recipients.length; i++) {
mint(recipients[i]);
}
}
function batchTransfer(
address from,
address to,
uint256[] memory tokenIds
) public {
for (uint256 i = 0; i < tokenIds.length; i++) {
transferFrom(from, to, tokenIds[i]);
}
}
function permitMint(
address to,
uint256 tokenId,
bytes memory signature,
uint256 deadline
) external {
require(block.timestamp <= deadline, "Signature expired");
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMN_SEPARATOR(),
keccak256(
abi.encode(
keccak256("PermitMint(address to,uint256 tokenId,uint256 deadline)"),
to,
tokenId,
deadline
)
)
)
);
address signer = ECDSA.recover(digest, signature);
require(hasRole(MINTER_ROLE, signer), "Invalid signature");
_mint(to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_safeTransfer(from, to, tokenId, _data);
}
function setApprovalForAll(address operator, bool approved)
public
virtual
override
{
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
const NFT = artifacts.require("AdvancedNFT");
module.exports = async function(deployer, network) {
const baseURI = network === 'mainnet'
? "ipfs://QmXo...Xo/"
: "https://api.testnet.example/tokens/";
await deployer.deploy(NFT, baseURI);
};
const { expect } = require('chai');
describe("NFT Contract", function() {
it("Should mint a token with correct owner", async function() {
const [owner, addr1] = await ethers.getSigners();
const NFT = await ethers.getContractFactory("MyNFT");
const nft = await NFT.deploy();
await nft.mint(addr1.address);
expect(await nft.ownerOf(1)).to.equal(addr1.address);
});
});
storage
指针减少SLOAD操作memory
和calldata
interface IGovernance {
function isApprovedMinter(address account) external view returns (bool);
}
contract GovernedNFT is ERC721 {
IGovernance public governance;
modifier onlyApproved() {
require(governance.isApprovedMinter(msg.sender), "Not approved");
_;
}
function mint(address to) public onlyApproved {
// mint logic
}
}
字数统计:约5550字 最后更新:2023年10月 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。