Skip to main content

solidity-patterns

Core Solidity design patterns for safe, maintainable smart contracts. Use when writing new contracts, reviewing architecture, or refactoring existing protocol code. Covers checks-effects-interactions, pull-over-push, guard checks, state machines, access restriction, and commit-reveal.

Zur Installation springen

Quellinformationen

Repository
ccashwell/evm-cortex
Letzte Quellaktivität
10. April 2026 um 16:31
Erkannte Sprache von SKILL.md
Englisch
Sterne
131
Forks
18

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
solidity-patterns
description
Core Solidity design patterns for safe, maintainable smart contracts. Use when writing new contracts, reviewing architecture, or refactoring existing protocol code. Covers checks-effects-interactions, pull-over-push, guard checks, state machines, access restriction, and commit-reveal.
# Solidity Design Patterns ## Checks-Effects-Interactions (CEI) The most critical pattern in Solidity. All functions that interact with external contracts must follow this order: 1. **Checks** — validate inputs and preconditions 2. **Effects** — update state 3. **Interactions** — call external contracts ```solidity function withdraw(uint256 amount) external { // CHECKS if (amount == 0) revert ZeroAmount(); if (balances[msg.sender] < amount) revert InsufficientBalance(); // EFFECTS balances[msg.sender] -= amount; // INTERACTIONS (bool success,) = msg.sender.call{value: amount}(""); if (!success) revert TransferFailed(); emit Withdrawn(msg.sender, amount); } ``` Violating CEI is the root cause of reentrancy. Even with ReentrancyGuard, always write CEI-compliant code as defense in depth. ## Pull-Over-Push Never iterate over recipients to push funds. Let users withdraw (pull) their own funds. ```solidity // BAD: Push pattern — one revert blocks everyone function distributeRewards(address[] calldata recipients, uint256[] calldata amounts) external { for (uint256 i; i < recipients.length; ++i) { payable(recipients[i]).transfer(amounts[i]); // reverts if any fails } } // GOOD: Pull pattern — each user claims independently mapping(address => uint256) public pendingRewards; function claimReward() external { uint256 amount = pendingRewards[msg.sender]; if (amount == 0) revert NothingToClaim(); pendingRewards[msg.sender] = 0; (bool success,) = msg.sender.call{value: amount}(""); if (!success) revert TransferFailed(); emit RewardClaimed(msg.sender, amount); } ``` ## Guard Check Pattern Centralize validation in modifiers or internal functions for reuse and consistency. ```solidity error Unauthorized(); error InvalidAmount(); error Expired(uint256 deadline); modifier onlyWhitelisted() { if (!whitelist[msg.sender]) revert Unauthorized(); _; } modifier validAmount(uint256 amount) { if (amount == 0 || amount > MAX_AMOUNT) revert InvalidAmount(); _; } modifier beforeDeadline(uint256 deadline) { if (block.timestamp > deadline) revert Expired(deadline); _; } function deposit(uint256 amount, uint256 deadline) external onlyWhitelisted validAmount(amount) beforeDeadline(deadline) { // core logic only — all checks in modifiers } ``` ## State Machine Pattern Enforce valid state transitions for multi-phase protocols (auctions, vesting, governance). ```solidity enum AuctionState { Created, Bidding, Ended, Settled } AuctionState public state; error InvalidState(AuctionState expected, AuctionState actual); modifier inState(AuctionState expected) { if (state != expected) revert InvalidState(expected, state); _; } function startBidding() external onlyOwner inState(AuctionState.Created) { state = AuctionState.Bidding; emit BiddingStarted(block.timestamp); } function endBidding() external onlyOwner inState(AuctionState.Bidding) { state = AuctionState.Ended; emit BiddingEnded(block.timestamp); } function settle() external inState(AuctionState.Ended) { state = AuctionState.Settled; // settlement logic } ``` ## Access Restriction Pattern Layer access control for different privilege levels. ```solidity import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; contract Treasury is AccessControl { bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); uint256 public constant LARGE_THRESHOLD = 100 ether; function executeSmall(address to, uint256 amount) external onlyRole(EXECUTOR_ROLE) { if (amount >= LARGE_THRESHOLD) revert ExceedsThreshold(); _transfer(to, amount); } function executeLarge(address to, uint256 amount) external onlyRole(MANAGER_ROLE) { _transfer(to, amount); } } ``` ## Commit-Reveal Pattern Prevent front-running by splitting actions into commit and reveal phases. ```solidity struct Commitment { bytes32 hash; uint64 timestamp; bool revealed; } mapping(address => Commitment) public commitments; uint256 public constant REVEAL_WINDOW = 1 hours; uint256 public constant MIN_COMMIT_AGE = 1 minutes; function commit(bytes32 hash) external { commitments[msg.sender] = Commitment({ hash: hash, timestamp: uint64(block.timestamp), revealed: false }); emit Committed(msg.sender, hash); } function reveal(uint256 value, bytes32 salt) external { Commitment storage c = commitments[msg.sender]; if (c.hash == bytes32(0)) revert NoCommitment(); if (c.revealed) revert AlreadyRevealed(); if (block.timestamp < c.timestamp + MIN_COMMIT_AGE) revert TooEarly(); if (block.timestamp > c.timestamp + REVEAL_WINDOW) revert RevealExpired(); bytes32 expected = keccak256(abi.encodePacked(msg.sender, value, salt)); if (c.hash != expected) revert InvalidReveal(); c.revealed = true; _processReveal(msg.sender, value); } ``` ## Pattern Selection Checklist | Situation | Pattern | |-----------|---------| | External calls after state changes | Checks-Effects-Interactions | | Distributing funds to many users | Pull-over-Push | | Repeated precondition checks | Guard Check / modifiers | | Multi-phase protocol flow | State Machine | | Tiered permissions | Access Restriction | | Sensitive action prone to front-running | Commit-Reveal | ## Anti-Patterns to Avoid - Nested external calls within loops - `tx.origin` for authorization (phishing vector) - `transfer()` / `send()` for ETH transfers (2300 gas limit breaks with EIP-1884) - Implicit state transitions without explicit enum tracking - Boolean flags instead of proper state machines
Auf GitHub ansehen