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 }
}
}- Deterministic slots: Avoids storage collisions
- Gas efficiency: Centralized storage access
- Predictable layout: Makes storage management easier
- Preserve state during contract upgrades
- Separate logic from data
- Enable complex upgrade paths
The pattern emerged from real financial losses:
- 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
Every safety pattern exists because someone lost millions. The AppStorage pattern is battle-tested survival knowledge.
- High-value contracts (DeFi, DAOs)
- Complex systems that will evolve
- Data preservation is critical
- User funds at stake
- Prototypes/testing
- Simple utility contracts
- Fresh start is better than migration complexity
- Basic tokens that can use swap mechanisms
// Organizes storage but doesn't enable upgrades alone
contract MyContract {
function someFunction() external {
AppStorage.Layout storage $ = AppStorage.store();
$.owner = msg.sender;
}
}// Enables actual upgrades via delegatecall
contract Proxy {
address implementation;
fallback() external payable {
// delegatecall to implementation
}
}Key Point: AppStorage alone ≠ upgradeable. You need both!
- Users keep same address
- Seamless upgrades
- All data preserved automatically
- ERC20: New token + swap mechanism
- NFTs: Metadata updates or new collection
- Data: Export/import or snapshot migration
- 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
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
- 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
- For Production: Use OpenZeppelin's battle-tested proxy implementations
- Storage Layout: Never change existing fields, only append new ones
- Testing: Thoroughly test upgrade paths before deployment
- Documentation: Clearly document storage layout changes
- Migration Planning: Plan user migration strategy from day one
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.