智能合约开发代币标准(ERC-20/721/1155)
基础2-3周
代币标准(ERC-20/721/1155)
深入理解并实现以太坊代币标准,包括同质化和非同质化代币
🎯 学习要点
掌握 ERC-20 标准接口与实现
理解 NFT 元数据与所有权管理
学习 ERC-1155 批量操作优势
防范常见代币漏洞(重入、溢出等)
📚 课程章节
ERC-20 标准详解2小时
ERC-721 NFT 实现3小时
ERC-1155 多代币标准2.5小时
代币安全与最佳实践2小时
代码示例
ERC-20 标准实现
使用 OpenZeppelin 库实现安全的 ERC-20 代币,包含铸造、销毁和批量转账功能
solidity
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.20;3 4import "@openzeppelin/contracts/token/ERC20/ERC20.sol";5import "@openzeppelin/contracts/access/Ownable.sol";6 7/**8 * @title MyToken9 * @dev 实现基础 ERC-20 代币,带有铸造和销毁功能10 */11contract MyToken is ERC20, Ownable {12 uint256 public constant MAX_SUPPLY = 1_000_000 * 10**18;13 14 constructor() ERC20("MyToken", "MTK") Ownable(msg.sender) {15 // 初始铸造给创建者16 _mint(msg.sender, 100_000 * 10**18);17 }18 19 /**20 * @dev 铸造新代币(仅限 owner)21 */22 function mint(address to, uint256 amount) external onlyOwner {23 require(totalSupply() + amount <= MAX_SUPPLY, "Exceeds max supply");24 _mint(to, amount);25 }26 27 /**28 * @dev 销毁持有者的代币29 */30 function burn(uint256 amount) external {31 _burn(msg.sender, amount);32 }33 34 /**35 * @dev 批量转账(Gas 优化)36 */37 function batchTransfer(38 address[] calldata recipients,39 uint256[] calldata amounts40 ) external returns (bool) {41 require(recipients.length == amounts.length, "Length mismatch");42 43 for (uint256 i = 0; i < recipients.length; i++) {44 _transfer(msg.sender, recipients[i], amounts[i]);45 }46 return true;47 }48}ERC-721 NFT 实现
实现完整的 NFT 合约,包含付费铸造、白名单、批量操作等功能
solidity
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.20;3 4import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";5import "@openzeppelin/contracts/access/Ownable.sol";6import "@openzeppelin/contracts/utils/Counters.sol";7 8/**9 * @title MyNFT10 * @dev ERC-721 NFT 合约,支持元数据存储和批量铸造11 */12contract MyNFT is ERC721URIStorage, Ownable {13 using Counters for Counters.Counter;14 Counters.Counter private _tokenIds;15 16 uint256 public constant MAX_SUPPLY = 10000;17 uint256 public constant MINT_PRICE = 0.05 ether;18 19 mapping(address => bool) public hasFreeMint;20 21 constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {}22 23 /**24 * @dev 公开铸造(需支付)25 */26 function mint(string memory tokenURI) external payable returns (uint256) {27 require(_tokenIds.current() < MAX_SUPPLY, "Sold out");28 require(msg.value >= MINT_PRICE, "Insufficient payment");29 30 _tokenIds.increment();31 uint256 newTokenId = _tokenIds.current();32 33 _safeMint(msg.sender, newTokenId);34 _setTokenURI(newTokenId, tokenURI);35 36 return newTokenId;37 }38 39 /**40 * @dev 白名单免费铸造41 */42 function freeMint(string memory tokenURI) external returns (uint256) {43 require(!hasFreeMint[msg.sender], "Already minted");44 require(_tokenIds.current() < MAX_SUPPLY, "Sold out");45 46 hasFreeMint[msg.sender] = true;47 _tokenIds.increment();48 uint256 newTokenId = _tokenIds.current();49 50 _safeMint(msg.sender, newTokenId);51 _setTokenURI(newTokenId, tokenURI);52 53 return newTokenId;54 }55 56 /**57 * @dev 批量铸造(仅限 owner)58 */59 function batchMint(60 address[] calldata recipients,61 string[] calldata tokenURIs62 ) external onlyOwner {63 require(recipients.length == tokenURIs.length, "Length mismatch");64 require(_tokenIds.current() + recipients.length <= MAX_SUPPLY, "Exceeds max supply");65 66 for (uint256 i = 0; i < recipients.length; i++) {67 _tokenIds.increment();68 uint256 newTokenId = _tokenIds.current();69 _safeMint(recipients[i], newTokenId);70 _setTokenURI(newTokenId, tokenURIs[i]);71 }72 }73 74 /**75 * @dev 提取合约余额76 */77 function withdraw() external onlyOwner {78 payable(owner()).transfer(address(this).balance);79 }80}ERC-1155 多代币标准
ERC-1155 允许在一个合约中管理多种代币类型,节省部署和转账 Gas
solidity
1// SPDX-License-Identifier: MIT2pragma solidity ^0.8.20;3 4import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";5import "@openzeppelin/contracts/access/Ownable.sol";6 7/**8 * @title GameItems9 * @dev ERC-1155 实现,管理游戏道具(同质化 + 非同质化)10 */11contract GameItems is ERC1155, Ownable {12 // 代币 ID 常量13 uint256 public constant GOLD = 0; // 同质化货币14 uint256 public constant SWORD = 1; // 非同质化武器15 uint256 public constant SHIELD = 2; // 非同质化护甲16 17 mapping(uint256 => string) private _tokenURIs;18 mapping(uint256 => uint256) public maxSupply;19 mapping(uint256 => uint256) public currentSupply;20 21 constructor() ERC1155("https://game.example/api/item/{id}.json") Ownable(msg.sender) {22 // 设置最大供应量23 maxSupply[GOLD] = type(uint256).max; // 无限24 maxSupply[SWORD] = 1000;25 maxSupply[SHIELD] = 500;26 27 // 初始铸造28 _mint(msg.sender, GOLD, 10000, "");29 }30 31 /**32 * @dev 铸造代币33 */34 function mint(35 address to,36 uint256 id,37 uint256 amount,38 bytes memory data39 ) external onlyOwner {40 require(currentSupply[id] + amount <= maxSupply[id], "Exceeds max supply");41 currentSupply[id] += amount;42 _mint(to, id, amount, data);43 }44 45 /**46 * @dev 批量铸造(Gas 优化)47 */48 function mintBatch(49 address to,50 uint256[] memory ids,51 uint256[] memory amounts,52 bytes memory data53 ) external onlyOwner {54 for (uint256 i = 0; i < ids.length; i++) {55 require(56 currentSupply[ids[i]] + amounts[i] <= maxSupply[ids[i]],57 "Exceeds max supply"58 );59 currentSupply[ids[i]] += amounts[i];60 }61 _mintBatch(to, ids, amounts, data);62 }63 64 /**65 * @dev 设置代币 URI66 */67 function setTokenURI(uint256 tokenId, string memory tokenURI) external onlyOwner {68 _tokenURIs[tokenId] = tokenURI;69 }70 71 /**72 * @dev 获取代币 URI73 */74 function uri(uint256 tokenId) public view override returns (string memory) {75 string memory tokenURI = _tokenURIs[tokenId];76 return bytes(tokenURI).length > 0 ? tokenURI : super.uri(tokenId);77 }78}