Solidity 编程规范

本章学习 Solidity 编程规范,掌握编写智能合约代码的通用规则。

视频Bilibili  |  Youtube
官网binschool.app
推特@BinSchool    DiscordBinDAO   微信:bkra50 

编程规范又叫代码规范,就是为了方便自己和他人阅读和理解源程序,而制定的编程时必须遵守的约定。Solidity 有自己特有的编程规范。

编程规范只是一个规范,可以遵守,也可以不遵守,但是要成为一个有良好编程风格的开发者,还是需要遵守编程规范的,这不仅方便自己阅读源程序,也方便与其它开发者进行交流。良好统一的编程风格,有助于提高代码的可读性和可维护性。

下面是关于 Solidity 编程风格的建议。

1. 代码组成顺序

代码中各部分的组成顺序如下:

  • pragma 语句
  • import 语句
  • interface
  • library
  • contract

interfacelibrarycontract 中,各组成部分顺序如下:

  • 类型声明(enum,struct)
  • 状态变量
  • 事件
  • 函数

2. 命名约定

  • 合约和库应该使用驼峰式命名。
    例如: SmartContract, Owner。
  • 合约和库名应该匹配它们的文件名。
  • 如果文件中有多个合约/库,应该使用核心合约/库的名称。
    例如,Owned.sol 文件:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Owned {
    address public owner;

    constructor() public {
      owner = msg.sender;
    }

    modifier onlyOwner {
        //....
    }

    function transferOwnership(address newOwner) public onlyOwner {
        //....
    }
}
  • 结构体名称
    驼峰式命名,例如: MyCoin。
  • 事件名称
    驼峰式命名,例如:AfterTransfer。
  • 函数名
    驼峰式命名,首字母小写,例如:initiateSupply。
  • 局部变量和状态变量
    驼峰式命名,首字母小写,例如:creatorAddress、supply。
  • 常量
    大写字母单词用下划线分隔,例如:MAX_BLOCKS。
  • 修饰符的名字
    驼峰式命名,首字母小写,例如:onlyAfter。
  • 枚举的名字
    驼峰式命名,例如:TokenGroup。

3. 代码布局

  • 缩进
    使用4个空格代替制表符作为缩进,避免空格与制表符混用。
  • 空2行规则
    2个合约定义之间空2行。
contract LedgerBalance {
    //...
}


contract Updater {
    //...
}
  • 空1行规则
    2个函数之间空1行。在只有声明的情况下,不需要空行。
contract A {
    function balance() public pure;
    function account() public pure;
}


contract B is A {
    function balance() public pure {
        // ...
    }

    function account() public pure {
        // ...
    }
}
  • 行长度
    一行不超过79个字符。
  • 换行规则
    函数声明中左括号不换行,每个参数一行并缩进,右括号换行,并对齐左括号所在行。
function_with_a_long_name(
    longArgument1,
    longArgument2,
    longArgument3
);

variable = function_with_a_long_name(
    longArgument1,
    longArgument2,
    longArgument3
);

event multipleArguments(
    address sender,
    address recipient,
    uint256 publicKey,
    uint256 amount,
    bytes32[] options
);

MultipleArguments(
    sender,
    recipient,
    publicKey,
    amount,
    options
);
  • 源码编码
    UTF-8
  • import
    import 语句应该放在文件的顶部,pragma 声明之后。
  • 函数顺序
    函数应该根据它们的可见性来分组。
contract A {
    constructor() public {
        // ...
    }

    // external functions
    // ...

    // external view functions
    // ...

    // external pure functions
    // ...

    // public functions
    // ...

    // internal functions
    // ...

    // private functions
    // ...
}
  • 避免多余空格
    避免在圆括号、方括号或大括号后有空格。
  • 控制结构
    大括号的左括号不换行,右括号换行,与左括号所在行对齐。
contract Coin {
    struct Bank {
        address owner;
        uint balance;
    }
}

if (x < 3) {
    x += 1;
} else if (x > 7) {
    x -= 1;
} else {
    x = 5;
}
if (x < 3)
    x += 1;
else 
    x -= 1;
  • 函数声明
    使用上面的大括号规则。添加可见性标签。可见性标签应该放在自定义修饰符之前。
function kill() public onlyOwner {
    selfdestruct(owner);
}
  • 映射
    在声明映射变量时避免多余空格。
mapping(uint => uint) map; // 不是 mapping (uint => uint) map; 
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;
  • 变量声明
    声明数组变量时避免多余空格。
uint[] x;  // 不是 unit [] x;
  • 字符串声明
    使用双引号声明字符串,而不是单引号。
str = "foo";
str = "Hamlet says, 'To be or not to be...'";

4. 修饰符顺序

Solidity 中,函数修饰符应该按照特定的顺序排列:

  • 可见性

首先是函数的可见性修饰符,如 public、external、internal、private。这些修饰符定义了函数在合约内外的可访问性。

  • 状态可变性

接下来是状态可变性修饰符,如 pure、view、payable。这些修饰符描述了函数是否读取或修改状态变量,或者是否接收以太币。

  • 自定义修饰符

如果函数使用了自定义修饰符,它们应该放在可见性和状态可变性修饰符之后。

  • 返回类型符

最后是返回类型声明(如果有的话),如 returns (uint256)。

函数声明中修饰符顺序示例:

function funcName() external view onlyOwner returns (uint256) {
}