欢迎光临
我们一直在努力

VS Code + Hardhat 智能合约开发完整指南

目录

VS Code + Hardhat 智能合约开发完整指南

第一章:环境搭建与配置

1.1 系统要求检查

1.2 VS Code 安装与配置

1.2.1 安装 VS Code

1.2.2 安装必备扩展

1.2.3 VS Code 工作区设置

第二章:Hardhat 项目初始化

2.1 创建新项目

2.2 项目结构

2.3 配置文件详解

2.3.1 Hardhat 配置文件

2.3.2 TypeScript 配置

2.3.3 ESLint 配置

2.3.4 Prettier 配置

2.3.5 环境变量配置

第三章:智能合约开发

3.1 创建智能合约

3.1.1 基础合约示例

3.1.2 接口定义

3.1.3 库合约

3.2 编译合约

3.3 编译配置文件

第四章:测试开发

4.1 测试环境配置

4.1.1 测试配置文件

4.1.2 基础测试文件

4.1.3 集成测试

4.2 运行测试

4.3 测试覆盖率

第五章:部署脚本

5.1 基础部署脚本

5.2 多网络部署脚本

5.3 升级部署脚本

5.4 运行部署脚本

第六章:交互与调试

6.1 Hardhat Console 使用

6.2 调试交易

6.3 交互脚本

第七章:自动化与工作流

7.1 npm 脚本配置

7.2 Git Hooks 配置

7.3 CI/CD 配置

第八章:高级功能与优化

8.1 自定义 Hardhat 任务

8.2 安全分析配置

8.3 性能优化

8.3.1 编译优化

8.3.2 测试优化

第九章:故障排除与调试

9.1 常见问题解决

9.1.1 编译错误

9.1.2 测试失败

9.1.3 部署失败

9.1.4 验证失败

9.2 调试技巧

9.2.1 使用 console.log

9.2.2 使用 Hardhat Network 日志

9.2.3 交易追踪

第十章:最佳实践总结

10.1 项目结构最佳实践

10.2 代码质量最佳实践

10.2.1 Solidity 编码规范

10.2.2 安全最佳实践

10.3 部署最佳实践

10.3.1 多阶段部署

10.3.2 紧急情况处理

10.4 监控与维护

10.4.1 事件监听

10.4.2 定期检查脚本

总结


我们正在使用VS Code和Hardhat进行智能合约开发。本指南将涵盖从环境设置到部署的完整流程。 注意:本指南假设你已安装Node.js(推荐版本v16或更高)和VS Code。

