I'm currently working on a dex project that allows users to create contracts for the trading pairs and wanted to reduce the cost of deployment. Came up with the solution below, a very small proxy (51 bytes, 64 with constructor).
The function buildSlimProxyBytecode build the proxy contract bytecode, the function buildSlimProxyConstructor concatenates the bytecode to a constructor and deploySlimProxy deploys it, returning the address of the new contract. This method requires the contract to be initialized with an initialize method instead of a constructor.
Any comments on this method would be appreciated, security related or not.
pragma solidity ^0.8.0;
contract SlimProxyDeployer {
function buildSlimProxyBytecode(address impl) internal pure returns (bytes memory) {
// calldatacopy(0, 0, calldatasize())
// 3660008037
// 0x36 CALLDATASIZE
// 0x60 PUSH1 0x00
// 0x80 DUP1
// 0x37 CALLDATACOPY
// let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// 600080368173xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx5af4
// 0x60 PUSH1 0x00
// 0x80 DUP1
// 0x36 CALLDATASIZE
// 0x81 DUP2
// 0x73 PUSH20 <concat-address-here>
// 0x5A GAS
// 0xF4 DELEGATECALL
// returndatacopy(0, 0, returndatasize())
// 3d6000803e
// 0x3D RETURNDATASIZE
// 0x60 PUSH1 0x00
// 0x80 DUP1
// 0x3E RETURNDATACOPY
// switch result
// case 0 { revert(0, returndatasize()) }
// case 1 { return(0, returndatasize()) }
// 60003d91600114603157fd5bf3
// 0x60 PUSH1 0x00
// 0x3D RETURNDATASIZE
// 0x91 SWAP2
// 0x60 PUSH1 0x01
// 0x14 EQ
// 0x60 PUSH1 0x31
// 0x57 JUMPI
// 0xFD REVERT
// 0x5B JUMPEST
// 0xF3 RETURN
return bytes.concat(
bytes(hex"3660008037600080368173"),
bytes20(impl),
bytes(hex"5af43d6000803e60003d91600114603157fd5bf3")
);
}
function buildSlimProxyConstructor(address impl) internal pure returns (bytes memory) {
// codecopy(0, ofs, codesize() - ofs)
// return(0, codesize() - ofs)
// 0x60 PUSH1 0x0D
// 0x80 DUP1
// 0x38 CODESIZE
// 0x03 SUB
// 0x80 DUP1
// 0x91 SWAP2
// 0x60 PUSH1 0x00
// 0x39 CODECOPY
// 0x60 PUSH1 0x00
// 0xF3 RETURN
// <concat-contract-code-here>
// 0x600D80380380916000396000F3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
return bytes.concat(
hex"600D80380380916000396000F3",
buildSlimProxyBytecode(impl)
);
}
function deploySlimProxy(address impl) internal returns (address deploymentAddr) {
bytes memory code = buildSlimProxyConstructor(impl);
assembly { deploymentAddr := create(callvalue(), add(code, 0x20), mload(code)) }
require(deploymentAddr != address(0), "deployment failed");
}
}
interface IDeployment {
function initialize() external;
}
contract ExampleFactory is SlimProxyDeployer {
address[] public deployments;
function deploy(address _impl) external {
address a = deploySlimProxy(_impl);
deployments.push(a);
IDeployment(a).initialize();
}
}