Skip to content

Instantly share code, notes, and snippets.

@afa7789
Created August 9, 2025 14:39
Show Gist options
  • Save afa7789/5a68012a5e0e7755564bc6ec6a7bd992 to your computer and use it in GitHub Desktop.
Save afa7789/5a68012a5e0e7755564bc6ec6a7bd992 to your computer and use it in GitHub Desktop.
Proxy Pattern Guide

Smart Contract Proxy Patterns Guide

What is the AppStorage Pattern?

The AppStorage pattern uses a deterministic storage slot to organize contract data:

library AppStorage {
    bytes32 constant POSITION = keccak256("app.storage.v1");
    
    struct Layout {
        address owner;
        mapping(address => uint256) balances;
        uint256 totalSupply;
    }
    
    function store() internal pure returns (Layout storage s) {
        bytes32 position = POSITION;
        assembly { s.slot := position }
    }
}

Why Use This Pattern?

Storage Optimization

  • Deterministic slots: Avoids storage collisions
  • Gas efficiency: Centralized storage access
  • Predictable layout: Makes storage management easier

Upgradability (with proxy)

  • Preserve state during contract upgrades
  • Separate logic from data
  • Enable complex upgrade paths

Historical Context: Learning from Disasters

The pattern emerged from real financial losses:

Major Incidents

  • Parity Multisig Hack (2017): $30M+ stolen due to storage collisions
  • Ethereum-Arbitrum Bridge: $250M+ at risk from proxy vulnerabilities
  • Audius Protocol: Governance exploit via storage manipulation

The Lesson

Every safety pattern exists because someone lost millions. The AppStorage pattern is battle-tested survival knowledge.

When to Use Proxy Patterns

✅ Use Proxy When:

  • High-value contracts (DeFi, DAOs)
  • Complex systems that will evolve
  • Data preservation is critical
  • User funds at stake

❌ Skip Proxy When:

  • Prototypes/testing
  • Simple utility contracts
  • Fresh start is better than migration complexity
  • Basic tokens that can use swap mechanisms

Two Components Needed for Upgradability

1. AppStorage Pattern (Data Organization)

// Organizes storage but doesn't enable upgrades alone
contract MyContract {
    function someFunction() external {
        AppStorage.Layout storage $ = AppStorage.store();
        $.owner = msg.sender;
    }
}

2. Proxy Contract (Upgrade Mechanism)

// Enables actual upgrades via delegatecall
contract Proxy {
    address implementation;
    
    fallback() external payable {
        // delegatecall to implementation
    }
}

Key Point: AppStorage alone ≠ upgradeable. You need both!

Migration Strategies

For Proxy Contracts

  • Users keep same address
  • Seamless upgrades
  • All data preserved automatically

For Non-Proxy Contracts

  • ERC20: New token + swap mechanism
  • NFTs: Metadata updates or new collection
  • Data: Export/import or snapshot migration

Handling Old Contracts

  • Can't delete: Blockchain is immutable
  • Let them coexist: Old contracts stay live
  • Migration incentives: Rewards for moving to V2
  • Deprecation notices: Warn users to migrate

Decision Framework

Is user data valuable/irreplaceable?
├─ YES → Use proxy pattern
└─ NO → Simple redeploy

Will the contract need frequent updates?
├─ YES → Use proxy pattern  
└─ NO → Consider simple deployment

Are users okay with migration hassle?
├─ YES → Simple redeploy might work
└─ NO → Use proxy for seamless upgrades

Real-World Examples

  • Uniswap V1→V2→V3: All versions coexist, users migrated for features
  • Compound V1→V2: V1 still active, just less used
  • OpenZeppelin Proxies: Industry standard for safe upgrades

Best Practices

  1. For Production: Use OpenZeppelin's battle-tested proxy implementations
  2. Storage Layout: Never change existing fields, only append new ones
  3. Testing: Thoroughly test upgrade paths before deployment
  4. Documentation: Clearly document storage layout changes
  5. Migration Planning: Plan user migration strategy from day one

Key Takeaway

The proxy pattern isn't just "good practice" - it's survival knowledge written in millions of dollars of losses. Use it when the cost of losing data exceeds the complexity of maintaining upgradeable contracts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment