Style guide Solidity
Немного отдохнем и прикоснемся к великому;). А именно, к оформлению нашего замечательного кода. Для каждого языка есть перечень рекомендаций по оформлению, которые позволят сделать Ваш код чуточку аккуратнее и гораздо более приятным для глаз других разработчиков.
Конечно, такие гайды не являются требованиями или ограничениями, однако, пользуясь этими правилами Вы сделаете легче Вашим же глазам. Давайте рассмотрим основные правила, опираясь на официальную документацию языка.
Отступы
Для отступа используется 4 пробельных символа. Могут быть заменены tab'ом, но нужно быть уверенным, что Вы пользуетесь только одним из предоставленных способов.
Пустые строки
Старайтесь отступать от объявлений "глобальных" структур (напр. контрактов) 2 пустые строки.
pragma solidity >=0.4.0 <0.9.0; contract A { // ... } contract B { // ... }
Между методами в пределах контракта - 1 пустая строка.
function spam() public pure override { // ... } function ham() public pure override { // ... }
Между виртуальными методами, переменными и другими однострочными конструкциями - допускается игнорировать отступы.
abstract contract A { function spam() public virtual pure; function ham() public virtual pure; }
Длина строки
Solidity опирается на рекомендации PEP-8 (Python) по ограничению строки в 79 символов, что позволяет легче читать Ваш код.
Если же строка должна быть перенесена, то учитываются следующие особенности:
- первый аргумент функции не должен примыкать к открывающей скобке
- следует использовать равные отступы для каждого аргумента
- каждый аргумент должен начинаться со своей собственной строки
- закрывающая скобка должна располагаться на последней строке, отдельно от аргументов.
thisFunctionCallIsReallyLong( longArgument1, longArgument2, longArgument3 ); event LongAndLotsOfArgs( address sender, address recipient, uint256 publicKey, uint256 amount, bytes32[] options ); LongAndLotsOfArgs( sender, recipient, publicKey, amount, options );
thisFunctionCallIsReallyLong(longArgument1, longArgument2, longArgument3 ); thisFunctionCallIsReallyLong(longArgument1, longArgument2, longArgument3 ); thisFunctionCallIsReallyLong( longArgument1, longArgument2, longArgument3 ); thisFunctionCallIsReallyLong( longArgument1, longArgument2, longArgument3 ); thisFunctionCallIsReallyLong( longArgument1, longArgument2, longArgument3);
Кодировка
Предпочтительна UTF-8 или ASCII
Импорты
Все импорты должны быть расположены в самом верху файла.
import "./Owned.sol"; contract A { // ... } contract B is Owned { // ... }
Порядок методов
Корректное расположение методов в контракте поможет читающим быстро определить, где у Вас расположен конструктор, где fallback - функция, и какие из методов они смогут вызывать.
Старайтесь оставлять view и pure методы в рамках группы последними.
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 // ... }
Фигурные скобки
Фигурные скобки, обозначающие тело контракта, библиотеки, функции и структуры, должны быть:
- Открыты в той же строке, что и объявление
- Закрыты на отдельной строке на том же уровне отступа, что и начало объявления
- Отделены от объявления одним пробельным символом
pragma solidity >=0.4.0 <0.9.0; contract Coin { struct Bank { address owner; uint balance; } }
Эти же рекомендации относятся и к конструкциям: if, else, while, for
.
Кроме того, между оператором и условием в скобках, должен быть один пробел. Также, один пробел находится между условием в скобках и открывающей фигурной скобкой.
if (...) { ... } for (...) { ... }
Для блока if
, включающего также условия else / else if
, эти условия должны находиться на той же строке, что и закрывающая скобка для if
.
if (x < 3) { x += 1; } else if (x > 7) { x -= 1; } else { x = 5; }
Объявление функций
При объявлении функций рекомендуется располагать открывающую скобку на той же строке, что и сама функция, отделив её пробельным символом. Закрывающая скобка должна находиться на том же уровне, что и начало функции.
function increment(uint x) public pure returns (uint) { return x + 1; } function increment(uint x) public pure onlyOwner returns (uint) { return x + 1; }
Порядок указания модификаторов функции должен быть следующий:
Для объявления длинных функций (превышающих длину одной строки) рекомендуется каждый аргумент помещать на новую строку. Фигурные скобки, в таком случае, располагаются на отдельных строках.
function thisFunctionHasLotsOfArguments( address a, address b, address c, address d, address e, address f ) public { doSomething(); }
Если же функция превышает длину строки из-за большого числа модификаторов - рекомендуется каждый из них также выносить на новую строку.
function thisFunctionNameIsReallyLong( address x, address y, address z ) public onlyOwner priced returns (address) { doSomething(); }
При использовании функции с одним оператором в теле, допускается использование однострочной конструкции.
function shortFunction() public { doSomething(); }
Mapping
Для mapping не отделяйте ключевое слово mapping от аргументов пробелами.
mapping(uint => uint) map; mapping(address => bool) registeredAddresses; mapping(uint => mapping(bool => Data[])) public data; mapping(uint => mapping(uint => s)) data;
Объявление переменных
Не отделяйте тип данных от названия переменных пробелом.
uint[] x;
Строки
Строки должны быть ограничены двойными кавычками (")
str = "foo"; str = "Hamlet says, 'To be or not to be...'";
Структура файла
Старайтесь компоновать описываемый .sol
файл в следующем порядке:
Стиль наименования
Старайтесь не использовать следующие буквы, как имена переменных (всегда старайтесь давать переменным, методам и модификаторам осмысленные имена):
CapWords
- контракты (напр. SimpleToken, SmartBank, CertificateHashRepository, Player, Owned)
- структуры данных (напр.
MyCoin, Position, PositionXY
) - события (напр.
Deposit, Transfer, Approval
) - объединения (enums) (напр.
TokenGroup
,Frame
,HashStyle
,CharacterLocation
mixedCase
- функции (напр.
getBalance, transfer, verifyOwner, addMember
) - аргументы функций (напр.
initialSupply, account, recipientAddress
) - переменные (напр.
totalSupply, balancesOf, isPreSale
)