Bird
Raised Fist0
Blockchain / Solidityprogramming~10 mins

Proxy pattern (upgradeable contracts) in Blockchain / Solidity - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Concept Flow - Proxy pattern (upgradeable contracts)
Client calls Proxy Contract
Proxy forwards call to Logic Contract
Logic Contract executes code
Logic Contract returns result
Proxy returns result to Client
Upgrade? Replace Logic Contract address in Proxy
Next calls use new Logic Contract
The proxy contract receives calls and forwards them to a logic contract. Upgrades happen by changing the logic contract address in the proxy.
Execution Sample
Blockchain / Solidity
contract Proxy {
  address logic;
  function call(bytes calldata data) external returns (bytes memory) {
    (bool success, bytes memory result) = logic.delegatecall(data);
    require(success, "Delegatecall failed");
    return result;
  }
  function upgrade(address newLogic) external {
    logic = newLogic;
  }
}
A proxy contract forwards calls to a logic contract and can upgrade by changing the logic address.
Execution Table
StepActionProxy logic addressCall dataDelegatecall targetResult
1Deploy Proxy with logic = LogicV1LogicV1---
2Client calls Proxy.call with data for function foo()LogicV1foo()LogicV1foo() result from LogicV1
3Upgrade Proxy logic to LogicV2LogicV2---
4Client calls Proxy.call with data for function foo()LogicV2foo()LogicV2foo() result from LogicV2
5Client calls Proxy.call with data for function bar()LogicV2bar()LogicV2bar() result from LogicV2
6No more upgrades, calls continue to LogicV2LogicV2foo()LogicV2foo() result from LogicV2
💡 Execution stops when no more calls or upgrades happen.
Variable Tracker
VariableStartAfter Step 1After Step 3Final
logicnullLogicV1LogicV2LogicV2
call data-foo()foo()foo() or bar()
result-foo() result from LogicV1foo() result from LogicV2foo() or bar() result from LogicV2
Key Moments - 3 Insights
Why does the proxy not contain the logic code itself?
Because the proxy only forwards calls to the logic contract using delegatecall, so the logic code stays in the logic contract and can be upgraded by changing its address (see execution_table steps 2 and 4).
How does upgrading the logic contract affect existing state?
The proxy holds the state, and delegatecall runs logic in the proxy's context, so upgrading logic changes behavior but keeps the same state (see variable 'logic' changes in variable_tracker).
What happens if the logic contract address is not updated?
Calls keep forwarding to the old logic contract, so no new features or fixes apply (see execution_table step 6).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what is the logic address after step 3?
ALogicV1
BLogicV2
Cnull
DProxy itself
💡 Hint
Check the 'Proxy logic address' column at step 3 in execution_table.
At which step does the proxy start forwarding calls to LogicV2?
AStep 2
BStep 3
CStep 4
DStep 5
💡 Hint
Look at the 'Delegatecall target' column in execution_table for when it changes to LogicV2.
If the upgrade function is never called, what happens to the calls?
AThey forward to LogicV1
BThey fail with error
CThey forward to LogicV2
DThey execute in Proxy contract
💡 Hint
See variable_tracker 'logic' variable if it never changes from LogicV1.
Concept Snapshot
Proxy pattern lets a contract forward calls to a logic contract.
The proxy holds state; logic contract holds code.
Upgrade by changing logic contract address in proxy.
Calls use delegatecall to run logic in proxy context.
This enables upgradeable contracts without losing state.
Full Transcript
The proxy pattern in upgradeable contracts works by having a proxy contract that receives calls from clients. Instead of executing logic itself, the proxy forwards these calls to a separate logic contract using delegatecall. This means the logic contract's code runs in the proxy's storage context, so the proxy holds the state. When an upgrade is needed, the proxy's stored logic contract address is changed to a new logic contract with updated code. Calls then forward to the new logic contract, enabling upgrades without losing stored data. The execution table shows deploying the proxy with LogicV1, calling functions forwarded to LogicV1, upgrading to LogicV2, and subsequent calls forwarding to LogicV2. Variables like 'logic' track the current logic contract address. Key moments clarify why the proxy doesn't hold logic code, how state is preserved, and what happens if no upgrade occurs. The visual quiz tests understanding of when upgrades happen and how calls are forwarded. This pattern is essential for blockchain contracts that need to evolve after deployment.

