0
0
Blockchain / Solidityprogramming~15 mins

Proxy pattern (upgradeable contracts) in Blockchain / Solidity - Deep Dive

Choose your learning style9 modes available
Overview - Proxy pattern (upgradeable contracts)
What is it?
The proxy pattern in blockchain is a way to make smart contracts upgradeable. Instead of changing the contract directly, a proxy contract forwards calls to a separate logic contract. This lets developers fix bugs or add features without losing stored data or users' trust. It works by separating storage and logic into two contracts that work together.
Why it matters
Without upgradeable contracts, once a smart contract is deployed, its code is fixed forever. This means bugs or missing features can cause permanent problems or losses. The proxy pattern solves this by allowing updates while keeping the contract's state intact. This protects users and lets developers improve contracts safely over time.
Where it fits
Before learning this, you should understand basic smart contracts and how blockchain stores data. After this, you can explore advanced upgrade patterns, security best practices, and tools for managing upgrades in real projects.
Mental Model
Core Idea
A proxy contract acts like a receptionist who forwards your requests to the right office, letting the office change without you noticing.
Think of it like...
Imagine a company where you always talk to the receptionist (proxy). The receptionist forwards your questions to the right department (logic contract). If the company changes departments or moves offices, the receptionist still forwards you correctly without you needing to know the details.
┌─────────────┐       forwards calls       ┌───────────────┐
│  User Call  │ ─────────────────────────> │ Proxy Contract│
└─────────────┘                            └───────────────┘
                                               │
                                               │ delegatecall
                                               ▼
                                       ┌─────────────────┐
                                       │ Logic Contract  │
                                       └─────────────────┘

Storage lives in Proxy Contract, Logic Contract contains code.
Build-Up - 7 Steps
1
FoundationUnderstanding Smart Contract Immutability
🤔
Concept: Smart contracts on blockchains cannot be changed once deployed.
When you deploy a smart contract, its code is stored permanently on the blockchain. This means you cannot fix bugs or add features by changing the contract itself. The data stored in the contract remains, but the code is locked.
Result
Contracts are permanent and unchangeable after deployment.
Knowing that contracts are immutable explains why upgrade patterns are necessary to improve or fix contracts after deployment.
2
FoundationSeparation of Storage and Logic
🤔
Concept: Storage and logic can be separated into different contracts.
Instead of putting data and code in one contract, you can store data in one contract (proxy) and put the code in another (logic). The proxy holds the data, and the logic contract contains the functions to manipulate that data.
Result
Data and code live separately but work together.
Separating storage and logic is the foundation that allows contracts to be upgradeable without losing data.
3
IntermediateHow Proxy Forwards Calls Using delegatecall
🤔Before reading on: do you think the proxy runs its own code or the logic contract's code when forwarding calls? Commit to your answer.
Concept: The proxy uses delegatecall to run logic contract code in its own storage context.
The proxy contract receives a call and uses a special command called delegatecall to execute the logic contract's code. Delegatecall runs the logic contract's code but uses the proxy's storage and address. This means data stays in the proxy, but code can be swapped.
Result
Calls to the proxy run logic contract code but keep data in proxy storage.
Understanding delegatecall is key because it lets the proxy act as a middleman that runs external code while keeping its own data safe.
4
IntermediateImplementing Upgradeability by Changing Logic Address
🤔Before reading on: do you think upgrading means changing the proxy or the logic contract? Commit to your answer.
Concept: Upgradeability is done by changing the logic contract address stored in the proxy.
The proxy stores the address of the current logic contract. To upgrade, you deploy a new logic contract with improvements and update the proxy's stored address to point to the new one. The proxy then forwards calls to the new logic contract without changing its own storage.
Result
The contract's behavior changes while data remains intact.
Knowing that only the logic address changes during upgrades helps prevent data loss and keeps user interactions seamless.
5
IntermediateStorage Slot Collision and Its Prevention
🤔Before reading on: do you think logic and proxy can use the same storage layout safely? Commit to your answer.
Concept: Storage slot collision happens when proxy and logic use overlapping storage, causing bugs.
Because delegatecall runs logic code in proxy storage, both must agree on storage layout. If the logic contract uses storage slots that the proxy also uses (like for the logic address), data can be overwritten or corrupted. Developers use reserved storage slots or special patterns to avoid this.
Result
Safe upgrades without corrupting stored data.
Understanding storage collisions prevents subtle bugs that can break upgradeable contracts in production.
6
AdvancedTransparent Proxy Pattern and Admin Restrictions
🤔Before reading on: do you think anyone can call upgrade functions on the proxy? Commit to your answer.
Concept: The transparent proxy pattern restricts upgrade functions to admins and avoids function clashes.
In the transparent proxy pattern, only the admin can call upgrade functions on the proxy. Regular users' calls are forwarded to the logic contract. This avoids conflicts where the proxy and logic contract have functions with the same name. The proxy checks caller identity to decide how to handle calls.
Result
Secure upgrades and clear separation of user/admin roles.
Knowing admin restrictions protects contracts from unauthorized upgrades and accidental function clashes.
7
ExpertDelegatecall Risks and Security Considerations
🤔Before reading on: do you think delegatecall can introduce security risks? Commit to your answer.
Concept: Delegatecall can cause security issues if not used carefully, such as reentrancy or storage corruption.
Because delegatecall runs external code in the proxy's context, bugs or malicious code in the logic contract can affect proxy storage or behavior. Attackers might exploit delegatecall to hijack storage or cause unexpected side effects. Developers must audit logic contracts, use access controls, and carefully design storage layouts to mitigate risks.
Result
Secure, upgradeable contracts that resist attacks.
Understanding delegatecall risks is crucial to avoid vulnerabilities that can lead to loss of funds or control.
Under the Hood
When a user calls the proxy contract, it does not execute its own code but uses delegatecall to run the logic contract's code. Delegatecall preserves the proxy's storage, msg.sender, and msg.value, making the logic contract's functions operate as if they were part of the proxy. The proxy holds a special storage slot with the logic contract's address. Upgrading means changing this address. The EVM executes delegatecall by loading the logic contract's code but reading and writing storage in the proxy's space.
Why designed this way?
Smart contracts are immutable by design to ensure trust and security. However, this immutability limits fixing bugs or adding features. The proxy pattern was designed to separate data and logic, allowing logic to be swapped while preserving data. Delegatecall was chosen because it lets one contract run another's code in its own context, enabling this separation. Alternatives like contract replacement lose stored data, so proxy pattern balances upgradeability and data persistence.
┌───────────────┐
│   User Call   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Proxy Contract│
│ - Storage    ◄┼─────────────┐
│ - Logic Addr │ │ delegatecall│
└──────┬────────┘             │
       │                      ▼
       │               ┌───────────────┐
       └──────────────►│Logic Contract │
                       │ - Code only   │
                       └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does upgrading a proxy contract change its storage data? Commit yes or no.
