Code Style
Vyper Style Guide
This document outlines the Vyper code style, structure and practices followed by the Curve development team.
Note that this guide is still under development. Do not hesitate to ask if anything is missing or unclear.
Project Organization
Contracts should be structured so that components are logically grouped together. Maintaining a consistent order makes it easier for the reader to locate code.
Each logical section should be separated by two blank lines. Within each section, multi-line statements should be seperated by one blank line. Single-line statements should have no blank lines between them, except to denote a logical seperation.
Content should be ordered as follows:
Imports and Interfaces
Contracts must be self contained. Import statements may only be used for built-in interfaces. Other interfaces are always inlined. This aids readability and simplifies the process of source verification on Etherscan.
Inlined interfaces should only include required functions (those that are called within the contract). Interfaces and the functions within should be sorted alphabetically. Each interface should be seperated by one blank line.
Events and Structs
Events and structs should be sorted alphabetically. Each definition should be seperated by one blank line, with two blank lines between the last event and the first struct.
Constants and Storage Variables
Constants should always be defined before storage variables, except when there is a logical reason to group them otherwise. Variable definitions should not be seperated by blank lines, but a single blank line can be used to create logical groupings.
Functions
The constructor function must always be first, followed by the fallback function (if the contract includes one). Regular functions should be logically grouped. Each function should be seperated by two blank lines.
Naming Conventions
Names adhere to PEP 8 naming conventions:
Events, interfaces and structs use the CapWords convention.
Function names are lowercase, with words separated by underscores when it improves readability. The only exception when adhering to a common interface such as ERC20.
Constants use all capital letters with underscores separating words.
Variables follow the same conventions as functions.
Leading Underscores
A single leading underscore marks an object as private or immutable.
For functions, a leading underscore indicates that the function is internal.
For variables, a leading underscore is used to indicate that the variable exists in calldata.
Booleans
Boolean values should be prefixed with
is_
.Booleans must not represent negative properties, (e.g.
is_not_set
). This can result in double-negative evaluations which are not intuitive for readers.
Code Style
As Vyper is syntactically similar to Python, all code should conform to the PEP 8 style guide with the following exceptions:
Maximum line length of 100
In general, we try to mimick the same linting process as would be applied by black if the code were Python.
Decorators
Function decorators should be ordered according to mutability, visibility, re-entrancy:
@view
@external
@nonreentrant('lock')
def foo():
Function Inputs
All input variables should be prepended with a single leading underscore to denote their immutability. The only exception is if the variable name is a single letter (such as i and j for the swap contract exchange methods).
Where possible, the entire function signature should be kept on a single line:
def foo(_goo: address, _bar: uint256, _baz: uint256) -> bool:
If this line would exceed 100 characters, each input argument should instead be placed on a new line and indented:
def multiline_foo(
_goo: address,
_bar: uint256,
_baz: uint256,
) -> bool:
Revert Strings
Revert strings must not exceed a maximum length of 32 characters. They should only be used in functions that are expected to be frequently called by average users. For other situations you should use a dev revert comment.
Python Style Guide
This document outlines the Python code style, structure and practices followed by the Curve development team.
Note that this guide is still under development. Do not hesitate to ask if anything is missing or unclear.
Linting and Pre-Commit Hooks
We use pre-commit hooks to simplify linting and ensure consistent formatting among contributors. Use of pre-commit
is not a requirement, but is highly recommended.
Install pre-commit
locally from the brownie root folder:
pip install pre-commit
pre-commit install
Commiting will now automatically run the local hooks and ensure that your commit passes all lint checks.
Naming Conventions
Names must adhere to PEP 8 naming conventions:
Modules have short, all-lowercase names. Underscores can be used in the module name if it improves readability.
Class names use the CapWords convention.
Exceptions follow the same conventions as other classes.
Function names are lowercase, with words separated by underscores when it improves readability.
Method names and instance variables follow the same conventions as functions.
Constants use all capital letters with underscores separating words.
Leading Underscores
A single leading underscore marks an object as private.
Classes and functions with one leading underscore are only used in the module where they are declared. They must not be imported.
Class attributes and methods with one leading underscore must only be accessed by methods within the same class.
Booleans
Boolean values should be prefixed with
is_
.Booleans must not represent negative properties, (e.g.
is_not_set
). This can result in double-negative evaluations which are not intuitive for readers.Methods that return a single boolean should use the @property decorator.
Methods
The following conventions should be used when naming functions or methods. Consistent naming provides logical consistency throughout the codebase and makes it easier for future readers to understand what a method does (and does not) do.
get_
: For simple data retrieval without any side effects.fetch_
: For retreivals that may have some sort of side effect.build_
: For creation of a new object that is derived from some other data.set_
: For adding a new value or modifying an existing one within an object.add_
: For adding a new attribute or other value to an object. Raises an exception if the value already exists.replace_
: For mutating an object. Should returnNone
on success or raise an exception if something is wrong.compare_
: For comparing values. ReturnsTrue
orFalse
, does not raise an exception.validate_
: ReturnsNone
or raises an exception if something is wrong.from_
: For class methods that instantiate an object based on the given input data.
For other functionality, choose names that clearly communicate intent without being overly verbose. Focus on what the method does, not on how the method does it.
Code Style
All code must conform to the PEP 8 style guide with the following exceptions:
Maximum line length of 100
We handle code formatting with black. This ensures a consistent style across the project and saves time by not having to be opinionated.
Imports
Import sequencing is handled with isort. We follow these additional rules:
Standard Library Imports
Standard libraries should be imported absolutely and without aliasing. Importing the library aids readability, as other users may be familiar with that library.
# Good
import os
os.stat('.')
# Bad
from os import stat
stat('.')
Strings
Strings substitutions should be performed via formatted string literals rather than the str.format method or other techniques.