---
applyTo: "**/*.sol"
description: Solidity contract conventions
---

# Solidity (0.8.x) with Foundry

- Build/test: `forge build`, `forge test -vvv`, `forge fmt --check`, `forge coverage`. Static analysis: `slither .` if installed.
- Pin the pragma to the project's compiler (`pragma solidity 0.8.30;`), not a floating `^`.
- Use audited OpenZeppelin contracts for tokens, access control (`Ownable2Step`, `AccessControl`) and `ReentrancyGuard`; don't reimplement them.
- Checks-effects-interactions order in every function that calls out; add `nonReentrant` where external calls follow state reads.
- Custom errors (`error InsufficientBalance(uint256 have, uint256 want);`) instead of revert strings.
- Validate inputs (zero address, bounds) and emit events for every state change.
- Never use `tx.origin` for auth, `block.timestamp` for randomness, or unchecked low-level `call` return values. Use `SafeERC20` for token transfers.
- Upgradeable contracts: follow the proxy pattern in use, no constructors (use initializers), preserve storage layout.
- NatSpec (`/// @notice`, `@param`) on external functions.

## Tests
Unit tests plus fuzz tests (`function testFuzz_x(uint256 amount)`) and invariant tests for core accounting. Fork tests use a pinned block number.

Never deploy, broadcast, or use private keys from the agent.
