Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💥 How to Compute the CREATE2 Deployment Address? #140

Closed
eyalc900 opened this issue Oct 5, 2024 · 1 comment · Fixed by #141
Closed

💥 How to Compute the CREATE2 Deployment Address? #140

eyalc900 opened this issue Oct 5, 2024 · 1 comment · Fixed by #141
Assignees
Labels
documentation 📖 Improvements or additions to documentation feature 💥 New feature or request
Milestone

Comments

@eyalc900
Copy link

eyalc900 commented Oct 5, 2024

Describe the desired feature:

the deployCreate2(salt,initCode) generates a deterministic address for the target contract.
But how can i get that address beforehand?
off-chain, I can do eth_call to get it, but on-chain (or foundry solidity script), we need a view function.

There is a view function computeCreate2Address(salt,initCodeHash) but it CANT be used:
the reason is that deployCreate2 doesn't use the salt as-is, but "guard" it. even simple salt like "0" is replaced.
There is also on view function to calculate the guarded value.

  • The documentation should be fixed, as the meaning of "salt" is completely different between those function families.
  • add a function that return the address of computeCreate2Address
  • or at minimum, add a view function that "guard" the salt.

Workaround

The following code can calculate the address in a script.
It has 2 shortcomings:

  • besides being ugly 2-method wrapper, it wastes actual deployment gas, and thus can't be used on a real network.
  • It can't calculate the address if the contract is already deployed.
   function internalDeployAddress(bytes32 salt, bytes memory initCode) public {
        address addr = createX.deployCreate2(salt, initCode);
        assembly {
            mstore(0x0, addr)
            revert(0,32)
        }
    }

    //workaround for a missing getDeployCreate2Address in ICreateX
    function getDeployAddress(bytes32 salt, bytes memory initCode) internal returns (address ret) {
        try this.internalDeployAddress(salt, initCode) {
        } catch (bytes memory reason) {
            (ret) = abi.decode(reason, (address));
        }
    }
@eyalc900 eyalc900 added the feature 💥 New feature or request label Oct 5, 2024
@pcaversaccio pcaversaccio changed the title [Feature-Request]: How to compute deploy address ? 💥 How to Compute CREATE2 Deployment Address? Oct 7, 2024
@pcaversaccio pcaversaccio added this to the 1.0.0 milestone Oct 7, 2024
@pcaversaccio
Copy link
Owner

pcaversaccio commented Oct 7, 2024

Hey, so the address can be calculated correctly if you understand how the salt value is "guarded":

createx/src/CreateX.sol

Lines 873 to 912 in 71925dd

/**
* @dev Implements different safeguarding mechanisms depending on the encoded values in the salt
* (`||` stands for byte-wise concatenation):
* => salt (32 bytes) = 0xbebebebebebebebebebebebebebebebebebebebe||ff||1212121212121212121212
* - The first 20 bytes (i.e. `bebebebebebebebebebebebebebebebebebebebe`) may be used to
* implement a permissioned deploy protection by setting them equal to `msg.sender`,
* - The 21st byte (i.e. `ff`) may be used to implement a cross-chain redeploy protection by
* setting it equal to `0x01`,
* - The last random 11 bytes (i.e. `1212121212121212121212`) allow for 2**88 bits of entropy
* for mining a salt.
* @param salt The 32-byte random value used to create the contract address.
* @return guardedSalt The guarded 32-byte random value used to create the contract address.
*/
function _guard(bytes32 salt) internal view returns (bytes32 guardedSalt) {
(SenderBytes senderBytes, RedeployProtectionFlag redeployProtectionFlag) = _parseSalt({salt: salt});
if (senderBytes == SenderBytes.MsgSender && redeployProtectionFlag == RedeployProtectionFlag.True) {
// Configures a permissioned deploy protection as well as a cross-chain redeploy protection.
guardedSalt = keccak256(abi.encode(msg.sender, block.chainid, salt));
} else if (senderBytes == SenderBytes.MsgSender && redeployProtectionFlag == RedeployProtectionFlag.False) {
// Configures solely a permissioned deploy protection.
guardedSalt = _efficientHash({a: bytes32(uint256(uint160(msg.sender))), b: salt});
} else if (senderBytes == SenderBytes.MsgSender) {
// Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
revert InvalidSalt({emitter: _SELF});
} else if (senderBytes == SenderBytes.ZeroAddress && redeployProtectionFlag == RedeployProtectionFlag.True) {
// Configures solely a cross-chain redeploy protection. In order to prevent a pseudo-randomly
// generated cross-chain redeploy protection, we enforce the zero address check for the first 20 bytes.
guardedSalt = _efficientHash({a: bytes32(block.chainid), b: salt});
} else if (
senderBytes == SenderBytes.ZeroAddress && redeployProtectionFlag == RedeployProtectionFlag.Unspecified
) {
// Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
revert InvalidSalt({emitter: _SELF});
} else {
// For the non-pseudo-random cases, the salt value `salt` is hashed to prevent the safeguard mechanisms
// from being bypassed. Otherwise, the salt value `salt` is not modified.
guardedSalt = (salt != _generateSalt()) ? keccak256(abi.encode(salt)) : salt;
}
}

Let's assume you use your own random salt value, in that case you can use the function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) with the salt value set equal to keccak256(abi.encode(salt)). You can easily calculate the deployment address for all different variations of the salt beforehand except for the pseudorandom case. All it takes is to understand how the salt value is guarded.

If you really need to incorporate the _guard logic into your contract, you can directly inherit from the CreateX contract the _guard function:

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.23;

import {CreateX} from "./CreateX.sol";

contract A is CreateX {
    address internal constant _CREATEX = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed;

    function getDeployAddress(bytes32 salt, bytes32 initCodeHash) public view returns (address computedAddress) {
        return CreateX(_CREATEX).computeCreate2Address(_guard(salt), initCodeHash);
    }
}

The code has been frozen long time ago and no changes will be applied to the CreateX source code. I will amend the documentation however in case someone else faces the same issue as you.

@pcaversaccio pcaversaccio changed the title 💥 How to Compute CREATE2 Deployment Address? 💥 How to Compute the CREATE2 Deployment Address? Oct 7, 2024
@pcaversaccio pcaversaccio added the documentation 📖 Improvements or additions to documentation label Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation 📖 Improvements or additions to documentation feature 💥 New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants