规范文档https://docs.soliditylang.org/en/v0.8.17/style-guide.html
1.缩进
每个缩进使用4个空格 应该避免使用空格和tab混合缩进
2.间隔
对于顶级声明来说应该使用两个空行
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract A {
// ...
}
contract B {
// ...
}
contract C {
// ...
}No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract A {
// ...
}
contract B {
// ...
}
contract C {
// ...
}合约中的函数间隔使用一个空行 抽象合约中的函数之间不使用空行
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
abstract contract A {
function spam() public virtual pure;
function ham() public virtual pure;
}
contract B is A {
function spam() public pure override {
// ...
}
function ham() public pure override {
// ...
}
}No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
abstract contract A {
function spam() virtual pure public;
function ham() public virtual pure;
}
contract B is A {
function spam() public pure override {
// ...
}
function ham() public pure override {
// ...
}
}3.每行最大的长度
- 推荐最大的长度为120个字符
对于换行应该遵循的规则:
- 第一个参数不应追加到左括号之后。
- 只使用一个缩进。
- 每个参数应独占一行。
- 结束符); 应单独放在最后一行。
对于函数调用的示例
Yes:
thisFunctionCallIsReallyLong(
longArgument1,
longArgument2,
longArgument3
);No:
thisFunctionCallIsReallyLong(longArgument1,
longArgument2,
longArgument3
);
thisFunctionCallIsReallyLong(longArgument1,
longArgument2,
longArgument3
);
thisFunctionCallIsReallyLong(
longArgument1, longArgument2,
longArgument3
);
thisFunctionCallIsReallyLong(
longArgument1,
longArgument2,
longArgument3
);
thisFunctionCallIsReallyLong(
longArgument1,
longArgument2,
longArgument3);赋值语句示例
Yes:
thisIsALongNestedMapping[being][set][toSomeValue] = someFunction(
argument1,
argument2,
argument3,
argument4
);No:
thisIsALongNestedMapping[being][set][toSomeValue] = someFunction(argument1,
argument2,
argument3,
argument4);事件定义和事件触发示例
Yes:
event LongAndLotsOfArgs(
address sender,
address recipient,
uint256 publicKey,
uint256 amount,
bytes32[] options
);
LongAndLotsOfArgs(
sender,
recipient,
publicKey,
amount,
options
);No:
event LongAndLotsOfArgs(address sender,
address recipient,
uint256 publicKey,
uint256 amount,
bytes32[] options);
LongAndLotsOfArgs(sender,
recipient,
publicKey,
amount,
options);4.源代码文件编码最好使用 UTF-8 或 ASCII 编码
5.Imports
imports语句应该始终放在文件的顶部
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
import "./Owned.sol";
contract A {
// ...
}
contract B is Owned {
// ...
}No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract A {
// ...
}
import "./Owned.sol";
contract B is Owned {
// ...
}6.函数的放置顺序
函数从前往后放置顺序
- constructor
- receive function (if exists)
- fallback function (if exists)
- external
- public
- internal
- private
对于同一优先级的函数, view and pure放在后面
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract A {
constructor() {
// ...
}
receive() external payable {
// ...
}
fallback() external {
// ...
}
// External functions
// ...
// External functions that are view
// ...
// External functions that are pure
// ...
// Public functions
// ...
// Internal functions
// ...
// Private functions
// ...
}No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract A {
// External functions
// ...
fallback() external {
// ...
}
receive() external payable {
// ...
}
// Private functions
// ...
// Public functions
// ...
constructor() {
// ...
}
// Internal functions
// ...
}7.表达式中的空格
应该避免使用多余的空格 括号、方括号或花括号内,单行函数声明除外。
Yes:
spam(ham[1], Coin({name: "ham"}));No:
spam( ham[ 1 ], Coin( { name: "ham" } ) );单行函数声明除外:
function singleLine() public { spam(); }逗号和分号之前去除多余的空格
Yes:
function spam(uint i, Coin coin) public;No:
function spam(uint i , Coin coin) public ;赋值和运算符周围的超过一个空格的也需要去除
Yes:
x = 1;
y = 2;
longVariable = 3;No:
x = 1;
y = 2;
longVariable = 3;不要在receive和fallback函数名的地方增加多余的空格
Yes:
receive() external payable {
...
}
fallback() external {
...
}No:
receive () external payable {
...
}
fallback () external {
...
}8.控制结构
合约、库、函数和结构体主体应该和左括号在一行
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract Coin {
struct Bank {
address owner;
uint balance;
}
}No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
contract Coin
{
struct Bank {
address owner;
uint balance;
}
}if、while 和 for 关键词与左括号之间应该有一个空格,右括号和左花括号之间也应该有一个空格
Yes:
if (...) {
...
}
for (...) {
...
}No:
if (...)
{
...
}
while(...){
}
for (...) {
...;}控制结构中如果包含的语句只在一行时可以省略括号
Yes:
if (x < 10)
x += 1;No:
if (x < 10)
someArray.push(Coin({
name: 'spam',
value: 42
}));对于包含 else 或 else if 子句的 if 块,else 应该与 if 的右括号放在同一行
Yes:
if (x < 3) {
x += 1;
} else if (x > 7) {
x -= 1;
} else {
x = 5;
}
if (x < 3)
x += 1;
else
x -= 1;No:
if (x < 3) {
x += 1;
}
else {
x -= 1;
}9.函数定义
对于比较短的函数定义,建议将函数体的左括号与函数定义保持在同一行。
右括号的缩进级别应与函数定义相同。
左括号前应有一个空格。
Yes:
function increment(uint x) public pure returns (uint) {
return x + 1;
}
function increment(uint x) public pure onlyOwner returns (uint) {
return x + 1;
}No:
function increment(uint x) public pure returns (uint)
{
return x + 1;
}
function increment(uint x) public pure returns (uint){
return x + 1;
}
function increment(uint x) public pure returns (uint) {
return x + 1;
}
function increment(uint x) public pure returns (uint) {
return x + 1;}函数的修饰符应该是按照如下顺序:
- Visibility
- Mutability
- Virtual
- Override
- 自定义 修饰符
Yes:
function balance(uint from) public view override returns (uint) {
return balanceOf[from];
}
function shutdown() public onlyOwner {
selfdestruct(owner);
}No:
function balance(uint from) public override view returns (uint) {
return balanceOf[from];
}
function shutdown() onlyOwner public {
selfdestruct(owner);
}对于较长的函数定义,建议将每个参数单独放在一行,并与函数内语句保持相同的缩进级别。右括号和右括号也应单独放在一行,并与函数定义保持相同的缩进级别。
Yes:
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f
)
public
{
doSomething();
}No:
function thisFunctionHasLotsOfArguments(address a, address b, address c,
address d, address e, address f) public {
doSomething();
}
function thisFunctionHasLotsOfArguments(address a,
address b,
address c,
address d,
address e,
address f) public {
doSomething();
}
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f) public {
doSomething();
}如果函数有修饰符 修饰符放在一列
Yes:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyOwner
priced
returns (address)
{
doSomething();
}
function thisFunctionNameIsReallyLong(
address x,
address y,
address z
)
public
onlyOwner
priced
returns (address)
{
doSomething();
}No:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyOwner
priced
returns (address) {
doSomething();
}
function thisFunctionNameIsReallyLong(address x, address y, address z)
public onlyOwner priced returns (address)
{
doSomething();
}
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyOwner
priced
returns (address) {
doSomething();
}多行输出参数和返回语句应遵循“最大行长度”部分中建议的换行长行的相同样式
Yes:
function thisFunctionNameIsReallyLong(
address a,
address b,
address c
)
public
returns (
address someAddressName,
uint256 LongArgument,
uint256 Argument
)
{
doSomething()
return (
veryLongReturnArg1,
veryLongReturnArg2,
veryLongReturnArg3
);
}No:
function thisFunctionNameIsReallyLong(
address a,
address b,
address c
)
public
returns (address someAddressName,
uint256 LongArgument,
uint256 Argument)
{
doSomething()
return (veryLongReturnArg1,
veryLongReturnArg1,
veryLongReturnArg1);
}对于继承的合约上的base需要参数的构造函数,如果函数很长或难以阅读,建议将base构造函数以与修饰符相同的方式放到多行。
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// Base contracts just to make this compile
contract B {
constructor(uint) {
}
}
contract C {
constructor(uint, uint) {
}
}
contract D {
constructor(uint) {
}
}
contract A is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
{
// do something with param5
x = param5;
}
}No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// Base contracts just to make this compile
contract B {
constructor(uint) {
}
}
contract C {
constructor(uint, uint) {
}
}
contract D {
constructor(uint) {
}
}
contract A is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4) {
x = param5;
}
}
contract X is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4) {
x = param5;
}
}对于简单的函数定义使用一行
function shortFunction() public { doSomething(); }10.Mappings
在变量声明中,不要用空格分隔关键字 mapping 和其类型。不要用空格分隔任何嵌套的 mapping 关键字和其类型。
Yes:
mapping(uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;No:
mapping (uint => uint) map;
mapping( address => bool ) registeredAddresses;
mapping (uint => mapping (bool => Data[])) public data;
mapping(uint => mapping (uint => s)) data;11.变量定义
定义数组的时候不要将类型和[]分开
Yes:
uint[] x;No:
uint [] x;12.Other Recommendations
string类型应该使用双引号而不是单引号
Yes:
str = "foo";
str = "Hamlet says, 'To be or not to be...'";No:
str = 'bar';
str = '"Be yourself; everyone else is already taken." -Oscar Wilde';运算符周围应该有一个空格
Yes:
x = 3;
x = 100 / 10;
x += 3 + 4;
x |= y && z;No:
x=3;
x = 100/10;
x += 3+4;
x |= y&&z;高优先级的运算符周围可以省略空格
Yes:
x = 2**3 + 5;
x = 2*y + 3*z;
x = (a+b) * (a-b);No:
x = 2** 3 + 5;
x = y+z;
x +=1;13.布局顺序
合约中的元素布局顺序
- Pragma statements
- Import statements
- Interfaces
- Libraries
- Contracts
在合约中库、接口顺序
- Type declarations
- State variables
- Events
- Modifiers
- Functions
14.命名规范
命名风格
为了避免困惑,以下的将指代不同命名风格
b(single lowercase letter)B(single uppercase letter)lowercaseUPPERCASEUPPER_CASE_WITH_UNDERSCORESCapitalizedWords(or CapWords)mixedCase(differs from CapitalizedWords by initial lowercase character!)
Note
在 CapWords 中使用首字母缩写时,请将首字母缩写的所有字母大写。因此,HTTPServerError 优于 HttpServerError。在 mixedCase 中使用首字母缩写时,请将首字母缩写的所有字母大写,但如果它是名称的开头,则保留第一个字母小写。因此,xmlHTTPRequest 优于 XMLHTTPRequest。
避免使用
l- Lowercase letter elO- Uppercase letter ohI- Uppercase letter eye
切勿将这些字符用作单字母变量名。它们通常与数字 1 和 0 难以区分。
合约和库名
- 合约和库的名字应该使用类似 CapWords的风格. Examples:
SimpleToken,SmartBank,CertificateHashRepository,Player,Congress,Owned. - 合约和库的名字应该匹配他们的文件名.
- 如果一个合约文件包含了多个合约或者库,那么这个文件名应该以核心的合约命名。如果可以避免尽量不要这样。
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// Owned.sol
contract Owned {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
owner = newOwner;
}
}Congress.sol:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
import "./Owned.sol";
contract Congress is Owned, TokenRecipient {
//...
}No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// owned.sol
contract owned {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
owner = newOwner;
}
}Congress.sol:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.0;
import "./owned.sol";
contract Congress is owned, tokenRecipient {
//...
}结构体名字
结构体名字应该使用 CapWords 风格. Examples: MyCoin, Position, PositionXY.
事件名字
事件名字应该使用CapWords 风格. Examples: Deposit, Transfer, Approval, BeforeTransfer, AfterTransfer.
函数名字
函数名字应该使用混合大小写. Examples: getBalance, transfer, verifyOwner, addMember, changeOwner.
函数参数名
函数参数应该使用缓和大小写.Examples: initialSupply, account, recipientAddress, senderAddress, newOwner.
当编写对自定义结构进行操作的库函数时,该结构应该是第一个参数,并且应始终命名为“self”
本地状态变量名
使用大小写混合Examples: totalSupply, remainingSupply, balancesOf, creatorAddress, isPreSale, tokenExchangeRate.
常量
常量应该使用大写字母并且下划线分割 Examples: MAX_BLOCKS, TOKEN_NAME, TOKEN_TICKER, CONTRACT_VERSION.
修饰符名
使用大小写混合 Examples: onlyBy, onlyAfter, onlyDuringThePreSale.
枚举类型名
名字命名应该是使用CapWords风格. Examples: TokenGroup, Frame, HashStyle, CharacterLocation.
15.NatSpec
建议使用 NatSpec 对所有公共接口(ABI 中的所有内容)进行完整注释。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
/// @author The Solidity Team
/// @title A simple storage example
contract SimpleStorage {
uint storedData;
/// Store `x`.
/// @param x the new value to store
/// @dev stores the number in the state variable `storedData`
function set(uint x) public {
storedData = x;
}
/// Return the stored value.
/// @dev retrieves the value of the state variable `storedData`
/// @return the stored value
function get() public view returns (uint) {
return storedData;
}
}| Tag | Context | |
|---|---|---|
@title | A title that should describe the contract/interface | contract, library, interface |
@author | The name of the author | contract, library, interface |
@notice | Explain to an end user what this does | contract, library, interface, function, public state variable, event |
@dev | Explain to a developer any extra details | contract, library, interface, function, state variable, event |
@param | Documents a parameter just like in Doxygen (must be followed by parameter name) | function, event |
@return | Documents the return variables of a contract’s function | function, public state variable |
@inheritdoc | Copies all missing tags from the base function (must be followed by the contract name) | function, public state variable |
@custom:... | Custom tag, semantics is application-defined | everywhere |
评论 (0)