Practice

(1/5)
1.

What is the main purpose of using the Proxy pattern in smart contracts?

easy
A. To upgrade contract logic without changing the contract address
B. To reduce gas fees by optimizing code
C. To create multiple copies of the same contract
D. To prevent any changes to the contract after deployment

Solution

  1. Step 1: Understand the Proxy pattern role

    The Proxy pattern allows a contract to forward calls to another contract, enabling upgrades.
  2. Step 2: Identify the main benefit

    This forwarding lets you change the logic contract without changing the proxy's address.
  3. Final Answer:

    To upgrade contract logic without changing the contract address -> Option A
  4. Quick Check:

    Proxy pattern = Upgrade logic without address change [OK]
Hint: Proxy pattern upgrades logic, keeps address same [OK]
Common Mistakes:
  • Thinking proxy reduces gas fees
  • Believing proxy creates contract copies
  • Assuming proxy prevents all changes
2.

Which Solidity keyword is used inside a proxy contract to forward calls to the implementation contract?

easy
A. delegatecall
B. call
C. transfer
D. send

Solution

  1. Step 1: Recall Solidity call types

    Solidity has several low-level calls: call, delegatecall, send, transfer.
  2. Step 2: Identify forwarding call for proxy

    Proxy contracts use delegatecall to run implementation code in proxy's context.
  3. Final Answer:

    delegatecall -> Option A
  4. Quick Check:

    Proxy forwarding uses delegatecall [OK]
Hint: Proxy uses delegatecall to keep storage context [OK]
Common Mistakes:
  • Confusing call with delegatecall
  • Using transfer or send which are for Ether
  • Not knowing delegatecall preserves storage
3.

Consider this simplified proxy contract snippet in Solidity:

contract Proxy {
    address implementation;
    
    fallback() external payable {
        (bool success, ) = implementation.delegatecall(msg.data);
        require(success);
    }
}

What happens if implementation address is zero?

medium
A. The contract will self-destruct
B. The call will succeed but do nothing
C. The fallback function will be ignored
D. The call will fail and revert the transaction

Solution

  1. Step 1: Understand delegatecall to zero address

    Calling delegatecall on address zero means no code to execute.
  2. Step 2: Effect of delegatecall failure

    delegatecall returns false on failure; require(success) then reverts transaction.
  3. Final Answer:

    The call will fail and revert the transaction -> Option D
  4. Quick Check:

    delegatecall to zero address = revert [OK]
Hint: delegatecall to zero address always fails [OK]
Common Mistakes:
  • Assuming call succeeds silently
  • Thinking fallback is skipped
  • Believing contract self-destructs
4.

Identify the bug in this proxy upgrade function:

function upgradeTo(address newImplementation) public {
    implementation = newImplementation;
}

What is the main issue?

medium
A. Implementation address is not validated
B. Missing event emission after upgrade
C. No access control, anyone can upgrade implementation
D. Function should be external, not public

Solution

  1. Step 1: Check function access control

    The function is public, so anyone can call it and change implementation.
  2. Step 2: Understand security risk

    Without restricting access, attackers can hijack the contract logic.
  3. Final Answer:

    No access control, anyone can upgrade implementation -> Option C
  4. Quick Check:

    Upgrade function needs access control [OK]
Hint: Always restrict upgrade function access [OK]
Common Mistakes:
  • Ignoring access control importance
  • Focusing only on event emission
  • Thinking public vs external affects security
5.

You want to upgrade a proxy contract to a new implementation that adds a new state variable. What must you ensure to avoid breaking storage layout?

hard
A. Rearrange all variables in the new implementation for optimization
B. Add new variables only at the end of existing storage variables
C. Remove unused variables from the old implementation
D. Change variable types to reduce storage size

Solution

  1. Step 1: Understand storage layout importance

    Proxy pattern requires storage layout consistency between implementations.
  2. Step 2: Correct way to add variables

    New variables must be appended to avoid overwriting existing storage slots.
  3. Final Answer:

    Add new variables only at the end of existing storage variables -> Option B
  4. Quick Check:

    Storage layout consistency = append variables [OK]
Hint: Append new variables to preserve storage layout [OK]
Common Mistakes:
  • Rearranging variables breaks storage
  • Removing old variables causes data loss
  • Changing types shifts storage slots