Contract Patterns Skill
This skill provides battle-tested patterns and examples for common smart contract functionality.
When to Use
Use this skill when:
- •Implementing access control mechanisms
- •Creating upgradeable contracts
- •Building token contracts (ERC20, ERC721, ERC1155)
- •Adding pausability to contracts
- •Protecting against reentrancy attacks
- •Following established security patterns
Pattern Categories
1. Access Control Patterns
See ./patterns/access-control.md for detailed documentation.
Common patterns:
- •Ownable - Single owner with privileged access
- •AccessControl - Role-based access control (RBAC)
- •Multisig - Multiple signatures required for actions
- •Timelock - Delayed execution for critical functions
When to use:
- •Ownable: Simple contracts with single admin
- •AccessControl: Complex permissions with multiple roles
- •Multisig: High-value contracts requiring consensus
- •Timelock: Governance and critical upgrades
2. Upgradeable Contract Patterns
See ./patterns/upgradeable-contracts.md for detailed documentation.
Common patterns:
- •Transparent Proxy - Separate admin and user logic
- •UUPS (Universal Upgradeable Proxy Standard) - Upgrade logic in implementation
- •Beacon Proxy - Multiple proxies sharing same implementation
- •Diamond Pattern (EIP-2535) - Multi-facet proxy for large contracts
When to use:
- •Transparent: When admin and user separation is critical
- •UUPS: Gas-efficient upgrades, upgrade logic in implementation
- •Beacon: Deploying many instances of same logic
- •Diamond: Large contracts exceeding size limits
3. Pausable Pattern
See ./patterns/pausable.md for detailed documentation.
Purpose: Emergency stop mechanism to pause contract functionality
When to use:
- •Contracts handling user funds
- •Contracts that may need emergency stops
- •Contracts under active development/monitoring
Key features:
- •Pause/unpause functionality
- •Restricted to authorized roles
- •Graceful degradation of functionality
4. Reentrancy Guard
See ./patterns/reentrancy-guard.md for detailed documentation.
Purpose: Prevent reentrancy attacks in functions that make external calls
When to use:
- •Functions that transfer ETH
- •Functions that call external contracts
- •Functions that modify state after external calls
Implementation:
- •Checks-Effects-Interactions pattern
- •ReentrancyGuard modifier
- •Mutex locks
5. Token Standards
See ./patterns/token-standards.md for detailed documentation.
ERC20 - Fungible tokens
- •Standard interface for tokens like USDC, DAI
- •Transfer, approve, transferFrom functionality
- •See
./examples/ERC20-example.sol
ERC721 - Non-fungible tokens (NFTs)
- •Unique tokens with individual ownership
- •Metadata support
- •See
./examples/ERC721-example.sol
ERC1155 - Multi-token standard
- •Batch operations for fungible and non-fungible tokens
- •Gas-efficient for multiple token types
- •See
./examples/ERC1155-example.sol
Integration with Code Principles
These patterns follow the code-principles from the foundation plugin:
- •DRY: Inherit from OpenZeppelin contracts instead of reimplementing
- •SOLID: Single responsibility for each pattern/module
- •KISS: Use simplest pattern that meets requirements
- •Security First: Battle-tested implementations over custom code
Note: Solidity-specific security concerns take precedence over general software principles.
OpenZeppelin Contracts
Most patterns are best implemented using OpenZeppelin contracts:
# Install OpenZeppelin forge install OpenZeppelin/openzeppelin-contracts # or npm install @openzeppelin/contracts
Available contracts:
- •
@openzeppelin/contracts/access/Ownable.sol - •
@openzeppelin/contracts/access/AccessControl.sol - •
@openzeppelin/contracts/security/Pausable.sol - •
@openzeppelin/contracts/security/ReentrancyGuard.sol - •
@openzeppelin/contracts/token/ERC20/ERC20.sol - •
@openzeppelin/contracts/token/ERC721/ERC721.sol - •
@openzeppelin/contracts/token/ERC1155/ERC1155.sol - •
@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol - •
@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol
Pattern Selection Guide
| Need | Pattern | Complexity | Gas Cost | Security |
|---|---|---|---|---|
| Single admin | Ownable | Low | Low | Medium |
| Multiple roles | AccessControl | Medium | Medium | High |
| Emergency stop | Pausable | Low | Low | High |
| Prevent reentrancy | ReentrancyGuard | Low | Low | Critical |
| Fungible tokens | ERC20 | Low | Low | High |
| NFTs | ERC721 | Medium | Medium | High |
| Multi-token | ERC1155 | High | Low | High |
| Simple upgrades | UUPS | Medium | Low | High |
| Admin separation | Transparent Proxy | Medium | Medium | High |
| Multiple instances | Beacon Proxy | High | Low | High |
| Large contracts | Diamond | Very High | Medium | Medium |
Best Practices
- •Prefer OpenZeppelin - Use audited implementations over custom code
- •Combine patterns carefully - Test interactions between patterns
- •Follow initialization patterns - Use proper constructor/initializer for upgradeable contracts
- •Test thoroughly - Each pattern has unique security considerations
- •Document deviations - If customizing standard patterns, document why
- •Keep it simple - Use simplest pattern that meets requirements
- •Security over gas optimization - Prioritize security when patterns conflict
Common Combinations
Pausable + AccessControl
contract MyContract is Pausable, AccessControl {
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
function pause() public onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() public onlyRole(PAUSER_ROLE) {
_unpause();
}
function criticalFunction() public whenNotPaused {
// Function logic
}
}
ERC20 + Ownable + Pausable
contract MyToken is ERC20, Ownable, Pausable {
constructor() ERC20("MyToken", "MTK") Ownable(msg.sender) {}
function pause() public onlyOwner {
_pause();
}
function _update(address from, address to, uint256 value)
internal
override
whenNotPaused
{
super._update(from, to, value);
}
}
UUPS + AccessControl + ReentrancyGuard
contract MyUpgradeableContract is
UUPSUpgradeable,
AccessControlUpgradeable,
ReentrancyGuardUpgradeable
{
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
function _authorizeUpgrade(address newImplementation)
internal
override
onlyRole(UPGRADER_ROLE)
{}
}
Anti-Patterns to Avoid
- •Custom access control - Use OpenZeppelin instead
- •Manual reentrancy protection - Use ReentrancyGuard
- •Incorrect upgrade patterns - Follow OpenZeppelin upgrade guides
- •Mixing storage layouts - Be careful with inheritance order
- •Skipping initialization - Always initialize upgradeable contracts
- •Ignoring token standards - Follow ERC specifications exactly
Pattern Files
This skill provides the following pattern documentation:
- •
./patterns/upgradeable-contracts.md- Proxy patterns - •
./patterns/access-control.md- Permission patterns - •
./patterns/pausable.md- Emergency stop pattern - •
./patterns/reentrancy-guard.md- Reentrancy protection - •
./patterns/token-standards.md- ERC20/721/1155 standards
Example Contracts
This skill provides the following examples:
- •
./examples/ERC20-example.sol- Fungible token implementation - •
./examples/ERC721-example.sol- NFT implementation - •
./examples/ERC1155-example.sol- Multi-token implementation - •
./examples/upgradeable-example.sol- UUPS upgradeable contract
Quick Reference
// Access Control import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; // Security import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; // Tokens import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; // Upgradeability import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
Remember: Always prefer battle-tested OpenZeppelin implementations over custom patterns. Security > Gas optimization > Code elegance.