Solidity Developer
Idiomatic Solidity at ChainSafe. Reviewer is HARD FAIL tier; this skill teaches you to stay out of HARD-FAIL territory. Full reference: languages/solidity/developer.md.
Tooling baselines
- Foundry preferred for new projects.
forge,cast,anvil. - Hardhat in entrenched projects.
- Pin pragma:
pragma solidity 0.8.24;(not floating^0.8.0). - OpenZeppelin Contracts for ERC-20, ERC-721, AccessControl, ReentrancyGuard, Pausable.
- Solady / Solmate when gas matters and audit capacity backs gas-tuned code.
- slither as a CI baseline. Specific findings muted with
// slither-disable-next-line ... reason: ...comments. - mythril for symbolic execution on critical contracts.
- solhint for style.
Imports
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
- Named imports over wildcard
import "...". - SPDX identifier at top of every file.
Checks-Effects-Interactions (the canonical pattern)
function withdraw(uint256 amount) external nonReentrant {
// Checks
if (amount == 0) revert ZeroAmount();
if (balances[msg.sender] < amount) revert Insufficient(amount, balances[msg.sender]);
// Effects
balances[msg.sender] -= amount;
totalDeposits -= amount;
// Interactions
(bool ok, ) = msg.sender.call{value: amount}("");
if (!ok) revert TransferFailed();
}
Always in this order. nonReentrant on every external function calling untrusted code.
Access control
contract Vault is AccessControl, ReentrancyGuard {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
function setFee(uint256 newFee) external onlyRole(ADMIN_ROLE) { /* ... */ }
}
AccessControloverOwnablefor multi-action contracts.- Lock down
DEFAULT_ADMIN_ROLE. - Multi-sig (Gnosis Safe) holds privileged roles in production. EOA only in dev/test.
- Timelocks on parameter changes.
Custom errors (0.8.4+)
error Insufficient(uint256 requested, uint256 available);
function withdraw(uint256 amount) external {
if (balances[msg.sender] < amount) revert Insufficient(amount, balances[msg.sender]);
}
Smaller bytecode than require strings; structured data for callers.
External calls
.call{value: amount}("")over.transfer/.send(which break with new gas pricing).- Check return values. Unchecked = silent bugs.
- Use OpenZeppelin's
SafeERC20.safeTransfer/safeTransferFrom— normalizes broken ERC-20s like USDT.
Function visibility
externaldefault for callable-from-outside (saves gas vspublicfor calldata args).publiconly when also called internally.internalfor cross-contract-inheritance helpers.privaterarely.
State variables
- Pack storage — group same-size types to share slots.
immutablefor constructor-set values.constantfor compile-time-known values.- Document storage layout for upgradeable contracts;
__gapreserves slots.
Testing (Foundry)
- Unit tests in
test/*.t.sol. - Fuzz tests by parameterizing function arguments.
- Invariant tests declare properties that must always hold;
forgefinds breaking sequences. - Differential tests against a reference implementation.
- Mainnet fork tests for integrations with deployed contracts.
Coverage via forge coverage. Security-critical code: 100% line coverage is necessary, not sufficient. Add fuzz + invariant tests.
CI baseline
forge buildforge test -vvvforge coveragewith threshold (typically 95%+ for security-critical)slither .solhint 'src/**/*.sol'- Mainnet fork tests on schedule
- Gas snapshot via
forge snapshot --checkto catch regressions
Anti-patterns (most are HARD FAIL findings)
tx.originfor authorization.- Floating pragma.
- Unbounded loops over user-controlled data.
- State changes after external calls.
.transfer/.sendfor ETH.- Custom math reimplementing audited primitives.
assemblywithout justification.- Owner-key control of production contracts.
Related
- Full reference:
languages/solidity/developer.md - Idioms:
languages/solidity/idioms.md - Gotchas:
languages/solidity/gotchas.md - Sister roles:
chainsafe-solidity-architect,chainsafe-solidity-reviewer(HARD FAIL tier)