Skip to content

Commit ec55ee9

Browse files
qiweiiimds1
andauthored
feat: add cheats for create and create2 address computation (#479)
* update vm * namespace internal console2_log * style: forge fmt * fix: avoid breaking changes to selectors and fix VmSafe test * fix: make bound pure * chore: clarify comment --------- Co-authored-by: Matt Solomon <[email protected]>
1 parent c22437a commit ec55ee9

File tree

4 files changed

+60
-36
lines changed

4 files changed

+60
-36
lines changed

src/StdCheats.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ abstract contract StdCheats is StdCheatsSafe {
697697
}
698698

699699
function changePrank(address msgSender) internal virtual {
700-
console2_log("changePrank is deprecated. Please use vm.startPrank instead.");
700+
console2_log_StdCheats("changePrank is deprecated. Please use vm.startPrank instead.");
701701
vm.stopPrank();
702702
vm.startPrank(msgSender);
703703
}
@@ -810,7 +810,7 @@ abstract contract StdCheats is StdCheatsSafe {
810810
}
811811

812812
// Used to prevent the compilation of console, which shortens the compilation time when console is not used elsewhere.
813-
function console2_log(string memory p0) private view {
813+
function console2_log_StdCheats(string memory p0) private view {
814814
(bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string)", p0));
815815
status;
816816
}

src/StdUtils.sol

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ abstract contract StdUtils {
5555
}
5656
}
5757

58-
function bound(uint256 x, uint256 min, uint256 max) internal view virtual returns (uint256 result) {
58+
function bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) {
5959
result = _bound(x, min, max);
60-
console2_log("Bound Result", result);
60+
console2_log_StdUtils("Bound Result", result);
6161
}
6262

6363
function _bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) {
@@ -80,9 +80,9 @@ abstract contract StdUtils {
8080
result = y < INT256_MIN_ABS ? int256(~(INT256_MIN_ABS - y) + 1) : int256(y - INT256_MIN_ABS);
8181
}
8282

83-
function bound(int256 x, int256 min, int256 max) internal view virtual returns (int256 result) {
83+
function bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) {
8484
result = _bound(x, min, max);
85-
console2_log("Bound result", vm.toString(result));
85+
console2_log_StdUtils("Bound result", vm.toString(result));
8686
}
8787

8888
function boundPrivateKey(uint256 privateKey) internal pure virtual returns (uint256 result) {
@@ -97,26 +97,8 @@ abstract contract StdUtils {
9797
/// @dev Compute the address a contract will be deployed at for a given deployer address and nonce
9898
/// @notice adapted from Solmate implementation (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibRLP.sol)
9999
function computeCreateAddress(address deployer, uint256 nonce) internal pure virtual returns (address) {
100-
// forgefmt: disable-start
101-
// 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.
102-
// A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it.
103-
if (nonce == 0x00) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80))));
104-
if (nonce <= 0x7f) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce))));
105-
106-
// Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length.
107-
if (nonce <= 2**8 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce))));
108-
if (nonce <= 2**16 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce))));
109-
if (nonce <= 2**24 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce))));
110-
// forgefmt: disable-end
111-
112-
// More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp
113-
// 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce)
114-
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
115-
// 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex)
116-
// We assume nobody can have a nonce large enough to require more than 32 bytes.
117-
return addressFromLast20Bytes(
118-
keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce)))
119-
);
100+
console2_log_StdUtils("computeCreateAddress is deprecated. Please use vm.computeCreateAddress instead.");
101+
return vm.computeCreateAddress(deployer, nonce);
120102
}
121103

122104
function computeCreate2Address(bytes32 salt, bytes32 initcodeHash, address deployer)
@@ -125,12 +107,14 @@ abstract contract StdUtils {
125107
virtual
126108
returns (address)
127109
{
128-
return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initcodeHash)));
110+
console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead.");
111+
return vm.computeCreate2Address(salt, initcodeHash, deployer);
129112
}
130113

