| name | solidity-style-guide |
|---|---|
| description | Apply the Aboudjem/solidity-style-guide rules to Solidity code — review, rewrite, or audit for naming, layout, formatting, NatSpec, custom errors, ERC-7201 storage, gas patterns, and Foundry test structure. Trigger when the user asks to "review Solidity style", "apply the style guide", "format .sol file", or mentions Solhint / Prettier / Foundry conventions in a Solidity repo. |
| version | 2.0.0 |
| license | MIT |
| homepage | https://github.com/Aboudjem/solidity-style-guide |
Use this skill whenever you are reviewing or writing Solidity, Foundry tests, or related configuration. It codifies the rules in Aboudjem/solidity-style-guide targeting Solidity 0.8.34.
Trigger automatically on:
.solfiles in the workspacefoundry.toml,remappings.txt,.solhint.jsonpresent- User says: review my contract, clean up this Solidity, apply the style guide, is this gas-optimal?, make this audit-ready
- Produce a checklist of violations grouped by category (layout → naming → formatting → NatSpec → best practices → tests → gas → security).
- Quote the offending snippet with file path and line number.
- Propose a minimal diff that makes it compliant. Preserve behavior; style changes only unless asked.
- Cite the rule inline, e.g.,
[§Best Practices > Using Custom Errors Over Require]. - Stop before applying unless the user asked for an auto-fix.
- SPDX license identifier on every
.solfile - One contract / interface / library per file; filename matches the core type
- Top-level order: pragmas → imports → events → errors → interfaces → libraries → contracts
- Inside a contract: type declarations → state vars → events → errors → modifiers → functions
- Function order:
constructor→receive→fallback→external→public→internal→private - Within each visibility group, put
viewthenpurelast
- Named imports only:
import {X} from "./X.sol"; - Sort by path length within groups; blank line between external and internal
- No wildcard imports
- Applications: pin to a single version —
pragma solidity 0.8.34; - Libraries / mixins: open range —
pragma solidity ^0.8.20; - Never
^0.8.0 ^0.9.0or other multi-constraint forms
- Always explicit:
uint256,int256,bytes1— neveruint,int,byte boolbooleans, notuintflags- Prefer
bytesoverstringwhen non-human-readable
| Element | Style | Notes |
|---|---|---|
| Contract / Library / Interface | PascalCase |
Interfaces prefixed I |
| Struct / Event / Enum / custom Error | PascalCase |
Events in past tense |
| Function / modifier / variable / argument | camelCase |
|
| Public constant | SNAKE_UPPER_CASE |
|
| Private / internal constant | _SNAKE_UPPER_CASE |
Leading _ |
| Non-external fn / state var | _leadingUnderscore |
|
| Avoid | l, O, I single-letter |
Visually ambiguous |
- 4-space indent, spaces (no tabs)
- Max line length 120 chars
- Strings in double quotes
- One space around binary operators; no space inside parens / brackets
mapping(K => V)— no space aftermappinguint[]— no space before[]- Braces on same line as declaration;
elseon the same line as the closing brace - Long signature: each arg on its own line, closing
)and{on their own lines
- Prefer custom errors. Use
require(cond, CustomError(arg1, arg2))when on Solidity ≥ 0.8.26 (via-ir) or ≥ 0.8.27 (legacy). Otherwiseif (!cond) revert CustomError(...);. - If a
requirestring message is unavoidable, keep it under 32 bytes. calldatafor read-only array / struct params onexternalfunctions.- Cache
.lengthbefore the loop header. - Prefer named returns for functions with ≥ 2 return values.
- Prefer named arguments in calls, events, errors with ≥ 3 parameters.
- Named parameters in mappings:
mapping(address user => mapping(address asset => uint256 amount)). - Interact with external contracts through typed interfaces; if using
call, useabi.encodeWithSelector. - For upgradeable contracts, use ERC-7201 namespaced storage with
@custom:storage-location erc7201:<namespace>. - Events in past tense:
OwnerUpdated,FundsDeposited. - Avoid inline assembly unless there is no high-level alternative; when used, document every opcode.
- Prefer composition over inheritance.
@title,@author,@notice,@dev,@param,@returnon every public / external function- Internal / library functions used by other devs: also NatSpec
- File naming:
MyContract.sol↔MyContract.t.sol; split large:MyContract.owner.t.sol,MyContract.deposits.t.sol - Test names:
test_Description,testFuzz_Description,test_RevertWhen_Condition,test_RevertIf_Condition,testFork_Description,testForkFuzz_RevertIf_Condition - Invariants:
invariant_Property(e.g.,invariant_TotalSupplyEqualsSumOfBalances) - No assertions inside
setUp; use a dedicatedtest_SetUpState - Verbose assertion messages:
assertEq(x, y, "Invariant violated: …")
- Mark values set once as
immutable; mark compile-time values asconstant - Prefer
uint256over smaller ints unless packing is intentional - Hoist array length out of loops; use
unchecked { ++i; }where safe (Solidity ≥ 0.8.22 already optimizes simple++iincrements, butuncheckedstill helps for custom counters) - Use transient storage (
tstore/tload, EIP-1153) for per-transaction flags like reentrancy locks when the EVM target supports it (Cancun+). Solidity 0.8.34 default EVM is Prague.
- Checks → Effects → Interactions
- Reentrancy guard (mutex) around any external call that precedes state writes
call{value: x}("")for Ether transfers; always check the returnedbool- No
tx.originfor authorization - Pull over push payment patterns where feasible
Scan the repo, produce a categorised violations report, do not edit.
Apply the minimal formatting and naming diffs (no logic changes) and print a summary. Behavior-changing rewrites remain advisory.
Copy the .solhint.json, .prettierrc, .editorconfig, and .prettierignore from this repo into the current project (do not overwrite if present without asking).