步骤概览:

  • 环境准备

  • 初始化Hardhat项目

  • 配置VS Code

  • 编写智能合约

  • 编译合约

  • 测试合约

  • 部署合约

  • 验证合约

  • 让我们开始吧!

    VS Code + Hardhat 智能合约开发完整指南

    第一章:环境搭建与配置

    1.1 系统要求检查

    # 检查 Node.js 版本
    node –version
    # 需要 v16.0.0 或更高版本

    # 检查 npm 版本
    npm –version
    # 需要 v7.0.0 或更高版本

    # 推荐使用 nvm 管理 Node.js 版本
    # macOS/Linux 安装 nvm
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

    # Windows 安装 nvm-windows
    # 下载地址:https://github.com/coreybutler/nvm-windows/releases

    # 使用 nvm 安装 Node.js
    nvm install 18
    nvm use 18

    1.2 VS Code 安装与配置

    1.2.1 安装 VS Code
    • 下载地址:https://code.visualstudio.com/

    • 选择对应系统版本下载安装

    1.2.2 安装必备扩展

    在 VS Code 扩展市场安装以下插件:

    // .vscode/extensions.json – 团队共享配置
    {
    "recommendations": [
    // Solidity 开发核心扩展
    "JuanBlanco.solidity", // Solidity 语言支持
    "NomicFoundation.hardhat-solidity", // Hardhat 集成
    "trufflesuite.truffle-vscode", // Truffle 集成(可选)

    // 开发工具
    "dbaeumer.vscode-eslint", // ESLint 集成
    "esbenp.prettier-vscode", // Prettier 代码格式化
    "eamodio.gitlens", // Git 增强
    "ms-vscode.test-adapter-converter", // 测试适配器

    // AI 辅助编程(可选但推荐)
    "GitHub.copilot", // GitHub Copilot
    "Codeium.codeium", // Codeium AI 助手

    // 其他实用工具
    "streetsidesoftware.code-spell-checker", // 拼写检查
    "mikestead.dotenv", // .env 文件支持
    "ms-azuretools.vscode-docker", // Docker 支持
    "ms-vscode-remote.remote-ssh" // 远程开发
    ]
    }

    1.2.3 VS Code 工作区设置

    // .vscode/settings.json
    {
    // 文件排除
    "files.exclude": {
    "**/.git": true,
    "**/.DS_Store": true,
    "**/node_modules": true,
    "**/build": true,
    "**/coverage": true,
    "**/cache": true
    },

    // Solidity 配置
    "solidity.compileUsingRemoteVersion": "v0.8.19+commit.7dd6d404",
    "solidity.defaultCompiler": "remote",
    "solidity.enableLocalNodeCompiler": false,

    // 编辑器通用设置
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
    "source.organizeImports": true
    },
    "editor.tabSize": 2,
    "editor.insertSpaces": true,
    "editor.detectIndentation": false,

    // 文件类型关联
    "files.associations": {
    "*.sol": "solidity",
    ".env*": "properties"
    },

    // Solidity 特定设置
    "[solidity]": {
    "editor.defaultFormatter": "JuanBlanco.solidity",
    "editor.formatOnSave": true,
    "editor.tabSize": 4
    },

    // TypeScript/JavaScript 设置
    "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
    },

    // 终端设置
    "terminal.integrated.defaultProfile.windows": "PowerShell",
    "terminal.integrated.defaultProfile.linux": "bash",
    "terminal.integrated.defaultProfile.osx": "zsh",

    // 工作区设置
    "workbench.iconTheme": "material-icon-theme",
    "workbench.colorTheme": "Default Dark+",

    // Git 设置
    "git.autofetch": true,
    "git.confirmSync": false,
    "git.enableSmartCommit": true
    }

    第二章:Hardhat 项目初始化

    2.1 创建新项目

    # 1. 创建项目目录
    mkdir my-hardhat-project
    cd my-hardhat-project

    # 2. 初始化 npm 项目
    npm init -y

    # 3. 安装 Hardhat
    npm install –save-dev hardhat

    # 4. 初始化 Hardhat 项目
    npx hardhat init

    # 选择项目类型:
    # ❯ Create a TypeScript project (推荐)
    # Create a JavaScript project
    # Create an empty hardhat.config.js

    # 5. 安装依赖包
    npm install –save-dev @nomicfoundation/hardhat-toolbox
    npm install –save-dev @typechain/hardhat @typechain/ethers-v5 typechain
    npm install –save-dev dotenv

    # 6. 安装 OpenZeppelin 合约库
    npm install @openzeppelin/contracts

    # 7. 安装开发工具
    npm install –save-dev eslint prettier eslint-config-prettier
    npm install –save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin

    2.2 项目结构

    my-hardhat-project/
    ├── contracts/ # Solidity 智能合约
    │ ├── interfaces/ # 接口定义
    │ ├── libraries/ # 库合约
    │ ├── tokens/ # 代币合约
    │ └── utils/ # 工具合约
    ├── scripts/ # 部署和交互脚本
    │ ├── deploy/ # 部署脚本
    │ ├── upgrade/ # 升级脚本
    │ └── tasks/ # Hardhat 自定义任务
    ├── test/ # 测试文件
    │ ├── unit/ # 单元测试
    │ ├── integration/ # 集成测试
    │ └── fixtures/ # 测试夹具
    ├── deployments/ # 部署记录(hardhat-deploy)
    ├── typechain-types/ # TypeScript 类型定义
    ├── artifacts/ # 编译产物
    ├── cache/ # 缓存文件
    ├── .vscode/ # VS Code 配置
    ├── .env # 环境变量
    ├── .env.example # 环境变量示例
    ├── hardhat.config.ts # Hardhat 配置
    ├── tsconfig.json # TypeScript 配置
    ├── package.json # 项目依赖
    └── README.md # 项目说明

    2.3 配置文件详解

    2.3.1 Hardhat 配置文件

    // hardhat.config.ts
    import { HardhatUserConfig } from "hardhat/config";
    import "@nomicfoundation/hardhat-toolbox";
    import "@typechain/hardhat";
    import "@nomiclabs/hardhat-ethers";
    import "@nomiclabs/hardhat-waffle";
    import "@nomiclabs/hardhat-etherscan";
    import "hardhat-gas-reporter";
    import "solidity-coverage";
    import "dotenv/config";

    const config: HardhatUserConfig = {
    // Solidity 编译器配置
    solidity: {
    version: "0.8.19",
    settings: {
    optimizer: {
    enabled: true,
    runs: 200,
    },
    // 通过 IR 编译(可选,可减少字节码大小)
    viaIR: process.env.VIA_IR === "true",
    // 输出详细编译信息
    outputSelection: {
    "*": {
    "*": ["abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers"],
    "": ["ast"]
    }
    }
    },
    },

    // 网络配置
    networks: {
    // 本地开发网络
    hardhat: {
    chainId: 31337,
    // 配置本地分叉(可选)
    forking: {
    url: process.env.ETH_MAINNET_URL || "",
    blockNumber: 17570000,
    enabled: process.env.FORKING_ENABLED === "true",
    },
    // 矿工配置
    mining: {
    auto: true,
    interval: 5000, // 区块生成间隔(毫秒)
    },
    // 初始账户配置
    accounts: {
    mnemonic: process.env.MNEMONIC || "test test test test test test test test test test test junk",
    count: 20,
    accountsBalance: "10000000000000000000000", // 10 ETH
    },
    },

    // 本地节点网络
    localhost: {
    url: "http://127.0.0.1:8545",
    chainId: 31337,
    timeout: 60000,
    },

    // 测试网络
    goerli: {
    url: process.env.GOERLI_RPC_URL || "",
    accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    chainId: 5,
    gasPrice: "auto",
    gasMultiplier: 1.2,
    timeout: 60000,
    },

    sepolia: {
    url: process.env.SEPOLIA_RPC_URL || "",
    accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    chainId: 11155111,
    },

    // Layer 2 网络
    polygon: {
    url: process.env.POLYGON_RPC_URL || "",
    accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    chainId: 137,
    },

    arbitrum: {
    url: process.env.ARBITRUM_RPC_URL || "",
    accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    chainId: 42161,
    },

    optimism: {
    url: process.env.OPTIMISM_RPC_URL || "",
    accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    chainId: 10,
    },

    // 主网(谨慎使用)
    mainnet: {
    url: process.env.MAINNET_RPC_URL || "",
    accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
    chainId: 1,
    gasPrice: process.env.MAINNET_GAS_PRICE
    ? parseInt(process.env.MAINNET_GAS_PRICE)
    : "auto",
    },
    },

    // Etherscan 验证配置
    etherscan: {
    apiKey: {
    mainnet: process.env.ETHERSCAN_API_KEY || "",
    goerli: process.env.ETHERSCAN_API_KEY || "",
    sepolia: process.env.ETHERSCAN_API_KEY || "",
    polygon: process.env.POLYGONSCAN_API_KEY || "",
    polygonMumbai: process.env.POLYGONSCAN_API_KEY || "",
    arbitrumOne: process.env.ARBISCAN_API_KEY || "",
    arbitrumGoerli: process.env.ARBISCAN_API_KEY || "",
    optimism: process.env.OPTIMISM_API_KEY || "",
    optimismGoerli: process.env.OPTIMISM_API_KEY || "",
    },
    customChains: [
    {
    network: "arbitrumGoerli",
    chainId: 421613,
    urls: {
    apiURL: "https://api-goerli.arbiscan.io/api",
    browserURL: "https://goerli.arbiscan.io/"
    }
    }
    ]
    },

    // Gas 报告配置
    gasReporter: {
    enabled: process.env.REPORT_GAS === "true",
    currency: "USD",
    coinmarketcap: process.env.COINMARKETCAP_API_KEY || "",
    token: "ETH",
    gasPrice: 21,
    excludeContracts: ["mocks/", "test/"],
    src: "./contracts",
    },

    // 测试配置
    mocha: {
    timeout: 60000,
    color: true,
    reporter: "spec",
    },

    // 路径配置
    paths: {
    sources: "./contracts",
    tests: "./test",
    cache: "./cache",
    artifacts: "./artifacts",
    },

    // TypeChain 配置
    typechain: {
    outDir: "typechain-types",
    target: "ethers-v5",
    alwaysGenerateOverloads: false,
    externalArtifacts: ["external-artifacts/*.json"],
    dontOverrideCompile: false,
    },

    // 合约大小分析
    contractSizer: {
    alphaSort: true,
    disambiguatePaths: false,
    runOnCompile: false,
    strict: true,
    only: [],
    except: [],
    },

    // 文档生成配置
    docgen: {
    path: "./docs",
    clear: true,
    runOnCompile: false,
    },
    };

    export default config;

    2.3.2 TypeScript 配置

    // tsconfig.json
    {
    "compilerOptions": {
    "target": "es2020",
    "module": "commonjs",
    "lib": ["es2020", "dom"],
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./",
    "strict": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "types": ["node", "mocha", "chai"],
    "baseUrl": ".",
    "paths": {
    "@/*": ["./*"],
    "@contracts/*": ["./contracts/*"],
    "@test/*": ["./test/*"],
    "@scripts/*": ["./scripts/*"],
    "@typechain/*": ["./typechain-types/*"]
    }
    },
    "include": [
    "./scripts/**/*",
    "./test/**/*",
    "./typechain-types/**/*",
    "./hardhat.config.ts",
    "./tasks/**/*"
    ],
    "exclude": [
    "node_modules",
    "dist",
    "cache",
    "artifacts",
    "coverage"
    ],
    "files": [
    "./hardhat.config.ts"
    ]
    }

    2.3.3 ESLint 配置

    // .eslintrc.js
    module.exports = {
    root: true,
    env: {
    browser: false,
    es2021: true,
    mocha: true,
    node: true,
    },
    plugins: ["@typescript-eslint"],
    extends: [
    "standard",
    "plugin:prettier/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:node/recommended",
    ],
    parser: "@typescript-eslint/parser",
    parserOptions: {
    ecmaVersion: 12,
    project: "./tsconfig.json",
    },
    settings: {
    node: {
    tryExtensions: [".js", ".json", ".node", ".ts"],
    },
    },
    rules: {
    "node/no-unsupported-features/es-syntax": [
    "error",
    { ignores: ["modules"] },
    ],
    "node/no-missing-import": "off",
    "node/no-unpublished-import": "off",
    "node/no-unpublished-require": "off",
    "node/no-unsupported-features/node-builtins": [
    "error",
    { version: ">=16.0.0" },
    ],
    "@typescript-eslint/explicit-function-return-type": "off",
    "@typescript-eslint/no-explicit-any": "warn",
    "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
    "no-console": ["warn", { allow: ["warn", "error", "info"] }],
    "prettier/prettier": [
    "error",
    {
    semi: true,
    trailingComma: "es5",
    singleQuote: false,
    printWidth: 100,
    tabWidth: 2,
    useTabs: false,
    },
    ],
    },
    overrides: [
    {
    files: ["hardhat.config.ts"],
    rules: {
    "node/no-unpublished-import": "off",
    },
    },
    {
    files: ["scripts/**/*.ts"],
    rules: {
    "no-process-exit": "off",
    },
    },
    {
    files: ["test/**/*.ts"],
    rules: {
    "no-unused-expressions": "off",
    },
    },
    ],
    };

    2.3.4 Prettier 配置

    // .prettierrc
    {
    "semi": true,
    "trailingComma": "es5",
    "singleQuote": false,
    "printWidth": 100,
    "tabWidth": 2,
    "useTabs": false,
    "bracketSpacing": true,
    "arrowParens": "always",
    "endOfLine": "lf"
    }

    2.3.5 环境变量配置

    # .env.example
    # 复制此文件为 .env 并填入实际值

    # ========== API Keys ==========
    # Etherscan
    ETHERSCAN_API_KEY=your_etherscan_api_key

    # 其他区块链浏览器
    POLYGONSCAN_API_KEY=your_polygonscan_api_key
    ARBISCAN_API_KEY=your_arbiscan_api_key
    OPTIMISM_API_KEY=your_optimism_api_key

    # Gas 报告
    COINMARKETCAP_API_KEY=your_coinmarketcap_api_key

    # ========== RPC URLs ==========
    # Ethereum
    MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/your-api-key
    GOERLI_RPC_URL=https://eth-goerli.g.alchemy.com/v2/your-api-key
    SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/your-api-key

    # Polygon
    POLYGON_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/your-api-key
    MUMBAI_RPC_URL=https://polygon-mumbai.g.alchemy.com/v2/your-api-key

    # Arbitrum
    ARBITRUM_RPC_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key
    ARBITRUM_GOERLI_RPC_URL=https://arb-goerli.g.alchemy.com/v2/your-api-key

    # Optimism
    OPTIMISM_RPC_URL=https://opt-mainnet.g.alchemy.com/v2/your-api-key
    OPTIMISM_GOERLI_RPC_URL=https://opt-goerli.g.alchemy.com/v2/your-api-key

    # ========== 私钥配置 ==========
    # 单个私钥
    PRIVATE_KEY=your_private_key_here

    # 多个私钥(用于多签或测试)
    PRIVATE_KEY_1=first_private_key
    PRIVATE_KEY_2=second_private_key
    PRIVATE_KEY_3=third_private_key

    # 助记词(可选)
    MNEMONIC="test test test test test test test test test test test junk"

    # ========== 其他配置 ==========
    # Gas 报告
    REPORT_GAS=true

    # 分叉配置
    FORKING_ENABLED=false
    ETH_MAINNET_URL=${MAINNET_RPC_URL}

    # IR 编译
    VIA_IR=false

    # 主网 Gas 价格(单位:gwei)
    MAINNET_GAS_PRICE=30

    第三章:智能合约开发

    3.1 创建智能合约

    3.1.1 基础合约示例

    // contracts/MyToken.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.19;

    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";
    import "@openzeppelin/contracts/utils/math/SafeMath.sol";

    /**
    * @title MyToken
    * @dev 一个支持铸币、销毁和暂停功能的 ERC20 代币
    */
    contract MyToken is ERC20, Ownable, Pausable {
    using SafeMath for uint256;

    // 最大供应量
    uint256 public constant MAX_SUPPLY = 1_000_000_000 * 10 ** 18; // 10亿代币

    // 铸造权限
    mapping(address => bool) public minters;

    // 事件
    event Mint(address indexed to, uint256 amount);
    event Burn(address indexed from, uint256 amount);
    event MinterAdded(address indexed account);
    event MinterRemoved(address indexed account);

    /**
    * @dev 构造函数
    * @param name 代币名称
    * @param symbol 代币符号
    * @param initialSupply 初始供应量
    */
    constructor(
    string memory name,
    string memory symbol,
    uint256 initialSupply
    ) ERC20(name, symbol) {
    require(initialSupply <= MAX_SUPPLY, "Initial supply exceeds max supply");

    // 铸造初始供应给部署者
    _mint(msg.sender, initialSupply);

    // 部署者默认拥有铸造权限
    minters[msg.sender] = true;
    emit MinterAdded(msg.sender);
    }

    /**
    * @dev 铸造新代币(仅限所有者或铸币者)
    * @param to 接收地址
    * @param amount 铸造数量
    */
    function mint(address to, uint256 amount)
    external
    whenNotPaused
    onlyMinter
    {
    require(totalSupply().add(amount) <= MAX_SUPPLY, "Exceeds max supply");
    _mint(to, amount);
    emit Mint(to, amount);
    }

    /**
    * @dev 销毁代币
    * @param amount 销毁数量
    */
    function burn(uint256 amount) external whenNotPaused {
    _burn(msg.sender, amount);
    emit Burn(msg.sender, amount);
    }

    /**
    * @dev 批量转账
    * @param recipients 接收者数组
    * @param amounts 数量数组
    */
    function batchTransfer(
    address[] calldata recipients,
    uint256[] calldata amounts
    ) external whenNotPaused {
    require(recipients.length == amounts.length, "Arrays length mismatch");

    for (uint256 i = 0; i < recipients.length; i++) {
    transfer(recipients[i], amounts[i]);
    }
    }

    /**
    * @dev 添加铸币者(仅所有者)
    * @param account 账户地址
    */
    function addMinter(address account) external onlyOwner {
    require(!minters[account], "Already a minter");
    minters[account] = true;
    emit MinterAdded(account);
    }

    /**
    * @dev 移除铸币者(仅所有者)
    * @param account 账户地址
    */
    function removeMinter(address account) external onlyOwner {
    require(minters[account], "Not a minter");
    minters[account] = false;
    emit MinterRemoved(account);
    }

    /**
    * @dev 暂停所有转账(仅所有者)
    */
    function pause() external onlyOwner {
    _pause();
    }

    /**
    * @dev 恢复所有转账(仅所有者)
    */
    function unpause() external onlyOwner {
    _unpause();
    }

    /**
    * @dev 重写转账函数,添加暂停检查
    */
    function _beforeTokenTransfer(
    address from,
    address to,
    uint256 amount
    ) internal virtual override {
    super._beforeTokenTransfer(from, to, amount);
    require(!paused(), "Token transfer while paused");
    }

    // 修改器
    modifier onlyMinter() {
    require(minters[msg.sender], "Caller is not a minter");
    _;
    }
    }

    3.1.2 接口定义

    // contracts/interfaces/IMyToken.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.19;

    interface IMyToken {
    // 事件
    event Mint(address indexed to, uint256 amount);
    event Burn(address indexed from, uint256 amount);
    event MinterAdded(address indexed account);
    event MinterRemoved(address indexed account);

    // 只读函数
    function MAX_SUPPLY() external view returns (uint256);
    function minters(address account) external view returns (bool);

    // 状态改变函数
    function mint(address to, uint256 amount) external;
    function burn(uint256 amount) external;
    function batchTransfer(
    address[] calldata recipients,
    uint256[] calldata amounts
    ) external;
    function addMinter(address account) external;
    function removeMinter(address account) external;
    function pause() external;
    function unpause() external;
    }

    3.1.3 库合约

    // contracts/libraries/TokenMath.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.19;

    library TokenMath {
    /**
    * @dev 计算代币数量对应的百分比
    * @param amount 代币数量
    * @param percentage 百分比(乘以100,如50表示50%)
    * @param totalSupply 总供应量
    */
    function calculatePercentage(
    uint256 amount,
    uint256 percentage,
    uint256 totalSupply
    ) internal pure returns (uint256) {
    require(percentage <= 10000, "Percentage too high"); // 最多10000 = 100%
    return (amount * percentage) / 10000;
    }

    /**
    * @dev 检查地址是否为零地址
    */
    function isZeroAddress(address addr) internal pure returns (bool) {
    return addr == address(0);
    }

    /**
    * @dev 安全地将 ETH 转账到指定地址
    */
    function safeTransferETH(address to, uint256 value) internal {
    (bool success, ) = to.call{value: value}(new bytes(0));
    require(success, "ETH transfer failed");
    }
    }

    3.2 编译合约

    # 编译所有合约
    npx hardhat compile

    # 清除缓存并重新编译
    npx hardhat clean
    npx hardhat compile

    # 查看编译详情
    npx hardhat compile –verbose

    # 只编译特定合约
    npx hardhat compile –contracts contracts/MyToken.sol

    # 查看合约大小
    npx hardhat size-contracts

    3.3 编译配置文件

    // scripts/compile-config.json
    {
    "compilerOptions": {
    "target": "0.8.19",
    "optimizer": {
    "enabled": true,
    "runs": 200
    },
    "viaIR": true,
    "metadata": {
    "bytecodeHash": "ipfs"
    },
    "outputSelection": {
    "*": {
    "*": [
    "abi",
    "evm.bytecode",
    "evm.deployedBytecode",
    "evm.methodIdentifiers",
    "metadata"
    ],
    "": ["ast"]
    }
    },
    "libraries": {}
    },
    "sources": {
    "contracts/MyToken.sol": {
    "content": "compiled from source"
    }
    }
    }

    第四章:测试开发

    4.1 测试环境配置

    4.1.1 测试配置文件

    // test/test-config.ts
    import { ethers } from "hardhat";
    import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";

    export interface TestContext {
    owner: SignerWithAddress;
    user1: SignerWithAddress;
    user2: SignerWithAddress;
    user3: SignerWithAddress;
    deployer: SignerWithAddress;
    }

    export async function setupTest(): Promise<TestContext> {
    const [owner, user1, user2, user3, deployer] = await ethers.getSigners();

    return {
    owner,
    user1,
    user2,
    user3,
    deployer,
    };
    }

    // 测试常量
    export const TEST_CONSTANTS = {
    TOKEN_NAME: "MyToken",
    TOKEN_SYMBOL: "MTK",
    INITIAL_SUPPLY: ethers.utils.parseEther("1000000"), // 100万
    MINT_AMOUNT: ethers.utils.parseEther("1000"),
    BURN_AMOUNT: ethers.utils.parseEther("100"),
    MAX_SUPPLY: ethers.utils.parseEther("1000000000"), // 10亿
    };

    4.1.2 基础测试文件

    // test/MyToken.test.ts
    import { expect } from "chai";
    import { ethers } from "hardhat";
    import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
    import { time, loadFixture } from "@nomicfoundation/hardhat-network-helpers";

    import { MyToken, MyToken__factory } from "../typechain-types";
    import { setupTest, TEST_CONSTANTS } from "./test-config";

    describe("MyToken", function () {
    // 测试上下文类型
    type FixtureResult = {
    token: MyToken;
    owner: SignerWithAddress;
    user1: SignerWithAddress;
    user2: SignerWithAddress;
    user3: SignerWithAddress;
    };

    // 测试夹具
    async function deployTokenFixture(): Promise<FixtureResult> {
    const { owner, user1, user2, user3 } = await setupTest();

    const MyTokenFactory = (await ethers.getContractFactory(
    "MyToken"
    )) as MyToken__factory;

    const token = await MyTokenFactory.deploy(
    TEST_CONSTANTS.TOKEN_NAME,
    TEST_CONSTANTS.TOKEN_SYMBOL,
    TEST_CONSTANTS.INITIAL_SUPPLY
    );

    await token.deployed();

    return { token, owner, user1, user2, user3 };
    }

    describe("部署", function () {
    it("应该正确设置代币名称和符号", async function () {
    const { token } = await loadFixture(deployTokenFixture);

    expect(await token.name()).to.equal(TEST_CONSTANTS.TOKEN_NAME);
    expect(await token.symbol()).to.equal(TEST_CONSTANTS.TOKEN_SYMBOL);
    });

    it("应该将初始供应量分配给部署者", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    const ownerBalance = await token.balanceOf(owner.address);
    expect(ownerBalance).to.equal(TEST_CONSTANTS.INITIAL_SUPPLY);
    });

    it("应该设置正确的总供应量", async function () {
    const { token } = await loadFixture(deployTokenFixture);

    const totalSupply = await token.totalSupply();
    expect(totalSupply).to.equal(TEST_CONSTANTS.INITIAL_SUPPLY);
    });

    it("应该设置部署者为铸币者", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    const isMinter = await token.minters(owner.address);
    expect(isMinter).to.be.true;
    });
    });

    describe("转账功能", function () {
    it("应该允许账户之间转账", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    const transferAmount = ethers.utils.parseEther("100");

    // 转账前余额
    const ownerBalanceBefore = await token.balanceOf(owner.address);
    const user1BalanceBefore = await token.balanceOf(user1.address);

    // 执行转账
    await token.connect(owner).transfer(user1.address, transferAmount);

    // 验证转账后余额
    const ownerBalanceAfter = await token.balanceOf(owner.address);
    const user1BalanceAfter = await token.balanceOf(user1.address);

    expect(ownerBalanceAfter).to.equal(
    ownerBalanceBefore.sub(transferAmount)
    );
    expect(user1BalanceAfter).to.equal(
    user1BalanceBefore.add(transferAmount)
    );
    });

    it("应该触发 Transfer 事件", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    const transferAmount = ethers.utils.parseEther("100");

    await expect(token.connect(owner).transfer(user1.address, transferAmount))
    .to.emit(token, "Transfer")
    .withArgs(owner.address, user1.address, transferAmount);
    });

    it("当余额不足时应该转账失败", async function () {
    const { token, user1, user2 } = await loadFixture(deployTokenFixture);

    const transferAmount = ethers.utils.parseEther("100");

    // user1 没有代币,转账应该失败
    await expect(
    token.connect(user1).transfer(user2.address, transferAmount)
    ).to.be.revertedWith("ERC20: transfer amount exceeds balance");
    });

    it("应该支持批量转账", async function () {
    const { token, owner, user1, user2, user3 } = await loadFixture(
    deployTokenFixture
    );

    const recipients = [user1.address, user2.address, user3.address];
    const amounts = [
    ethers.utils.parseEther("100"),
    ethers.utils.parseEther("200"),
    ethers.utils.parseEther("300"),
    ];

    // 执行批量转账
    await token.connect(owner).batchTransfer(recipients, amounts);

    // 验证每个接收者的余额
    expect(await token.balanceOf(user1.address)).to.equal(amounts[0]);
    expect(await token.balanceOf(user2.address)).to.equal(amounts[1]);
    expect(await token.balanceOf(user3.address)).to.equal(amounts[2]);
    });

    it("批量转账时数组长度不匹配应该失败", async function () {
    const { token, owner, user1, user2 } = await loadFixture(
    deployTokenFixture
    );

    const recipients = [user1.address, user2.address];
    const amounts = [ethers.utils.parseEther("100")]; // 长度不匹配

    await expect(
    token.connect(owner).batchTransfer(recipients, amounts)
    ).to.be.revertedWith("Arrays length mismatch");
    });
    });

    describe("铸币功能", function () {
    it("铸币者应该可以铸造新代币", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    const mintAmount = TEST_CONSTANTS.MINT_AMOUNT;
    const totalSupplyBefore = await token.totalSupply();

    // 执行铸币
    await token.connect(owner).mint(user1.address, mintAmount);

    // 验证
    const user1Balance = await token.balanceOf(user1.address);
    const totalSupplyAfter = await token.totalSupply();

    expect(user1Balance).to.equal(mintAmount);
    expect(totalSupplyAfter).to.equal(totalSupplyBefore.add(mintAmount));
    });

    it("应该触发 Mint 事件", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    const mintAmount = TEST_CONSTANTS.MINT_AMOUNT;

    await expect(token.connect(owner).mint(user1.address, mintAmount))
    .to.emit(token, "Mint")
    .withArgs(user1.address, mintAmount);
    });

    it("非铸币者不应该可以铸造代币", async function () {
    const { token, user1, user2 } = await loadFixture(deployTokenFixture);

    const mintAmount = TEST_CONSTANTS.MINT_AMOUNT;

    await expect(
    token.connect(user1).mint(user2.address, mintAmount)
    ).to.be.revertedWith("Caller is not a minter");
    });

    it("铸造超过最大供应量应该失败", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    // 尝试铸造超过最大供应量的代币
    const excessiveAmount = TEST_CONSTANTS.MAX_SUPPLY.add(
    ethers.utils.parseEther("1")
    );

    await expect(
    token.connect(owner).mint(owner.address, excessiveAmount)
    ).to.be.revertedWith("Exceeds max supply");
    });
    });

    describe("销毁功能", function () {
    it("用户应该可以销毁自己的代币", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    const burnAmount = TEST_CONSTANTS.BURN_AMOUNT;
    const ownerBalanceBefore = await token.balanceOf(owner.address);
    const totalSupplyBefore = await token.totalSupply();

    // 执行销毁
    await token.connect(owner).burn(burnAmount);

    // 验证
    const ownerBalanceAfter = await token.balanceOf(owner.address);
    const totalSupplyAfter = await token.totalSupply();

    expect(ownerBalanceAfter).to.equal(ownerBalanceBefore.sub(burnAmount));
    expect(totalSupplyAfter).to.equal(totalSupplyBefore.sub(burnAmount));
    });

    it("应该触发 Burn 事件", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    const burnAmount = TEST_CONSTANTS.BURN_AMOUNT;

    await expect(token.connect(owner).burn(burnAmount))
    .to.emit(token, "Burn")
    .withArgs(owner.address, burnAmount);
    });
    });

    describe("权限管理", function () {
    it("所有者应该可以添加铸币者", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    // 验证 user1 初始不是铸币者
    expect(await token.minters(user1.address)).to.be.false;

    // 添加铸币者
    await token.connect(owner).addMinter(user1.address);

    // 验证
    expect(await token.minters(user1.address)).to.be.true;
    });

    it("应该触发 MinterAdded 事件", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    await expect(token.connect(owner).addMinter(user1.address))
    .to.emit(token, "MinterAdded")
    .withArgs(user1.address);
    });

    it("非所有者不应该可以添加铸币者", async function () {
    const { token, user1, user2 } = await loadFixture(deployTokenFixture);

    await expect(
    token.connect(user1).addMinter(user2.address)
    ).to.be.revertedWith("Ownable: caller is not the owner");
    });

    it("所有者应该可以移除铸币者", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    // 先添加为铸币者
    await token.connect(owner).addMinter(user1.address);
    expect(await token.minters(user1.address)).to.be.true;

    // 移除铸币者
    await token.connect(owner).removeMinter(user1.address);

    // 验证
    expect(await token.minters(user1.address)).to.be.false;
    });

    it("应该触发 MinterRemoved 事件", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    // 先添加
    await token.connect(owner).addMinter(user1.address);

    // 移除并验证事件
    await expect(token.connect(owner).removeMinter(user1.address))
    .to.emit(token, "MinterRemoved")
    .withArgs(user1.address);
    });
    });

    describe("暂停功能", function () {
    it("所有者应该可以暂停合约", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    await token.connect(owner).pause();

    expect(await token.paused()).to.be.true;
    });

    it("暂停时转账应该失败", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    // 暂停合约
    await token.connect(owner).pause();

    const transferAmount = ethers.utils.parseEther("100");

    await expect(
    token.connect(owner).transfer(user1.address, transferAmount)
    ).to.be.revertedWith("Token transfer while paused");
    });

    it("暂停时铸造应该失败", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    // 暂停合约
    await token.connect(owner).pause();

    const mintAmount = TEST_CONSTANTS.MINT_AMOUNT;

    await expect(
    token.connect(owner).mint(user1.address, mintAmount)
    ).to.be.revertedWith("Pausable: paused");
    });

    it("所有者应该可以恢复合约", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    // 暂停
    await token.connect(owner).pause();
    expect(await token.paused()).to.be.true;

    // 恢复
    await token.connect(owner).unpause();
    expect(await token.paused()).to.be.false;
    });

    it("非所有者不应该可以暂停合约", async function () {
    const { token, user1 } = await loadFixture(deployTokenFixture);

    await expect(token.connect(user1).pause()).to.be.revertedWith(
    "Ownable: caller is not the owner"
    );
    });
    });

    describe("边界情况和安全测试", function () {
    it("不应该允许向零地址转账", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    const transferAmount = ethers.utils.parseEther("100");

    await expect(
    token.connect(owner).transfer(ethers.constants.AddressZero, transferAmount)
    ).to.be.revertedWith("ERC20: transfer to the zero address");
    });

    it("不应该允许铸造到零地址", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    const mintAmount = TEST_CONSTANTS.MINT_AMOUNT;

    await expect(
    token.connect(owner).mint(ethers.constants.AddressZero, mintAmount)
    ).to.be.revertedWith("ERC20: mint to the zero address");
    });

    it("应该正确处理大额数字", async function () {
    const { token, owner } = await loadFixture(deployTokenFixture);

    const largeAmount = TEST_CONSTANTS.MAX_SUPPLY.sub(
    TEST_CONSTANTS.INITIAL_SUPPLY
    );

    // 铸造剩余的最大供应量
    await token.connect(owner).mint(owner.address, largeAmount);

    // 验证总供应量等于最大供应量
    const totalSupply = await token.totalSupply();
    expect(totalSupply).to.equal(TEST_CONSTANTS.MAX_SUPPLY);
    });

    it("Gas 消耗测试", async function () {
    const { token, owner, user1 } = await loadFixture(deployTokenFixture);

    const transferAmount = ethers.utils.parseEther("100");

    // 测量转账的 Gas 消耗
    const tx = await token.connect(owner).transfer(user1.address, transferAmount);
    const receipt = await tx.wait();

    console.log(`转账 Gas 消耗: ${receipt.gasUsed.toString()}`);

    // 验证 Gas 消耗在合理范围内
    expect(receipt.gasUsed.toNumber()).to.be.lessThan(100000);
    });
    });
    });

    4.1.3 集成测试

    // test/integration/TokenFlow.test.ts
    import { expect } from "chai";
    import { ethers } from "hardhat";
    import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";

    import { MyToken, MyToken__factory } from "../../typechain-types";

    describe("MyToken 集成测试", function () {
    async function deployFixture() {
    const [owner, user1, user2, user3] = await ethers.getSigners();

    const MyTokenFactory = (await ethers.getContractFactory(
    "MyToken"
    )) as MyToken__factory;

    const token = await MyTokenFactory.deploy(
    "Integration Token",
    "INT",
    ethers.utils.parseEther("1000000")
    );

    await token.deployed();

    return { token, owner, user1, user2, user3 };
    }

    describe("完整业务流程", function () {
    it("应该完成完整的代币生命周期", async function () {
    const { token, owner, user1, user2 } = await loadFixture(deployFixture);

    // 1. 初始状态验证
    const initialOwnerBalance = await token.balanceOf(owner.address);
    expect(initialOwnerBalance).to.equal(ethers.utils.parseEther("1000000"));

    // 2. 转账
    const transferAmount = ethers.utils.parseEther("500");
    await token.connect(owner).transfer(user1.address, transferAmount);

    expect(await token.balanceOf(user1.address)).to.equal(transferAmount);

    // 3. 添加铸币者
    await token.connect(owner).addMinter(user1.address);
    expect(await token.minters(user1.address)).to.be.true;

    // 4. 新铸币者铸造代币
    const mintAmount = ethers.utils.parseEther("200");
    await token.connect(user1).mint(user2.address, mintAmount);

    expect(await token.balanceOf(user2.address)).to.equal(mintAmount);

    // 5. 批量转账
    const recipients = [owner.address, user1.address];
    const amounts = [
    ethers.utils.parseEther("50"),
    ethers.utils.parseEther("30"),
    ];

    await token.connect(user2).batchTransfer(recipients, amounts);

    // 6. 销毁代币
    const burnAmount = ethers.utils.parseEther("100");
    await token.connect(owner).burn(burnAmount);

    // 7. 暂停合约
    await token.connect(owner).pause();
    expect(await token.paused()).to.be.true;

    // 8. 验证暂停后不能转账
    await expect(
    token.connect(user1).transfer(owner.address, ethers.utils.parseEther("10"))
    ).to.be.revertedWith("Token transfer while paused");

    // 9. 恢复合约
    await token.connect(owner).unpause();
    expect(await token.paused()).to.be.false;

    // 10. 最终余额验证
    const finalTotalSupply = await token.totalSupply();
    console.log(`最终总供应量: ${ethers.utils.formatEther(finalTotalSupply)}`);

    // 验证所有操作后的总供应量
    const expectedTotalSupply = ethers.utils.parseEther("1000000")
    .add(mintAmount) // 铸造增加
    .sub(burnAmount); // 销毁减少

    expect(finalTotalSupply).to.equal(expectedTotalSupply);
    });
    });
    });

    4.2 运行测试

    # 运行所有测试
    npx hardhat test

    # 运行特定测试文件
    npx hardhat test ./test/MyToken.test.ts

    # 运行特定测试套件
    npx hardhat test –grep "部署"

    # 并行运行测试(加快速度)
    npx hardhat test –parallel

    # 显示详细日志
    npx hardhat test –verbose

    # 生成测试覆盖率报告
    npx hardhat coverage

    # 运行测试并生成 Gas 报告
    REPORT_GAS=true npx hardhat test

    # 在指定网络运行测试
    npx hardhat test –network hardhat

    # 运行集成测试
    npx hardhat test ./test/integration/

    # 调试测试(在 VS Code 中)
    # 1. 点击测试旁边的调试按钮
    # 2. 或使用 F5 启动调试

    4.3 测试覆盖率

    # 安装覆盖率工具
    npm install –save-dev solidity-coverage

    # 在 hardhat.config.ts 中配置
    import "solidity-coverage";

    # 运行覆盖率测试
    npx hardhat coverage

    # 生成 HTML 报告
    npx hardhat coverage –testfiles "test/*.ts" –solcoverjs .solcover.js

    第五章:部署脚本

    5.1 基础部署脚本

    // scripts/deploy.ts
    import { ethers, upgrades, network, run } from "hardhat";
    import fs from "fs";
    import path from "path";

    // 部署配置接口
    interface DeploymentConfig {
    tokenName: string;
    tokenSymbol: string;
    initialSupply: string;
    verifyContract: boolean;
    saveDeployment: boolean;
    }

    // 默认部署配置
    const DEFAULT_CONFIG: DeploymentConfig = {
    tokenName: "MyToken",
    tokenSymbol: "MTK",
    initialSupply: "1000000", // 100万代币
    verifyContract: true,
    saveDeployment: true,
    };

    async function main() {
    console.log(`🚀 开始部署到网络: ${network.name}`);

    // 获取部署配置
    const config = await getDeploymentConfig();
    console.log("📋 部署配置:", config);

    // 获取部署者账户
    const [deployer] = await ethers.getSigners();
    console.log(`👤 部署者地址: ${deployer.address}`);
    console.log(`💰 部署者余额: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);

    // 部署合约
    console.log("⏳ 正在部署 MyToken 合约…");
    const MyToken = await ethers.getContractFactory("MyToken");

    const initialSupply = ethers.utils.parseEther(config.initialSupply);
    const token = await MyToken.deploy(
    config.tokenName,
    config.tokenSymbol,
    initialSupply
    );

    await token.deployed();

    console.log(`✅ MyToken 合约部署成功!`);
    console.log(`📝 合约地址: ${token.address}`);
    console.log(`🏷️ 代币名称: ${await token.name()}`);
    console.log(`🔤 代币符号: ${await token.symbol()}`);
    console.log(`📊 总供应量: ${ethers.utils.formatEther(await token.totalSupply())}`);

    // 验证合约(如果需要)
    if (config.verifyContract && network.name !== "hardhat" && network.name !== "localhost") {
    console.log("🔍 正在验证合约…");
    await verifyContract(token.address, [
    config.tokenName,
    config.tokenSymbol,
    initialSupply,
    ]);
    }

    // 保存部署信息(如果需要)
    if (config.saveDeployment) {
    await saveDeploymentInfo(token, config);
    }

    // 执行部署后操作
    await postDeploymentActions(token, deployer);

    console.log("🎉 部署流程完成!");
    }

    /**
    * 获取部署配置
    */
    async function getDeploymentConfig(): Promise<DeploymentConfig> {
    // 可以根据网络调整配置
    const networkName = network.name;

    let config = { …DEFAULT_CONFIG };

    // 网络特定配置
    switch (networkName) {
    case "mainnet":
    config.verifyContract = true;
    config.initialSupply = "10000000"; // 主网使用1000万
    break;
    case "goerli":
    case "sepolia":
    config.verifyContract = true;
    config.initialSupply = "1000000"; // 测试网使用100万
    break;
    case "hardhat":
    case "localhost":
    config.verifyContract = false;
    break;
    }

    // 从环境变量覆盖配置
    if (process.env.TOKEN_NAME) {
    config.tokenName = process.env.TOKEN_NAME;
    }
    if (process.env.TOKEN_SYMBOL) {
    config.tokenSymbol = process.env.TOKEN_SYMBOL;
    }
    if (process.env.INITIAL_SUPPLY) {
    config.initialSupply = process.env.INITIAL_SUPPLY;
    }
    if (process.env.VERIFY_CONTRACT) {
    config.verifyContract = process.env.VERIFY_CONTRACT === "true";
    }

    return config;
    }

    /**
    * 验证合约
    */
    async function verifyContract(contractAddress: string, args: any[]) {
    try {
    console.log(`⏳ 等待区块确认…`);
    // 等待足够的区块确认
    await new Promise(resolve => setTimeout(resolve, 30000));

    console.log(`🔧 开始验证合约 ${contractAddress}…`);
    await run("verify:verify", {
    address: contractAddress,
    constructorArguments: args,
    });

    console.log("✅ 合约验证成功!");
    } catch (error: any) {
    if (error.message.toLowerCase().includes("already verified")) {
    console.log("📝 合约已经验证过了");
    } else {
    console.error("❌ 合约验证失败:", error);
    }
    }
    }

    /**
    * 保存部署信息
    */
    async function saveDeploymentInfo(token: any, config: DeploymentConfig) {
    const networkName = network.name;
    const chainId = network.config.chainId;

    const deploymentDir = path.join(__dirname, "..", "deployments");
    const networkDir = path.join(deploymentDir, networkName);

    // 创建目录
    if (!fs.existsSync(networkDir)) {
    fs.mkdirSync(networkDir, { recursive: true });
    }

    // 部署信息
    const deploymentInfo = {
    network: networkName,
    chainId: chainId,
    contract: "MyToken",
    address: token.address,
    deployer: (await ethers.getSigners())[0].address,
    deploymentTime: new Date().toISOString(),
    config: config,
    transactionHash: token.deployTransaction.hash,
    blockNumber: token.deployTransaction.blockNumber || "pending",
    abi: JSON.parse(token.interface.format(ethers.utils.FormatTypes.json) as string),
    };

    // 保存到文件
    const deploymentFile = path.join(networkDir, "MyToken.json");
    fs.writeFileSync(
    deploymentFile,
    JSON.stringify(deploymentInfo, null, 2)
    );

    console.log(`💾 部署信息已保存到: ${deploymentFile}`);

    // 同时保存到前端目录(如果存在)
    const frontendDir = path.join(__dirname, "..", "frontend", "src", "contracts");
    if (fs.existsSync(frontendDir)) {
    const frontendInfo = {
    address: token.address,
    abi: deploymentInfo.abi,
    network: networkName,
    chainId: chainId,
    };

    fs.writeFileSync(
    path.join(frontendDir, "MyToken.json"),
    JSON.stringify(frontendInfo, null, 2)
    );

    console.log("📱 合约信息已保存到前端目录");
    }
    }

    /**
    * 部署后操作
    */
    async function postDeploymentActions(token: any, deployer: any) {
    console.log("⚙️ 执行部署后操作…");

    try {
    // 示例:添加额外的铸币者
    const additionalMinter = process.env.ADDITIONAL_MINTER;
    if (additionalMinter) {
    console.log(`👥 添加额外铸币者: ${additionalMinter}`);
    const tx = await token.connect(deployer).addMinter(additionalMinter);
    await tx.wait();
    console.log("✅ 额外铸币者添加成功");
    }

    // 示例:铸造额外代币
    const extraMintAmount = process.env.EXTRA_MINT_AMOUNT;
    if (extraMintAmount) {
    console.log(`🪙 铸造额外代币: ${extraMintAmount}`);
    const amount = ethers.utils.parseEther(extraMintAmount);
    const tx = await token.connect(deployer).mint(deployer.address, amount);
    await tx.wait();
    console.log("✅ 额外代币铸造成功");
    }

    // 示例:转移所有权
    const newOwner = process.env.NEW_OWNER;
    if (newOwner) {
    console.log(`👑 转移合约所有权到: ${newOwner}`);
    const tx = await token.connect(deployer).transferOwnership(newOwner);
    await tx.wait();
    console.log("✅ 所有权转移成功");
    }

    } catch (error) {
    console.error("❌ 部署后操作失败:", error);
    }
    }

    /**
    * 错误处理
    */
    main()
    .then(() => process.exit(0))
    .catch((error) => {
    console.error("💥 部署失败:", error);
    process.exit(1);
    });

    5.2 多网络部署脚本

    // scripts/deploy-multiple.ts
    import { ethers, network, run } from "hardhat";
    import { DeployFunction } from "hardhat-deploy/types";
    import { HardhatRuntimeEnvironment } from "hardhat/types";

    // 部署函数
    const deployFunc: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
    const { deployments, getNamedAccounts } = hre;
    const { deploy, log } = deployments;
    const { deployer } = await getNamedAccounts();

    log(`🚀 部署 MyToken 到 ${network.name} 网络…`);

    // 网络特定配置
    const networkConfig = getNetworkConfig(network.name);

    const MyToken = await deploy("MyToken", {
    from: deployer,
    args: [
    networkConfig.tokenName,
    networkConfig.tokenSymbol,
    ethers.utils.parseEther(networkConfig.initialSupply),
    ],
    log: true,
    waitConfirmations: networkConfig.waitConfirmations,
    });

    log(`✅ MyToken 部署成功: ${MyToken.address}`);

    // 验证合约
    if (networkConfig.verify && MyToken.transactionHash) {
    log("🔍 验证合约…");
    await verifyContract(MyToken.address, [
    networkConfig.tokenName,
    networkConfig.tokenSymbol,
    ethers.utils.parseEther(networkConfig.initialSupply),
    ]);
    }

    return true;
    };

    // 网络配置
    function getNetworkConfig(networkName: string) {
    const defaultConfig = {
    tokenName: "MyToken",
    tokenSymbol: "MTK",
    initialSupply: "1000000",
    waitConfirmations: 1,
    verify: false,
    };

    const configs: Record<string, any> = {
    hardhat: {
    …defaultConfig,
    verify: false,
    },
    localhost: {
    …defaultConfig,
    verify: false,
    },
    goerli: {
    …defaultConfig,
    initialSupply: "500000",
    waitConfirmations: 6,
    verify: true,
    },
    sepolia: {
    …defaultConfig,
    initialSupply: "500000",
    waitConfirmations: 6,
    verify: true,
    },
    mainnet: {
    …defaultConfig,
    tokenName: "MyToken Mainnet",
    initialSupply: "10000000",
    waitConfirmations: 12,
    verify: true,
    },
    polygon: {
    …defaultConfig,
    tokenName: "MyToken Polygon",
    initialSupply: "1000000",
    waitConfirmations: 5,
    verify: true,
    },
    };

    return configs[networkName] || defaultConfig;
    }

    // 验证合约
    async function verifyContract(contractAddress: string, args: any[]) {
    try {
    await run("verify:verify", {
    address: contractAddress,
    constructorArguments: args,
    });
    } catch (error: any) {
    if (error.message.toLowerCase().includes("already verified")) {
    console.log("✅ 合约已经验证过了");
    } else {
    console.error("❌ 验证失败:", error);
    }
    }
    }

    export default deployFunc;
    deployFunc.tags = ["MyToken"];

    5.3 升级部署脚本

    // scripts/deploy-upgradeable.ts
    import { ethers, upgrades, network } from "hardhat";

    async function main() {
    console.log(`🚀 部署可升级合约到 ${network.name}`);

    const [deployer] = await ethers.getSigners();
    console.log(`👤 部署者: ${deployer.address}`);

    // 部署逻辑合约
    console.log("⏳ 部署 MyTokenV1…");
    const MyTokenV1 = await ethers.getContractFactory("MyTokenV1");
    const proxy = await upgrades.deployProxy(MyTokenV1, [
    "MyToken",
    "MTK",
    ethers.utils.parseEther("1000000"),
    ], {
    initializer: "initialize",
    kind: "uups", // 或 "transparent"
    });

    await proxy.deployed();
    console.log(`✅ 代理合约地址: ${proxy.address}`);

    // 获取逻辑合约地址
    const logicAddress = await upgrades.erc1967.getImplementationAddress(proxy.address);
    console.log(`🔧 逻辑合约地址: ${logicAddress}`);

    // 获取代理管理员地址
    const adminAddress = await upgrades.erc1967.getAdminAddress(proxy.address);
    console.log(`👑 代理管理员地址: ${adminAddress}`);

    // 保存部署信息
    const deploymentInfo = {
    network: network.name,
    chainId: network.config.chainId,
    proxyAddress: proxy.address,
    logicAddress: logicAddress,
    adminAddress: adminAddress,
    deployer: deployer.address,
    deploymentTime: new Date().toISOString(),
    };

    console.log("📋 部署信息:", deploymentInfo);

    // 验证逻辑合约
    if (network.name !== "hardhat" && network.name !== "localhost") {
    console.log("🔍 验证逻辑合约…");
    try {
    await run("verify:verify", {
    address: logicAddress,
    });
    } catch (error) {
    console.error("验证失败:", error);
    }
    }
    }

    main()
    .then(() => process.exit(0))
    .catch((error) => {
    console.error(error);
    process.exit(1);
    });

    5.4 运行部署脚本

    # 部署到本地网络
    npx hardhat run scripts/deploy.ts –network localhost

    # 部署到测试网
    npx hardhat run scripts/deploy.ts –network goerli

    # 部署到主网
    npx hardhat run scripts/deploy.ts –network mainnet

    # 带参数部署
    TOKEN_NAME="MyAwesomeToken" TOKEN_SYMBOL="MAT" npx hardhat run scripts/deploy.ts –network goerli

    # 部署并验证
    npx hardhat run scripts/deploy.ts –network sepolia –verify

    # 部署可升级合约
    npx hardhat run scripts/deploy-upgradeable.ts –network goerli

    # 使用 hardhat-deploy 插件
    npx hardhat deploy –network goerli –tags MyToken

    # 交互式部署(使用 console)
    npx hardhat console –network localhost
    > const MyToken = await ethers.getContractFactory("MyToken")
    > const token = await MyToken.deploy("Test", "TST", ethers.utils.parseEther("1000"))
    > await token.deployed()
    > console.log("合约地址:", token.address)

    第六章:交互与调试

    6.1 Hardhat Console 使用

    # 启动 Hardhat Console
    npx hardhat console –network localhost

    # 在 console 中操作
    > const [owner, user1] = await ethers.getSigners()
    > const MyToken = await ethers.getContractFactory("MyToken")
    > const token = await MyToken.deploy("MyToken", "MTK", ethers.utils.parseEther("1000000"))
    > await token.deployed()
    > console.log("合约地址:", token.address)

    # 调用合约方法
    > await token.name()
    'MyToken'
    > await token.balanceOf(owner.address)
    BigNumber { _hex: '0x…', _isBigNumber: true }
    > await token.transfer(user1.address, ethers.utils.parseEther("100"))
    > await token.balanceOf(user1.address)
    BigNumber { _hex: '0x…', _isBigNumber: true }

    # 查看交易
    > const tx = await token.transfer(user1.address, ethers.utils.parseEther("50"))
    > await tx.wait()
    > console.log("交易哈希:", tx.hash)

    # 退出 console
    > .exit

    6.2 调试交易

    // scripts/debug-transaction.ts
    import { ethers } from "hardhat";

    async function debugTransaction() {
    // 1. 获取合约实例
    const tokenAddress = "0x…"; // 替换为实际地址
    const MyToken = await ethers.getContractFactory("MyToken");
    const token = MyToken.attach(tokenAddress);

    // 2. 获取最近的事件
    console.log("📜 获取最近的事件…");
    const filter = token.filters.Transfer();
    const events = await token.queryFilter(filter, -10000, 'latest');
    console.log("最近转账事件:", events.length);

    // 3. 调试特定交易
    const txHash = "0x…"; // 替换为交易哈希
    console.log(`🔍 调试交易: ${txHash}`);

    const tx = await ethers.provider.getTransaction(txHash);
    console.log("交易详情:", {
    from: tx.from,
    to: tx.to,
    value: ethers.utils.formatEther(tx.value),
    gasPrice: ethers.utils.formatUnits(tx.gasPrice!, "gwei"),
    data: tx.data,
    });

    const receipt = await ethers.provider.getTransactionReceipt(txHash);
    console.log("交易收据:", {
    status: receipt.status,
    gasUsed: receipt.gasUsed.toString(),
    logs: receipt.logs.length,
    });

    // 4. 使用 Hardhat 调试器
    console.log("🐛 使用 Hardhat 调试器…");
    // 在终端运行: npx hardhat debug <txHash>

    // 5. 分析 Gas 消耗
    console.log("⛽ 分析 Gas 消耗…");
    const gasUsed = receipt.gasUsed;
    const gasPrice = tx.gasPrice!;
    const gasCost = gasUsed.mul(gasPrice);
    console.log(`Gas 消耗: ${gasUsed.toString()}`);
    console.log(`Gas 成本: ${ethers.utils.formatEther(gasCost)} ETH`);
    }

    debugTransaction()
    .then(() => process.exit(0))
    .catch((error) => {
    console.error(error);
    process.exit(1);
    });

    6.3 交互脚本

    // scripts/interact.ts
    import { ethers } from "hardhat";

    async function interactWithContract() {
    console.log("🤖 开始与合约交互…");

    // 1. 获取合约实例
    const tokenAddress = process.env.TOKEN_ADDRESS;
    if (!tokenAddress) {
    throw new Error("请设置 TOKEN_ADDRESS 环境变量");
    }

    const MyToken = await ethers.getContractFactory("MyToken");
    const token = MyToken.attach(tokenAddress);

    // 2. 获取基本信息
    console.log("📊 获取合约基本信息…");
    const name = await token.name();
    const symbol = await token.symbol();
    const totalSupply = await token.totalSupply();
    const owner = await token.owner();

    console.log(`代币名称: ${name}`);
    console.log(`代币符号: ${symbol}`);
    console.log(`总供应量: ${ethers.utils.formatEther(totalSupply)}`);
    console.log(`所有者: ${owner}`);

    // 3. 获取账户余额
    const [deployer, user1] = await ethers.getSigners();
    const deployerBalance = await token.balanceOf(deployer.address);
    const user1Balance = await token.balanceOf(user1.address);

    console.log(`\\n💰 账户余额:`);
    console.log(`部署者: ${ethers.utils.formatEther(deployerBalance)} ${symbol}`);
    console.log(`用户1: ${ethers.utils.formatEther(user1Balance)} ${symbol}`);

    // 4. 执行转账
    console.log(`\\n🔄 执行转账…`);
    const transferAmount = ethers.utils.parseEther("100");

    console.log(`从部署者转账 100 ${symbol} 到用户1…`);
    const tx = await token.connect(deployer).transfer(user1.address, transferAmount);
    await tx.wait();
    console.log("✅ 转账成功!");

    // 5. 验证转账结果
    const newDeployerBalance = await token.balanceOf(deployer.address);
    const newUser1Balance = await token.balanceOf(user1.address);

    console.log(`\\n📊 转账后余额:`);
    console.log(`部署者: ${ethers.utils.formatEther(newDeployerBalance)} ${symbol}`);
    console.log(`用户1: ${ethers.utils.formatEther(newUser1Balance)} ${symbol}`);

    // 6. 检查铸币者权限
    console.log(`\\n👥 检查铸币者权限…`);
    const isDeployerMinter = await token.minters(deployer.address);
    const isUser1Minter = await token.minters(user1.address);

    console.log(`部署者是铸币者: ${isDeployerMinter}`);
    console.log(`用户1是铸币者: ${isUser1Minter}`);

    // 7. 如果部署者是铸币者,执行铸币
    if (isDeployerMinter) {
    console.log(`\\n🪙 执行铸币操作…`);
    const mintAmount = ethers.utils.parseEther("500");

    console.log(`铸币 500 ${symbol} 给用户1…`);
    const mintTx = await token.connect(deployer).mint(user1.address, mintAmount);
    await mintTx.wait();
    console.log("✅ 铸币成功!");

    const finalUser1Balance = await token.balanceOf(user1.address);
    console.log(`用户1最终余额: ${ethers.utils.formatEther(finalUser1Balance)} ${symbol}`);
    }

    // 8. 获取合约事件
    console.log(`\\n📜 获取最近的事件…`);
    const transferEvents = await token.queryFilter(
    token.filters.Transfer(),
    -1000,
    'latest'
    );

    console.log(`最近 ${transferEvents.length} 次转账事件:`);
    transferEvents.slice(-5).forEach((event, i) => {
    console.log(` ${i + 1}. From: ${event.args!.from} -> To: ${event.args!.to}, Amount: ${ethers.utils.formatEther(event.args!.value)}`);
    });
    }

    interactWithContract()
    .then(() => {
    console.log("\\n🎉 交互完成!");
    process.exit(0);
    })
    .catch((error) => {
    console.error("❌ 交互失败:", error);
    process.exit(1);
    });

    第七章:自动化与工作流

    7.1 npm 脚本配置

    // package.json 中的 scripts 部分
    {
    "scripts": {
    // 开发
    "compile": "hardhat compile",
    "clean": "hardhat clean",
    "typechain": "hardhat typechain",

    // 测试
    "test": "hardhat test",
    "test:watch": "hardhat test –watch",
    "test:gas": "REPORT_GAS=true hardhat test",
    "test:coverage": "hardhat coverage",
    "test:debug": "hardhat test –debug",

    // 代码质量
    "lint": "npm run lint:sol && npm run lint:ts",
    "lint:sol": "solhint 'contracts/**/*.sol'",
    "lint:ts": "eslint '**/*.ts' –fix",
    "format": "prettier –write '**/*.{js,ts,json,md,sol}'",
    "format:check": "prettier –check '**/*.{js,ts,json,md,sol}'",

    // 安全分析
    "slither": "slither .",
    "mythril": "myth analyze contracts/*.sol",

    // 部署
    "deploy:local": "hardhat run scripts/deploy.ts –network localhost",
    "deploy:goerli": "hardhat run scripts/deploy.ts –network goerli",
    "deploy:sepolia": "hardhat run scripts/deploy.ts –network sepolia",
    "deploy:mainnet": "hardhat run scripts/deploy.ts –network mainnet",

    // 验证
    "verify:goerli": "hardhat verify –network goerli",
    "verify:sepolia": "hardhat verify –network sepolia",

    // 节点
    "node": "hardhat node",
    "node:fork": "FORKING_ENABLED=true hardhat node",

    // 控制台
    "console": "hardhat console –network localhost",
    "console:goerli": "hardhat console –network goerli",

    // 任务
    "accounts": "hardhat accounts",
    "balance": "hardhat balance –address",
    "flatten": "hardhat flatten",

    // 完整工作流
    "build": "npm run clean && npm run compile && npm run typechain",
    "validate": "npm run lint && npm run test && npm run build",
    "predeploy": "npm run validate",
    "ci": "npm run lint && npm run test:coverage && npm run build"
    }
    }

    7.2 Git Hooks 配置

    // package.json 中的 husky 配置
    {
    "husky": {
    "hooks": {
    "pre-commit": "lint-staged",
    "pre-push": "npm run test",
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
    },
    "lint-staged": {
    "*.sol": [
    "solhint –fix",
    "prettier –write"
    ],
    "*.{js,ts}": [
    "eslint –fix",
    "prettier –write"
    ],
    "*.{json,md}": [
    "prettier –write"
    ]
    }
    }
    # 安装 Git Hooks 工具
    npm install –save-dev husky lint-staged @commitlint/cli @commitlint/config-conventional

    # 初始化 husky
    npx husky install

    # 添加 pre-commit hook
    npx husky add .husky/pre-commit "npx lint-staged"

    # 添加 commit-msg hook
    npx husky add .husky/commit-msg 'npx –no-install commitlint –edit "$1"'
    // commitlint.config.js
    module.exports = {
    extends: ["@commitlint/config-conventional"],
    rules: {
    "type-enum": [
    2,
    "always",
    [
    "feat", // 新功能
    "fix", // 修复bug
    "docs", // 文档更新
    "style", // 代码格式调整
    "refactor", // 代码重构
    "test", // 测试相关
    "chore", // 构建过程或辅助工具变动
    "revert", // 回滚提交
    "perf", // 性能优化
    "ci", // CI配置
    "build" // 构建系统
    ],
    ],
    },
    };

    7.3 CI/CD 配置

    # .github/workflows/ci.yml
    name: CI/CD Pipeline

    on:
    push:
    branches: [ main, develop ]
    pull_request:
    branches: [ main ]

    jobs:
    test:
    runs-on: ubuntu-latest
    strategy:
    matrix:
    node-version: [16.x, 18.x]

    steps:
    – uses: actions/checkout@v3

    – name: Use Node.js ${{ matrix.node-version }}
    uses: actions/setup-node@v3
    with:
    node-version: ${{ matrix.node-version }}
    cache: 'npm'

    – name: Install dependencies
    run: npm ci

    – name: Lint
    run: npm run lint

    – name: Run tests
    run: npm test

    – name: Run coverage
    run: npm run test:coverage

    – name: Upload coverage to Codecov
    uses: codecov/codecov-action@v3
    with:
    files: ./coverage/lcov.info

    – name: Build project
    run: npm run build

    deploy-staging:
    needs: test
    if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest

    steps:
    – uses: actions/checkout@v3

    – name: Use Node.js 18
    uses: actions/setup-node@v3
    with:
    node-version: 18
    cache: 'npm'

    – name: Install dependencies
    run: npm ci

    – name: Deploy to Sepolia Testnet
    run: |
    npx hardhat run scripts/deploy.ts –network sepolia
    env:
    SEPOLIA_RPC_URL: ${{ secrets.SEPOLIA_RPC_URL }}
    PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
    ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}

    deploy-production:
    needs: test
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest

    steps:
    – uses: actions/checkout@v3

    – name: Use Node.js 18
    uses: actions/setup-node@v3
    with:
    node-version: 18
    cache: 'npm'

    – name: Install dependencies
    run: npm ci

    – name: Deploy to Mainnet
    run: |
    npx hardhat run scripts/deploy.ts –network mainnet
    env:
    MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
    PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
    ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}

    第八章:高级功能与优化

    8.1 自定义 Hardhat 任务

    // tasks/index.ts
    import { task } from "hardhat/config";
    import { HardhatRuntimeEnvironment } from "hardhat/types";

    // 账户相关任务
    task("accounts", "显示所有账户和余额")
    .setAction(async (taskArgs, hre: HardhatRuntimeEnvironment) => {
    const accounts = await hre.ethers.getSigners();

    console.log("📋 账户列表:");
    console.log("=".repeat(80));

    for (let i = 0; i < accounts.length; i++) {
    const account = accounts[i];
    const balance = await account.getBalance();
    const ethBalance = hre.ethers.utils.formatEther(balance);

    console.log(`#${i}: ${account.address}`);
    console.log(` 余额: ${ethBalance} ETH`);
    console.log(` 交易次数: ${await account.getTransactionCount()}`);
    console.log("-".repeat(80));
    }
    });

    // 余额查询任务
    task("balance", "查询指定地址的余额")
    .addParam("address", "要查询的地址")
    .setAction(async (taskArgs, hre: HardhatRuntimeEnvironment) => {
    const balance = await hre.ethers.provider.getBalance(taskArgs.address);
    const ethBalance = hre.ethers.utils.formatEther(balance);

    console.log(`💰 地址 ${taskArgs.address} 的余额:`);
    console.log(` ${ethBalance} ETH`);
    console.log(` ${balance.toString()} Wei`);
    });

    // 网络信息任务
    task("network", "显示当前网络信息")
    .setAction(async (taskArgs, hre: HardhatRuntimeEnvironment) => {
    const network = hre.network;
    const provider = hre.ethers.provider;

    console.log("🌐 网络信息:");
    console.log("=".repeat(80));
    console.log(`名称: ${network.name}`);
    console.log(`链ID: ${network.config.chainId}`);

    const blockNumber = await provider.getBlockNumber();
    console.log(`当前区块: ${blockNumber}`);

    const gasPrice = await provider.getGasPrice();
    console.log(`当前Gas价格: ${hre.ethers.utils.formatUnits(gasPrice, "gwei")} Gwei`);

    const feeData = await provider.getFeeData();
    console.log(`基础费用: ${hre.ethers.utils.formatUnits(feeData.maxFeePerGas!, "gwei")} Gwei`);
    console.log(`优先费用: ${hre.ethers.utils.formatUnits(feeData.maxPriorityFeePerGas!, "gwei")} Gwei`);
    });

    // 合约验证任务
    task("verify-contract", "验证合约")
    .addParam("address", "合约地址")
    .addOptionalVariadicPositionalParam("args", "构造函数参数", [])
    .setAction(async (taskArgs, hre: HardhatRuntimeEnvironment) => {
    console.log(`🔍 验证合约 ${taskArgs.address}…`);

    try {
    await hre.run("verify:verify", {
    address: taskArgs.address,
    constructorArguments: taskArgs.args,
    });
    console.log("✅ 合约验证成功!");
    } catch (error: any) {
    if (error.message.toLowerCase().includes("already verified")) {
    console.log("📝 合约已经验证过了");
    } else {
    console.error("❌ 验证失败:", error.message);
    }
    }
    });

    // Gas 估算任务
    task("estimate-gas", "估算合约方法调用的Gas消耗")
    .addParam("contract", "合约名称")
    .addParam("method", "方法名称")
    .addOptionalVariadicPositionalParam("args", "方法参数", [])
    .setAction(async (taskArgs, hre: HardhatRuntimeEnvironment) => {
    const { contract, method, args } = taskArgs;

    console.log(`⛽ 估算 ${contract}.${method} 的Gas消耗…`);

    const Contract = await hre.ethers.getContractFactory(contract);
    const contractInstance = await Contract.deploy();
    await contractInstance.deployed();

    try {
    const estimate = await contractInstance.estimateGas[method](…args);
    console.log(`预估Gas消耗: ${estimate.toString()}`);

    const gasPrice = await hre.ethers.provider.getGasPrice();
    const gasCost = estimate.mul(gasPrice);
    console.log(`预估成本: ${hre.ethers.utils.formatEther(gasCost)} ETH`);
    } catch (error: any) {
    console.error("❌ 估算失败:", error.message);
    }
    });

    // 导出任务
    export {};

    8.2 安全分析配置

    // scripts/security-analysis.ts
    import { exec } from "child_process";
    import { promisify } from "util";

    const execAsync = promisify(exec);

    async function runSecurityAnalysis() {
    console.log("🛡️ 开始安全分析…");

    try {
    // 1. 运行 Slither
    console.log("\\n1. 运行 Slither 分析…");
    try {
    const { stdout: slitherOutput } = await execAsync("slither .");
    console.log("Slither 分析完成");
    console.log(slitherOutput);
    } catch (slitherError: any) {
    console.log("Slither 输出:", slitherError.stdout);
    }

    // 2. 运行 Mythril
    console.log("\\n2. 运行 Mythril 分析…");
    try {
    const { stdout: mythrilOutput } = await execAsync(
    "myth analyze contracts/MyToken.sol"
    );
    console.log("Mythril 分析完成");
    console.log(mythrilOutput);
    } catch (mythrilError: any) {
    console.log("Mythril 输出:", mythrilError.stdout);
    }

    // 3. 运行 Solhint
    console.log("\\n3. 运行 Solhint 检查…");
    try {
    const { stdout: solhintOutput } = await execAsync("npm run lint:sol");
    console.log("Solhint 检查完成");
    console.log(solhintOutput);
    } catch (solhintError: any) {
    console.log("Solhint 发现问题:", solhintError.stdout);
    }

    // 4. 运行 Echidna(如果安装)
    console.log("\\n4. 运行 Echidna 模糊测试…");
    try {
    const { stdout: echidnaOutput } = await execAsync(
    "echidna-test contracts/MyToken.sol –contract MyToken"
    );
    console.log("Echidna 测试完成");
    console.log(echidnaOutput);
    } catch (echidnaError: any) {
    console.log("Echidna 测试失败或未安装:", echidnaError.message);
    }

    console.log("\\n✅ 安全分析完成!");
    } catch (error) {
    console.error("❌ 安全分析失败:", error);
    }
    }

    // 安装安全分析工具
    async function installSecurityTools() {
    console.log("🔧 安装安全分析工具…");

    const tools = [
    { name: "Slither", command: "pip3 install slither-analyzer" },
    { name: "Mythril", command: "pip3 install mythril" },
    { name: "Echidna", command: "brew install echidna" }, // macOS
    { name: "Solhint", command: "npm install -g solhint" },
    ];

    for (const tool of tools) {
    console.log(`安装 ${tool.name}…`);
    try {
    await execAsync(tool.command);
    console.log(`✅ ${tool.name} 安装成功`);
    } catch (error) {
    console.log(`⚠️ ${tool.name} 安装失败:`, error);
    }
    }
    }

    // 运行分析
    runSecurityAnalysis();

    8.3 性能优化

    8.3.1 编译优化

    // hardhat.config.ts 中的优化配置
    const config: HardhatUserConfig = {
    solidity: {
    version: "0.8.19",
    settings: {
    optimizer: {
    enabled: true,
    runs: 1000, // 根据合约使用频率调整
    details: {
    yul: true,
    yulDetails: {
    stackAllocation: true,
    optimizerSteps: "dhfoDgvulfnTUtnIf"
    }
    }
    },
    viaIR: true, // 启用 IR 编译(减少字节码大小)
    metadata: {
    bytecodeHash: "ipfs", // 使用 IPFS 哈希
    },
    outputSelection: {
    "*": {
    "*": [
    "abi",
    "evm.bytecode",
    "evm.deployedBytecode",
    "evm.methodIdentifiers",
    "metadata"
    ],
    "": ["ast"]
    }
    }
    },
    },
    };

    8.3.2 测试优化

    // 使用 fixture 提高测试性能
    import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";

    // 在多个测试中共享部署状态
    async function deployFixture() {
    const [owner, user1, user2] = await ethers.getSigners();
    const MyToken = await ethers.getContractFactory("MyToken");
    const token = await MyToken.deploy(
    "Test Token",
    "TEST",
    ethers.utils.parseEther("1000000")
    );

    return { token, owner, user1, user2 };
    }

    describe("MyToken 测试", function () {
    // 使用 fixture,避免每次测试都重新部署
    it("测试1", async function () {
    const { token, owner } = await loadFixture(deployFixture);
    // 测试逻辑
    });

    it("测试2", async function () {
    const { token, owner, user1 } = await loadFixture(deployFixture);
    // 测试逻辑
    });
    });

    第九章:故障排除与调试

    9.1 常见问题解决

    9.1.1 编译错误

    # 1. 清除缓存重新编译
    npx hardhat clean
    npx hardhat compile

    # 2. 检查 Solidity 版本兼容性
    # 确保 hardhat.config.ts 和合约中的版本一致

    # 3. 检查导入路径
    # 确保 OpenZeppelin 等依赖已正确安装

    # 4. 查看详细错误信息
    npx hardhat compile –verbose

    9.1.2 测试失败

    # 1. 增加测试超时时间
    # 在 hardhat.config.ts 中配置 mocha.timeout

    # 2. 查看详细错误堆栈
    npx hardhat test –verbose

    # 3. 单独运行失败测试
    npx hardhat test –grep "特定测试名"

    # 4. 调试测试
    npx hardhat test –debug

    9.1.3 部署失败

    # 1. 检查网络连接
    # 确保 RPC URL 正确且可访问

    # 2. 检查账户余额
    # 确保部署账户有足够的 ETH 支付 Gas

    # 3. 检查 Gas 价格
    # 在网络拥堵时适当提高 Gas 价格

    # 4. 查看部署日志
    npx hardhat run scripts/deploy.ts –network goerli –verbose

    9.1.4 验证失败

    # 1. 等待足够区块确认
    # 某些网络需要等待多个区块确认后才能验证

    # 2. 检查构造函数参数
    # 确保验证时使用的参数与部署时一致

    # 3. 使用 flatten 命令
    # 如果合约有多个文件,先 flatten
    npx hardhat flatten contracts/MyToken.sol > MyTokenFlattened.sol

    # 4. 手动验证
    # 在 Etherscan 上手动验证

    9.2 调试技巧

    9.2.1 使用 console.log

    // 在 Solidity 0.8.0+ 中使用 console.log
    pragma solidity ^0.8.19;

    import "hardhat/console.sol"; // 导入 Hardhat 的 console

    contract DebugContract {
    uint256 public value;

    function setValue(uint256 _value) public {
    console.log("设置前的值:", value);
    console.log("新值:", _value);
    console.log("调用者:", msg.sender);

    value = _value;

    console.log("设置后的值:", value);
    }
    }

    9.2.2 使用 Hardhat Network 日志

    // 在 hardhat.config.ts 中启用详细日志
    const config: HardhatUserConfig = {
    networks: {
    hardhat: {
    loggingEnabled: true, // 启用日志
    chainId: 31337,
    },
    },
    };

    // 或在测试中启用
    beforeEach(async function () {
    // 启用 Hardhat Network 日志
    await network.provider.send("hardhat_setLoggingEnabled", [true]);
    });

    9.2.3 交易追踪

    // 追踪交易执行
    async function traceTransaction(txHash: string) {
    const trace = await network.provider.send("debug_traceTransaction", [
    txHash,
    {
    tracer: "callTracer",
    tracerConfig: {
    onlyTopCall: false,
    withLog: true,
    },
    },
    ]);

    console.log("交易追踪:", JSON.stringify(trace, null, 2));
    }

    // 或在 Hardhat Console 中
    > const tx = await token.transfer(user1.address, 100)
    > await tx.wait()
    > console.log(await network.provider.send("debug_traceTransaction", [tx.hash]))

    第十章:最佳实践总结

    10.1 项目结构最佳实践

    project/
    ├── contracts/ # 智能合约
    │ ├── interfaces/ # 接口定义
    │ ├── libraries/ # 库合约
    │ ├── tokens/ # 代币相关合约
    │ ├── governance/ # 治理合约
    │ ├── staking/ # 质押合约
    │ └── utils/ # 工具合约
    ├── scripts/ # 脚本
    │ ├── deploy/ # 部署脚本
    │ ├── upgrade/ # 升级脚本
    │ ├── tasks/ # Hardhat 任务
    │ └── utils/ # 脚本工具函数
    ├── test/ # 测试
    │ ├── unit/ # 单元测试
    │ ├── integration/ # 集成测试
    │ ├── fixtures/ # 测试夹具
    │ └── mocks/ # 模拟合约
    ├── deployments/ # 部署记录
    ├── docs/ # 文档
    ├── .vscode/ # VS Code 配置
    ├── .github/ # GitHub 工作流
    └── config/ # 配置文件

    10.2 代码质量最佳实践

    10.2.1 Solidity 编码规范

    // 1. 使用明确的 SPDX 许可证标识
    // SPDX-License-Identifier: MIT

    // 2. 使用稳定的 Solidity 版本
    pragma solidity ^0.8.19;

    // 3. 导入顺序:标准库 -> 第三方库 -> 本地合约
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "../interfaces/IMyToken.sol";
    import "../libraries/TokenMath.sol";

    // 4. 合约结构顺序:
    // – 类型声明
    // – 状态变量
    // – 事件
    // – 修饰器
    // – 构造函数
    // – 接收/回退函数
    // – 外部函数
    // – 公开函数
    // – 内部函数
    // – 私有函数

    // 5. 使用 NatSpec 注释
    /**
    * @title MyToken
    * @author Your Name
    * @notice 这是一个示例 ERC20 代币合约
    * @dev 实现了铸币、销毁和暂停功能
    */
    contract MyToken is ERC20, Ownable {
    // 状态变量注释
    uint256 public constant MAX_SUPPLY = 1_000_000_000 * 10 ** 18;

    // 事件注释
    event Mint(address indexed to, uint256 amount);

    /**
    * @dev 构造函数
    * @param name 代币名称
    * @param symbol 代币符号
    * @param initialSupply 初始供应量
    */
    constructor(
    string memory name,
    string memory symbol,
    uint256 initialSupply
    ) ERC20(name, symbol) {
    // 构造函数逻辑
    }

    /**
    * @dev 铸造新代币
    * @param to 接收地址
    * @param amount 铸造数量
    * @notice 仅所有者或铸币者可调用
    * @custom:requirements 铸造后总供应量不能超过 MAX_SUPPLY
    */
    function mint(address to, uint256 amount) external onlyOwner {
    // 函数逻辑
    }
    }

    10.2.2 安全最佳实践

    // 1. 使用 SafeMath(Solidity 0.8+ 已内置)
    // 2. 检查-效果-交互模式
    function withdraw(uint256 amount) external nonReentrant {
    // 检查
    require(balances[msg.sender] >= amount, "余额不足");
    require(amount > 0, "金额必须大于0");

    // 效果
    balances[msg.sender] -= amount;
    totalSupply -= amount;

    // 交互
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "转账失败");
    }

    // 3. 使用 OpenZeppelin 的安全合约
    import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/security/Pausable.sol";

    // 4. 避免硬编码值
    uint256 public constant MAX_SUPPLY = 1_000_000_000 * 10 ** 18;
    address public constant TREASURY = 0x…;

    // 5. 适当的访问控制
    modifier onlyOwner() {
    require(msg.sender == owner, "Not owner");
    _;
    }

    modifier onlyMinter() {
    require(minters[msg.sender], "Not minter");
    _;
    }

    // 6. 输入验证
    function transfer(address to, uint256 amount) public override returns (bool) {
    require(to != address(0), "Transfer to zero address");
    require(amount > 0, "Transfer amount must be positive");
    require(balanceOf(msg.sender) >= amount, "Insufficient balance");

    return super.transfer(to, amount);
    }

    10.3 部署最佳实践

    10.3.1 多阶段部署

    // scripts/deploy-phased.ts
    async function phasedDeployment() {
    console.log("🚀 开始多阶段部署");

    // 阶段1:部署核心合约
    console.log("\\n📦 阶段1: 部署核心合约");
    const MyToken = await ethers.getContractFactory("MyToken");
    const token = await MyToken.deploy(…);
    await token.deployed();
    console.log(`✅ MyToken 部署: ${token.address}`);

    // 等待确认
    await new Promise(resolve => setTimeout(resolve, 30000));

    // 阶段2:配置合约
    console.log("\\n⚙️ 阶段2: 配置合约");
    await token.addMinter(treasuryAddress);
    await token.transfer(treasuryAddress, initialTreasuryAmount);
    console.log("✅ 合约配置完成");

    // 阶段3:验证合约
    console.log("\\n🔍 阶段3: 验证合约");
    await run("verify:verify", {
    address: token.address,
    constructorArguments: […],
    });
    console.log("✅ 合约验证完成");

    // 阶段4:转移所有权(如果需要)
    console.log("\\n👑 阶段4: 转移所有权");
    await token.transferOwnership(multisigAddress);
    console.log(`✅ 所有权转移到: ${multisigAddress}`);

    console.log("\\n🎉 多阶段部署完成!");
    }

    10.3.2 紧急情况处理

    // scripts/emergency.ts
    // 紧急情况处理脚本
    async function handleEmergency() {
    const tokenAddress = process.env.TOKEN_ADDRESS;
    const emergencyKey = process.env.EMERGENCY_PRIVATE_KEY;

    if (!tokenAddress || !emergencyKey) {
    throw new Error("缺少必要的环境变量");
    }

    // 连接到合约
    const emergencySigner = new ethers.Wallet(emergencyKey, ethers.provider);
    const MyToken = await ethers.getContractFactory("MyToken", emergencySigner);
    const token = MyToken.attach(tokenAddress);

    console.log("🚨 执行紧急操作");

    // 1. 暂停合约
    console.log("⏸️ 暂停合约…");
    const pauseTx = await token.pause();
    await pauseTx.wait();
    console.log("✅ 合约已暂停");

    // 2. 转移剩余资金到安全地址
    console.log("💰 转移资金到安全地址…");
    const safeAddress = process.env.SAFE_ADDRESS;
    const balance = await token.balanceOf(tokenAddress);

    if (balance.gt(0)) {
    const transferTx = await token.transfer(safeAddress, balance);
    await transferTx.wait();
    console.log(`✅ 已转移 ${ethers.utils.formatEther(balance)} 代币到安全地址`);
    }

    // 3. 记录紧急操作
    console.log("📝 记录紧急操作…");
    const emergencyInfo = {
    timestamp: new Date().toISOString(),
    action: "emergency_pause",
    performer: emergencySigner.address,
    tokenAddress: tokenAddress,
    safeAddress: safeAddress,
    transferredAmount: balance.toString(),
    };

    console.log("紧急操作记录:", emergencyInfo);

    console.log("🛡️ 紧急操作完成");
    }

    10.4 监控与维护

    10.4.1 事件监听

    // scripts/monitor.ts
    import { ethers } from "hardhat";

    async function monitorContract() {
    const tokenAddress = process.env.TOKEN_ADDRESS;
    const MyToken = await ethers.getContractFactory("MyToken");
    const token = MyToken.attach(tokenAddress);

    console.log("👂 开始监听合约事件…");

    // 监听所有 Transfer 事件
    token.on("Transfer", (from, to, value, event) => {
    console.log(`🔄 转账事件:`);
    console.log(` 从: ${from}`);
    console.log(` 到: ${to}`);
    console.log(` 金额: ${ethers.utils.formatEther(value)}`);
    console.log(` 交易哈希: ${event.transactionHash}`);
    console.log(` 区块号: ${event.blockNumber}`);
    console.log("-".repeat(50));
    });

    // 监听 Mint 事件
    token.on("Mint", (to, amount, event) => {
    console.log(`🪙 铸币事件:`);
    console.log(` 接收者: ${to}`);
    console.log(` 金额: ${ethers.utils.formatEther(amount)}`);
    console.log("-".repeat(50));
    });

    // 监听暂停事件
    token.on("Paused", (account) => {
    console.log(`⏸️ 合约被暂停`);
    console.log(` 操作者: ${account}`);
    console.log("-".repeat(50));
    });

    // 监听恢复事件
    token.on("Unpaused", (account) => {
    console.log(`▶️ 合约恢复`);
    console.log(` 操作者: ${account}`);
    console.log("-".repeat(50));
    });

    // 保持脚本运行
    console.log("监控中…按 Ctrl+C 停止");

    process.on("SIGINT", () => {
    console.log("\\n🛑 停止监控");
    token.removeAllListeners();
    process.exit(0);
    });
    }

    monitorContract().catch(console.error);

    10.4.2 定期检查脚本

    // scripts/health-check.ts
    import { ethers } from "hardhat";

    async function healthCheck() {
    console.log("🏥 执行合约健康检查");

    const tokenAddress = process.env.TOKEN_ADDRESS;
    const MyToken = await ethers.getContractFactory("MyToken");
    const token = MyToken.attach(tokenAddress);

    const checks = [
    {
    name: "合约状态",
    check: async () => {
    const paused = await token.paused();
    return !paused;
    },
    errorMessage: "合约处于暂停状态",
    },
    {
    name: "总供应量",
    check: async () => {
    const totalSupply = await token.totalSupply();
    const maxSupply = await token.MAX_SUPPLY();
    return totalSupply.lte(maxSupply);
    },
    errorMessage: "总供应量超过最大供应量",
    },
    {
    name: "所有者权限",
    check: async () => {
    const owner = await token.owner();
    return owner !== ethers.constants.AddressZero;
    },
    errorMessage: "所有者地址为零地址",
    },
    {
    name: "代币符号",
    check: async () => {
    const symbol = await token.symbol();
    return symbol.length > 0;
    },
    errorMessage: "代币符号为空",
    },
    ];

    let allPassed = true;

    for (const check of checks) {
    try {
    const passed = await check.check();
    if (passed) {
    console.log(`✅ ${check.name}: 正常`);
    } else {
    console.log(`❌ ${check.name}: ${check.errorMessage}`);
    allPassed = false;
    }
    } catch (error) {
    console.log(`⚠️ ${check.name}: 检查失败 – ${error.message}`);
    allPassed = false;
    }
    }

    if (allPassed) {
    console.log("\\n🎉 所有健康检查通过!");
    } else {
    console.log("\\n⚠️ 发现潜在问题,请及时处理");
    process.exit(1);
    }
    }

    healthCheck().catch(console.error);

    总结

    本指南详细介绍了如何使用 VS Code 和 Hardhat 进行智能合约开发的全流程,包括:

  • 环境搭建:配置 VS Code 和安装必要扩展

  • 项目初始化:创建和配置 Hardhat TypeScript 项目

  • 智能合约开发:编写、编译和优化 Solidity 合约

  • 测试开发:编写全面的单元测试和集成测试

  • 部署流程:多网络部署、验证和升级

  • 交互调试:使用 Hardhat Console 和调试工具

  • 自动化工作流:配置 CI/CD 和 Git Hooks

  • 高级功能:自定义任务、安全分析和性能优化

  • 故障排除:常见问题解决和调试技巧

  • 最佳实践:代码质量、安全和部署的最佳实践

  • 通过遵循本指南,您可以建立一个专业、高效且可维护的智能合约开发环境,提高开发效率并确保代码质量。记住,智能合约开发需要特别注意安全性,务必进行充分测试和安全审计后再部署到生产环境。

    赞(0)
    未经允许不得转载:171主机测评 » VS Code + Hardhat 智能合约开发完整指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址