一、一个solidity文件的标准开头,要包括:
1、代码许可声明,格式如下:
//SPDX-Licence-identifier MIT
编译器不检验代码许可声明,但是会将这部分内容放入编译后的字节码中。
2、编译器版本约束,格式如下:
pragma >=0.8.0 <0.9.0或者pragma ^0.8.0。
两者表示相同编译器版本的范围约束。
3、ABI编码方案(可选),格式为:
pragma abicoder v1 或pragma abicoder v2。
v2与v1的区别在于v2实现了对任意嵌套数组、结构体的abi编解码支持,同时做了更多的额外校验和安全检查,因此gas消耗要更高,v2是v1的一个超集,因此存量采用v1的代码可以直接被v2兼容。
但是要注意,如果采用了v1的合约在调用了采用v2的合约时,如果要解码只有v2合约才支持的数据则会失败。
在0.8.0版本之后,v2为默认编译版本。
二、solidity中的文件导入格式有如下几种:
1、import * as symbolName from filename;后续使用的时候可以通过symbolName.symbol的方式显示引用导入文件中的内容。
2、import filename as symbolName;用法同上。
3、import {symbol1 as alias, symbol2} from fileName;这种方式则是有选择的导入文件中的要素,同时针对命名冲突的情况,还可以在导入的时候指定别名。
当然也可以直接导入fileName,但是不推荐,因为最顶层的导入文件如果有修改,可能会带来命名冲突。
4、solidity也能通过url进行导入以及从本地npm库导入,比如:
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/math/SafeMath.sol";
或
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
通过指定符号的方式导入文件,会将被导入文件中引用的若干依赖全部导入,但是只有直接被导入文件中的符号才会在当前文件中可见,被导入文件的其他可见符号在当前操作导入的文件中并不可见(于此相对的是直接导入文件名,则被导入文件中的可见符号在当前文件均可见),比如文件2定义的合约B继承了文件1导入的合约A,文件3定义的合约C继承了文件2中导入的合约B,那么在文件3中合约B是可见符号,合约A不是。如果合约A、合约B均需要带参数构造,并且合约B(abstract contract,抽象函数可以不实现继承的父类的方法)中并没有指定A的构造参数,那么在合约C中需要实现对A和B行带参数初始化的构造函数(假设C不是抽象合约),但是在C中只有B是可见的,所以还要再额外导入文件1的合约A才能使得A在C中可见,在引入多文件且文件间存在继承关系的合约,要注意有些方法可能在子合约中被重写,这时会出现多个合约中存在同名函数而冲突的问题,可以通过冲突方法的重写(override)来解决该问题。
指定符号导入
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20, ERC20Votes {}
其中ERC20Votes虽然继承了ERC20,但通过import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"只导入ERC20Votes,为了让ERC20也可见,需要再import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"。
指定文件名导入
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
contract TestToken is ERC20,ERC20Votes{}
因为在ERC20Votes.sol中ERC20也是可见符号,所以在合约TestToken中不需要再显式引入ERC20。