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 成本变化;外部调用默认转发全部 gas | support deprecated |
spuriousDragon | exp指令 gas 成本上升 | support deprecated |
byzantium | assembly 可用returndatacopy、returndatasize、staticcall;revert()不浪费 gas | support deprecated |
constantinople | assembly 可用create2、extcodehash、shl、shr、sar;移位操作改用移位指令、gas 更低 | — |
petersburg | 行为与 constantinople 相同 | — |
istanbul | assembly 可用chainid、selfbalance | — |
berlin | SLOAD、*CALL、BALANCE、EXT*、SELFDESTRUCTgas 成本上升,编译器按冷访问成本计算 | — |
london | 可通过block.basefee/ inline assembly 的basefee()读取基础费 | — |
paris | 引入prevrandao()、block.prevrandao;block.difficulty语义改变,inline assembly 中禁用difficulty() | — |
shanghai | 引入push0,代码更小、gas 更省 | — |
cancun | block.blobbasefee/blobbasefee()、blobhash()、mcopy、transient storage 的tstore/tload | — |
prague | 文档未列出额外变更 | — |
osaka | inline 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):
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.(版本名按实际值填入)。过老的版本有弃用警告。目标版本低于
constantinople(即byzantium、spuriousDragon、tangerineWhistle、homestead)时,编译输出会带警告: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.
验证设置是否生效
文档没有单独的"确认输出",但编译器自身提供了四类可核对的信号:
版本特定特性触发类型检查报错。由于
evmVersion影响 type checking,用某版本才有的全局变量却选了更老的版本,编译会直接失败。源码 libsolidity/analysis/TypeChecker.cpp 中的检查包括:chainid需istanbul及以上、basefee需london及以上、blobbasefee需cancun及以上、prevrandao需paris及以上、codehash需constantinople及以上、new C{salt: ...}需constantinople及以上。例如合约里用了block.basefee,却编译--evm-version berlin,就会在类型检查阶段报错——这反过来也是核对"目标版本是否过低"的手段。deprecated 警告。选了
constantinople之前的版本会看到上文的弃用警告,出现该警告说明你确实在按过老的目标编译。experimental 模式报错。想验证 experimental 版本是否被正确拦截,去掉
--experimental再跑一次,应看到对应的 experimental 报错。检查生成的 opcodes。在 Standard JSON 的
outputSelection里请求evm.bytecode.opcodes(文档列出的可用输出项 "Opcodes list"),得到该合约实际使用的指令序列,再对照本表核对版本特征:目标为shanghai及以上时应当允许出现PUSH0;cancun及以上才允许出现MCOPY、TSTORE、TLOAD;constantinople以下不应出现CREATE2、EXTCODEHASH等。这一步是把"选对了版本"落到字节码层面的核对方式。
边界与限制
- 编译目标版本不决定链上运行时语义。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),仅供参考