使用 Web3.js 部署并交互智能合约的完整指南

·

一步步学会 Solidity 合约编译、部署与 Web3.js 交互,面向前端开发者、区块链初学者与 dApp 架构者。

内容速览

本文将带你完成以下 7 个关键动作:

  1. 检查运行环境
  2. 初始化 Node 项目
  3. 用 Solidity 编写并保存智能合约
  4. 生成 合约 ABI字节码
  5. 利用 Hardhat 建立开发网络并配置 Web3.js
  6. 用 Web3.js 部署智能合约
  7. 调用合约函数并验证结果

👉 十分钟无废话代码+DEMO,边学边跑高效入门智能合约交互。

前置条件

打开终端,验证版本:

node -v   # 输出版本即可
npm -v    # 输出版本即可

第一步:项目初始化

建立工作目录并进入:

mkdir smart-contract-tutorial
cd smart-contract-tutorial

自动生成 package.json

npm init -y

第二步:编写 Solidity 合约

新建 MyContract.sol,内容如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MyContract {
    uint256 public myNumber;

    constructor(uint256 _initial) {
        myNumber = _initial;
    }

    function setMyNumber(uint256 _newValue) public {
        myNumber = _newValue;
    }
}

作用:


第三步:编译合约 & 产出 ABI + 字节码

安装 solc 编译器:

npm install solc@latest

创建 compile.js

/* compile.js */
const solc = require('solc');
const path = require('path');
const fs   = require('fs');

const fileName = 'MyContract.sol';
const contractName = 'MyContract';

const sourceCode = fs.readFileSync(path.join(__dirname, fileName), 'utf8');

// 配置输入
const input = {
  language: 'Solidity',
  sources: { [fileName]: { content: sourceCode } },
  settings: { outputSelection: { '*': { '*': ['*'] } } }
};

const compiled = JSON.parse(solc.compile(JSON.stringify(input)));
const bytecode = compiled.contracts[fileName][contractName].evm.bytecode.object;
const abi      = compiled.contracts[fileName][contractName].abi;

fs.writeFileSync('./MyContractBytecode.bin', bytecode);
fs.writeFileSync('./MyContractAbi.json', JSON.stringify(abi, null, 2));

运行:

node compile.js

成功后会生成:


第四步:启动 Hardhat 开发网络

安装依赖:

npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox

初始化 Hardhat:

npx hardhat init

按提示一路回车选 JavaScript 模板即可。

启动本地链:

npx hardhat node

终端会输出 20 个测试账号公开私钥与连接 URL(默认 http://127.0.0.1:8545/)。
⚠️ 切勿泄露私钥到公链,仅限于开发!


第五步:连接 Web3.js

安装 Web3.js:

npm install web3

新建 index.js 做连接验证:

const { Web3 } = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');

web3.eth.getChainId()
  .then(console.log)
  .catch(console.error);

运行:

node index.js

看到 31337(Hardhat 默认链 ID),即表示 Web3.js 已对接成功。


第六步:部署合约到开发网络

部署脚本 deploy.js

const { Web3 } = require('web3');
const fs = require('fs');
const path = require('path');

const web3 = new Web3('http://127.0.0.1:8545');
const abi  = JSON.parse(fs.readFileSync('./MyContractAbi.json'));
const bytecode = fs.readFileSync('./MyContractBytecode.bin', 'utf8');

const deploy = async () => {
  const [deployer] = await web3.eth.getAccounts();
  console.log('部署账户:', deployer);

  const contract = new web3.eth.Contract(abi);
  const deployerInstance = contract.deploy({
    data: '0x' + bytecode,
    arguments: [123]   // 构造函数传参
  });

  const gasEstimate = await deployerInstance.estimateGas({ from: deployer });
  console.log('预计 gas: ', gasEstimate);

  const deployed = await deployerInstance.send({ from: deployer, gas: gasEstimate });
  const address = deployed.options.address;
  console.log('合约地址:', address);
  fs.writeFileSync('./MyContractAddress.txt', address);
};

deploy().catch(console.error);

运行

node deploy.js

示例输出:

部署账户: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
预计 gas:  123987
合约地址: 0x9fE1...

第七步:与合约交互

创建 interact.js

const { Web3 } = require('web3');
const fs = require('fs');
const path = require('path');

const web3 = new Web3('http://127.0.0.1:8545');
const abi  = JSON.parse(fs.readFileSync('./MyContractAbi.json'));
const address = fs.readFileSync('./MyContractAddress.txt', 'utf8').trim();

const contract = new web3.eth.Contract(abi, address);

const interact = async () => {
  const [caller] = await web3.eth.getAccounts();

  // 读取当前值
  const val = await contract.methods.myNumber().call();
  console.log('当前值:', val);

  // 发送修改交易
  const receipt = await contract.methods.setMyNumber(Number(val) + 1).send({
    from: caller,
    gas: 100000
  });
  console.log('交易哈希:', receipt.transactionHash);

  // 再读值
  const val2 = await contract.methods.myNumber().call();
  console.log('更新后值:', val2);
};

interact().catch(console.error);

运行:

node interact.js

预期输出:

当前值: 123
交易哈希: 0xe89b...
更新后值: 124

👉 动手跑通示例后,再来试试一键导出 ABI 方案,提升合约调试效率。


常见问题(FAQ)

Q1:提示 Error: invalid opcode
A:检查 Solidity 版本与 solc 版本是否一致,或者在 compile.js 内手动指定同版本。

Q2:部署时报错 gas estimation failed
A:开发网络账户余额不足?默认 10000 ETH,确认地址正确;也可能是 constructor 参数类型不匹配。

Q3:调用函数时得到 transaction reverted
A:合约逻辑导致回滚,如 require 条件不满足;可在 Hardhat 打印详细 revert 原因。

Q4:如何切换到真实的测试网?
A:把 Web3 的连接字符串改为 Goerli 等相关 RPC,并导入有测试币的钱包私钥即可。

Q5:ABI 文件太长可以精简吗?
A:无需删减,前端反序列化会依赖完整签名;若仅做调用,可使用 web3.eth.abi.decodeParameters 方式手工编解码函数。


最佳实践小贴士

将以上流程走通,你就拥有了一张从合约代码到链上交互的「全链路通行证」。下一步,可以尝试把前端 React 用 ethers.jsWagmi 结合起来,真正打造用户可见的 dApp。祝你开发愉快!