Tutorial·9 min का पठन·Solingo द्वारा

Proxy Patterns समझें — Transparent, UUPS और Beacon

Smart contract upgradeability की तीन main patterns। Architecture, trade-offs और production examples।

# Proxy Patterns समझें — Transparent, UUPS और Beacon

Smart contracts immutable होते हैं, लेकिन upgradeable proxies से logic update करना possible है। तीन main patterns हैं, हर एक की अपनी trade-offs।

Upgradeability क्यों?

// Contract deploy हुआ

contract TokenV1 {

mapping(address => uint) public balances;

function transfer(address to, uint amount) public {

// Bug discovered! लेकिन fix नहीं कर सकते...

}

}

Problem: Bugs, new features, security patches — immutable contracts में impossible।

Solution: Proxy pattern।

Proxy Basics

Core Concept

User → Proxy Contract → Implementation Contract

(storage) (logic)

  • Proxy: Storage + delegatecall
  • Implementation: Pure logic
contract Proxy {

address public implementation;

fallback() external payable {

address impl = implementation;

assembly {

calldatacopy(0, 0, calldatasize())

let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)

returndatacopy(0, 0, returndatasize())

switch result

case 0 { revert(0, returndatasize()) }

default { return(0, returndatasize()) }

}

}

}

Delegatecall Magic

// Proxy storage में execute होता है

// Implementation की logic use होती है

// msg.sender और msg.value preserved रहते हैं

Pattern 1: Transparent Proxy

Architecture

contract TransparentProxy {

address public implementation;

address public admin;

modifier ifAdmin() {

if (msg.sender == admin) {

_;

} else {

_fallback();

}

}

function upgrade(address newImpl) external ifAdmin {

implementation = newImpl;

}

function _fallback() internal {

// delegatecall to implementation

}

fallback() external payable {

require(msg.sender != admin);

_fallback();

}

}

Key Feature: Function Selector Clash Prevention

Problem:

// Proxy में upgrade() है

// Implementation में भी upgrade() function हो सकता है

// Conflict!

Solution: Admin transparent proxy functions call कर सकता है, users नहीं।

Gas Cost

  • Admin calls: Normal gas
  • User calls: Extra SLOAD (~100 gas) — admin check के लिए

Example: OpenZeppelin

import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

contract MyProxy is TransparentUpgradeableProxy {

constructor(

address logic,

address admin,

bytes memory data

) TransparentUpgradeableProxy(logic, admin, data) {}

}

Pattern 2: UUPS (Universal Upgradeable Proxy Standard)

Architecture

contract UUPSProxy {

address public implementation;

fallback() external payable {

// Simple delegatecall, no admin check

_delegate(implementation);

}

}

contract ImplementationV1 is UUPSUpgradeable {

address public implementation;

function upgradeTo(address newImpl) external onlyOwner {

implementation = newImpl; // Self-upgrade

}

}

Key Feature: Upgrade Logic in Implementation

  • Proxy: Minimal, gas-efficient
  • Implementation: Contains upgrade logic

Gas Cost

User calls: ~2,000 gas cheaper than Transparent (no admin check)

Security Trade-off

⚠️ Risk: अगर implementation में upgrade function forget हो गया, proxy permanently locked।

// ❌ DANGEROUS — no upgrade function!

contract ImplementationV2 {

// Forgot to add upgradeTo()...

// Ab upgrade impossible!

}

Example: OpenZeppelin UUPS

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract MyContractV1 is UUPSUpgradeable, OwnableUpgradeable {

function initialize() public initializer {

__Ownable_init();

__UUPSUpgradeable_init();

}

function _authorizeUpgrade(address) internal override onlyOwner {}

}

Pattern 3: Beacon Proxy

Architecture

Multiple Proxies → Beacon → Implementation
contract Beacon {

address public implementation;

address public owner;

function upgrade(address newImpl) external {

require(msg.sender == owner);

implementation = newImpl;

}

}

contract BeaconProxy {

address public beacon;

fallback() external payable {

address impl = Beacon(beacon).implementation();

_delegate(impl);

}

}

Key Feature: Multiple Proxies, Single Upgrade

// 1000 token proxies

// 1 beacon upgrade → सभी 1000 update हो जाते हैं

Use Case: Factory Patterns

contract TokenFactory {

UpgradeableBeacon public beacon;

function createToken(string memory name) external returns (address) {

BeaconProxy proxy = new BeaconProxy(

address(beacon),

abi.encodeCall(Token.initialize, (name))

);

return address(proxy);

}

function upgradeAll(address newImpl) external onlyOwner {

beacon.upgradeTo(newImpl);

// सभी tokens automatically upgraded!

}

}

Gas Cost

  • Deployment: Cheapest (shared beacon)
  • Calls: Extra SLOAD for beacon lookup (~100 gas)

Pattern Comparison

| Feature | Transparent | UUPS | Beacon |

|---|---|---|---|

| Gas per call | Highest | Lowest | Medium |

| Complexity | Medium | Medium | High |

| Proxy size | Large | Small | Small |

| Risk | Low | Medium (lock risk) | Low |

| Best for | Single contracts | Gas-sensitive | Multiple instances |

Storage Collisions

The Problem

// Proxy storage layout

contract Proxy {

address implementation; // slot 0

address admin; // slot 1

}

// Implementation storage layout

contract Implementation {

uint public totalSupply; // slot 0 — COLLISION!

mapping(address => uint) balances; // slot 1 — COLLISION!

}

Solution: EIP-1967 Storage Slots

// OpenZeppelin standard

bytes32 private constant IMPLEMENTATION_SLOT =

0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

bytes32 private constant ADMIN_SLOT =

0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

function _setImplementation(address newImpl) internal {

assembly {

sstore(IMPLEMENTATION_SLOT, newImpl)

}

}

Ye slots random hash से generate होते हैं — collision impossible।

Initialization

Constructors proxies में काम नहीं करते:

// ❌ Constructor proxy में ignored होता है

contract Implementation {

constructor() {

owner = msg.sender; // यह proxy में reflect नहीं होगा

}

}

// ✅ Initialize function use करें

contract Implementation {

bool private initialized;

function initialize(address _owner) external {

require(!initialized);

owner = _owner;

initialized = true;

}

}

OpenZeppelin Initializable

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract MyContract is Initializable {

function initialize() public initializer {

// Setup code

}

}

Production Examples

  • Transparent: Compound, MakerDAO
  • UUPS: Aave V3, newer protocols
  • Beacon: NFT collections, token factories

निष्कर्ष

Proxy pattern choice depends on:

  • Single contract → Transparent या UUPS
  • Gas-critical → UUPS
  • Multiple instances → Beacon
  • Risk tolerance → Transparent (safest)
  • Best practice: OpenZeppelin libraries use करें — battle-tested और secure।

    Upgradeability powerful tool है, लेकिन centralization risk है। Governance mechanisms (timelocks, multisigs) जरूर add करें।

    Practice में लगाने के लिए तैयार हैं?

    Solingo पर interactive exercises के साथ इन concepts को apply करें।

    मुफ्त में शुरू करें