Open Remscar opened 3 years ago
Similar to #272 but smaller in scope. The salt would be only for the proxy itself.
Can you elaborate how this would work? As far as I can tell we would need to deploy a create2 factory contract, which would also need to be on the same address on every network, so it's kind of a circular problem.
So hopefully it would do something like:
1 Deploy a Create2Factory
contract and store reference in the .oz
folder (or somewhere) for future use (only do if salt
is used)
2 Use the deployed Create2 Factory contract for any 'create2' deployments
deployProxy
with a DeployOptions
field which is salt
the Create2Factory
is used to deploy and initialize an instance of the AdminUpgradeabilityProxy
contract with the passed salt
The Create2Factory
contract could just be (or based on) the ProxyFactory
contract found in the now deprecated @openzeppelin/upgrades
package (@openzeppelin/upgrades/contracts/upgradeability/ProxyFactory.sol
)
Example of the factory:
contract Create2Factory {
function deploy(uint256 _salt, address _logic, address _admin, bytes memory _data) public returns (address) {
return _deployProxy(_salt, _logic, _admin, _data, msg.sender);
}
function _deployProxy(uint256 _salt, address _logic, address _admin, bytes memory _data, address _sender) internal returns (address) {
InitializableAdminUpgradeabilityProxy proxy = _createProxy(_salt, _sender);
emit ProxyCreated(address(proxy));
proxy.initialize(_logic, _admin, _data);
return address(proxy);
}
function _createProxy(uint256 _salt, address _sender) internal returns (InitializableAdminUpgradeabilityProxy) {
address payable addr;
bytes memory code = type(InitializableAdminUpgradeabilityProxy).creationCode;
bytes32 salt = _getSalt(_salt, _sender);
assembly {
addr := create2(0, add(code, 0x20), mload(code), salt)
if iszero(extcodesize(addr)) {
revert(0, 0)
}
}
return InitializableAdminUpgradeabilityProxy(addr);
}
function _getSalt(uint256 _salt, address _sender) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(_salt, _sender));
}
}
While it would be nice to have just one Create2Factory
contract per network (for all users) so a new one doesn't have to be deployed by an individual user, it would need to be predeployed by the OpenZeppelin team and also raises security questions. Perhaps there would exist a single instance per network that gets used, and if it's not available on the network the user is using, they deploy a new version.
Yeah the hard thing is getting the factory in the same address on every network. As far as I know this is only possible to do reliably using "Nick's method".
The ability to deploy proxies on the same address on every network sounds good, but unfortunately we don't have enough bandwidth right now to make it a priority.
Gnosis safe factories (whose job it is to make gnosis safe proxies) already do this, they have a single contract that can exist at the same address in every network. And in fact they even have a “replay” script that they encourage everyone to use to deploy the factory to networks where it doesn’t exist yet which will deploy to the same canonical address. My understanding is that the resulting address for create2
is that it is a hash of the byte code for the contract and user provided salt. So if you use create2
to also deploy the factory as well, does it really matter who deploys the factory if the same salt is provided? Could we provide a “replay” like gnosis does for their factories to let anyone deploy the factory to the canonical address for any network? Here is the gnosis replay https://github.com/gnosis/safe-contract-deployment-replay and here is some more documentation around this https://docs.gnosis.io/safe/docs/contracts_other_evm/
@habdelra, an EOA (wallet with a private key) cannot use create2
. create2
power can only be used by a smart contract. This is why you need a factory.
You first need to use a script similar to what gnosis does to create the factory at the right address ... and then (and only then) you can use create2 to instanciate "child-contracts".
If you want a generic factory, that is available on most networks, and can be used to deploy anything (simple contract or complex factories) you can use this one.
All the details necessary to deploy it on any new chain, including hardhat/ganache chains, can be found here
Last shameless plug: my devcon talk that discuss the need for such factory
If you want a generic factory, that is available on most networks, and can be used to deploy anything (simple contract or complex factories) you can use this one.
There is also Axelar's Constant Address Deployer that can be used to deploy a new smart contract using CREATE2. But how can we use it to deploy an upgradeable contract?
If you want a generic factory, that is available on most networks, and can be used to deploy anything (simple contract or complex factories) you can use this one.
I am checking how to deploy using create2 on a selfdestructed contract but I see that unless I send the exact same bytecode it deploys to a new address. It's confusing because I read that only the salt and the factory address are used to generate it. Can you please clarify? thanks
The ability to deploy proxies via create2 is the last remaining thing keeping me from using the upgrades plugin to deploy, is this feature planned?
Not planned at the moment.
When we implement this we will need to use a standard create2 factory that is deployed on multiple chains. If someone has a favorite factory please share the link here.
Have you checked Axelar as mentioned above? They specialize in cross-chain. See: https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/tree/main/contracts/deploy https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/scripts/create3Deployer.js
This could also be worth having a look into: https://github.com/ZeframLou/create3-factory
It describes the difference between CREATE2
and CREATE3
:
One could use a
CREATE2
factory that deterministically deploys contracts to an address that's unrelated to the deployer's nonce, but the address is still related to the hash of the contract's creation code. This means if you wanted to use different constructor parameters on different chains, the deployed contracts will have different addresses.A
CREATE3
factory offers the best solution: the address of the deployed contract is determined by only the deployer address and the salt. This makes it far easier to deploy contracts to multiple chains at the same addresses.
Any updates on this functionality?
It would be helpful if it were possible to define a salt when calling
deployProxy
which would indicate to use CREATE2 and deploy out a proxy at a deterministic address.Happy to elaborate more, but for instance if I wanted to deploy the same upgradable proxy on two different chains, and make it so they have the same address, it wouldn't be possible when using this library.