Bird
Raised Fist0
Blockchain / Solidityprogramming~20 mins

Proxy pattern (upgradeable contracts) in Blockchain / Solidity - Practice Problems & Coding Challenges

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
Challenge - 5 Problems
🎖️
Proxy Pattern Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
Predict Output
intermediate
2:00remaining
Output of a simple proxy delegatecall

Consider a proxy contract that delegates calls to an implementation contract. What will be the output of the following Solidity code snippet when calling getValue() through the proxy?

Blockchain / Solidity
pragma solidity ^0.8.0;

contract Implementation {
    uint public value;
    function setValue(uint _value) public {
        value = _value;
    }
    function getValue() public view returns (uint) {
        return value;
    }
}

contract Proxy {
    uint public value;
    address public implementation;

    constructor(address _impl) {
        implementation = _impl;
    }

    fallback() external payable {
        (bool success, ) = implementation.delegatecall(msg.data);
        require(success);
    }
}
AReturns the value stored in Implementation contract's storage
BReverts with an error
C0
DReturns a random number
Attempts:
2 left
💡 Hint

Remember that delegatecall runs code in the context of the calling contract's storage.

🧠 Conceptual
intermediate
1:30remaining
Storage collision in proxy pattern

Why is it important to carefully manage storage layout in upgradeable proxy contracts?

ABecause storage variables in the implementation contract are copied to the proxy on deployment
BBecause storage variables in the proxy and implementation share the same storage slots during delegatecall
CBecause the proxy contract cannot have any storage variables
DBecause storage variables in the implementation contract are ignored during delegatecall
Attempts:
2 left
💡 Hint

Think about how delegatecall works with storage.

🔧 Debug
advanced
2:30remaining
Identify the bug causing upgrade failure

The following proxy upgrade code fails to preserve state after upgrading the implementation. What is the main cause?

Blockchain / Solidity
pragma solidity ^0.8.0;

contract ImplementationV1 {
    uint public count;
    function increment() public {
        count += 1;
    }
}

contract ImplementationV2 {
    uint public count;
    uint public newVar;
    function increment() public {
        count += 1;
    }
    function setNewVar(uint _val) public {
        newVar = _val;
    }
}

contract Proxy {
    uint public count;
    address public implementation;

    constructor(address _impl) {
        implementation = _impl;
    }

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

    function upgrade(address _newImpl) public {
        implementation = _newImpl;
    }
}
AImplementationV2 added a new storage variable breaking storage layout compatibility
BUpgrade function does not call initialize on new implementation
CProxy contract does not have a fallback function
DDelegatecall is used instead of call
Attempts:
2 left
💡 Hint

Check storage variables order and count in proxy and implementations.

📝 Syntax
advanced
1:30remaining
Identify the syntax error in proxy fallback

Which option correctly implements a fallback function in Solidity 0.8+ for a proxy contract that forwards calls using delegatecall?

Afallback() external payable { (bool success, ) = implementation.delegatecall(msg.data); require(success); }
Bfunction fallback() external { (bool success, ) = implementation.delegatecall(msg.data); require(success); }
Cfallback() public { (bool success, ) = implementation.call(msg.data); require(success); }
Dfunction fallback() external payable { (bool success, ) = implementation.delegatecall(msg.data); require(success); }
Attempts:
2 left
💡 Hint

Recall the correct syntax for fallback functions in Solidity 0.8+.

🚀 Application
expert
3:00remaining
Calculate number of storage slots used by proxy and implementation

A proxy contract has 2 storage variables: address implementation and uint256 owner. The implementation contract has 3 storage variables: uint256 count, bool active, and uint256 data. How many storage slots does the proxy contract use after upgrade if the implementation is called via delegatecall?

ACannot determine without more info
B3 storage slots
C2 storage slots
D5 storage slots
Attempts:
2 left
💡 Hint

Remember that delegatecall uses the proxy's storage layout, so all variables must fit in proxy storage.

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