ProxyForge is a lightweight, gas-optimized framework for deploying and managing upgradeable proxies on the Ethereum Virtual Machine (EVM). It maintains compatibility with OpenZeppelin's proxy architecture while introducing low-level optimizations and slot-based metadata tracking.
- Transparent Proxy Pattern: ERC-1967 compliant with admin isolation for security
- Assembly Optimized: Extensive use of inline assembly for maximum efficiency
- Gas-Optimized Deployments: Highly efficient CREATE and CREATE2 proxy deployments
- Comprehensive Management: Deploy, upgrade, transfer ownership, and revoke proxies
- Deterministic Addresses: CREATE2 support with collision-resistant salt validation
- Modular Design: Components can be used independently or via the factory
proxy-forge/
├── deployments/...
├── script/
│ ├── BaseScript.sol
│ ├── Deploy.s.sol
│ ├── DeployProxy.s.sol
│ └── UpgradeProxy.s.sol
├── src/
│ ├── interfaces/
│ │ ├── IForgeProxy.sol
│ │ ├── IForgeProxyAdmin.sol
│ │ └── IProxyForge.sol
│ ├── proxy/
│ │ ├── ForgeProxy.sol
│ │ └── ForgeProxyAdmin.sol
│ └── ProxyForge.sol
└── test/
├── proxy/
│ ├── ForgeProxy.t.sol
│ └── ForgeProxyAdmin.t.sol
├── shared/...
├── ProxyForge.fuzz.t.sol
└── ProxyForge.t.sol
ProxyForge: The main factory contract that handles deployment and management operations
- Deploys new proxy instances using CREATE or CREATE2
- Manages proxy ownership and implementation upgrades
- Provides deterministic address computation
ForgeProxy: Gas-optimized upgradeable proxy implementation
- Follows ERC-1967 standard for storage slots
- Automatic admin contract deployment during construction
- Assembly-optimized fallback routing for maximum efficiency
ForgeProxyAdmin: Ultra-lightweight admin contract for proxy management
- Handles upgrade operations with calldata transformation
- Implements standard ownership patterns
- Compatible with OpenZeppelin ProxyAdmin interface (v5.0.0)
ForgeProxy is deployed on the following chains:
| Network | Chain ID | Address |
|---|---|---|
| Ethereum | 1 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
| Ethereum Sepolia | 11155111 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
| Optimism | 10 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
| Polygon | 137 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
| Base | 8453 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
| Base Sepolia | 84532 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
| Arbitrum One | 42161 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
| Arbitrum Sepolia | 421614 | 0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe |
forge install fomoweth/proxy-forgegit clone https://github.com/fomoweth/proxy-forge.gitforge build --sizes# Run all tests
forge test# Run with detailed logs
forge test -vvv
# Run with gas reporting
forge test --gas-report
# Run specific test
forge test --match-path test/ProxyForge.fuzz.t.solforge script \
script/Deploy.s.sol:Deploy \
-vvv \
--slow \
--multi \
--broadcastforge verify-contract <CONTRACT_ADDRESS> \
src/ProxyForge.sol:ProxyForge \
--compiler-version v0.8.30+commit.73712a01 \
--verifier etherscan \
--etherscan-api-key <ETHERSCAN_API_KEY> \
--chain-id <CHAIN_ID>import {IProxyForge} from"lib/proxy-forge/interfaces/IProxyForge.sol";
contractMyContract {
IProxyForge publicconstant PROXY_FORGE =IProxyForge(0x58b819827cB18Ba425906C69E1Bfb22F27Cb1bCe);
function deployProxy(addressimplementation) externalreturns (addressproxy) {
// Deploy a new proxy with msg.sender as ownerreturn PROXY_FORGE.deploy(implementation, msg.sender);
}
function deployProxy(addressimplementation, uint96identifier) externalreturns (addressproxy) {
// Create a deterministic salt (first 20 bytes must be caller or zero address)bytes32 salt =bytes32((uint256(uint160(msg.sender)) <<96) |uint256(identifier));
// Deploy with CREATE2 for deterministic addressreturn PROXY_FORGE.deployDeterministic(implementation, msg.sender, salt);
}
function deployProxy(addressimplementation, bytesmemorydata) externalreturns (addressproxy) {
// Encode initialization call databytesmemory initData =abi.encodeWithSignature("initialize(bytes)", data);
// Deploy and initialize in one transactionreturn PROXY_FORGE.deployAndCall(implementation, msg.sender, initData);
}
}function deploy(addressimplementation, addressowner) externalpayablereturns (addressproxy);
function deployAndCall(addressimplementation, addressowner, bytescalldatadata) externalpayablereturns (addressproxy);
function deployDeterministic(addressimplementation, addressowner, bytes32salt) externalpayablereturns (addressproxy);
function deployDeterministicAndCall(addressimplementation, addressowner, bytes32salt, bytescalldatadata) externalpayablereturns (addressproxy);function upgrade(addressproxy, addressimplementation) externalpayable;
function upgradeAndCall(addressproxy, addressimplementation, bytescalldatadata) externalpayable;
function revoke(addressproxy) externalpayable;
function changeOwner(addressproxy, addressowner) externalpayable;function adminOf(addressproxy) externalviewreturns (addressadmin);
function implementationOf(addressproxy) externalviewreturns (addressimplementation);
function ownerOf(addressproxy) externalviewreturns (addressowner);
function computeProxyAddress(uint256nonce) externalviewreturns (addressproxy);
function computeProxyAddress(addressimplementation, bytes32salt, bytescalldatadata) externalviewreturns (addressproxy);event ProxyDeployed(addressindexedproxy, addressindexedowner, bytes32indexedsalt);
event ProxyUpgraded(addressindexedproxy, addressindexedimplementation);
event ProxyOwnerChanged(addressindexedproxy, addressindexedowner);
event ProxyRevoked(addressindexedproxy);error InvalidProxy();
error InvalidProxyImplementation();
error InvalidProxyOwner();
error InvalidSalt();
error UnauthorizedAccount(addressaccount);
error UpgradeFailed();The following repositories served as key references during the development of this project: