0
0
Blockchain / Solidityprogramming~10 mins

Proxy pattern (upgradeable contracts) in Blockchain / Solidity

Choose your learning style9 modes available
Introduction

The proxy pattern helps you upgrade smart contracts without losing data or changing the contract address. It separates logic from data, so you can fix bugs or add features easily.

You want to fix bugs in a deployed smart contract without losing user data.
You want to add new features to a contract without forcing users to switch to a new address.
You want to save gas by reusing logic contracts for multiple proxies.
You want to keep a stable contract address for integrations while upgrading logic behind it.
Syntax
Blockchain / Solidity
contract Proxy {
    address implementation;

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

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

The fallback function forwards calls to the logic contract using delegatecall.

The upgradeTo function changes the logic contract address to upgrade functionality.

Examples
Basic proxy contract that forwards calls to an implementation contract and allows upgrading it.
Blockchain / Solidity
contract Proxy {
    address implementation;

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

    function upgradeTo(address newImplementation) external {
        implementation = newImplementation;
    }
}
First version of logic contract with a simple state variable and setter.
Blockchain / Solidity
contract LogicV1 {
    uint public x;

    function setX(uint _x) public {
        x = _x;
    }
}
Second version of logic contract that changes behavior by doubling the input.
Blockchain / Solidity
contract LogicV2 {
    uint public x;

    function setX(uint _x) public {
        x = _x * 2;
    }
}
Sample Program

This example shows a proxy contract that forwards calls to a logic contract using delegatecall. The owner can upgrade the logic contract address. LogicV1 sets a value directly, LogicV2 doubles the value before setting it.

Blockchain / Solidity
pragma solidity ^0.8.0;

contract Proxy {
    address public implementation;
    address public owner;

    constructor(address _implementation) {
        implementation = _implementation;
        owner = msg.sender;
    }

    fallback() external payable {
        address impl = implementation;
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(0, 0, size)
            switch result
            case 0 { revert(0, size) }
            default { return(0, size) }
        }
    }

    function upgradeTo(address newImplementation) external {
        require(msg.sender == owner, "Only owner can upgrade");
        implementation = newImplementation;
    }
}

contract LogicV1 {
    uint public x;

    function setX(uint _x) public {
        x = _x;
    }
}

contract LogicV2 {
    uint public x;

    function setX(uint _x) public {
        x = _x * 2;
    }
}
OutputSuccess
Important Notes

Storage layout must be the same between logic versions to avoid corrupting data.

Only the proxy holds the data; logic contracts are stateless.

Use access control on upgrade functions to prevent unauthorized upgrades.

Summary

The proxy pattern separates data and logic to allow contract upgrades.

Calls to the proxy are forwarded to the current logic contract using delegatecall.

Upgrading means changing the logic contract address without changing the proxy address.