智能合约:Solidity(基于以太坊或兼容链,如 Polygon、BSC)(仅供学习区块链知识,不可进行违法开发应用)

发布于:2025-03-14 ⋅ 阅读:(12) ⋅ 点赞:(0)

开发工具: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 MUSD
        musd.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 ABI

const 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);

    // 授权 USDC
    const 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;

部署与测试

  1. 编译与部署
    • 用 Hardhat 编译合约:npx hardhat compile。
    • 部署到测试网:编辑 hardhat.config.js,添加网络配置,然后运行 npx hardhat run scripts/deploy.js --network sepolia。
  2. 测试流程
    • 用 MetaMask 连接测试网,获取测试 USDC(可用 faucet)。
    • 商家调用 listItem,检查 NFT 是否铸造。
    • 用户授权 USDC 并调用 buyItem,验证 NFT 和 USDC 转移。