Last active
October 9, 2021 08:47
-
-
Save GNSPS/ba7b88565c947cfd781d44cf469c2ddb to your computer and use it in GitHub Desktop.
Improved `delegatecall` proxy contract factory (Solidity) [v0.0.5]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** | |
* Shoutouts: | |
* | |
* Bytecode origin https://www.reddit.com/r/ethereum/comments/6ic49q/any_assembly_programmers_willing_to_write_a/dj5ceuw/ | |
* Modified version of Vitalik's https://www.reddit.com/r/ethereum/comments/6c1jui/delegatecall_forwarders_how_to_save_5098_on/ | |
* Credits to Jorge Izquierdo (@izqui) for coming up with this design here: https://gist.github.com/izqui/7f904443e6d19c1ab52ec7f5ad46b3a8 | |
* Credits to Stefan George (@Georgi87) for inspiration for many of the improvements from Gnosis Safe: https://github.com/gnosis/gnosis-safe-contracts | |
* | |
* This version has many improvements over the original @izqui's library like using REVERT instead of THROWing on failed calls. | |
* It also implements the awesome design pattern for initializing code as seen in Gnosis Safe Factory: https://github.com/gnosis/gnosis-safe-contracts/blob/master/contracts/ProxyFactory.sol | |
* but unlike this last one it doesn't require that you waste storage on both the proxy and the proxied contracts (v. https://github.com/gnosis/gnosis-safe-contracts/blob/master/contracts/Proxy.sol#L8 & https://github.com/gnosis/gnosis-safe-contracts/blob/master/contracts/GnosisSafe.sol#L14) | |
* | |
* | |
* v0.0.2 | |
* The proxy is now only 60 bytes long in total. Constructor included. | |
* No functionalities were added. The change was just to make the proxy leaner. | |
* | |
* v0.0.3 | |
* Thanks @dacarley for noticing the incorrect check for the subsequent call to the proxy. ๐ | |
* Note: I'm creating a new version of this that doesn't need that one call. | |
* Will add tests and put this in its own repository soonโข. | |
* | |
* v0.0.4 | |
* All the merit in this fix + update of the factory is @dacarley 's. ๐ | |
* Thank you! ๐ | |
* | |
* v0.0.5 | |
* Huge design problem was overwriting the last 9 bytes of you extra "_data" array with zeros! ๐ฑ | |
* If you had this deployed please redeploy the updated version. | |
* | |
* Potential updates can be found at https://gist.github.com/GNSPS/ba7b88565c947cfd781d44cf469c2ddb | |
* | |
***/ | |
pragma solidity 0.4.19; | |
/* solhint-disable no-inline-assembly, indent, state-visibility, avoid-low-level-calls */ | |
contract ProxyFactory { | |
event ProxyDeployed(address proxyAddress, address targetAddress); | |
event ProxiesDeployed(address[] proxyAddresses, address targetAddress); | |
function createManyProxies(uint256 _count, address _target, bytes _data) | |
external | |
{ | |
address[] memory proxyAddresses = new address[](_count); | |
for (uint256 i = 0; i < _count; ++i) { | |
proxyAddresses[i] = createProxyImpl(_target, _data); | |
} | |
ProxiesDeployed(proxyAddresses, _target); | |
} | |
function createProxy(address _target, bytes _data) | |
external | |
returns (address proxyContract) | |
{ | |
proxyContract = createProxyImpl(_target, _data); | |
ProxyDeployed(proxyContract, _target); | |
} | |
function createProxyImpl(address _target, bytes _data) | |
internal | |
returns (address proxyContract) | |
{ | |
assembly { | |
let contractCode := mload(0x40) // Find empty storage location using "free memory pointer" | |
mstore(add(contractCode, 0x0b), _target) // Add target address, with a 11 bytes [i.e. 23 - (32 - 20)] offset to later accomodate first part of the bytecode | |
mstore(sub(contractCode, 0x09), 0x000000000000000000603160008181600b9039f3600080808080368092803773) // First part of the bytecode, shifted left by 9 bytes, overwrites left padding of target address | |
mstore(add(contractCode, 0x2b), 0x5af43d828181803e808314602f57f35bfd000000000000000000000000000000) // Final part of bytecode, offset by 43 bytes | |
proxyContract := create(0, contractCode, 60) // total length 60 bytes | |
if iszero(extcodesize(proxyContract)) { | |
revert(0, 0) | |
} | |
// check if the _data.length > 0 and if it is forward it to the newly created contract | |
let dataLength := mload(_data) | |
if iszero(iszero(dataLength)) { | |
if iszero(call(gas, proxyContract, 0, add(_data, 0x20), dataLength, 0, 0)) { | |
revert(0, 0) | |
} | |
} | |
} | |
} | |
} | |
/*** | |
* | |
* PROXY contract (bytecode) | |
603160008181600b9039f3600080808080368092803773f00df00df00df00df00df00df00df00df00df00d5af43d828181803e808314602f57f35bfd | |
* | |
* PROXY contract (opcodes) | |
0 PUSH1 0x31 | |
2 PUSH1 0x00 | |
4 DUP2 | |
5 DUP2 | |
6 PUSH1 0x0b | |
8 SWAP1 | |
9 CODECOPY | |
10 RETURN | |
11 PUSH1 0x00 | |
13 DUP1 | |
14 DUP1 | |
15 DUP1 | |
16 DUP1 | |
17 CALLDATASIZE | |
18 DUP1 | |
19 SWAP3 | |
20 DUP1 | |
21 CALLDATACOPY | |
22 PUSH20 0xf00df00df00df00df00df00df00df00df00df00d | |
43 GAS | |
44 DELEGATECALL | |
45 RETURNDATASIZE | |
46 DUP3 | |
47 DUP2 | |
48 DUP2 | |
49 DUP1 | |
50 RETURNDATACOPY | |
51 DUP1 | |
52 DUP4 | |
53 EQ | |
54 PUSH1 0x2f | |
56 JUMPI | |
57 RETURN | |
58 JUMPDEST | |
59 REVERT | |
* | |
***/ |
Excuse me for a stupid question, but is this safe or unsafe to inherit from this contract and call its functions?
Yeah, that should be perfectly OK! ๐ However, I'd probably deploy this somewhere on mainnet and just call it with an external call from another contract.
That way you prevent bloating the other contract and save some deployment costs. ๐ ๐
And I add a ProxyRouter
contract. https://gist.github.com/VieYang/44143dd927c6bd6c09a21672fdc6ebc9
Is there something error? thank you.
about add(_data, 0x20)
, why add 0x20 for _data
?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It calls whatever function you want! ๐ Just need to specify the correct function ID as the first 4 bytes of the calldata you pass along! ๐