Skip to content

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 输出为准
pragmapragma solidity 0.8.37;锁死确切版本,不用 ^
EVM versionshanghai必须显式指定,不能依赖默认值
优化器enabled: true, runs: 200可按实际调整
许可证GPL-3.0注意传染性,闭源组件勿直接复制

为什么 EVM 必须显式写死

Solidity 编译器的默认 EVM 版本随编译器走,且各工具链默认值互不相同:

编译器 / 工具默认 EVM
solc 0.8.20 – 0.8.24shanghai
solc 0.8.25 – 0.8.29cancun
solc ≥ 0.8.30prague
solc 0.8.37osaka
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:官方二进制(推荐,命令行直用)

  1. 打开 Releases 页。
  2. 找到 v0.8.37 → Assets → 下载 solc-windows.exe。
  3. 放置并重命名,例如 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 compile

Foundry

# foundry.toml
[profile.default]
solc_version = "0.8.37"
evm_version  = "shanghai"
optimizer    = true
optimizer_runs = 200
forge build

solc 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运行时字节码
.abiABI 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.txt

2. asm.txt 只有一行 Compiler run successful...

原因:命令里带了 -o <dir>,汇编被写入目录,stdout 只剩提示信息。

解决:要单文件就去掉 -o,用 > 接收 stdout:

solc-837.exe --evm-version shanghai --asm fullchain.sol > asm.txt

3. 没有生成任何汇编文件

原因:命令里漏了 --asm——--bin --abi 只产出字节码与 ABI。

4. 部署报 invalid opcode: opcode 0x5f not defined

0x5f = PUSH0,说明编译时按 Shanghai 出码,但链当时尚未完成 Shanghai 升级。

  • VoidChain 升级前:需降级 --evm-version london(或 paris)
  • VoidChain 升级后(当前):保留 shanghai 即可

若仍报其他 opcode,按此表对照降级:

报错 opcode名称引入版本应降到
0x5fPUSH0Shanghaiparis
0x44PREVRANDAOParislondon
0x48BASEFEELondonberlin
0x46/0x47CHAINID/SELFBALANCEIstanbulistanbul

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