使用编译器¶
使用命令行编译器¶
注解
这一节并不适用于 solcjs
Basic usage¶
solc
是 Solidity 源码库的构建目标之一,它是 Solidity 的命令行编译器。你可使用 solc --help
命令来查看它的所有选项的解释。该编译器可以生成各种输出,范围从简单的二进制文件、汇编文件到用于估计“gas”使用情况的抽象语法树(解析树)。如果你只想编译一个文件,你可以运行 solc --bin sourceFile.sol
来生成二进制文件。
如果你想通过 solc
获得一些更高级的输出信息,可以通过 solc -o outputDirectory --bin --ast --asm sourceFile.sol
命令将所有的输出都保存到一个单独的文件夹中。
Optimizer options¶
Before you deploy your contract, activate the optimizer while compiling using solc --optimize --bin sourceFile.sol
.
By default, the optimizer will optimize the contract for 200 runs.
If you want to optimize for initial contract deployment and get the smallest output, set it to --runs=1
.
If you expect many transactions and don’t care for higher deployment cost and output size, set --runs
to a high number.
This parameter has effects on the following (this might change in the future):
- the size of the binary search in the function dispatch routine
- the way constants like large numbers or strings are stored
Path remapping¶
命令行编译器会自动从文件系统中读取并导入的文件,但同时,它也支持通过 prefix=path
选项将路径重定向。比如:
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol
这实质上是告诉编译器去搜索 /usr/local/lib/dapp-bin
目录下的所有以 github.com/ethereum/dapp-bin/
开头的文件,如果编译器找不到这样的文件,它会接着读取 /usr/local/lib/fallback
目录下的所有文件(空前缀意味着始终匹配)。solc
不会从位于重定向目标之外和显式指定的源文件所在目录之外的文件系统读取文件,所以,类似 import "/etc/passwd";
这样的语句,编译器只会在你添加了 =/
选项之后,才会尝试到根目录下加载 /etc/passwd
文件。
如果重定向路径下存在多个匹配,则选择具有最长公共前缀的那个匹配。
When accessing the filesystem to search for imports, all paths are treated as if they were fully qualified paths.
This behaviour can be customized by adding the command line option --base-path
with a path to be prepended
before each filesystem access for imports is performed. Furthermore, the part added via --base-path
will not appear in the contract metadata.
出于安全原因,编译器限制了它可以访问的目录。在命令行中指定的源文件的路径(及其子目录)和通过重定向定义的路径可用于 import
语句,其他的则会被拒绝。额外路径(及其子目录)可以通过 --allow-paths /sample/path,/another/sample/path
进行配置。
Everything inside the path specified via --base-path
is always allowed.
Library linking¶
If your contracts use libraries, you will notice that the bytecode contains substrings of the form __$53aea86b7d70b31448b230b20ae141a537$__
. These are placeholders for the actual library addresses.
The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name.
The bytecode file will also contain lines of the form // <placeholder> -> <fq library name>
at the end to help
identify which libraries the placeholders represent. Note that the fully qualified library name
is the path of its source file and the library name separated by :
.
You can use solc
as a linker meaning that it will insert the library addresses for you at those points:
Either add --libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"
to your command to provide an address for each library (use commas or spaces as separators) or store the string in a file (one library per line) and run solc
using --libraries fileName
.
If solc
is called with the option --standard-json
, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a “success” state and report any errors via the JSON output.
The option --base-path
is also processed in standard-json mode.
If solc
is called with the option --link
, all input files are interpreted to be unlinked binaries (hex-encoded) in the __$53aea86b7d70b31448b230b20ae141a537$__
-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except --libraries
are ignored (including -o
) in this case.
警告
Manually linking libraries on the generated bytecode is discouraged because it does not update contract metadata. Since metadata contains a list of libraries specified at the time of compilation and bytecode contains a metadata hash, you will get different binaries, depending on when linking is performed.
You should ask the compiler to link the libraries at the time a contract is compiled by either
using the --libraries
option of solc
or the libraries
key if you use the
standard-JSON interface to the compiler.
注解
The library placeholder used to be the fully qualified name of the library itself
instead of the hash of it. This format is still supported by solc --link
but
the compiler will no longer output it. This change was made to reduce
the likelihood of a collision between libraries, since only the first 36 characters
of the fully qualified library name could be used.
Setting the EVM version to target¶
When you compile your contract code you can specify the Ethereum virtual machine version to compile for to avoid particular features or behaviours.
警告
Compiling for the wrong EVM version can result in wrong, strange and failing behaviour. Please ensure, especially if running a private chain, that you use matching EVM versions.
On the command line, you can select the EVM version as follows:
solc --evm-version <VERSION> contract.sol
In the standard JSON interface, use the "evmVersion"
key in the "settings"
field:
{
"sources": { ... },
"settings": {
"optimizer": { ... },
"evmVersion": "<VERSION>"
}
}
Target options¶
Below is a list of target EVM versions and the compiler-relevant changes introduced at each version. Backward compatibility is not guaranteed between each version.
homestead
- (oldest version)
tangerineWhistle
- Gas cost for access to other accounts increased, relevant for gas estimation and the optimizer.
- All gas sent by default for external calls, previously a certain amount had to be retained.
spuriousDragon
- Gas cost for the
exp
opcode increased, relevant for gas estimation and the optimizer.
- Gas cost for the
byzantium
- Opcodes
returndatacopy
,returndatasize
andstaticcall
are available in assembly. - The
staticcall
opcode is used when calling non-library view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions. - It is possible to access dynamic data returned from function calls.
revert
opcode introduced, which means thatrevert()
will not waste gas.
- Opcodes
constantinople
- Opcodes
create2`, ``extcodehash
,shl
,shr
andsar
are available in assembly. - Shifting operators use shifting opcodes and thus need less gas.
- Opcodes
petersburg
- The compiler behaves the same way as with constantinople.
istanbul
(default)- Opcodes
chainid
andselfbalance
are available in assembly.
- Opcodes
berlin
(experimental)
编译器输入输出JSON描述¶
下面展示的这些JSON格式是编译器API使用的,当然,在 solc
上也是可用的。有些字段是可选的(参见注释),并且它们可能会发生变化,但所有的变化都应该是后向兼容的。
编译器API需要JSON格式的输入,并以JSON格式输出编译结果。
注释是不允许的,这里仅用于解释目的。
输入说明¶
{
// 必选: 源代码语言,比如“Solidity”,“serpent”,“lll”,“assembly”等
language: "Solidity",
// 必选
sources:
{
// 这里的键值是源文件的“全局”名称,可以通过remappings引入其他文件(参考下文)
"myFile.sol":
{
// 可选: 源文件的kaccak256哈希值,可用于校验通过URL加载的内容。
"keccak256": "0x123...",
// 必选(除非声明了 "content" 字段): 指向源文件的URL。
// URL(s) 会按顺序加载,并且结果会通过keccak256哈希值进行检查(如果有keccak256的话)
// 如果哈希值不匹配,或者没有URL返回成功,则抛出一个异常。
"urls":
[
"bzzr://56ab...",
"ipfs://Qma...",
"file:///tmp/path/to/file.sol"
]
},
"mortal":
{
// 可选: 该文件的keccak256哈希值
"keccak256": "0x234...",
// 必选(除非声明了 "urls" 字段): 源文件的字面内容
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
}
},
// 可选
settings:
{
// 可选: Stop compilation after the given stage. Currently only "parsing" is valid here
"stopAfter": "parsing",
// 可选: 重定向参数的排序列表
remappings: [ ":g/dir" ],
// 可选: 优化器配置
optimizer: {
// 默认为 disabled
enabled: true,
// 基于你希望运行多少次代码来进行优化。
// 较小的值可以使初始部署的费用得到更多优化,较大的值可以使高频率的使用得到优化。
runs: 200,
// Switch optimizer components on or off in detail.
// The "enabled" switch above provides two defaults which can be
// tweaked here. If "details" is given, "enabled" can be omitted.
"details": {
// The peephole optimizer is always on if no details are given,
// use details to switch it off.
"peephole": true,
// The unused jumpdest remover is always on if no details are given,
// use details to switch it off.
"jumpdestRemover": true,
// Sometimes re-orders literals in commutative operations.
"orderLiterals": false,
// Removes duplicate code blocks
"deduplicate": false,
// Common subexpression elimination, this is the most complicated step but
// can also provide the largest gain.
"cse": false,
// Optimize representation of literal numbers and strings in code.
"constantOptimizer": false,
// The new Yul optimizer. Mostly operates on the code of ABI coder v2
// and inline assembly.
// It is activated together with the global optimizer setting
// and can be deactivated here.
// Before Solidity 0.6.0 it had to be activated through this switch.
"yul": false,
// Tuning options for the Yul optimizer.
"yulDetails": {
// Improve allocation of stack slots for variables, can free up stack slots early.
// Activated by default if the Yul optimizer is activated.
"stackAllocation": true,
// Select optimization steps to be applied.
// Optional, the optimizer will use the default sequence if omitted.
"optimizerSteps": "dhfoDgvulfnTUtnIf..."
}
},
// 指定需编译的EVM的版本。会影响代码的生成和类型检查。可用的版本为:homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople
evmVersion: "byzantium",
// Optional: Change compilation pipeline to go through the Yul intermediate representation.
// This is a highly EXPERIMENTAL feature, not to be used for production. This is false by default.
"viaIR": true,
// Optional: Debugging settings
"debug": {
// How to treat revert (and require) reason strings. Settings are
// "default", "strip", "debug" and "verboseDebug".
// "default" does not inject compiler-generated revert strings and keeps user-supplied ones.
// "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects
// "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
// "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented)
"revertStrings": "default"
}
// 可选: 元数据配置
metadata: {
// 只可使用字面内容,不可用URLs (默认设为 false)
useLiteralContent: true,
// Use the given hash method for the metadata hash that is appended to the bytecode.
// The metadata hash can be removed from the bytecode via option "none".
// The other options are "ipfs" and "bzzr1".
// If the option is omitted, "ipfs" is used by default.
"bytecodeHash": "ipfs"
},
// 库的地址。如果这里没有把所有需要的库都给出,会导致生成输出数据不同的未链接对象
libraries: {
// 最外层的 key 是使用这些库的源文件的名字。
// 如果使用了重定向, 在重定向之后,这些源文件应该能匹配全局路径
// 如果源文件的名字为空,则所有的库为全局引用
"myFile.sol": {
"MyLib": "0x123123..."
}
}
// 以下内容可以用于选择所需的输出。
// 如果这个字段被忽略,那么编译器会加载并进行类型检查,但除了错误之外不会产生任何输出。
// 第一级的key是文件名,第二级是合约名称,如果合约名为空,则针对文件本身(进行输出)。
// 若使用通配符*,则表示所有合约。
//
// 可用的输出类型如下所示:
// abi - ABI
// ast - 所有源文件的AST
// devdoc - 开发者文档(natspec)
// userdoc - 用户文档(natspec)
// metadata - 元数据
// ir - 去除语法糖(desugaring)之前的新汇编格式
// irOptimized - Intermediate representation after optimization
// storageLayout - Slots, offsets and types of the contract's state variables.
// evm.assembly - 去除语法糖(desugaring)之后的新汇编格式
// evm.legacyAssembly - JSON的旧样式汇编格式
// evm.bytecode.object - 字节码对象
// evm.bytecode.opcodes - 操作码列表
// evm.bytecode.sourceMap - 源码映射(用于调试)
// evm.bytecode.linkReferences - 链接引用(如果是未链接的对象)
// evm.bytecode.generatedSources - Sources generated by the compiler
// evm.deployedBytecode* - 部署的字节码(具有evm.bytecode所有的选项)
// evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables
// evm.methodIdentifiers - 函数哈希值列表
// evm.gasEstimates - 函数的gas预估量
// ewasm.wast - Ewasm in WebAssembly S-expressions 格式
// ewasm.wasm - Ewasm in WebAssembly 二进制格式
//
// 请注意,如果使用 `evm` ,`evm.bytecode` ,`ewasm` 等选项,会选择其所有的子项作为输出。 另外,`*`可以用作通配符来请求所有内容。
//
outputSelection: {
// 为每个合约生成元数据和字节码输出。
"*": {
"*": [ "metadata","evm.bytecode" ]
},
// 启用“def”文件中定义的“MyContract”合约的abi和opcodes输出。
"def": {
"MyContract": [ "abi","evm.bytecode.opcodes" ]
},
},
"modelChecker":
{
// Choose which model checker engine to use: all (default), bmc, chc, none.
"engine": "chc",
// Timeout for each SMT query in milliseconds.
// If this option is not given, the SMTChecker will use a deterministic
// resource limit by default.
// A given timeout of 0 means no resource/time restrictions for any query.
"timeout": 20000
}
}
}
}
输出说明¶
{
// 可选:如果没有遇到错误/警告,则不出现
errors: [
{
// 可选:源文件中的位置
sourceLocation: {
file: "sourceFile.sol",
start: 0,
end: 100
],
// Optional: Further locations (e.g. places of conflicting declarations)
secondarySourceLocations: [
{
"file": "sourceFile.sol",
"start": 64,
"end": 92,
"message": "Other declaration is here:"
}
],
// 强制: 错误类型,例如 “TypeError”, “InternalCompilerError”, “Exception”等.
// 可在文末查看完整的错误类型列表
type: "TypeError",
// 强制: 发生错误的组件,例如“general”,“ewasm”等
component: "general",
// 强制:错误的严重级别(“error”或“warning”)
severity: "error",
// 可选: 引起错误的唯一编码
"errorCode": "3141",
// 强制
message: "Invalid keyword",
// 可选: 带错误源位置的格式化消息
formattedMessage: "sourceFile.sol:100: Invalid keyword"
}
],
// 这里包含了文件级别的输出。可以通过outputSelection来设置限制/过滤。
sources: {
"sourceFile.sol": {
// 标识符(用于源码映射)
id: 1,
// AST对象
ast: {}
}
},
// 这里包含了合约级别的输出。 可以通过outputSelection来设置限制/过滤。
contracts: {
"sourceFile.sol": {
// 如果使用的语言没有合约名称,则该字段应该留空。
"ContractName": {
// 以太坊合约的应用二进制接口(ABI)。如果为空,则表示为空数组。
// 请参阅 https://docs.soliditylang.org/en/develop/abi-spec.html
abi: [],
// 请参阅元数据输出文档(序列化的JSON字符串)
metadata: "{...}",
// 用户文档(natspec)
userdoc: {},
// 开发人员文档(natspec)
devdoc: {},
// 中间表示形式 (string)
ir: "",
// EVM相关输出
evm: {
// 汇编 (string)
assembly: "",
// 旧风格的汇编 (object)
legacyAssembly: {},
// 字节码和相关细节
bytecode: {
// 十六进制字符串的字节码
object: "00fe",
// 操作码列表 (string)
opcodes: "",
// 源码映射的字符串。 请参阅源码映射的定义
sourceMap: "",
// Array of sources generated by the compiler. Currently only
// contains a single Yul file.
"generatedSources": [{
// Yul AST
"ast": { ... }
// Source file in its text form (may contain comments)
"contents":"{ function abi_decode(start, end) -> data { data := calldataload(start) } }",
// Source file ID, used for source references, same "namespace" as the Solidity source files
"id": 2,
"language": "Yul",
"name": "#utility.yul"
}]
// 如果这里给出了信息,则表示这是一个未链接的对象
linkReferences: {
"libraryFile.sol": {
// 字节码中的字节偏移;链接时,从指定的位置替换20个字节
"Library1": [
{ start: 0,length: 20 },
{ start: 200,length: 20 }
]
}
}
},
deployedBytecode: {
... // 与上面相同的布局
"immutableReferences": {
// There are two references to the immutable with AST ID 3, both 32 bytes long. One is
// at bytecode offset 42, the other at bytecode offset 80.
"3": [{ "start": 42, "length": 32 }, { "start": 80, "length": 32 }]
}
},
// 函数哈希的列表
methodIdentifiers: {
"delegate(address)": "5c19a95c"
},
// 函数的gas预估量
gasEstimates: {
creation: {
codeDepositCost: "420000",
executionCost: "infinite",
totalCost: "infinite"
},
external: {
"delegate(address)": "25000"
},
internal: {
"heavyLifting()": "infinite"
}
}
},
// Ewasm相关的输出
ewasm: {
// S-expressions格式
wast: "",
// 二进制格式(十六进制字符串)
wasm: ""
}
}
}
}
}
错误类型¶
JSONError
: JSON输入不符合所需格式,例如,输入不是JSON对象,不支持的语言等。IOError
: IO和导入处理错误,例如,在提供的源里包含无法解析的URL或哈希值不匹配。ParserError
: 源代码不符合语言规则。DocstringParsingError
: 注释块中的NatSpec标签无法解析。SyntaxError
: 语法错误,例如continue
在for
循环外部使用。DeclarationError
: 无效的,无法解析的或冲突的标识符名称 比如Identifier not found
。TypeError
: 类型系统内的错误,例如无效类型转换,无效赋值等。UnimplementedFeatureError
: 编译器当前不支持该功能,但预计将在未来的版本中支持。InternalCompilerError
: 在编译器中触发的内部错误——应将此报告为一个issue。Exception
: 编译期间的未知失败——应将此报告为一个issue。CompilerError
: 编译器堆栈的无效使用——应将此报告为一个issue。FatalError
: 未正确处理致命错误——应将此报告为一个issue。Warning
: 警告,不会停止编译,但应尽可能处理。
Compiler tools¶
solidity-upgrade¶
solidity-upgrade
can help you to semi-automatically upgrade your contracts
to breaking language changes. While it does not and cannot implement all
required changes for every breaking release, it still supports the ones, that
would need plenty of repetitive manual adjustments otherwise.
注解
solidity-upgrade
carries out a large part of the work, but your
contracts will most likely need further manual adjustments. We recommend
using a version control system for your files. This helps reviewing and
eventually rolling back the changes made.
警告
solidity-upgrade
is not considered to be complete or free from bugs, so
please use with care.
How it works¶
You can pass (a) Solidity source file(s) to solidity-upgrade [files]
. If
these make use of import
statement which refer to files outside the
current source file’s directory, you need to specify directories that
are allowed to read and import files from, by passing
--allow-paths [directory]
. You can ignore missing files by passing
--ignore-missing
.
solidity-upgrade
is based on libsolidity
and can parse, compile and
analyse your source files, and might find applicable source upgrades in them.
Source upgrades are considered to be small textual changes to your source code.
They are applied to an in-memory representation of the source files
given. The corresponding source file is updated by default, but you can pass
--dry-run
to simulate to whole upgrade process without writing to any file.
The upgrade process itself has two phases. In the first phase source files are
parsed, and since it is not possible to upgrade source code on that level,
errors are collected and can be logged by passing --verbose
. No source
upgrades available at this point.
In the second phase, all sources are compiled and all activated upgrade analysis modules are run alongside compilation. By default, all available modules are activated. Please read the documentation on available modules for further details.
This can result in compilation errors that may
be fixed by source upgrades. If no errors occur, no source upgrades are being
reported and you’re done.
If errors occur and some upgrade module reported a source upgrade, the first
reported one gets applied and compilation is triggered again for all given
source files. The previous step is repeated as long as source upgrades are
reported. If errors still occur, you can log them by passing --verbose
.
If no errors occur, your contracts are up to date and can be compiled with
the latest version of the compiler.
Available upgrade modules¶
Module | Version | Description |
---|---|---|
constructor |
0.5.0 | Constructors must now be defined using the
constructor keyword. |
visibility |
0.5.0 | Explicit function visibility is now mandatory,
defaults to public . |
abstract |
0.6.0 | The keyword abstract has to be used if a
contract does not implement all its functions. |
virtual |
0.6.0 | Functions without implementation outside an
interface have to be marked virtual . |
override |
0.6.0 | When overriding a function or modifier, the new
keyword override must be used. |
dotsyntax |
0.7.0 | The following syntax is deprecated:
f.gas(...)() , f.value(...)() and
(new C).value(...)() . Replace these calls by
f{gas: ..., value: ...}() and
(new C){value: ...}() . |
now |
0.7.0 | The now keyword is deprecated. Use
block.timestamp instead. |
constructor-visibility |
0.7.0 | Removes visibility of constructors. |
Please read 0.5.0 release notes, 0.6.0 release notes and 0.7.0 release notes and 0.8.0 release notes for further details.
Synopsis¶
Usage: solidity-upgrade [options] contract.sol
Allowed options:
--help Show help message and exit.
--version Show version and exit.
--allow-paths path(s)
Allow a given path for imports. A list of paths can be
supplied by separating them with a comma.
--ignore-missing Ignore missing files.
--modules module(s) Only activate a specific upgrade module. A list of
modules can be supplied by separating them with a comma.
--dry-run Apply changes in-memory only and don't write to input
file.
--verbose Print logs, errors and changes. Shortens output of
upgrade patches.
--unsafe Accept *unsafe* changes.
Bug Reports / Feature requests¶
If you found a bug or if you have a feature request, please file an issue on Github.
Example¶
Assume that you have the following contract in Source.sol
:
pragma solidity >=0.6.0 <0.6.4;
// This will not compile after 0.7.0
// SPDX-License-Identifier: GPL-3.0
contract C {
// FIXME: remove constructor visibility and make the contract abstract
constructor() internal {}
}
contract D {
uint time;
function f() public payable {
// FIXME: change now to block.timestamp
time = now;
}
}
contract E {
D d;
// FIXME: remove constructor visibility
constructor() public {}
function g() public {
// FIXME: change .value(5) => {value: 5}
d.f.value(5)();
}
}
Required changes¶
The above contract will not compile starting from 0.7.0. To bring the contract up to date with the
current Solidity version, the following upgrade modules have to be executed:
constructor-visibility
, now
and dotsyntax
. Please read the documentation on
available modules for further details.
Running the upgrade¶
It is recommended to explicitly specify the upgrade modules by using --modules
argument.
$ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol
The command above applies all changes as shown below. Please review them carefully (the pragmas will have to be updated manually.)
pragma solidity ^0.7.0;
// SPDX-License-Identifier: GPL-3.0
abstract contract C {
// FIXME: remove constructor visibility and make the contract abstract
constructor() {}
}
contract D {
uint time;
function f() public payable {
// FIXME: change now to block.timestamp
time = block.timestamp;
}
}
contract E {
D d;
// FIXME: remove constructor visibility
constructor() {}
function g() public {
// FIXME: change .value(5) => {value: 5}
d.f{value: 5}();
}
}