Common Belief:Upgrading a proxy contract replaces all its data with the new logic contract's data.
Tap to reveal reality
Reality:Upgrading only changes the logic contract address; the proxy's storage remains unchanged.
Why it matters:Believing storage changes leads to fear of data loss and misuse of upgrade patterns.
Quick: Can anyone call the upgrade function on a proxy contract? Commit yes or no.
Common Belief:Anyone can upgrade the proxy contract by calling its upgrade function.
Tap to reveal reality
Reality:Only authorized admins can call upgrade functions; others' calls are forwarded to logic.
Why it matters:Thinking upgrades are open risks unauthorized changes and security breaches.
Quick: Does delegatecall run the logic contract's code in its own storage? Commit yes or no.
Common Belief:Delegatecall runs the logic contract's code using the logic contract's storage.
Tap to reveal reality
Reality:Delegatecall runs logic code but uses the proxy contract's storage.
Why it matters:Misunderstanding this causes storage layout bugs and data corruption.
Quick: Is the proxy pattern the only way to upgrade smart contracts? Commit yes or no.
Common Belief:Proxy pattern is the only method to upgrade smart contracts.
Tap to reveal reality
Reality:Other methods exist, like contract replacement with data migration or diamond pattern.
Why it matters:Believing this limits exploration of better or more suitable upgrade strategies.
Expert Zone
1
The order of storage variables in proxy and logic contracts must be carefully aligned to avoid subtle bugs.
2
Using immutable variables in logic contracts can cause unexpected behavior because delegatecall ignores them.
3
Gas costs can increase with multiple delegatecalls in complex upgradeable contract systems, affecting performance.
When NOT to use
Avoid proxy pattern when contract logic is simple and unlikely to change, or when upgrade complexity outweighs benefits. Alternatives include deploying new contracts with data migration or using the diamond pattern for modular upgrades.
Production Patterns
In production, transparent proxies with admin roles are common to secure upgrades. Upgrade scripts automate logic contract deployment and proxy pointing. Testing includes simulating upgrades and verifying storage integrity. Some projects use UUPS proxies for gas efficiency and simpler upgrade logic.
Connections
Object-Oriented Programming (OOP) Proxy Pattern
The blockchain proxy pattern builds on the OOP proxy concept of forwarding calls to another object.
Understanding OOP proxies helps grasp how blockchain proxies forward calls and separate concerns.
Software Version Control
Upgradeable contracts are like version control systems that let you update software without losing history.
Knowing version control concepts clarifies why preserving contract state while changing logic is valuable.
Network Load Balancers
Proxy contracts act like load balancers that route requests to different servers (logic contracts).
Seeing proxies as routers helps understand how calls are forwarded transparently and can be switched.
Common Pitfalls
#1Upgrading logic contract without matching storage layout causes data corruption.
Wrong approach:Deploy new logic contract with different variable order and update proxy to point to it.
Correct approach:Ensure new logic contract has the same storage layout or uses reserved slots before upgrading.
Root cause:Misunderstanding that delegatecall uses proxy storage and requires consistent layout.
#2Allowing anyone to call upgrade function on proxy contract.
Wrong approach:function upgrade(address newLogic) public { logic = newLogic; } // no access control
Correct approach:function upgrade(address newLogic) public onlyAdmin { logic = newLogic; } // restrict access
Root cause:Ignoring security best practices and role-based access control.
#3Calling logic contract functions directly instead of through proxy.
Wrong approach:User interacts with logic contract address directly, bypassing proxy.
Correct approach:User always interacts with proxy contract address to ensure upgradeability.
Root cause:Not understanding that proxy is the user interface and logic contract is backend.
Key Takeaways
Smart contracts are immutable, so the proxy pattern enables upgradeability by separating storage and logic.
The proxy contract forwards calls to a logic contract using delegatecall, running external code in its own storage context.
Upgrading means changing the logic contract address in the proxy, preserving all stored data safely.
Security requires restricting upgrade functions to admins and carefully managing storage layouts to avoid bugs.
Understanding delegatecall's behavior and risks is essential to build safe, upgradeable contracts.