开发工具:Hardhat 或 Truffle(编译、部署合约),OpenZeppelin(安全合约模板)
代码实现
1. MUSD 代币合约
基于 ERC-20 标准,使用 OpenZeppelin 模板。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract MUSD is ERC20 {constructor() ERC20("Mall USD", "MUSD") {_mint(msg.sender, 1000000 * 10**18); // 初始发行 100万 MUSD}// 商城合约可以调用此函数铸造更多 MUSD 作为奖励function mint(address to, uint256 amount) public {_mint(to, amount);}
}
2. NFT 商品合约
基于 ERC-721,每个商品是一个独特的 NFT。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";contract ProductNFT is ERC721 {using Counters for Counters.Counter;Counters.Counter private _tokenIds;mapping(uint256 => string) private _tokenURIs;constructor() ERC721("ProductNFT", "PNFT") {}function mintNFT(address to, string memory tokenURI) public returns (uint256) {_tokenIds.increment();uint256 newItemId = _tokenIds.current();_mint(to, newItemId);_setTokenURI(newItemId, tokenURI);return newItemId;}function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal {_tokenURIs[tokenId] = _tokenURI;}function tokenURI(uint256 tokenId) public view override returns (string memory) {return _tokenURIs[tokenId];}
}
3. 商城合约
核心逻辑,处理上架和购买。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ProductNFT.sol";
import "./MUSD.sol";contract Mall {ProductNFT public productNFT;IERC20 public usdc; // USDC 合约地址MUSD public musd;struct Item {uint256 tokenId;address seller;uint256 price; // 以 USDC 为单位bool isSold;}mapping(uint256 => Item) public items;uint256 public itemCount;event ItemListed(uint256 tokenId, address seller, uint256 price);event ItemSold(uint256 tokenId, address buyer, uint256 price);constructor(address _productNFT, address _usdc, address _musd) {productNFT = ProductNFT(_productNFT);usdc = IERC20(_usdc);musd = MUSD(_musd);}// 商家上架商品function listItem(string memory tokenURI, uint256 price) external {uint256 tokenId = productNFT.mintNFT(address(this), tokenURI);itemCount++;items[tokenId] = Item(tokenId, msg.sender, price, false);emit ItemListed(tokenId, msg.sender, price);}// 用户用 USDC 购买商品function buyItem(uint256 tokenId) external {Item storage item = items[tokenId];require(!item.isSold, "Item already sold");require(usdc.transferFrom(msg.sender, item.seller, item.price), "USDC transfer failed");item.isSold = true;productNFT.transferFrom(address(this), msg.sender, tokenId);// 奖励买家 1 MUSDmusd.mint(msg.sender, 1 * 10**18);emit ItemSold(tokenId, msg.sender, item.price);}
}
4. 前端实现(React + Ethers.js)
以下是简化的前端代码,假设已配置好 MetaMask 和合约地址。
import { ethers } from "ethers";
import React, { useState } from "react";
import MallABI from "./abis/Mall.json"; // 商城合约 ABI
import USDCABI from "./abis/USDC.json"; // USDC ABIconst MallDApp = () => {const [provider, setProvider] = useState(null);const [account, setAccount] = useState(null);const mallAddress = "0xYourMallContractAddress";const usdcAddress = "0xYourUSDCContractAddress"; // 测试网 USDC 地址// 连接钱包const connectWallet = async () => {const provider = new ethers.BrowserProvider(window.ethereum);const accounts = await provider.send("eth_requestAccounts", []);setProvider(provider);setAccount(accounts[0]);};// 商家上架商品const listItem = async () => {const signer = await provider.getSigner();const mallContract = new ethers.Contract(mallAddress, MallABI, signer);const tokenURI = "ipfs://your-ipfs-hash"; // 商品元数据const price = ethers.parseUnits("10", 6); // 假设 10 USDC,6 位小数await mallContract.listItem(tokenURI, price);};// 用户购买商品const buyItem = async (tokenId) => {const signer = await provider.getSigner();const mallContract = new ethers.Contract(mallAddress, MallABI, signer);const usdcContract = new ethers.Contract(usdcAddress, USDCABI, signer);// 授权 USDCconst price = items[tokenId].price; // 从状态或合约获取价格await usdcContract.approve(mallAddress, price);await mallContract.buyItem(tokenId);};return (<div><button onClick={connectWallet}>连接钱包</button><button onClick={listItem}>上架商品</button><button onClick={() => buyItem(1)}>购买商品 (ID: 1)</button></div>);
};export default MallDApp;
部署与测试
- 编译与部署:
- 用 Hardhat 编译合约:npx hardhat compile。
- 部署到测试网:编辑 hardhat.config.js,添加网络配置,然后运行 npx hardhat run scripts/deploy.js --network sepolia。
- 测试流程:
- 用 MetaMask 连接测试网,获取测试 USDC(可用 faucet)。
- 商家调用 listItem,检查 NFT 是否铸造。
- 用户授权 USDC 并调用 buyItem,验证 NFT 和 USDC 转移。