Solhint
Solhint is a linter for your Solidity Smart Contracts. It will flag things like unspecified visibility modifier on variables.
You can add Solhint to your project with:
yarn add --dev solhint
yarn solhint --init
then you can add your rules on the generated file .solhint.json
:
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", "^0.8.0"],
"func-visibility": ["warn", { "ignoreConstructors": true }]
}
}
To run the linter execute: yarn solhint contracts/*.sol
and it will display something like:
2:1 error Compiler version ^0.8.9 does not satisfy the ^0.5.8 semver requirement compiler-version
13:5 warning Explicitly mark visibility in function (Set ignoreConstructors to true if using solidity >=0.7.0) func-visibility
14:9 warning Error message for require is too long reason-string
15:13 warning Avoid to make time-based decisions in your business logic not-rely-on-time
27:17 warning Avoid to make time-based decisions in your business logic not-rely-on-time
30:48 warning Avoid to make time-based decisions in your business logic not-rely-on-time
✖ 6 problems (1 error, 5 warnings)
hardhat-deploy
The hardhat-deploy plugin makes easier the deploying process and testing of your Smart Contracts deployed with HardHat.
Some of the features are:
- Deploy contracts.
- Keep track of the deployments.
- Environment replication.
- Configuration files for deploying to multiple environments.
You can find other usages here.
Two dependencies are needed:
yarn add --dev hardhat-deploy
yarn add --dev @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers ethers
Then you can include it on your HardHat config file and configure the deployer account.
hardhat.config.js
:
require("@nomicfoundation/hardhat-toolbox")
require("hardhat-deploy")
module.exports = {
solidity: "0.8.8",
namedAccounts: {
deployer: {
default: 0,
},
},
}
We are selecting the first account as the default deployer account.
Next, create a deploy/
folder. This folder will be used by the hardhat-deploy
plugin.
You can define a version of your smart contract given a name like deploy/01-deploy-hello-world.js
.
module.exports = async ({ getNamedAccounts, deployments }) => {
const { deploy } = deployments
const { deployer } = await getNamedAccounts()
await deploy("HelloWorld", {
from: deployer,
args: [],
log: true,
})
}
module.exports.tags = ["all", "helloWorld"]
now you can run a node locally and all the contracts within the deploy/
folder will get deployed.
yarn hardhat node
will print:
deploying "HelloWorld" (tx: 0xb92b6e2217005023fcbe3265e59b3ebea9dee0359194cf2837fa8c3383f7198d)...: deployed at 0x5FbDB2315678afecb367f032d93F642f64180aa3 with 381167 gas
Solidity Style Guide
Layout contract elements in the following order:
- Pragma statements
- Import statements
- Interfaces
- Libraries
- Contracts
Inside each contract, library or interface, use the following order:
- Type declarations
- State variables
- Events
- Modifiers
- Functions
Functions should be grouped according to their visibility and ordered:
- constructor
- receive function (if exists)
- fallback function (if exists)
- external
- public
- internal
- private
The modifier order for a function should be:
- Visibility
- Mutability
- Virtual
- Override
- Custom modifiers
Other important conventions:
- Contracts and libraries should be named using the CapWords style
- Contract and library names should also match their filenames.
- Avoid multiple contracts on the same file.
All these rules and much more can be found on the Official Solidity Style Guide.
Solidity Documentation
Use NatSpec guidelines for documenting your Smart Contracts.
/**
* @title A contract for greeting
* @author Sergio
* @notice This contract is a smart contract example
* @dev This implements...
*/
contract HelloWorld {
// your code
}
Debugging
You can use VSCode for debugging. Put a break point on your JavaScript code and run the debugger from VSCode.
Another trick to debug your Solidity Smart Contracts is to use the Solidity console.log
.
pragma solidity ^0.8.8;
import "hardhat/console.sol";
contract HelloWorld {
function helloWorld() public view returns (string memory) {
console.log("Processing hello world...");
if (bytes(from).length == 0) {
return "Hello World!";
}
return string(abi.encodePacked("Hello World from ", from, "!"));
}
}
Gas Cost and Opcodes
Depending on the operation you perform on your Smart Contract a different gas cost is associated to it. Solidity operations get translated to machine language and they are called “Opcodes”. A reference for the opcodes and their gas cost can be found on the Ethereum site.
Learning about opcodes is important in order to optimize the gas consumption of your Smart Contracts.
When using storage variable the convention is to append a s_
so we don’t forget that those variables are using a lot of gas.