✅ Secure Patterns: Checks-Effects-Interactions
Learn battle-tested patterns for secure contract design
Protect your dApp from common vulnerabilities
Your Progress
0 / 5 completed✅ Secure Design Patterns
Knowing vulnerabilities isn't enough—you need battle-tested patterns to prevent them. This section covers 12 secure patterns used by production protocols: access control (Ownable, RBAC, two-step transfers), state management (checks-effects-interactions, pull payments, rate limiting), upgradability (transparent proxies, storage gaps, immutable patterns), and emergency controls (pausable, circuit breakers, time locks). Each pattern includes complete code examples, use cases, benefits, and trade-offs. These aren't theoretical—they're patterns from Uniswap, Aave, Compound, and OpenZeppelin contracts securing billions in production.
🎮 Interactive: Pattern Library
Browse secure patterns by category. Each includes production-ready code, use cases, benefits, and trade-offs.
Access Control
Restrict function access to authorized users
Ownable Pattern
Single admin control
contract Ownable {
address public owner;
constructor() { owner = msg.sender; }
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function withdraw() external onlyOwner {
payable(owner).transfer(address(this).balance);
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0));
owner = newOwner;
}
}- • Simple, gas-efficient
- • Clear ownership transfer
- • Standard pattern
- • Single point of failure
- • If owner key lost, contract locked
Role-Based Access Control (RBAC)
Multiple permission levels
import "@openzeppelin/contracts/access/AccessControl.sol";
contract RBACExample is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
bytes32 public constant MINTER_ROLE = keccak256("MINTER");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address to, uint amount) external onlyRole(MINTER_ROLE) {
// Only minters can mint
}
function pause() external onlyRole(ADMIN_ROLE) {
// Only admins can pause
}
}- • Flexible permissions
- • Multiple admins
- • Granular control
- • More complex
- • Higher gas costs
- • Role management overhead
Two-Step Ownership Transfer
Prevent accidental ownership loss
contract TwoStepOwnable {
address public owner;
address public pendingOwner;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) external onlyOwner {
pendingOwner = newOwner;
}
function acceptOwnership() external {
require(msg.sender == pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}- • Prevents typo mistakes
- • New owner must confirm
- • Reversible before acceptance
- • Requires two transactions
- • Pending state complexity