Web3实战:打造属于你的NFT数字资产Web3时代,NFT(非同质化代币)正重塑数字所有权的未来。无论是独一无二的艺术品还是虚拟资产,ERC721标准让你轻松实现NFT的创建与管理。本文通过一个完整的实战案例,带你深入Solidity智能合约开发,快速部署属于你的NFT代币,解锁Web3开发的无
Web3时代,NFT(非同质化代币)正重塑数字所有权的未来。无论是独一无二的艺术品还是虚拟资产,ERC721标准让你轻松实现NFT的创建与管理。本文通过一个完整的实战案例,带你深入Solidity智能合约开发,快速部署属于你的NFT代币,解锁Web3开发的无限可能。准备好加入这场数字资产革命了吗?
本文通过一个基于OpenZeppelin的ERC721智能合约MyERC721Token,展示了NFT开发的完整流程,包括合约编写、部署脚本、Sepolia测试网部署及验证。合约支持URI存储、可销毁、投票和EIP712签名等功能,适合Web3开发者快速上手。文章还涵盖部署问题优化与实用建议,为打造个性化NFT项目提供清晰指引。
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Votes.sol";
contract MyERC721Token is ERC721, ERC721URIStorage, ERC721Burnable, Ownable, EIP712, ERC721Votes {
uint256 private _nextTokenId;
constructor(address initialOwner)
ERC721("MyERC721Token", "MTK721")
Ownable(initialOwner)
EIP712("MyERC721Token", "1")
{}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
// The following functions are overrides required by Solidity.
function _update(address to, uint256 tokenId, address auth)
internal
override(ERC721, ERC721Votes)
returns (address)
{
return super._update(to, tokenId, auth);
}
function _increaseBalance(address account, uint128 value) internal override(ERC721, ERC721Votes) {
super._increaseBalance(account, value);
}
function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) {
return super.supportsInterface(interfaceId);
}
}
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Votes.sol";
这些行导入了OpenZeppelin的标准化、经过审计的合约库,为NFT合约扩展功能:
DAO
治理)。contract MyERC721Token is ERC721, ERC721URIStorage, ERC721Burnable, Ownable, EIP712, ERC721Votes {
uint256 private _nextTokenId;
constructor(address initialOwner)
ERC721("MyERC721Token", "MTK721")
Ownable(initialOwner)
EIP712("MyERC721Token", "1")
{}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
以下函数是Solidity要求的重写,用于解决多重继承中的冲突,确保功能正确实现:
7.1 更新函数
function _update(address to, uint256 tokenId, address auth)
internal
override(ERC721, ERC721Votes)
returns (address)
{
return super._update(to, tokenId, auth);
}
7.2 余额更新函数
function _increaseBalance(address account, uint128 value) internal override(ERC721, ERC721Votes) {
super._increaseBalance(account, value);
}
ERC721
和 ERC721Votes
。7.3 元数据查询函数
function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}
7.4 接口支持函数
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) {
return super.supportsInterface(interfaceId);
}
ERC721URIStorage
,确保正确报告支持的接口。supportsInterface
方法,返回是否支持指定接口。ERC721URIStorage
存储和查询NFT元数据。ERC721Burnable
支持销毁功能。Ownable
限制关键操作。ERC721Votes
允许NFT用于投票。EIP712
启用结构化数据签名。OpenZeppelin
的模块化库,代码安全且易于扩展。ERC721Votes
参与DAO投票。MyERC721Token 是一个功能全面的ERC721 NFT智能合约,集成了铸造、销毁、元数据管理、治理投票和签名验证等功能。通过OpenZeppelin的标准化库,代码安全可靠,适合Web3开发者快速构建NFT项目。理解其结构和逻辑,有助于深入掌握Solidity开发和Web3生态的NFT应用。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
import {MyERC721Token} from "../src/MyERC721Token.sol";
contract MyERC721TokenScript is Script {
MyERC721Token public my721token;
function setUp() public {}
function run() public {
vm.startBroadcast();
my721token = new MyERC721Token(msg.sender);
console.log("MyERC721Token deployed to:", address(my721token));
vm.stopBroadcast();
}
}
NFTMarketHub on main [!?] via ⬢ v22.1.0 via 🅒 base took 1m 18.6s
➜ source .env
NFTMarketHub on main [!?] via ⬢ v22.1.0 via 🅒 base
➜ forge script --chain sepolia script/MyERC721Token.s.sol:MyERC721TokenScript --rpc-url $SEPOLIA_RPC_URL --broadcast --account MetaMask --verify -vvvv
[⠊] Compiling...
[⠔] Compiling 1 files with Solc 0.8.20
[⠒] Solc 0.8.20 finished in 1.44s
Compiler run successful!
Enter keystore password:
Traces:
[2119248] MyERC721TokenScript::run()
├─ [0] VM::startBroadcast()
│ └─ ← [Return]
├─ [2072840] → new MyERC721Token@0xC39B0eE94143C457449e16829837FD59d722933C
│ ├─ emit OwnershipTransferred(previousOwner: 0x0000000000000000000000000000000000000000, newOwner: DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38])
│ └─ ← [Return] 10002 bytes of code
├─ [0] console::log("MyERC721Token deployed to:", MyERC721Token: [0xC39B0eE94143C457449e16829837FD59d722933C]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::stopBroadcast()
│ └─ ← [Return]
└─ ← [Stop]
Script ran successfully.
== Logs ==
MyERC721Token deployed to: 0xC39B0eE94143C457449e16829837FD59d722933C
## Setting up 1 EVM.
==========================
Simulated On-chain Traces:
[2072840] → new MyERC721Token@0xC39B0eE94143C457449e16829837FD59d722933C
├─ emit OwnershipTransferred(previousOwner: 0x0000000000000000000000000000000000000000, newOwner: DefaultSender: [0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38])
└─ ← [Return] 10002 bytes of code
==========================
Chain 11155111
Estimated gas price: 10.85383427 gwei
Estimated total gas used for script: 2990587
Estimated amount required: 0.03245933566801649 ETH
==========================
##### sepolia
✅ [Success]Hash: 0x8e5b0e3a9df4e5231b88d28af9c0e6e903bf7afac027a2ee54bf5faaf67b40c0
Contract Address: 0xC39B0eE94143C457449e16829837FD59d722933C
Block: 6326900
Paid: 0.012441733790006772 ETH (2301162 gas * 5.406717906 gwei)
✅ Sequence #1 on sepolia | Total Paid: 0.012441733790006772 ETH (2301162 gas * avg 5.406717906 gwei)
==========================
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
##
Start verification for (1) contracts
Start verifying contract `0xC39B0eE94143C457449e16829837FD59d722933C` deployed on sepolia
Submitting verification for [src/MyERC721Token.sol:MyERC721Token] 0xC39B0eE94143C457449e16829837FD59d722933C.
Submitting verification for [src/MyERC721Token.sol:MyERC721Token] 0xC39B0eE94143C457449e16829837FD59d722933C.
Submitting verification for [src/MyERC721Token.sol:MyERC721Token] 0xC39B0eE94143C457449e16829837FD59d722933C.
Submitted contract for verification:
Response: `OK`
GUID: `q1v8v6kswcqvnzfdifksth4hdk1ss7ukejzxxfuktumivdrr5e`
URL: https://sepolia.etherscan.io/address/0xc39b0ee94143c457449e16829837fd59d722933c
Contract verification status:
Response: `NOTOK`
Details: `Pending in queue`
Contract verification status:
Response: `OK`
Details: `Pass - Verified`
Contract successfully verified
All (1) contracts were verified!
Transactions saved to: /Users/qiaopengjun/Code/solidity-code/NFTMarketHub/broadcast/MyERC721Token.s.sol/11155111/run-latest.json
Sensitive values saved to: /Users/qiaopengjun/Code/solidity-code/NFTMarketHub/cache/MyERC721Token.s.sol/11155111/run-latest.json
<https://sepolia.etherscan.io/address/0xc39b0ee94143c457449e16829837fd59d722933c>
NFTMarketHub on main [!?] via ⬢ v22.1.0 via 🅒 base
➜ source .env
NFTMarketHub on main [!?] via ⬢ v22.1.0 via 🅒 base
➜ forge script --chain sepolia MyERC721TokenScript --rpc-url $SEPOLIA_RPC_URL --broadcast --verify -vvvv
[⠊] Compiling...
[⠔] Compiling 1 files with Solc 0.8.20
[⠒] Solc 0.8.20 finished in 1.46s
Compiler run successful!
Traces:
[2120084] MyERC721TokenScript::run()
├─ [0] VM::envUint("PRIVATE_KEY") [staticcall]
│ └─ ← [Return] <env var value>
├─ [0] VM::envAddress("ACCOUNT_ADDRESS") [staticcall]
│ └─ ← [Return] <env var value>
├─ [0] VM::startBroadcast(<pk>)
│ └─ ← [Return]
├─ [2072840] → new MyERC721Token@0x7eA36391c7127A7f40E5c23212A8016d6E494546
│ ├─ emit OwnershipTransferred(previousOwner: 0x0000000000000000000000000000000000000000, newOwner: 0x750Ea21c1e98CcED0d4557196B6f4a5974CCB6f5)
│ └─ ← [Return] 10002 bytes of code
├─ [0] console::log("MyERC721Token deployed to:", MyERC721Token: [0x7eA36391c7127A7f40E5c23212A8016d6E494546]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::stopBroadcast()
│ └─ ← [Return]
└─ ← [Stop]
Script ran successfully.
== Logs ==
MyERC721Token deployed to: 0x7eA36391c7127A7f40E5c23212A8016d6E494546
## Setting up 1 EVM.
==========================
Simulated On-chain Traces:
[2072840] → new MyERC721Token@0x7eA36391c7127A7f40E5c23212A8016d6E494546
├─ emit OwnershipTransferred(previousOwner: 0x0000000000000000000000000000000000000000, newOwner: 0x750Ea21c1e98CcED0d4557196B6f4a5974CCB6f5)
└─ ← [Return] 10002 bytes of code
==========================
Chain 11155111
Estimated gas price: 53.318074191 gwei
Estimated total gas used for script: 2990587
Estimated amount required: 0.159452339540640117 ETH
==========================
##### sepolia
✅ [Success]Hash: 0x77190a6bbe59f4b98dc06b1219ca34fcf5cc1ace40f6998bb26568fcb93e5380
Contract Address: 0x7eA36391c7127A7f40E5c23212A8016d6E494546
Block: 6355525
Paid: 0.057633696474121704 ETH (2301162 gas * 25.045475492 gwei)
✅ Sequence #1 on sepolia | Total Paid: 0.057633696474121704 ETH (2301162 gas * avg 25.045475492 gwei)
==========================
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
##
Start verification for (1) contracts
Start verifying contract `0x7eA36391c7127A7f40E5c23212A8016d6E494546` deployed on sepolia
Submitting verification for [src/MyERC721Token.sol:MyERC721Token] 0x7eA36391c7127A7f40E5c23212A8016d6E494546.
Submitting verification for [src/MyERC721Token.sol:MyERC721Token] 0x7eA36391c7127A7f40E5c23212A8016d6E494546.
Submitting verification for [src/MyERC721Token.sol:MyERC721Token] 0x7eA36391c7127A7f40E5c23212A8016d6E494546.
Submitted contract for verification:
Response: `OK`
GUID: `bgjivlpsttkmre2wq8etbj9gbv6txntfhwwe3rnh3zzduq12pm`
URL: https://sepolia.etherscan.io/address/0x7ea36391c7127a7f40e5c23212a8016d6e494546
Contract verification status:
Response: `NOTOK`
Details: `Pending in queue`
Contract verification status:
Response: `OK`
Details: `Pass - Verified`
Contract successfully verified
All (1) contracts were verified!
Transactions saved to: /Users/qiaopengjun/Code/solidity-code/NFTMarketHub/broadcast/MyERC721Token.s.sol/11155111/run-latest.json
Sensitive values saved to: /Users/qiaopengjun/Code/solidity-code/NFTMarketHub/cache/MyERC721Token.s.sol/11155111/run-latest.json
NFTMarketHub on main [!?] via ⬢ v22.1.0 via 🅒 base took 1m 27.4s
➜
<https://sepolia.etherscan.io/address/0x7ea36391c7127a7f40e5c23212a8016d6e494546#code>
通过本次实战,我们成功开发并部署了一个功能强大的ERC721 NFT合约MyERC721Token,在Sepolia测试网上实现数字资产的创建与管理。结合OpenZeppelin的标准化工具和Foundry的部署流程,Web3开发者可以高效构建安全可靠的NFT项目。未来,你可以扩展合约功能,接入NFT市场或探索跨链应用,在Web3的浪潮中打造属于自己的数字资产帝国。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!