For solidity we generally follow the style guide as shown in the solidity documentation with some exceptions:
All variable name should be in snake case, just like in python. Function names on the other hand should be mixedCase. MixedCase is essentially like CamelCase but with the initial letter being a small letter. This helps us to easily determine which function calls are smart contract calls in the python code side.
function iDoSomething(uint awesome_argument) {
doSomethingElse();
}
Calls into other contracts might call back into our contract. This causes problems when storage accesses and an external call interleave. For example,
check(storage[address])
other_contract.method()
storage[address] = new_value
possibly has a bug, where an attacker can set up other_contract.method()
so that it calls back into this piece of code.
Then, the check()
still sees an old value.
When you write Solidity code, be aware of the distinction between assert(cond)
and require(cond)
.
assert(cond)
and require(cond)
both cause a failure in the EVM execution when cond
evaluates to 0. They use different EVM opcodes that cause different gas consumptions. More importantly, a convention dictates when to use which. Use assert(cond)
only when you are confident that cond
is always true. When an assert
fires, that's considered as a bug in the Solidity program (or the Solidity compiler). For detecting invalid user inputs or invalid return values from other contracts, use require()
.
We currently check the return values from all external function calls. In the Solidity code, all external function calls should happen within require(...)
unless the function returns nothing.
When we implement a function that has nothing to return, we make the function always return true. So we have a more consistent visual look without naked calls.
A signature should be useful only in one context. For this purpose, we follow a convention dictating the format of signed messages. The first fields of a signed message must look like::
address destination_of_the_message, uint256 chain_id_of_the_destination, uint256 message_type
following the usual prefix of Ethereum signatures \x19Ethereum Signed Message:\n<message_length>
.
- Solidity documentation usually has an answer somewhere.
- Also keep an eye of upcoming changes.
- Remix allows step-execute a transaction.
- (Not So) Smart Contracts contains examples of common vulnerabilities.
- Awesome Ethereum Security contains relevant links.
This repository follows the same guidelines as the Raiden Client, regarding the Python code used in tests and scripts: https://github.com/raiden-network/raiden/blob/master/CONTRIBUTING.md#coding-style.
We consider it's good to have one function just doing one thing.
Read our Test Guide
Currently, our setup is:
- for core contracts:
./raiden_contracts/data/source/raiden
- for 3rd party services:
./raiden_contracts/data/source/services
- libraries:
./raiden_contracts/data/source/lib
- non-production test contracts:
./raiden_contracts/data/source/test
Update package constants with the new contract's name, events, gas requirements etc.
Note: gas requirements are currently calculated using ./raiden_contracts/tests/test_print_gas.py, so the contract needs to be added here. This is not optimal: raiden-network#16
- add a new file with the new contract fixture in ./raiden_contracts/tests/fixtures, to be used across tests and add that file to ./raiden_contracts/tests/fixtures/init.py
- read our Test Guide for how to add new tests for the contract
- make sure it's covered by our compilation tests
- add it to the deployment tests
We need to add the new contract to the precompiled data. If the new contract is located in one of the existing directories, then it will automatically be added. If a new contracts directory is created, then it must be added to the compilation process: https://github.com/raiden-network/raiden-contracts/blob/810ea24b61221f74939a732e6f20f20184039507/raiden_contracts/contract_manager.py#L250-L267
Make sure the new contract's precompiled data and deployment info is included in ./raiden_contracts/data, by adding the contract to the deployment process
- if it is a core contract, add it to deploy_raiden_contracts and to verify_deployed_contracts
- if it is a service contract, add it to deploy_service_contracts and to verify_deployed_service_contracts
- add it to the Etherscan verification script
Check if the deployment info file path still stands: https://github.com/raiden-network/raiden-contracts/blob/810ea24b61221f74939a732e6f20f20184039507/raiden_contracts/contract_manager.py#L270-L274
We need to make sure the contract and related data is part of the release process:
- update .bumpversion_contracts.cfg
- mention the addition in the CHANGELOG.md
- make sure the deployment and precompiled data for the new contract is contained in the MANIFEST.in referenced files.