# 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:
Best practice: OpenZeppelin libraries use करें — battle-tested और secure।
Upgradeability powerful tool है, लेकिन centralization risk है। Governance mechanisms (timelocks, multisigs) जरूर add करें।