The minimal proxy pattern helps create many copies of a smart contract cheaply by sharing code. It saves gas and storage on the blockchain.
Minimal proxy (clone) pattern in Blockchain / Solidity
contract Clone {
address public implementation;
constructor(address _implementation) {
implementation = _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()) }
}
}
}The proxy contract stores the address of the original contract (implementation).
Calls to the proxy are forwarded to the implementation using delegatecall, preserving context.
contract MinimalProxy {
address public implementation;
constructor(address _impl) {
implementation = _impl;
}
fallback() external payable {
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), sload(0), 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}// Using OpenZeppelin's Clones library
import "@openzeppelin/contracts/proxy/Clones.sol";
contract Factory {
address public implementation;
constructor(address _impl) {
implementation = _impl;
}
function createClone() external returns (address) {
return Clones.clone(implementation);
}
}This program shows a logic contract and a minimal proxy that forwards calls to it. The proxy stores data separately but uses logic from the original contract.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Logic { uint public x; function setX(uint _x) public { x = _x; } } contract MinimalProxy { address public implementation; constructor(address _impl) { implementation = _impl; } 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()) } } } } // Deployment steps (not in code): // 1. Deploy Logic contract. // 2. Deploy MinimalProxy with Logic's address. // 3. Call setX on MinimalProxy to set x in proxy's storage. // After calling setX(42) on MinimalProxy, reading x from MinimalProxy returns 42.
The proxy uses delegatecall to run code in the context of the proxy's storage.
Minimal proxies are very cheap to deploy compared to full contracts.
Be careful with storage layout to avoid conflicts between proxy and implementation.
Minimal proxy pattern creates cheap clones by forwarding calls to a shared implementation.
It saves gas and storage on the blockchain.
Useful for deploying many similar contracts efficiently.