---
description: Smart Contract Development
alwaysApply: false
---

# Smart Contract Development

Solidity patterns and best practices for secure, maintainable smart contracts.

## File Structure

Order within a contract: type declarations → state variables (constants → immutables → storage) → events → errors → modifiers → constructor → external → public → internal → private → view/pure.

## Naming Conventions

- **Constants**: `SCREAMING_SNAKE_CASE` (e.g., `MAX_FEE`)
- **Immutables**: `i_` prefix (e.g., `i_owner`)
- **Storage**: `s_` prefix (e.g., `s_totalSupply`)
- **Events**: past tense (e.g., `Deposited`, `FeeUpdated`)
- **Errors**: descriptive with args (e.g., `InsufficientBalance(uint256, uint256)`)

## Security Patterns

### Checks-Effects-Interactions (CEI)

```solidity
function withdraw(uint256 amount) external nonReentrant {
    if (s_balances[msg.sender] < amount) revert InsufficientBalance(amount, s_balances[msg.sender]);
    s_balances[msg.sender] -= amount;  // effect before interaction
    (bool ok,) = msg.sender.call{value: amount}("");
    if (!ok) revert TransferFailed();
}
```

### Access Control
- Use OpenZeppelin `AccessControl` for role-based permissions
- Never use `tx.origin` for authentication

### Input Validation
- Check zero addresses, zero amounts, allowed tokens, and limits on all external functions

## Common Patterns

- **Pull over push** — let users claim rewards instead of pushing payments (prevents DoS)
- **Emergency stops** — use `Pausable`; always allow withdrawals even when paused
- **Timelocks** — require delay for sensitive admin operations (fee changes, upgrades)

## OpenZeppelin Essentials

- Tokens: `ERC20`, `ERC721`, `ERC1155`
- Security: `ReentrancyGuard`, `Pausable`
- Access: `Ownable`, `AccessControl`
- Utils: `SafeERC20`, `Math`, `EnumerableSet`

## NatSpec

All public/external functions must have `@notice`, `@param`, and `@return` NatSpec tags.

## Anti-Patterns

- **Unchecked external calls** — always use `SafeERC20` or check return values
- **Unbounded loops** — paginate with `(start, count)` parameters
- **Floating pragma** — use exact version (`0.8.20` not `^0.8.0`)
- **Magic numbers** — extract to named constants