131114
/// @dev returns the address of a contract created with CREATE2 using the default CREATE2 deployer
132115
function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) internal pure returns (address) {
133-
return computeCreate2Address(salt, initCodeHash, CREATE2_FACTORY);
116+
console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead.");
117+
return vm.computeCreate2Address(salt, initCodeHash);
134118
}
135119

136120
/// @dev returns the hash of the init code (creation code + no args) used in CREATE2 with no constructor arguments
@@ -184,15 +168,42 @@ abstract contract StdUtils {
184168
return address(uint160(uint256(bytesValue)));
185169
}
186170

187-
// Used to prevent the compilation of console, which shortens the compilation time when console is not used elsewhere.
171+
// This section is used to prevent the compilation of console, which shortens the compilation time when console is
172+
// not used elsewhere. We also trick the compiler into letting us make the console log methods as `pure` to avoid
173+
// any breaking changes to function signatures.
174+
function _castLogPayloadViewToPure(function(bytes memory) internal view fnIn)
175+
internal
176+
pure
177+
returns (function(bytes memory) internal pure fnOut)
178+
{
179+
assembly {
180+
fnOut := fnIn
181+
}
182+
}
183+
184+
function _sendLogPayload(bytes memory payload) internal pure {
185+
_castLogPayloadViewToPure(_sendLogPayloadView)(payload);
186+
}
187+
188+
function _sendLogPayloadView(bytes memory payload) private view {
189+
uint256 payloadLength = payload.length;
190+
address consoleAddress = CONSOLE2_ADDRESS;
191+
/// @solidity memory-safe-assembly
192+
assembly {
193+
let payloadStart := add(payload, 32)
194+
let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
195+
}
196+
}
197+
198+
function console2_log_StdUtils(string memory p0) private pure {
199+
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
200+
}
188201

189-
function console2_log(string memory p0, uint256 p1) private view {
190-
(bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,uint256)", p0, p1));
191-
status;
202+
function console2_log_StdUtils(string memory p0, uint256 p1) private pure {
203+
_sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
192204
}
193205

194-
function console2_log(string memory p0, string memory p1) private view {
195-
(bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,string)", p0, p1));
196-
status;
206+
function console2_log_StdUtils(string memory p0, string memory p1) private pure {
207+
_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
197208
}
198209
}

src/Vm.sol

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,19 @@ interface VmSafe {
459459
// Gets the deployed bytecode from an artifact file. Takes in the relative path to the json file
460460
function getDeployedCode(string calldata artifactPath) external view returns (bytes memory runtimeBytecode);
461461

462+
// Compute the address a contract will be deployed at for a given deployer address and nonce.
463+
function computeCreateAddress(address deployer, uint256 nonce) external pure returns (address);
464+
465+
// Compute the address of a contract created with CREATE2 using the given CREATE2 deployer.
466+
function computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer)
467+
external
468+
pure
469+
returns (address);
470+
471+
// Compute the address of a contract created with CREATE2 using foundry's default CREATE2
472+
// deployer: 0x4e59b44847b379578588920cA78FbF26c0B4956C, https://github.com/Arachnid/deterministic-deployment-proxy
473+
function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) external pure returns (address);
474+
462475
// ======== JSON Parsing and Manipulation ========
463476

464477
// -------- Reading --------

test/Vm.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ contract VmTest is Test {
99
// inadvertently moved between Vm and VmSafe. This test must be updated each time a function is
1010
// added to or removed from Vm or VmSafe.
1111
function test_interfaceId() public {
12-
assertEq(type(VmSafe).interfaceId, bytes4(0x7006b885), "VmSafe");
12+
assertEq(type(VmSafe).interfaceId, bytes4(0x5e4a68ae), "VmSafe");
1313
assertEq(type(Vm).interfaceId, bytes4(0x316cedc3), "Vm");
1414
}
1515
}

0 commit comments

Comments
 (0)