Solidity 怎么选择并设置 --evm-version 让合约匹配目标链的 EVM 版本
2026/9/13 15:03:30 网站建设 项目流程

Solidity 怎么选择并设置 --evm-version 让合约匹配目标链的 EVM 版本

【免费下载链接】soliditySolidity, the Smart Contract Programming Language项目地址: https://gitcode.com/GitHub_Trending/so/solidity

编译 Solidity 合约时,solc 默认按当前版本对应的主网 EVM 生成代码,但你的合约可能部署在特性集不同的链上(尤其是私有链)。Solidity 通过命令行选项--evm-version和 Standard JSON 设置evmVersion让你指定编译目标 EVM 版本,文档明确警告:编译到错误的 EVM 版本会导致"wrong, strange and failing behavior",尤其在私有链上必须确保编译器使用的 EVM 版本与链实际部署的版本一致(见 docs/using-the-compiler.rst 的 "Setting the EVM Version to Target" 一节)。

本文的任务路径是:先根据目标链支持的特性选出版本值,再在命令行或 Standard JSON 中设置,最后通过编译器自身的报错、警告和 opcodes 输出来验证设置生效。

第一步:从版本列表里选出目标版本

docs/using-the-compiler.rst 列出了全部可选值以及每个版本对编译器相关的变更。文档同时说明:各版本之间不保证向后兼容,所以选值时以目标链实际部署的硬分叉为准,而不是"就低不就高"。当前默认值是osaka(代码中currentVersion = Version::Osaka,见 liblangutil/EVMVersion.h)。

版本值编译器相关变更(据文档摘录)状态
homestead最老版本support deprecated
tangerineWhistle访问其他账户的 gas 成本变化;外部调用默认转发全部 gassupport deprecated
spuriousDragonexp指令 gas 成本上升support deprecated
byzantiumassembly 可用returndatacopyreturndatasizestaticcallrevert()不浪费 gassupport deprecated
constantinopleassembly 可用create2extcodehashshlshrsar;移位操作改用移位指令、gas 更低
petersburg行为与 constantinople 相同
istanbulassembly 可用chainidselfbalance
berlinSLOAD*CALLBALANCEEXT*SELFDESTRUCTgas 成本上升,编译器按冷访问成本计算
london可通过block.basefee/ inline assembly 的basefee()读取基础费
paris引入prevrandao()block.prevrandaoblock.difficulty语义改变,inline assembly 中禁用difficulty()
shanghai引入push0,代码更小、gas 更省
cancunblock.blobbasefee/blobbasefee()blobhash()mcopy、transient storage 的tstore/tload
prague文档未列出额外变更
osakainline assembly 可用clz内建函数默认
amsterdam文档未列出额外变更experimental

文档本身不提供"链名 → 版本"对照表;选值依据就是上表中各版本引入/变更的特性。另外注意@future是 Standard JSON 中列出的可选值(experimental),用于跟踪尚未部署的特性。

如果目标链支持 transient storage 等新特性,版本选错会直接编译失败:docs/contracts/transient-storage.rst 明确写着 transient storage 要求 EVM 版本cancunor newer。

在命令行设置 --evm-version

文档给出的用法:

solc --evm-version <VERSION> contract.sol

其中<VERSION>替换为上表中的某个版本名,例如为支持 london 特性的链编译:

solc --evm-version london contract.sol

选择--evm-version的取值时,用solc --help查看全部选项说明,该选项的帮助文本会列出允许的版本名。

两个容易踩坑的点,编译器会直接报错或警告(报错文案来自源码 solc/CommandLineParser.cpp 与 solc/CommandLineInterface.cpp):

  1. experimental 版本必须开启 experimental 模式amsterdam@future属于 experimental,不带--experimental时命令会失败:

    solc --experimental --evm-version amsterdam contract.sol

    报错原文为:EVM version 'amsterdam' is experimental and can only be selected in experimental mode. To enable experimental mode, use the --experimental flag.(版本名按实际值填入)。

  2. 过老的版本有弃用警告。目标版本低于constantinople(即byzantiumspuriousDragontangerineWhistlehomestead)时,编译输出会带警告:Support for EVM versions older than constantinople is deprecated and will be removed in the future.如果你的链确实停留在这些分叉,按文档标注这些版本的 support 已 deprecated,但仍可编译。

