教程区块链区块链技术ch1818.3 铸造页:前端铸造 UI 与 MetaMask 交互

本页目录

铸造页是 NFT 项目的"门面"——好的铸造体验能提升 200% 的转化率。


18.3.1 铸造页架构

graph TD
    User[用户] --> UI[铸造UI]
    UI --> Check[检查: 白名单? 余额? 供应量?]
    Check --> |通过| Tx[构造交易: mint(quantity)]
    Check --> |不通过| Error[显示错误/提示]
    Tx --> MM[MetaMask 签名]
    MM --> Pending[交易 pending]
    Pending --> Confirm[确认/失败]
    Confirm --> Success[显示铸造结果+链接]
    
    style Check fill:#fff3e0
    style Success fill:#e8f5e9

18.3.2 前端代码

tsx
// components/Minter.tsx
import { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import MyNFT from '../abi/MyNFT.json';

const CONTRACT_ADDRESS = "0x...";
const MINT_PRICE = ethers.parseEther("0.01");

export const Minter = () => {
  const [quantity, setQuantity] = useState(1);
  const [supply, setSupply] = useState({ total: 0, max: 10000 });
  const [price, setPrice] = useState(MINT_PRICE);
  const [loading, setLoading] = useState(false);
  const [txHash, setTxHash] = useState('');

  const provider = new ethers.BrowserProvider(window.ethereum);

  useEffect(() => {
    const loadSupply = async () => {
      const contract = new ethers.Contract(CONTRACT_ADDRESS, MyNFT, await provider.getSigner());
      const [minted, max, mintPrice] = await Promise.all([
        contract.totalMinted(),
        contract.maxSupply(),
        contract.mintPrice(),
      ]);
      setSupply({ total: Number(minted), max: Number(max) });
      setPrice(mintPrice);
    };
    loadSupply();
  }, []);

  const mint = async () => {
    setLoading(true);
    try {
      const signer = await provider.getSigner();
      const contract = new ethers.Contract(CONTRACT_ADDRESS, MyNFT, signer);
      
      const tx = await contract.mint(quantity, {
        value: price * BigInt(quantity),
      });
      
      setTxHash(tx.hash);
      await tx.wait();
      
      // 刷新供应量
      const newTotal = await contract.totalMinted();
      setSupply(s => ({ ...s, total: Number(newTotal) }));
    } finally {
      setLoading(false);
    }
  };

  const remaining = supply.max - supply.total;

  return (
    <div className="minter">
      <h2>Mint Your NFT</h2>
      <div className="supply">{supply.total} / {supply.max} 已铸造</div>
      
      <div className="quantity-selector">
        <button onClick={() => setQuantity(Math.max(1, q-1))}>-</button>
        <span>{quantity}</span>
        <button onClick={() => setQuantity(Math.min(10, q+1))}>+</button>
      </div>

      <div className="price">总计: {ethers.formatEther(price * BigInt(quantity))} ETH</div>

      <button 
        onClick={mint} 
        disabled={loading || remaining <= 0}
        className="mint-btn"
      >
        {loading ? '铸造中...' : remaining <= 0 ? '已售完' : '立即铸造'}
      </button>

      {txHash && (
        <a href={`https://etherscan.io/tx/${txHash}`} target="_blank">
          查看交易 →
        </a>
      )}
    </div>
  );
};

18.3.3 UX 最佳实践

场景处理方式
未连接钱包显示"连接钱包"按钮
余额不足禁用按钮,提示 ETH 余额
已售完按钮变灰,显示"已售完"
网络错误显示"请切换到正确网络"
交易失败显示错误信息,允许重试
铸造成功弹出成功动画,提供 OpenSea 链接

, 前往 → 18.4 元数据 |*

评论

0

评论加载中…

发表评论

0/2000