Hello World

Solidity Style Guide

规范文档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.每行最大的长度

对于函数调用的示例
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

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.函数的放置顺序

函数从前往后放置顺序

对于同一优先级的函数, 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;
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;}
函数的修饰符应该是按照如下顺序:
  1. Visibility
  2. Mutability
  3. Virtual
  4. Override
  5. 自定义 修饰符
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.布局顺序

合约中的元素布局顺序
  1. Pragma statements
  2. Import statements
  3. Interfaces
  4. Libraries
  5. Contracts
在合约中库、接口顺序
  1. Type declarations
  2. State variables
  3. Events
  4. Modifiers
  5. Functions

14.命名规范

命名风格

为了避免困惑,以下的将指代不同命名风格

Note

在 CapWords 中使用首字母缩写时,请将首字母缩写的所有字母大写。因此,HTTPServerError 优于 HttpServerError。在 mixedCase 中使用首字母缩写时,请将首字母缩写的所有字母大写,但如果它是名称的开头,则保留第一个字母小写。因此,xmlHTTPRequest 优于 XMLHTTPRequest。

避免使用

切勿将这些字符用作单字母变量名。它们通常与数字 1 和 0 难以区分。

合约和库名
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
@titleA title that should describe the contract/interfacecontract, library, interface
@authorThe name of the authorcontract, library, interface
@noticeExplain to an end user what this doescontract, library, interface, function, public state variable, event
@devExplain to a developer any extra detailscontract, library, interface, function, state variable, event
@paramDocuments a parameter just like in Doxygen (must be followed by parameter name)function, event
@returnDocuments the return variables of a contract’s functionfunction, public state variable
@inheritdocCopies all missing tags from the base function (must be followed by the contract name)function, public state variable
@custom:...Custom tag, semantics is application-definedeverywhere

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »