VoidChain_Shanghai环境搭建
VoidChain 合约开发环境搭建说明(EVM Shanghai)
适用前提:VoidChain 主网已完成 Shanghai 兼容升级,链上支持 PUSH0 (0x5f)。
编译器统一 0.8.37,EVM 版本显式锁定 shanghai。
一、版本矩阵(必须对齐)
| 组件 | 版本 / 取值 | 说明 |
|---|---|---|
| Solidity 编译器 | 0.8.37+commit.f401782d | 文件名可能写作 solc-0.8.7.exe,以 --version 输出为准 |
| pragma | pragma solidity 0.8.37; | 锁死确切版本,不用 ^ |
| EVM version | shanghai | 必须显式指定,不能依赖默认值 |
| 优化器 | enabled: true, runs: 200 | 可按实际调整 |
| 许可证 | GPL-3.0 | 注意传染性,闭源组件勿直接复制 |
为什么 EVM 必须显式写死
Solidity 编译器的默认 EVM 版本随编译器走,且各工具链默认值互不相同:
| 编译器 / 工具 | 默认 EVM |
|---|---|
| solc 0.8.20 – 0.8.24 | shanghai |
| solc 0.8.25 – 0.8.29 | cancun |
| solc ≥ 0.8.30 | prague |
| solc 0.8.37 | osaka |
| Hardhat(自 0.8.20 起) | paris(不跟随 solc) |
| Foundry | 跟随 solc |
由于 VoidChain 实际档位是 Shanghai,任何默认值漂移都会导致:
- 默认值过高(cancun/prague/osaka)→ 生成
MCOPY/TLOAD/TSTORE/BLOB*→ 部署报invalid opcode - 默认值过低(paris/london)→ 不生成
PUSH0,白白损失 gas 优化
结论:无论在哪个工具链,都必须显式写 evmVersion: "shanghai"。
二、编译器安装(Windows)
方式 A:官方二进制(推荐,命令行直用)
- 打开 Releases 页。
- 找到 v0.8.37 → Assets → 下载
solc-windows.exe。 - 放置并重命名,例如
C:\Chain00\sol\solc-837.exe。
mkdir C:\Chain00\sol
cd C:\Chain00\sol
Invoke-WebRequest -Uri "https://github.com/ethereum/solidity/releases/download/v0.8.37/solc-windows.exe" -OutFile "solc-837.exe"
.\solc-837.exe --version期望输出:
solc, the solidity compiler commandline interface
Version: 0.8.37+commit.f401782d可选:加入 PATH
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Chain00\sol", "User")方式 B:静态二进制仓库(版本最全,带 SHA256)
方式 C:npm(仅快速验证)
npm install -g solc # 注意:命令是 solcjs,是 JS 版,慢于原生二进制不建议作为产线编译工具。
方式 D:Hardhat / Foundry 自动下载
框架会自行拉取指定版本到缓存,只需在配置里写版本号即可。
三、源码规范
1. pragma 必须全部一致
项目内所有 .sol 文件头统一改为:
pragma solidity 0.8.37;⚠️ 原
pragma solidity >=0.7.0 <0.9.0;会放行 0.7.x(无内置溢出检查),必须全部替换。只改部分文件会因版本约束冲突导致编译失败。
2. 不要引入 SafeMath
0.8.x 已内置算术溢出检查,再引 SafeMath 属冗余,徒增体积与 gas。
3. 升级后需人工修复的问题(0.8 不自动解决)
| 问题 | 升 0.8 能否解决 |
|---|---|
| 算术溢出 / 下溢 | ✅ 自动解决 |
零地址锁币 transfer(0x0) | ❌ 需另加 require(_to != address(0)) |
| 函数无鉴权 / 覆写共享状态指针 | ❌ 需改 view + 局部变量 + 权限校验 |
| 数组删除缺存在性校验 | ❌ 需补 require |
| 记录重复消费 / txid 熵不足 | ❌ 需逻辑修复 |
四、编译配置
Hardhat
// hardhat.config.js
module.exports = {
solidity: {
version: "0.8.37",
settings: {
evmVersion: "shanghai",
optimizer: { enabled: true, runs: 200 }
}
},
networks: {
voidchain: {
url: "https://<VoidChain RPC>", // TODO 填实际 RPC
chainId: <chainId>, // TODO 填实际 chainId
accounts: ["0x<私钥>"]
}
}
};npx hardhat compileFoundry
# foundry.toml
[profile.default]
solc_version = "0.8.37"
evm_version = "shanghai"
optimizer = true
optimizer_runs = 200forge buildsolc CLI(Windows PowerShell)
cd C:\Chain00\sol\dex
solc-837.exe --evm-version shanghai --optimize `
--asm --bin --abi `
-o C:\TChain00\sol\class `
fullchain.sol参数说明:
| 参数 | 作用 |
|---|---|
--evm-version shanghai | 必须,锁定 EVM 档位 |
--asm | 生成汇编,用于 opcode 校验 |
--bin / --abi | 生成字节码与 ABI |
-o <dir> | 输出到目录(此时 stdout 只剩提示信息) |
五、编译产物说明(易混淆)
使用 -o 输出到目录时,产物后缀如下:
| 后缀 | 内容 |
|---|---|
.bin | 部署字节码(十六进制) |
.bin-runtime | 运行时字节码 |
.abi | ABI JSON |
.evm | 汇编助记符(这就是 --asm 的输出,不是 .asm) |
⚠️ 常见误区:以为
--asm会生成.asm文件——实际上 solc 写到-o目录时用的是.evm。
六、必做的 opcode 校验
1. 禁止出现的 Shanghai 之后的操作码
Select-String -Path C:\TChain00\sol\class\*.evm `
-Pattern 'MCOPY|TLOAD|TSTORE|BLOBHASH|BLOBBASEFEE'期望:零输出。
注意:不要只搜 COPY——会误命中 CODECOPY、EXTCODECOPY、CALLDATACOPY、RETURNDATACOPY 这些正常指令。必须搜完整词 MCOPY。
2. 确认 Shanghai 已生效(PUSH0)
Select-String -Path C:\TChain00\sol\class\*.evm -Pattern 'PUSH0' |
Select-Object -First 3期望:有输出(说明确实按 shanghai 编译,而非降级到 paris/london)。
PUSH0 只在编译器需要压入常量 0 时才生成。若合约中暂无 address(0) 比较等场景,无输出也属正常——需结合下一条的 evmVersion 字段综合判定。
3. 最权威确认:元数据字段
solc-837.exe --evm-version shanghai --metadata fullchain.sol > meta.txt
Select-String -Path meta.txt -Pattern '"evmVersion":"[a-z]+"' -AllMatches |
ForEach-Object { $_.Matches.Value } | Sort-Object -Unique期望输出唯一一行:
"evmVersion":"shanghai"同时确认编译器版本:
Select-String -Path meta.txt -Pattern '"version":"[0-9][^"]*"' -AllMatches |
ForEach-Object { $_.Matches.Value } | Sort-Object -Unique期望:"version":"0.8.37+commit.f401782d"
七、常见问题排查
1. findstr 搜不到内容,但编辑器里能搜到
原因:PowerShell 的 > 重定向默认输出 UTF-16LE(每字符后跟 \0),findstr 按单字节读取匹配不上。
解决:改用 Select-String(自动识别编码),或用 CMD 重定向(ANSI),或显式指定:
solc-837.exe --evm-version shanghai --metadata fullchain.sol |
Out-File -Encoding utf8 meta.txt2. asm.txt 只有一行 Compiler run successful...
原因:命令里带了 -o <dir>,汇编被写入目录,stdout 只剩提示信息。
解决:要单文件就去掉 -o,用 > 接收 stdout:
solc-837.exe --evm-version shanghai --asm fullchain.sol > asm.txt3. 没有生成任何汇编文件
原因:命令里漏了 --asm——--bin --abi 只产出字节码与 ABI。
4. 部署报 invalid opcode: opcode 0x5f not defined
0x5f = PUSH0,说明编译时按 Shanghai 出码,但链当时尚未完成 Shanghai 升级。
- VoidChain 升级前:需降级
--evm-version london(或paris) - VoidChain 升级后(当前):保留
shanghai即可
若仍报其他 opcode,按此表对照降级:
| 报错 opcode | 名称 | 引入版本 | 应降到 |
|---|---|---|---|
0x5f | PUSH0 | Shanghai | paris |
0x44 | PREVRANDAO | Paris | london |
0x48 | BASEFEE | London | berlin |
0x46/0x47 | CHAINID/SELFBALANCE | Istanbul | istanbul |
5. Error: unrecognised option '-Path'
原因:solc ... | Select-String -Path ... 两条命令被粘成一条执行,-Path 被 solc 当作自己的参数。
解决:拆成两条独立命令分别回车;或改用 CMD 的 findstr。
6. 出现带数字后缀的变体(如 BaseToken1.evm)
说明存在同名合约冲突(多个源文件定义同名合约,或同一文件被以不同路径重复 import)。
风险:ABI 与字节码归属混乱,部署可能拿错。需清理重复 import。
7. 部分 .bin 为 0 KB
0 KB 表示该合约为 abstract / interface / 纯库,无部署字节码。
若 BaseToken、FullChain、Export、Import、MyChain、MyToken 中任何一个为 0 KB,说明被声明为 abstract 或存在未实现函数,需处理后才能部署。
Get-ChildItem C:\TChain00\sol\class\*.bin |
Where-Object { $_.Length -eq 0 } | Select-Object Name八、部署前验收清单
- 全部
.sol文件头为pragma solidity 0.8.37; -
solc --version输出0.8.37+commit.f401782d -
evmVersion元数据为shanghai(且唯一) -
.evm中MCOPY|TLOAD|TSTORE|BLOB*零命中 - 待部署合约的
.bin均非 0 KB - 无同名合约重复变体
- VoidChain 主网试部署一次(本地编译通过 ≠ 上链可运行)
- 不变式测试:
Σ balances == _totalSupply在transfer/MapAssets/Payouts/GasPay后恒成立(0.8 下-=由静默回绕变为 revert) - 重新提交区块浏览器合约验证(字节码已变,旧记录失效)
已部署合约不可原地升级,字节码变更后需重新部署并迁移状态。
九、快速自检脚本(复制即用)
cd C:\Chain00\sol\dex
# 1. 版本
solc-837.exe --version
# 2. 编译
solc-837.exe --evm-version shanghai --optimize --asm --bin --abi `
-o C:\TChain00\sol\class fullchain.sol
# 3. 元数据确认
solc-837.exe --evm-version shanghai --metadata fullchain.sol > meta.txt
Select-String -Path meta.txt -Pattern '"evmVersion":"[a-z]+"' -AllMatches |
ForEach-Object { $_.Matches.Value } | Sort-Object -Unique
# 4. 禁用操作码(应无输出)
Select-String -Path C:\TChain00\sol\class\*.evm `
-Pattern 'MCOPY|TLOAD|TSTORE|BLOBHASH|BLOBBASEFEE'
# 5. PUSH0(应有输出)
Select-String -Path C:\TChain00\sol\class\*.evm -Pattern 'PUSH0' |
Select-Object -First 3
# 6. 空字节码检查
Get-ChildItem C:\TChain00\sol\class\*.bin |
Where-Object { $_.Length -eq 0 } | Select-Object Name