Solidity Best Practices
SkillDev toolsGuide for writing secure, gas-efficient, and maintainable Solidity smart contracts following established patterns and avoiding common pitfalls.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Solidity Best Practices skill
What this skill tells your AI
The instructions your AI receives, as published by nirholas/three.ws in data/skills/development/solidity-best-practices/SKILL.md and read by ahel’s review.
When to use this skill
Use when the user asks about:
- Writing or reviewing Solidity code
- Smart contract design patterns
- Gas optimization techniques in Solidity
- Security best practices for contract development
- Structuring a Solidity project
Development Guidelines
1. Project Structure
Recommended contract organization:
contracts/— main contract source filescontracts/interfaces/— interface definitions (IProtocol.sol)contracts/libraries/— shared library codecontracts/utils/— utility contracts (access control, pausable, etc.)test/— test files mirroring the contracts directory structurescript/— deployment and interaction scripts
2. Coding Standards
Follow these Solidity style conventions:
- Use Solidity 0.8.x or later (built-in overflow checks)
- Specify exact compiler version with
pragma solidity 0.8.24;(not floating^) - Order contract elements: state variables, events, errors, modifiers, constructor, external, public, internal, private functions
- Use custom errors instead of
requirestrings (saves gas):error InsufficientBalance(uint256 available, uint256 required); - Use
immutablefor variables set once in the constructor - Use
constantfor compile-time known values - Prefix internal/private functions with underscore:
_validateInput() - Use NatSpec comments for all public/external functions
3. Gas Optimization Patterns
High-impact optimizations:
- Storage packing: Group variables smaller than 32 bytes together in a single storage slot (e.g.,
uint128 a; uint128 b;uses one slot vs two foruint256) - Calldata over memory: Use
calldatafor external function array/struct parameters that are read-only - Caching storage reads: Read a storage variable into a local variable if accessed multiple times in a function
- Short-circuit conditions: Place cheap checks before expensive ones in
requirechains - Unchecked math: Use
unchecked {}for arithmetic that provably cannot overflow (e.g., loop counters) - Avoid zero-to-nonzero writes: Initializing a storage slot from 0 costs 20,000 gas vs 5,000 for nonzero-to-nonzero
- Use mappings over arrays when you don't need iteration — mappings have O(1) access
- Batch operations: Combine multiple operations into single transactions where possible
4. Security Patterns
Essential security practices:
- Checks-Effects-Interactions (CEI): Validate inputs, update state, then make external calls — prevents reentrancy
- Reentrancy guard: Use OpenZeppelin's
ReentrancyGuardfor functions making external calls with value - Access control: Use role-based access (OpenZeppelin
AccessControl) rather than single-owner patterns - Pull over push: Let users withdraw funds rather than pushing payments — avoids DoS via revert
- Timelocks: Add delay to sensitive admin operations so users can react
- Input validation: Validate all external inputs — check for zero addresses, bounds, array lengths
- Safe token transfers: Use OpenZeppelin's
SafeERC20for token interactions - Event emission: Emit events for all state changes — essential for off-chain monitoring
5. Upgrade Patterns
When contracts need to be upgradeable:
- Prefer UUPS over Transparent Proxy (lower gas for users)
- Always initialize via
initialize()function (not constructor) withinitializermodifier - Reserve storage gaps:
uint256[50] private __gap;in base contracts - Never change storage variable order or types in upgrades
- Disable initializers on implementation: add
_disableInitializers()in constructor - Test upgrade compatibility before deploying
6. Testing Requirements
Every contract should have:
- Unit tests for each function covering happy path and revert cases
- Integration tests for multi-contract interactions
- Fuzz tests for functions with numerical inputs (Foundry's
forge test) - Invariant tests for protocol-wide properties that must always hold
- Fork tests against mainnet state for DeFi integrations
- Gas snapshots to track gas regression across changes
7. Deployment Checklist
Before deploying to mainnet:
- All tests passing with 100% line coverage on critical paths
- Static analysis run (Slither, Aderyn) with no high/critical findings
- Formal verification on core invariants if applicable
- Professional audit completed for high-value contracts
- Deployment script tested on testnet fork
- Constructor/initializer arguments double-checked
- Verify source code on block explorer immediately after deployment
- Admin roles transferred to multisig/governance
- Emergency pause mechanism tested
Signals
- GitHub stars
- 114
- Forks
- 29
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
solidity-best-practices- Source
- github.com/nirholas/three.ws