Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
howydev committed Jan 28, 2024
1 parent f5f30a7 commit e61b43a
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 122 deletions.
9 changes: 9 additions & 0 deletions .gitmodules
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
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at ae570f
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at 01ef44
1 change: 1 addition & 0 deletions lib/solady
Submodule solady added at 74e571
6 changes: 3 additions & 3 deletions remappings.txt
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
41 changes: 0 additions & 41 deletions script/Base.s.sol

This file was deleted.

13 changes: 0 additions & 13 deletions script/Deploy.s.sol

This file was deleted.

8 changes: 0 additions & 8 deletions src/Foo.sol

This file was deleted.

195 changes: 195 additions & 0 deletions src/SingleKeccakCreate3.sol
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 }());
}
}
50 changes: 50 additions & 0 deletions src/SingleKeccakCreate3Proxy.sol
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))
}
}
}
8 changes: 8 additions & 0 deletions src/interfaces/ISingleKeccakCreate3.sol
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);
}
Loading

0 comments on commit e61b43a

Please sign in to comment.