generated from PaulRBerg/foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
414 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[submodule "lib/solady"] | ||
path = lib/solady | ||
url = https://github.com/Vectorized/solady | ||
[submodule "lib/openzeppelin-contracts"] | ||
path = lib/openzeppelin-contracts | ||
url = https://github.com/OpenZeppelin/openzeppelin-contracts | ||
[submodule "lib/forge-std"] | ||
path = lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std |
Submodule openzeppelin-contracts
added at
01ef44
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ | ||
@prb/test/=node_modules/@prb/test/ | ||
forge-std/=node_modules/forge-std/ | ||
@solady/=lib/solady/ | ||
@openzeppelin-contracts/=lib/openzeppelin-contracts/ | ||
@forge-std/=lib/forge-std/src |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity >=0.8.23; | ||
|
||
import { CREATE3 } from "@solady/src/utils/CREATE3.sol"; | ||
import { Create2 } from "@openzeppelin-contracts/contracts/utils/Create2.sol"; | ||
import { SingleKeccakCreate3Proxy } from "./SingleKeccakCreate3Proxy.sol"; | ||
import { ISingleKeccakCreate3 } from "./interfaces/ISingleKeccakCreate3.sol"; | ||
|
||
import "@forge-std/console.sol"; | ||
|
||
/** | ||
* @title SingleKeccakCreate3 | ||
* @notice CREATE3 library using a single keccak256 hash operation | ||
* @dev Uses SSTORE2 concepts to get this | ||
*/ | ||
abstract contract SingleKeccakCreate3 is ISingleKeccakCreate3 { | ||
/** | ||
* @dev 1st byte - 0 = has no immutable args, 1 = has args | ||
* @dev 2nd byte - 0 = has no storage args, 1 = has args | ||
* @dev right 20 bytes - address pointer | ||
*/ | ||
bytes32 public flagsAndPointer; | ||
|
||
bytes32 public immutableArgs; | ||
bytes32 public storageArgs; | ||
|
||
uint256 internal constant _DATA_OFFSET = 1; | ||
|
||
error DeploymentFailed(); | ||
error LengthMismatch(); | ||
|
||
/** | ||
* @notice Private helper function to set up SSTORE2 pointer | ||
* @dev some logic lifted from solady SSTORE2. Uses create2 instead of create | ||
* @param data contract creation code | ||
*/ | ||
function _setupPointer(bytes memory data) private returns (bytes32 addr) { | ||
assembly { | ||
let originalDataLength := mload(data) | ||
let dataSize := add(originalDataLength, _DATA_OFFSET) | ||
mstore( | ||
// Do a out-of-gas revert if `dataSize` is more than 2 bytes. | ||
// The actual EVM limit may be smaller and may change over time. | ||
add(data, gt(dataSize, 0xffff)), | ||
// Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2. | ||
or(0xfd61000080600a3d393df300, shl(0x40, dataSize)) | ||
) | ||
|
||
dataSize := add(dataSize, 0xa) | ||
let dataStart := add(data, 0x15) | ||
|
||
// layout: | ||
// ptr: 00000000_00000000_000000FF_20BYTESADDRESS | ||
// ptr+32: salt = 0 | ||
// ptr+64: initBytecodeHash | ||
let ptr := mload(0x40) | ||
mstore(add(ptr, 0x40), keccak256(dataStart, dataSize)) | ||
mstore(ptr, or(shl(160, 0xff), address())) | ||
addr := shr(96, shl(96, keccak256(add(ptr, 0x0b), 85))) | ||
|
||
// if no code at address, deploy | ||
if iszero(extcodesize(addr)) { | ||
let deployedAddr := create2(0, dataStart, dataSize, returndatasize()) | ||
if iszero(deployedAddr) { | ||
mstore(0x00, 0x30116425) | ||
revert(0x1c, 0x04) | ||
} | ||
} | ||
|
||
// Restore original length of the variable size `data`. | ||
mstore(data, originalDataLength) | ||
} | ||
} | ||
|
||
// TODO: turn into const | ||
function _getDeployedAddress(bytes32 salt) internal view returns (address addr) { | ||
bytes memory proxy = type(SingleKeccakCreate3Proxy).creationCode; | ||
assembly { | ||
// layout : | ||
// ptr: 00000000_00000000_000000FF_20BYTESADDRESS | ||
// ptr+32: salt = 0 | ||
// ptr+64: initBytecodeHash | ||
let ptr := mload(0x40) | ||
mstore(add(ptr, 0x40), keccak256(add(proxy, 0x20), mload(proxy))) | ||
mstore(add(ptr, 0x20), salt) | ||
mstore(ptr, or(shl(160, 0xff), address())) | ||
addr := keccak256(add(ptr, 0x0b), 85) | ||
} | ||
} | ||
|
||
/** | ||
* @notice Deploy a contract using single keccak create3 | ||
* @param salt salt for create3 | ||
* @param creationCode contract creation code | ||
* @param value value to send | ||
*/ | ||
function _deploy(bytes32 salt, bytes calldata creationCode, uint256 value) internal returns (address) { | ||
flagsAndPointer = _setupPointer(creationCode); | ||
|
||
return address(new SingleKeccakCreate3Proxy{ salt: salt, value: value }()); | ||
} | ||
|
||
/** | ||
* @notice Deploy a contract using single keccak create3 | ||
* @param salt salt for create3 | ||
* @param creationCode contract creation code | ||
* @param immutableArgsVals array of bytes32 immutable arguments | ||
* @param immutableArgsLoc array of memory locations to replace with immutable arguments | ||
* @param value value to send | ||
*/ | ||
function _deploy( | ||
bytes32 salt, | ||
bytes calldata creationCode, | ||
bytes32[] calldata immutableArgsVals, | ||
uint256[] calldata immutableArgsLoc, | ||
uint256 value | ||
) | ||
internal | ||
returns (address) | ||
{ | ||
if (immutableArgsVals.length != immutableArgsLoc.length) { | ||
revert LengthMismatch(); | ||
} | ||
|
||
flagsAndPointer = _setupPointer(creationCode) | bytes32(uint256(1) << 255); // todo does this work | ||
immutableArgs = _setupPointer(abi.encode(immutableArgsVals, immutableArgsLoc)); | ||
|
||
return address(new SingleKeccakCreate3Proxy{ salt: salt, value: value }()); | ||
} | ||
|
||
/** | ||
* @notice Deploy a contract using single keccak create3 | ||
* @param salt salt for create3 | ||
* @param creationCode contract creation code | ||
* @param storageArgsVals array of bytes32 storage arguments | ||
* @param storageArgsLoc array of storage locations to save data into | ||
* @param value value to send | ||
*/ | ||
function _deploy( | ||
bytes32 salt, | ||
bytes calldata creationCode, | ||
bytes32[] calldata storageArgsVals, | ||
bytes32[] calldata storageArgsLoc, | ||
uint256 value | ||
) | ||
internal | ||
returns (address createdContract) | ||
{ | ||
if (storageArgsVals.length != storageArgsLoc.length) { | ||
revert LengthMismatch(); | ||
} | ||
|
||
flagsAndPointer = _setupPointer(creationCode) | bytes32(uint256(1) << 254); // todo does this work | ||
storageArgs = _setupPointer(abi.encode(storageArgsVals, storageArgsLoc)); | ||
|
||
return address(new SingleKeccakCreate3Proxy{ salt: salt, value: value }()); | ||
} | ||
|
||
/** | ||
* @notice Deploy a contract using single keccak create3 | ||
* @param salt salt for create3 | ||
* @param creationCode contract creation code | ||
* @param storageArgsVals array of bytes32 storage arguments | ||
* @param storageArgsLoc array of storage locations to save data into | ||
* @param immutableArgsVals array of bytes32 immutable arguments | ||
* @param immutableArgsLoc array of memory locations to replace with immutable arguments | ||
* @param value value to send | ||
*/ | ||
function _deploy( | ||
bytes32 salt, | ||
bytes calldata creationCode, | ||
bytes32[] calldata storageArgsVals, | ||
bytes32[] calldata storageArgsLoc, | ||
bytes32[] calldata immutableArgsVals, | ||
uint256[] calldata immutableArgsLoc, | ||
uint256 value | ||
) | ||
internal | ||
returns (address createdContract) | ||
{ | ||
if (immutableArgsVals.length != immutableArgsLoc.length) { | ||
revert LengthMismatch(); | ||
} | ||
|
||
if (storageArgsVals.length != storageArgsLoc.length) { | ||
revert LengthMismatch(); | ||
} | ||
|
||
flagsAndPointer = _setupPointer(creationCode) | bytes32(uint256(3) << 254); // todo does this work | ||
storageArgs = _setupPointer(abi.encode(storageArgsVals, storageArgsLoc)); | ||
immutableArgs = _setupPointer(abi.encode(immutableArgsVals, immutableArgsLoc)); | ||
|
||
return address(new SingleKeccakCreate3Proxy{ salt: salt, value: value }()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity >=0.8.23; | ||
|
||
import { SSTORE2 } from "@solady/src/utils/SSTORE2.sol"; | ||
import { ISingleKeccakCreate3 } from "./interfaces/ISingleKeccakCreate3.sol"; | ||
|
||
contract SingleKeccakCreate3Proxy { | ||
constructor() payable { | ||
bytes32 p = ISingleKeccakCreate3(msg.sender).flagsAndPointer(); | ||
bytes memory creationCode = SSTORE2.read(address(uint160(uint256(p)))); // TODO does this work | ||
|
||
p = p >> 254; | ||
// has immutable args | ||
if (uint256(p) % 2 == 1) { | ||
(bytes32[] memory immutableArgsVals, uint256[] memory immutableArgsLoc) = abi.decode( | ||
SSTORE2.read(address(uint160(uint256(ISingleKeccakCreate3(msg.sender).immutableArgs())))), | ||
(bytes32[], uint256[]) | ||
); | ||
|
||
for (uint256 i = 0; i < immutableArgsVals.length; i++) { | ||
assembly { | ||
// location of immutableArgLocation in memory array | ||
let offset := add(0x20, mul(i, 0x20)) | ||
let argLoc := add(immutableArgsLoc, offset) | ||
let valLoc := add(immutableArgsLoc, offset) | ||
// add creationCode offset to get the actual location relative to 0 memory | ||
mstore(add(creationCode, mload(argLoc)), mload(valLoc)) | ||
} | ||
} | ||
} | ||
|
||
// has storage args | ||
if (uint256(p) > 1) { | ||
(bytes32[] memory storageArgsVals, bytes32[] memory storageArgsLoc) = abi.decode( | ||
SSTORE2.read(address(uint160(uint256(ISingleKeccakCreate3(msg.sender).immutableArgs())))), | ||
(bytes32[], bytes32[]) | ||
); | ||
|
||
for (uint256 i = 0; i < storageArgsVals.length; i++) { | ||
assembly { | ||
sstore(add(storageArgsLoc, add(0x20, mul(i, 0x20))), add(storageArgsVals, add(0x20, mul(i, 0x20)))) | ||
} | ||
} | ||
} | ||
|
||
assembly { | ||
return(add(creationCode, 0x20), mload(creationCode)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity >=0.8.23; | ||
|
||
interface ISingleKeccakCreate3 { | ||
function flagsAndPointer() external view returns (bytes32); | ||
function immutableArgs() external view returns (bytes32); | ||
function storageArgs() external view returns (bytes32); | ||
} |
Oops, something went wrong.