I would like to know the precomputed address my UUPS proxy will be deployed to.
Code to reproduce
I came up with this code to test it but it doesn't let me call the computeAddress function (low delegatecall failed...).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract OrganizerTemplate is Initializable {
function initialize(bytes memory _foo,
bytes32 _salt) public payable initializer {
}
}
contract Factory is Initializable, OrganizerTemplate {
address public template;
function initialize(address organizerTemplate) public payable initializer {
template = organizerTemplate;
}
function getBytes(string memory value) public view returns(bytes memory){
return bytes(value);
}
function deployReal(address _owner,
uint256 _foo,
bytes32 _salt
) public {
address organizerClone = ClonesUpgradeable.clone(template);
ERC1967Proxy organizerProxy = new ERC1967Proxy{salt: _salt}(
organizerClone,
abi.encodeWithSelector(
OrganizerTemplate(address(0)).initialize.selector,
_owner,
_foo
)
);
}
// Returns the address of the newly deployed contract
function computeAddress(
address _owner,
bytes memory _foo, // USED TO BE UINT
bytes32 _salt
) public payable returns (address) {
// This syntax is a newer way to invoke create2 without assembly, you just need to pass salt
// https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2
return address(new ERC1967Proxy{salt: _salt}(_owner, _foo));
}
}
Environment
Hardhat
Im now trying with the following code I adjusted from this solidity docs example (0.8.17):
function createDSalted(bytes32 salt, uint arg) public payable returns (address, address) {
// This complicated expression just tells you how the address
// can be pre-computed. It is just there for illustration.
// You actually only need ``new D{salt: salt}(arg)``.
address organizerClone = ClonesUpgradeable.cloneDeterministic(template, salt);
address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(abi.encodePacked(
type(ERC1967Proxy).creationCode,
abi.encode(arg)
))
)))));
ERC1967Proxy proxy = new ERC1967Proxy{salt: salt, value: msg.value}(
organizerClone,
abi.encodeWithSelector(
OrganizerTemplate(address(0)).initialize.selector,
arg
)
);
return (address(proxy),predictedAddress); // THESE TWO SHOULD BE THE SAME
}
However, Im getting two different addresses . Im trying to use CloneDeterministic from ClonesUpgradeable.sol but still the addresses differ...
Here is the solution. I was not putting the right args in the predictedAddress field:
function createDSalted(bytes32 salt, uint arg) public payable returns (address, address) {
// This complicated expression just tells you how the address
// can be pre-computed. It is just there for illustration.
// You actually only need ``new D{salt: salt}(arg)``.
address organizerClone = ClonesUpgradeable.cloneDeterministic(template, salt);
address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(abi.encodePacked(
type(ERC1967Proxy).creationCode,
abi.encode(organizerClone,
abi.encodeWithSelector(
OrganizerTemplate(address(0)).initialize.selector,
arg
))
))
)))));
ERC1967Proxy proxy = new ERC1967Proxy{
salt: salt,
value: msg.value
}(
organizerClone,
abi.encodeWithSelector(
OrganizerTemplate(address(0)).initialize.selector,
arg
)
);
return (address(proxy),predictedAddress);
// require(address(proxy) == predictedAddress);
}
}
However, if the only thing you want is the pre-computed address for off-chain operations or namespace handling you can simply use Create2 contract using the hash I defined in my example:
address preComputedwithCreate2 = Create2Upgradeable.computeAddress(salt, keccak256(abi.encodePacked(
type(ERC1967Proxy).creationCode,
abi.encode(organizerTemplate,
abi.encodeWithSelector(
Organizer(address(0)).initialize.selector,
organizationName,
LomEventV3Template,
MarketPlaceV1,
MinterTemplate
))
)));
2 Likes
My repository covers deploying a UUPS upgradeable contract via CREATE3: https://github.com/SKYBITDev3/SKYBIT-Keyless-Deployment
The deployment script in particular that you'd want is: https://github.com/SKYBITDev3/SKYBIT-Keyless-Deployment/blob/main/scripts/deployViaCREATE3-TESTERC20UG.js
CREATE3 is preferred over CREATE2 as the creation code no longer affects the address, making it easier e.g. if you want to deploy a contract to the same address on multiple (including future) blockchains.