diff --git a/src/Script.sol b/src/Script.sol index d7567618..e8d7b817 100644 --- a/src/Script.sol +++ b/src/Script.sol @@ -11,4 +11,29 @@ abstract contract Script { address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); Vm public constant vm = Vm(VM_ADDRESS); + + /// @dev Compute the address a contract will be deployed at for a given deployer address and nonce + /// @notice adapated from Solmate implementation (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibRLP.sol) + function computeCreateAddress(address deployer, uint256 nonce) internal pure returns (address) { + // The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0. + // A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it. + if (nonce == 0x00) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80)))); + if (nonce <= 0x7f) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce)))); + + // Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length. + if (nonce <= type(uint8).max) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce)))); + if (nonce <= type(uint16).max) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce)))); + if (nonce <= type(uint24).max) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce)))); + + // More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp + // 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce) + // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) + // 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex) + // We assume nobody can have a nonce large enough to require more than 32 bytes. + return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce)))); + } + + function addressFromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { + return address(uint160(uint256(bytesValue))); + } } diff --git a/src/test/Script.t.sol b/src/test/Script.t.sol new file mode 100644 index 00000000..166ef3e5 --- /dev/null +++ b/src/test/Script.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; + +contract ScriptTest is Test +{ + function testGenerateCorrectAddress() external { + address creation = computeCreateAddress(0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9, 14); + assertEq(creation, 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45); + } +} \ No newline at end of file