gate-evm-tools
v1.0.13
Published
Gate 智能合约工具集 - 字节码检查、合约升级验证、合约状态同步
Maintainers
Readme
Gate Smart Contract Tools
🛠️ Gate 智能合约工具集 - 字节码检查、合约升级验证、合约状态同步
功能特性
🔧 配置修复(fix)
- ✅ 自动检测 Hardhat 配置文件的兼容性问题
- ✅ 智能修复 ESM 模块导入错误
- ✅ 交互式确认修复操作
- ✅ 自动备份原配置文件
- ✅ 支持仅检查模式
🔍 字节码检查(check)
- ✅ 检查链下合约代码和链上合约代码是否一致
- ✅ 支持检查单个合约、指定网络或所有合约
- ✅ 自动检测可升级合约(ERC1967代理),获取实现合约地址进行比对
- ✅ 智能识别 Constructor 参数差异和 Immutable 变量差异
- ✅ 自动移除元数据进行比较
- ✅ 生成详细的 JSON 报告
- ✅ 支持增量更新报告
- ✅ 检测到配置错误时提供自动修复选项
🔄 合约状态同步(sync)
- ✅ 同步链上合约状态到本地 .openzeppelin 文件
- ✅ 自动读取链上实现合约地址
- ✅ 获取并更新存储布局信息
✅ 升级验证(validate)
- ✅ 自动检测合约类型(代理合约 or 实现合约)
- ✅ 代理模式:验证升级安全性并生成 upgradeCalldata
- ✅ 实现模式:验证存储布局兼容性并部署新合约
- ✅ 支持通过工厂合约 Create2 部署新实现(可选 salt)
- ✅ 检查存储布局兼容性
- ✅ 自动部署新实现合约
- ✅ 生成 upgradeToAndCall 的 calldata
- ✅ 支持同一合约和不同合约的升级模式
- ✅ 支持带构造函数参数的合约
📋 Calldata 解析(parse)
- ✅ 解析 EVM calldata,显示函数名称和参数
- ✅ 支持从 Hardhat artifacts 自动查找 ABI
- ✅ 支持指定自定义 ABI 文件路径
- ✅ 支持直接提供 ABI 数组
- ✅ 支持批量解析多个 calldata
- ✅ 友好的彩色输出和 JSON 导出
一、发布 NPM 包
1. 准备发布
# 进入项目目录
cd evm-checkcode-cli
# 安装依赖
npm install
# 编译 TypeScript
npm run build2. 配置 NPM 账户
# 登录 NPM(如果还未登录)
npm login
# 输入用户名、密码、邮箱3. 发布到 NPM
# 检查包信息
npm pack --dry-run
# 发布包
npm publish
# 如果包名已被占用,可以发布到作用域下
# 先修改 package.json 中的 name 为 "@your-username/bytecode-checker-cli"
# 然后执行:
npm publish --access public4. 更新版本
# 更新补丁版本(1.0.0 -> 1.0.1)
npm version patch
# 更新小版本(1.0.0 -> 1.1.0)
npm version minor
# 更新大版本(1.0.0 -> 2.0.0)
npm version major
# 发布新版本
npm publish二、使用 NPM 包
1. 安装
在你的 Hardhat 项目中安装:
npm install --save-dev gate-evm-tools或全局安装:
npm install -g gate-evm-tools2. 创建配置文件
在项目根目录创建 contractInfo.json 文件:
{
"eth": {
"Vault": "0x80aaf2e4636c510e067a5d300d8bafd48027addf",
"VaultCrossChainRelay": "0x060194eec4556096baaabd6bf553d2658d6a66ab"
},
"bsc": {
"Vault": "0x2cb7d2603a5f43b9fe79e98f09fe3eec40b6765d",
"VaultCrossChainRelay": "0x23ae3a565e0896866e7725fe6d49fd777359c162"
}
}格式说明:
- 第一层 key 是网络名称(必须与
hardhat.config.js中的网络名称一致) - 第二层 key 是合约名称(必须与编译的合约名称一致)
- value 是合约地址(可以是代理合约地址,工具会自动检测并获取实现合约地址)
支持可升级合约: 如果配置中的地址是 ERC1967 代理合约,工具会自动:
- 检测该地址是否为代理合约
- 读取实现合约地址(从 ERC1967 存储槽)
- 使用实现合约地址进行字节码比对
这意味着你可以直接在配置文件中填写代理合约地址,无需手动查找实现合约地址。
3. 配置 Hardhat
确保 hardhat.config.js 中配置了相应的网络:
module.exports = {
networks: {
eth: {
url: "https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY",
},
bsc: {
url: "https://bsc-dataseed.binance.org/",
}
}
};4. 编译合约
npx hardhat compile5. 使用工具
📋 查看帮助
npx gate-tool --help🔧 配置修复(推荐首次使用)
如果在使用过程中遇到 Hardhat 配置导入错误,可以使用 fix 命令自动修复:
# 自动检测并修复配置问题(交互式)
npx gate-tool fix
# 仅检查问题,不执行修复
npx gate-tool fix --check
# 跳过确认,直接修复
npx gate-tool fix --yes常见问题:
Cannot find module 'hardhat/config'- 导入语句需要使用type关键字Did you mean to import "hardhat/config.js"?- ESM 模块兼容性问题
fix 命令会自动将:
import { HardhatUserConfig } from "hardhat/config";修复为:
import type { HardhatUserConfig } from "hardhat/config";🔍 字节码检查
# 检查所有合约
npx gate-tool check
# 检查指定合约
npx gate-tool check --contract Vault
# 检查指定网络
npx gate-tool check --network eth
# 指定配置文件路径
npx gate-tool check --config ./config/contracts.json
# 指定输出报告路径
npx gate-tool check --output ./reports/result.json🔄 同步链上合约状态
当合约通过 calldata 方式由其他人(如多签钱包)执行升级后,本地的 .openzeppelin 文件不会自动更新。使用此命令可以从链上读取最新状态并更新本地文件。
# 同步合约状态
npx gate-tool sync \
--proxy 0x1234... \
--contract CounterUUPS \
--network sepolia✅ 验证升级安全性并生成 calldata
验证合约升级的安全性,并生成 upgradeToAndCall 的 calldata。
支持两种模式:
- 代理合约模式:验证升级安全性并生成 upgradeCalldata
- 实现合约模式:验证代码一致性并部署新合约
模式 1: 代理合约升级验证
# 不同合约升级(从 CounterUUPS 升级到 CounterUUPSV2)
npx gate-tool validate \
--proxy 0x1234... \
--old CounterUUPS \
--new CounterUUPSV2 \
--network sepolia
# 同一合约升级(修改现有合约后升级)
npx gate-tool validate \
--proxy 0x1234... \
--old CounterUUPS \
--new CounterUUPS \
--network sepolia
# 指定输出文件路径
npx gate-tool validate \
--proxy 0x1234... \
--old CounterUUPS \
--new CounterUUPSV2 \
--network sepolia \
--output ./upgrade-info.json输出文件: upgradeCalldata.json
包含:
- 代理合约地址
- 旧实现合约地址
- 新实现合约地址
- upgradeToAndCall calldata
模式 2: 实现合约验证与部署
验证新合约与链上实现合约的存储布局兼容性,并部署新的实现合约。
# 验证存储布局兼容性并部署新合约
npx gate-tool validate \
--proxy 0xImplementationAddress \
--new LedgerImplA \
--network mainnet
# 注意:实现合约模式不需要 --old 参数工作流程:
- 🔍 自动检测地址类型(代理 or 实现)
- 📦 编译合约
- 🔐 验证存储布局兼容性(使用 Hardhat Upgrades 插件)
- ✅ 验证通过后部署新的实现合约
- 💾 保存部署信息到
implementationDeploy.json
输出文件: implementationDeploy.json
包含:
- 旧实现合约地址
- 新实现合约地址(新部署的)
- 存储布局兼容状态
- 时间戳和网络信息
使用场景:
- ✅ 验证新合约能否安全替换旧实现合约
- ✅ 确认存储槽位布局兼容
- ✅ 在验证通过后立即部署新的实现合约
- ✅ 获取新部署的实现合约地址用于后续升级
重要说明:
- 该模式不是比较代码完全一致
- 而是验证存储布局兼容性
- 即使代码有修改(如新增函数、修改逻辑),只要存储布局兼容就可以升级
- 这是可升级合约的核心安全机制
构造函数参数
# 如果新合约的构造函数需要参数
npx gate-tool validate \
--proxy 0x1234... \
--old CounterUUPS \
--new CounterUUPSV2 \
--network sepolia \
--constructor-args '["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", 100]'
# 构造函数参数示例
# 单个地址参数
--constructor-args '["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"]'
# 多个参数(地址 + 数值)
--constructor-args '["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", 1000000]'
# 复杂参数(地址 + 字符串 + 数值 + 布尔值)
--constructor-args '["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "MyToken", 18, true]'工厂部署(可选)
如果需要通过工厂合约进行 Create2 部署,可以传入工厂地址(可选 salt)。
# 使用工厂合约部署新实现
npx gate-tool validate \
--proxy 0x1234... \
--old CounterUUPS \
--new CounterUUPSV2 \
--network sepolia \
--factory 0xFactoryAddress
# 指定 salt(bytes32)
npx gate-tool validate \
--proxy 0x1234... \
--old CounterUUPS \
--new CounterUUPSV2 \
--network sepolia \
--factory 0xFactoryAddress \
--factory-salt 0x0000000000000000000000000000000000000000000000000000000000001234说明:
- 不传
--factory时,默认走 EOA 部署逻辑 --factory-salt为可选,未传时默认使用全 0
注意:
- 代理模式:会部署新实现合约(仅用于验证),但不会升级代理合约。请使用生成的 calldata 通过多签钱包执行升级。
- 实现模式:会部署新实现合约并返回地址,可用于后续的手动升级操作。
构造函数参数格式说明:
--constructor-args 参数接受 JSON 数组格式的字符串。参数类型会自动根据合约定义进行匹配。
常见类型示例:
- 地址 (address):
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" - 数值 (uint256/uint):
1000000或"1000000" - 字符串 (string):
"MyToken" - 布尔值 (bool):
true或false - 字节 (bytes):
"0x1234..."
示例:
// 合约构造函数
constructor(
address _token,
string memory _name,
uint256 _decimals,
bool _isPaused
) {
// ...
}对应的命令:
npx gate-tool validate \
--proxy 0x... \
--old OldContract \
--new NewContract \
--network mainnet \
--constructor-args '["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "MyToken", 18, false]'📋 解析 Calldata
解析 EVM calldata,将其转换为人类可读的函数调用信息。
# 从 JSON 文件读取(推荐)
npx gate-tool parse --input parseCalldata.example.json
# 直接从命令行参数(使用合约名称从 artifacts 查找 ABI)
npx gate-tool parse \
--to 0x5FbDB2315678afecb367f032d93F642f64180aa3 \
--contract Counter \
--calldata 0x3fb5c1cb0000000000000000000000000000000000000000000000000000000000000064
# 使用自定义 ABI 文件
npx gate-tool parse \
--to 0x1234... \
--abi-path ./custom-abi/MyContract.json \
--calldata 0x...
# 批量解析并保存结果
npx gate-tool parse \
--input batch-calldata.json \
--output results.json输入文件格式示例:
{
"to": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"contractName": "Counter",
"calldata": "0x3fb5c1cb0000000000000000000000000000000000000000000000000000000000000064"
}批量解析格式(数组 包含三种形式):
[
{
"to": "0x...",
"contractName": "Counter",
"calldata": "0x..."
},
{
"to": "0x...",
"abiPath": "./abi.json",
"calldata": "0x..."
},
{
"": "0x...",
"abi": [
{
"inputs": [
{"name": "spender", "type": "address"},
{"name": "amount", "type": "uint256"}
],
"name": "approve",
"outputs": [{"name": "", "type": "bool"}],
"stateMutability": "nonpayable",
"type": "function"
}
],
"calldata": "0x..."
}
]支持三种 ABI 来源方式:
- contractName - 从 Hardhat artifacts 自动查找(需要先编译合约)
- abiPath - 指定 ABI 文件路径
- abi - 直接在 JSON 中提供 ABI 数组
6. 命令选项
check 命令
| 选项 | 简写 | 说明 | 默认值 |
|------|------|------|--------|
| --contract <name> | -c | 指定要检查的合约名称 | - |
| --network <name> | -n | 指定要检查的网络名称 | - |
| --config <path> | - | 指定配置文件路径 | ./contractInfo.json |
| --output <path> | -o | 指定输出报告文件路径 | ./bytecode-check-report.json |
sync 命令
| 选项 | 简写 | 说明 | 必需 |
|------|------|------|------|
| --proxy <address> | - | 代理合约地址 | ✅ |
| --contract <name> | - | 合约名称 | ✅ |
| --network <name> | -n | 网络名称 | - |
validate 命令
| 选项 | 简写 | 说明 | 必需 |
|------|------|------|------|
| --proxy <address> | - | 代理合约地址 | ✅ |
| --old <name> | - | 旧合约名称(代理模式时使用) | - |
| --new <name> | - | 新合约名称 | ✅ |
| --network <name> | -n | 网络名称 | - |
| --output <path> | -o | 输出文件路径 | ./upgradeCalldata.json |
| --constructor-args <args> | - | 新合约构造函数参数(JSON数组格式) | - |
| --factory <address> | - | 工厂合约地址(可选,Create2 部署) | - |
| --factory-salt <salt> | - | 工厂部署 salt(bytes32,可选) | - |
parse 命令
| 选项 | 简写 | 说明 | 默认值 |
|------|------|------|--------|
| --input <path> | -i | 输入 JSON 文件路径 | - |
| --to <address> | - | 目标合约地址(命令行模式) | - |
| --calldata <data> | - | Calldata 十六进制数据(命令行模式) | - |
| --contract <name> | -c | 合约名称(从 artifacts 查找 ABI) | - |
| --abi-path <path> | - | 自定义 ABI 文件路径 | - |
| --output <path> | -o | 输出 JSON 文件路径 | - |
ABI 来源优先级:直接提供的 abi > abiPath > contractName
五、完整使用示例
场景 1: 验证实现合约存储布局并部署新版本
假设你有一个已部署的实现合约,需要验证新合约的存储布局兼容性并部署新版本:
# 1. 从 .openzeppelin 文件中找到实现合约地址
cat .openzeppelin/unknown-10088.json | grep "address"
# 2. 验证存储布局兼容性并部署新合约
npx gate-tool validate \
--proxy 0x40397e8F64561AabC793514F1b28B01A2ebE6875 \
--new LedgerImplA \
--network gateLayer
# 输出示例:
# ✅ 验证成功!
#
# 存储布局验证与部署结果:
# ────────────────────────────────────────────────────────────
# 旧实现合约: 0x40397e8F64561AabC793514F1b28B01A2ebE6875
# 新实现合约: 0x1234567890abcdef1234567890abcdef12345678
# 存储布局: ✅ 兼容
#
# 输出文件: /path/to/implementationDeploy.json
# ────────────────────────────────────────────────────────────
# 3. 查看部署信息
cat implementationDeploy.json输出文件内容:
{
"type": "implementation",
"oldImplementationAddress": "0x40397e8F64561AabC793514F1b28B01A2ebE6875",
"newImplementationAddress": "0x1234567890abcdef1234567890abcdef12345678",
"contractName": "LedgerImplA",
"storageLayoutCompatible": true,
"timestamp": "2024-01-01T00:00:00.000Z",
"network": "gateLayer"
}场景 2: 代理合约升级流程
# 1. 验证升级安全性并生成 calldata
npx gate-tool validate \
--proxy 0xProxyAddress \
--old CounterUUPS \
--new CounterUUPSV2 \
--network mainnet
# 输出示例:
# ✅ 验证成功!
#
# 升级信息摘要:
# ────────────────────────────────────────────────────────────
# 代理合约: 0xProxyAddress
# 旧实现: 0xOldImpl
# 新实现: 0xNewImpl
#
# Calldata: 0x4f1ef286000000000000000000000000...
#
# 输出文件: /path/to/upgradeCalldata.json
# ────────────────────────────────────────────────────────────
# 2. 使用生成的 calldata 在多签钱包中执行升级
# - 打开多签钱包(如 Gnosis Safe)
# - 使用 upgradeCalldata.json 中的 target 和 calldata
# - 提交交易并收集签名
# 3. 升级完成后,同步本地状态
npx gate-tool sync \
--proxy 0xProxyAddress \
--contract CounterUUPSV2 \
--network mainnet场景 3: 检查多个合约的字节码
# 1. 准备配置文件 contractInfo.json
cat > contractInfo.json << EOF
{
"contracts": {
"mainnet": {
"Vault": "0x1234...",
"Token": "0x5678..."
},
"sepolia": {
"Vault": "0xabcd..."
}
}
}
EOF
# 2. 执行检查
npx gate-tool check
# 3. 查看报告
cat bytecode-check-report.json场景 4: 解析升级交易的 calldata
# 1. 从多签钱包获取待执行的 calldata
# 2. 解析 calldata 查看具体操作
npx gate-tool parse \
--to 0xProxyAddress \
--contract CounterUUPS \
--calldata 0x4f1ef286000000000000000000000000...
# 输出示例:
# 📋 Calldata 解析结果
# ════════════════════════════════════════════════
# 目标合约: 0xProxyAddress
# 合约名称: CounterUUPS
#
# 函数名称: upgradeToAndCall
# 函数签名: upgradeToAndCall(address,bytes)
#
# 参数列表:
# ────────────────────────────────────────────────
# 1. newImplementation (address)
# 0x1234567890abcdef1234567890abcdef12345678
#
# 2. data (bytes)
# 0x
# ════════════════════════════════════════════════