Minimal Proxy Example
ERC-1167
Bytecode of minimal proxy

Initialisation code:
3d602d80600a3d3981f3
This bit is responsible for deployment and stores the runtime bytecode on-chain.
To know more, read up on smart contract creation code.
Copy calldata:
363d3d373d3d3d363d73
When a transaction call is made to the minimal proxy, the proxy save the calldata passed to memory so that it can forward the said calldata to the implementation contract via
delegatecall
.This is how a function call and its parameters are passed along to the main contract to be executed.
Note that while the execution logic is stored on the implementation contract, with the use of
delegatecall
, the execution logic is “ported over” and executed within the context of the proxy. This means that the storage of the proxy gets modified, not the implementation contract.
Implementation address:
bebebebebebebebebebebebebebebebebebebebe
This is a placeholder address and should be replaced with the address of the actual implementation contract.
Delegatecall instruction:
5af43d82803e903d91602b57fd5bf3
Once the delegate call is executed, the minimal proxy will return the result of the call if it was successful. If an error occurred, it will revert the transaction.
Deploying minimal proxies with CREATE2
/// @dev Deploys a new minimal contract via create2
/// @param implementation Address of Implementation contract
/// @param salt Random number of choice
function deploy(address implementation, uint256 salt) external returns (address) {
// cast address to bytes
bytes20 implementationBytes = bytes20(implementation);
// minimal proxy address
address proxy;
assembly {
// free memory pointer
let pointer := mload(0x40)
// mstore 32 bytes at the start of free memory
mstore(pointer, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
// overwrite the trailing 0s above with implementation contract's address in bytes
mstore(add(pointer, 0x14), implementationBytes)
// store 32 bytes to memory starting at "clone" + 40 bytes
mstore(add(pointer, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
// create a new contract, send 0 Ether
proxy := create2(0, pointer, 0x37, salt)
}
return proxy;
}
Since we need to manipulate the bytecode, to update the implementation contract address, the assembly that you see above is necessary.
For the most part, the steps do not change and end-users can simply pass their implementation contract’s address as a parameter. Note that using create2
requires a salt to be passed. In this context, a salt is an arbitrary value (32 bytes) of the sender’s choice.
Last updated