合约部署与验证
合约部署与验证
本页内容正在整理中,欢迎贡献。
概述
合约部署是将编译后的字节码发布到区块链的过程。源码验证让任何人都可以在 Etherscan 上查看合约的 Solidity 源码,增强透明度和用户信任。本文涵盖 Hardhat 和 Foundry 的部署与验证流程。
主要内容
部署前检查清单
- 所有测试通过(
forge test/npx hardhat test) - 覆盖率达标(建议 >90%)
- 经过安全审计(主网部署必须)
- 构造函数参数确认无误
- Gas 预算估算充足
- 多签钱包或 Timelock 作为 Owner(主网推荐)
- 升级代理模式设计已评估
Hardhat 部署脚本
import { ethers, run } from "hardhat";
async function main() { console.log("开始部署...");
const MyContract = await ethers.getContractFactory("MyContract");
// 估算 Gas const deployTx = await MyContract.getDeployTransaction(/* 构造参数 */); const gasEstimate = await ethers.provider.estimateGas(deployTx); console.log("预估 Gas:", gasEstimate.toString());
// 部署 const contract = await MyContract.deploy(/* 构造参数 */); await contract.waitForDeployment(); const address = await contract.getAddress(); console.log("合约地址:", address);
// 等待足够区块数再验证(Etherscan 索引需要时间) console.log("等待 5 个区块确认..."); await contract.deploymentTransaction()?.wait(5);
// 验证 await run("verify:verify", { address, constructorArguments: [/* 构造参数 */], }); console.log("验证成功!");}
main().catch(console.error);Foundry 部署与验证
# 单命令部署并验证forge create src/MyContract.sol:MyContract \ --rpc-url $MAINNET_RPC_URL \ --private-key $PRIVATE_KEY \ --etherscan-api-key $ETHERSCAN_API_KEY \ --verify \ --constructor-args <arg1> <arg2>
# 使用脚本部署(推荐)forge script script/Deploy.s.sol:DeployScript \ --rpc-url $MAINNET_RPC_URL \ --broadcast \ --verify \ --etherscan-api-key $ETHERSCAN_API_KEY \ -vvvv
# 单独验证已部署合约forge verify-contract \ <合约地址> \ src/MyContract.sol:MyContract \ --chain mainnet \ --etherscan-api-key $ETHERSCAN_API_KEY \ --constructor-args $(cast abi-encode "constructor(address,uint256)" $ARG1 $ARG2)多链验证配置
etherscan: { apiKey: { mainnet: process.env.ETHERSCAN_API_KEY!, sepolia: process.env.ETHERSCAN_API_KEY!, optimisticEthereum: process.env.OPTIMISM_API_KEY!, arbitrumOne: process.env.ARBISCAN_API_KEY!, base: process.env.BASESCAN_API_KEY!, polygon: process.env.POLYGONSCAN_API_KEY!, },},代理合约部署
大多数生产合约使用可升级代理模式:
// OpenZeppelin Upgrades Plugin (Hardhat)import { upgrades } from "hardhat";
// 部署可升级合约const MyContractV1 = await ethers.getContractFactory("MyContractV1");const proxy = await upgrades.deployProxy(MyContractV1, [/* 初始化参数 */], { initializer: "initialize",});await proxy.waitForDeployment();console.log("代理地址:", await proxy.getAddress());
// 升级const MyContractV2 = await ethers.getContractFactory("MyContractV2");await upgrades.upgradeProxy(await proxy.getAddress(), MyContractV2);代理模式类型:
| 模式 | 优点 | 缺点 |
|---|---|---|
| Transparent Proxy | 简单 | Admin 不能调用实现合约 |
| UUPS | Gas 更便宜 | 升级逻辑在实现合约中 |
| Beacon Proxy | 多合约统一升级 | 结构更复杂 |
部署记录管理
// deployments/sepolia.json(建议提交到 git){ "MyToken": { "address": "0x1234...", "txHash": "0xabcd...", "blockNumber": 5000000, "deployedAt": "2024-01-01T00:00:00Z", "constructorArgs": ["1000000000000000000000000"] }}安全注意事项
- 私钥保护:生产部署使用硬件钱包(Ledger/Trezor)
- 多签控制:合约 Owner 设置为 Gnosis Safe 多签地址
- Timelock:管理员操作增加时间锁延迟
- 广播前模拟:
forge script --simulate预演部署