在 Standard JSON 接口设置 evmVersion

自动化构建或工具链集成通常走 Standard JSON 接口(solc --standard-json从标准输入读 JSON、标准输出返回 JSON)。在"settings"字段里加"evmVersion"键:

{ "language": "Solidity", "sources": { "myFile.sol": { "urls": ["/tmp/path/to/myFile.sol"] } }, "settings": { "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "london" } }

文档对该键的说明是:"Version of the EVM to compile for (optional). Affects type checking and code generation.",即版本设置会同时影响类型检查和代码生成,不只是换一套指令。Standard JSON 中合法取值的完整清单(含@future,experimental)也写在 docs/using-the-compiler.rst 的 Input Description 注释里。

Standard JSON 路径下的两条校验与命令行对应:

  • 版本名非法时报Invalid EVM version requested.(libsolidity/interface/StandardCompiler.cpp)。
  • experimental 版本必须同时设置"settings": { "experimental": true },否则报EVM version '<name>' is experimental and can only be used with the 'settings.experimental' option enabled.

验证设置是否生效

文档没有单独的"确认输出",但编译器自身提供了四类可核对的信号:

  1. 版本特定特性触发类型检查报错。由于evmVersion影响 type checking,用某版本才有的全局变量却选了更老的版本,编译会直接失败。源码 libsolidity/analysis/TypeChecker.cpp 中的检查包括:chainidistanbul及以上、basefeelondon及以上、blobbasefeecancun及以上、prevrandaoparis及以上、codehashconstantinople及以上、new C{salt: ...}constantinople及以上。例如合约里用了block.basefee,却编译--evm-version berlin,就会在类型检查阶段报错——这反过来也是核对"目标版本是否过低"的手段。

  2. deprecated 警告。选了constantinople之前的版本会看到上文的弃用警告,出现该警告说明你确实在按过老的目标编译。

  3. experimental 模式报错。想验证 experimental 版本是否被正确拦截,去掉--experimental再跑一次,应看到对应的 experimental 报错。

  4. 检查生成的 opcodes。在 Standard JSON 的outputSelection里请求evm.bytecode.opcodes(文档列出的可用输出项 "Opcodes list"),得到该合约实际使用的指令序列,再对照本表核对版本特征:目标为shanghai及以上时应当允许出现PUSH0cancun及以上才允许出现MCOPYTSTORETLOADconstantinople以下不应出现CREATE2EXTCODEHASH等。这一步是把"选对了版本"落到字节码层面的核对方式。

边界与限制

  • 编译目标版本不决定链上运行时语义。docs/yul.rst 明确提醒:无论编译器里选了哪个 EVM 版本,指令语义取决于最终部署的链。docs/units-and-global-variables.rst 对selfdestruct的说明同样指出,Cancun 后selfdestruct不再删除合约账户,这一行为变化"dependent on the EVM version of the chain on which the contract is deployed",而 "The--evm-versionsetting used when compiling the contract has no bearing on it"。也就是说--evm-version管的是"生成什么代码",链的实际 EVM 管的是"代码怎么跑",两者要分别对齐。
  • 各版本之间不保证向后兼容(文档原文:"Backward compatibility is not guaranteed between each version"),升级目标版本后应重新验证字节码与 gas 估算,而不是默认无变化。
  • 文档对错误版本的总体警告是:可能导致错误、怪异、失败的行为,私有链部署前务必核对链实际运行的 EVM 版本。

完成以上步骤后,你可以用solc --help确认选项拼写、用编译报错/警告确认版本名与模式正确、用evm.bytecode.opcodes输出确认指令集符合所选版本,三者都通过即说明--evm-version已按目标链要求生效。

【免费下载链接】soliditySolidity, the Smart Contract Programming Language项目地址: https://gitcode.com/GitHub_Trending/so/solidity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询