Layout of a Solidity Source File
本文档翻译自 Solidity docs。
源文件可以包含任意数量的 contract定义,import 指令和 pragma 指令。
Pragmas
pragma
关键字可用于启用某些编译器功能或检查。 pragma指令始终是源文件的本地指令,因此如果要在所有项目中启用它,则必须将pragma添加到所有文件中。如果 import 另一个文件,该文件中的pragma将不会自动应用于导入文件。
Version Pragma
源文件可以(并且应该)使用所谓的版本编译指示进行注释,以拒绝使用可能引入不兼容更改的未来编译器版本进行编译。我们尝试将这些更改保持在绝对最小值,尤其是以语义变化也需要更改语法的方式引入更改,但这当然不总是可行的。因此,至少对于包含重大更改的版本来读取更改日志总是一个好主意,这些版本将始终具有 0.x.0
或 x.0.0
形式的版本。
版本编译指示使用如下:
pragma solidity ^0.4.0;
这样的源文件不会使用早于版本0.4.0的编译器进行编译,并且它也不能在从版本0.5.0开始的编译器上工作(通过使用 ^
添加第二个条件)。这背后的想法是在版本 0.5.0
之前不会有任何重大更改,因此我们始终可以确保我们的代码将按照我们的预期方式进行编译。我们没有修复编译器的确切版本,因此仍然可以使用bugfix版本。
可以为编译器版本指定更复杂的规则,表达式遵循 npm 使用的表达式。
Note
使用版本编译指示不会更改编译器的版本。它也不会启用或禁用编译器的功能。它只是指示编译器检查其版本是否与pragma所需的版本匹配。如果不匹配,编译器将发出错误。
Experimental Pragma
第二个pragma是实验性的pragma。它可用于启用默认情况下尚未启用的编译器或语言的功能。目前支持以下实验编译指示:
ABIEncoderV2
新的ABI编码器能够对任意嵌套的数组和结构进行编码和解码。它产生的优化代码不太理想(这部分代码的优化器仍在开发中)并且没有像旧编码器那样接收到太多的测试。您可以使用 pragma experimental ABIEncoderV2;
激活它。
SMTChecker
构建Solidity编译器时必须启用此组件,因此它不适用于所有Solidity二进制文件。构建说明解释了如何激活此选项。它在大多数版本中为Ubuntu PPA版本激活,但不适用于solc-js,Docker镜像,Windows二进制文件或静态构建的Linux二进制文件。
如果您使用 pragma experimental SMTChecker;
,那么您将获得通过查询SMT求解器获得的其他安全警告。该组件尚不支持Solidity语言的所有功能,可能会输出许多警告。如果报告不支持的功能,分析可能不完整。
Importing other Source Files
Syntax and Semantics
Solidity支持与JavaScript中可用的导入语句非常相似的语句(来自ES6),尽管Solidity不知道“默认导出”的概念。
在全局级别,您可以使用以下形式的import语句:
import "filename";
此语句将所有全局符号从“filename”(以及在那里导入的符号)导入当前全局范围(与ES6不同,但向后兼容Solidity)。建议不要使用这种简单的形式,因为它会以不可预测的方式污染命名空间:如果在“filename”中添加新的顶级项,它们将自动出现在所有从“filename”导入的文件中。最好明确导入特定符号。
以下示例创建一个新的全局符号 symbolName
,其成员是 “filename”
中的所有全局符号。
import * as symbolName from "filename";
如果存在命名冲突,您还可以在导入时重命名符号。此代码创建新的全局符号 alias
和 symbol2
,它们分别从 “filename”
中引用 symbol1
和 symbol2
。
import {symbol1 as alias, symbol2} from "filename";
另一种语法不是ES6的一部分,但可能很方便:
import "filename" as symbolName;
等价于 import * as symbolName from "filename";
。
Note
如果使用 import “filename.sol” 作为 moduleName;,则从 “filename.sol” 中作为 moduleName.C 访问名为 C 的合约,而不是直接使用 C.
Paths
在上面,filename
始终被视为带 /
作为目录分隔符的路径。.
作为当前和 ..
作为父目录。什么时候 .
或 ..
后跟一个字符,除了 /
,它不被视为当前或父目录。所有路径名都被视为绝对路径,除非它们以当前路径 .
开头,或者父目录 ..
。
要从与当前文件相同的目录导入文件 x
,请使用 import "./x" as x;
。如果使用 import "x" as x;
相反,可能引用不同的文件(在全局 “include director” 中)。
这取决于编译器(见下文)如何实际解析路径。通常,目录层次结构不需要严格映射到本地文件系统,它也可以映射到通过发现的资源,例如 ipfs,http或git。
Note
始终使用 `import "./filename.sol";` 之类的相对导入;并避免在路径说明符中使用 `..`。在后一种情况下,最好使用全局路径并设置重映射,如下所述。
Use in Actual Compilers
调用编译器时,您可以指定如何发现路径的第一个元素以及路径前缀重映射。例如,您可以设置重映射,以便从您的本地目录 /usr/local/dapp-bin/library
中实际读取从虚拟目录 github.com/ethereum/dapp-bin/library
导入的所有内容。如果应用多个重映射,则首先尝试具有最长密钥的那个。不允许使用空前缀。重映射可以取决于上下文,允许您配置要导入的包,例如,同名库的不同版本。
solc:
对于solc(命令行编译器),您将这些路径重映射提供为 context:prefix=target
arguments,其中 context:
和 = target
部分都是可选的(在这种情况下,target
默认为 prefix
)。编译常规文件的所有重映射值(包括它们的依赖关系)。
这种机制是向后兼容的(只要没有文件名包含 =
或 :
),因此不会发生重大变化。导入以 prefix
开头的文件的 context
目录中或下面的所有文件都通过将 prefix
替换为 target
来重定向。
例如,如果将 github.com/ethereum/dapp-bin/
本地克隆到 /usr/local/dapp-bin
,则可以在源文件中使用以下内容:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
然后运行编译器:
solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol
作为一个更复杂的示例,假设您依赖于使用您签出到 /usr/local/dapp-bin_old
的旧版dapp-bin的模块,那么您可以运行:
solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \
module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
source.sol
这意味着 module2
中的所有导入都指向旧版本,但 module1
中的导入指向新版本。
Note
`solc` 只允许您包含某些目录中的文件。它们必须位于其中一个显式指定的源文件的目录(或子目录)中,或者位于重映射目标的目录(或子目录)中。如果要允许直接绝对包含,请添加重映射 `/=/`。
如果存在多个导致有效文件的重映射,则选择具有最长公共前缀的重映射。
Remix:
Remix 为GitHub提供自动重映射,并通过网络自动检索文件。您可以导入上面的可迭代映射,例如
::
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
Remix可能会在将来添加其他源代码提供程序。
Comments
可以使用单行注释 (//) 和多行注释 (/.../)。
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
Note
单行注释由utf8编码中的任何unicode行终止符(LF,VF,FF,CR,NEL,LS或PS)终止。注释后终结符仍然是源代码的一部分,因此如果它不是ascii符号(这些是NEL,LS和PS),则会导致解析器错误。
此外,还有另一种称为natspec注释的注释,其文档尚未编写。它们使用三斜杠 (///) 或双星号块 (/** ... */) 编写,它们应直接在函数声明或语句之上使用。您可以在这些注释中使用 Doxygen样式 的标记来记录函数,注释形式验证的条件,并提供在用户尝试调用函数时向用户显示的确认文本。
在下面的示例中,我们记录了合约的标题,两个函数参数的说明和两个返回变量。
pragma solidity >=0.4.0 <0.6.0;
/** @title Shape calculator. */
contract ShapeCalculator {
/** @dev Calculates a rectangle's surface and perimeter.
* @param w Width of the rectangle.
* @param h Height of the rectangle.
* @return s The calculated surface.
* @return p The calculated perimeter.
*/
function rectangle(uint w, uint h) public pure returns (uint s, uint p) {
s = w * h;
p = 2 * (w + h);
}
}
项目源代码
项目源代码会逐步上传到 Github,地址为 https://github.com/windstamp/dapp。
Contributor
- Windstamp, https://github.com/windstamp