Style Guide

Adhering to a consistent coding style is crucial for maintaining readability and quality across all Decentra projects. This style guide outlines the conventions that developers must follow when contributing code.

General Guidelines

  • File Names: Use CamelCase for contract names and snake_case for filenames.

  • Line Length: Keep lines to a maximum of 80 characters.

  • Indentation: Use 4 spaces per indentation level. Do not use tabs.

  • Comments: Use // for single-line comments and /* ... */ for multi-line comments. Always use Natspec comments for public and external functions.

Contract and File Structure

  • Order of Elements: Arrange the elements in the following order:

    1. Pragmas

    2. Imports

    3. Interfaces

    4. Libraries

    5. Contracts

    6. Enums

    7. Structs

    8. State Variables

    9. Events

    10. Modifiers

    11. Functions

State Variables

  • Grouping: Group state variables by their type and purpose. For example, keep all uint variables together, all address variables together, and so on.

  • Ordering: Order state variables from the most to the least important. Within groups, order them alphabetically.

  • Visibility: Explicitly define the visibility of state variables (private, internal, public).

  • Naming: Use descriptive names in camelCase and avoid abbreviations.

Example:

contract Example {
    // Grouped by type
    uint256 public totalSupply;
    uint256 private _decimals;
    
    // Grouped by type
    address public owner;
    address private _admin;

    // Grouped by purpose
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
}

To optimize storage slots, aim to group to uints of type 128 (for example) one after the other.

Functions

  • Order of Functions: Order functions by their visibility and purpose:

    1. Constructor

    2. Fallback function (if applicable)

    3. External functions

    4. Public functions

    5. Internal functions

    6. Private functions

  • Modifiers: List modifiers directly above the function they modify.

  • Ordering within Functions: Order arguments from inputs to outputs. For example, function transfer(address recipient, uint256 amount).

  • Function Naming: Use descriptive names in camelCase. Function names should start with a verb.

Example:

contract Example {
    // Constructor
    constructor() public {
        // Initialization code
    }

    // External function
    function transfer(address recipient, uint256 amount) external returns (bool) {
        // Function code
    }

    // Public function
    function approve(address spender, uint256 amount) public returns (bool) {
        // Function code
    }

    // Internal function
    function _transfer(address sender, address recipient, uint256 amount) internal {
        // Function code
    }

    // Private function
    function _calculateReward(address user) private view returns (uint256) {
        // Function code
    }
}

Function Parameters

  • Ordering: Order function parameters from inputs to outputs.

  • Naming: Use descriptive parameter names in camelCase. Avoid using single-letter names except for loop indices, function parameters are expected to have a "_" before them as an industry standard.

Example:

function setApprovalForAll(address _operator, bool _approved) external {
    // Function code
}

Events

  • Naming: Use PascalCase for event names and camelCase for parameter names.

  • Ordering Parameters: Order parameters with indexed fields first, followed by non-indexed fields.

Example:

event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);

Error Handling

  • Revert Messages: Provide descriptive error messages in require, assert, and revert statements.

  • Custom Errors: Prefer using custom errors for gas optimization.

Example:

if (x <= y) {
        revert InsufficientBalance()
}

custom errors are defined above the smart contract name declaration.

Import Statements

  • Order: Place import statements at the top of the file, grouped and ordered alphabetically.

  • Aliasing: Use aliasing to avoid naming conflicts.

Example:

import "./Ownable.sol";
import "./ERC20.sol";

Documentation

  • Natspec Comments: Use Natspec comments for all public and external functions, as well as for any complex internal logic.

  • Comment Placement: Place comments directly above the code they refer to.

Example:

/**
 * @dev Transfers tokens to a specified address.
 * @param _recipient The address to transfer to.
 * @param _amount The amount to be transferred.
 * @return A boolean value indicating whether the operation succeeded.
 */
function transfer(address _recipient, uint256 _amount) external returns (bool) {
    // Function code
}
 

By following these style guidelines, you ensure that your code is consistent, readable, and maintainable. This contributes to the overall quality and reliability of Decentra projects.

Contributions that do no adhere to the style guide will be considered invalidated by default.