Skip to content

Commit

Permalink
Merge pull request #49 from pentagonxyz/feat/mechanisms/huff-clones
Browse files Browse the repository at this point in the history
feat: Huff clones
  • Loading branch information
refcell authored Sep 15, 2022
2 parents 20f79be + 9552c44 commit 3824157
Show file tree
Hide file tree
Showing 8 changed files with 558 additions and 0 deletions.
75 changes: 75 additions & 0 deletions src/mechanisms/huff-clones/ExampleClone.huff
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/// @title ExampleClone
/// @notice Clones with Immutable Args Library
/// Original implementation:
/// https://github.com/wighawag/clones-with-immutable-args/blob/master/src/ExampleClone.sol
/// @author wighawag <https://github.com/wighawag>
/// @author zefram <https://github.com/boredGenius>
/// @author hari <https://github.com/hrkrshnn>
/// @author z0r0z <https://github.com/z0r0z>
/// @author clabby <https://github.com/clabby>

#include "./HuffClone.huff"

#define function param1() view returns (address)
#define function param2() view returns (uint256)
#define function param3() view returns (uint64)
#define function param4() view returns (uint8)
#define function param5(uint256) view returns (uint256[] memory)

#define macro PARAM_1() = takes (0) returns (0) {
0x00 GET_ARG_ADDRESS() // [arg_addr]
0x00 mstore // []
0x20 0x00 return
}

#define macro PARAM_2() = takes (0) returns (0) {
0x14 GET_ARG_UINT_256() // [arg_uint]
0x00 mstore // []
0x20 0x00 return
}

#define macro PARAM_3() = takes (0) returns (0) {
0x34 GET_ARG_UINT_64() // [arg_uint]
0x00 mstore // []
0x20 0x00 return
}

#define macro PARAM_4() = takes (0) returns (0) {
0x3C GET_ARG_UINT_8() // [arg_uint]
0x00 mstore // []
0x20 0x00 return
}

#define macro PARAM_5() = takes (0) returns (0) {
0x04 calldataload // [arr_len]

// Store pointer in word before array
0x20 0x00 mstore // [arr_len]

dup1 0x00 // [0x00, arr_len, arr_len]
GET_ARG_UINT_256_ARR(0x20) // [ptr, arr_len]
swap1 // [arr_len, ptr]
0x05 shl // [arr_len * 0x20, ptr]
0x40 add // [arr_len * 0x20 + 0x40, ptr]
0x00 return // [ptr]
}

#define macro MAIN() = takes (0) returns (0) {
pc calldataload 0xE0 shr
dup1 __FUNC_SIG(param1) eq param1 jumpi
dup1 __FUNC_SIG(param2) eq param2 jumpi
dup1 __FUNC_SIG(param3) eq param3 jumpi
dup1 __FUNC_SIG(param4) eq param4 jumpi
dup1 __FUNC_SIG(param5) eq param5 jumpi

param1:
PARAM_1()
param2:
PARAM_2()
param3:
PARAM_3()
param4:
PARAM_4()
param5:
PARAM_5()
}
100 changes: 100 additions & 0 deletions src/mechanisms/huff-clones/ExampleCloneFactory.huff
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/// @title ExampleCloneFactory
/// @notice Clones with Immutable Args Library
/// Original implementation:
/// https://github.com/wighawag/clones-with-immutable-args/blob/master/src/ExampleCloneFactory.sol
/// @author wighawag <https://github.com/wighawag>
/// @author zefram <https://github.com/boredGenius>
/// @author hari <https://github.com/hrkrshnn>
/// @author z0r0z <https://github.com/z0r0z>
/// @author clabby <https://github.com/clabby>

#include "./HuffCloneLib.huff"

#define function createClone(address,uint256,uint64,uint8) nonpayable returns (address)
#define function createArrClone(uint256[] calldata) nonpayable returns (address)

#define constant IMPL_SLOT = FREE_STORAGE_POINTER()

/// @notice Creates an `ExampleClone` contract
#define macro CREATE_CLONE() = takes (0) returns (0) {
0x64 calldataload // [uint8]
0x44 calldataload // [uint64, uint8]
0x24 calldataload // [uint256, uint64, uint8]
0x04 calldataload // [address, uint256, uint64, uint8]

// data len = 61 (0x3D)
0x3D 0x40 mstore // [address, uint256, uint64, uint8]

// Store address << 0x60 @ 0x60
0x60 shl // [address << 0x60, uint256, uint64, uint8]
0x60 mstore // [uint256, uint64, uint8]

// Store uint256 @ 0x74
0x74 mstore // [uint64, uint8]

// Store uint64 << 0xC0 @ 0x94
0xC0 shl // [uint64 << 0xC0, uint8]
0x94 mstore // [uint8]

// Store uint8 << 0xF8 @ 0x9C
0xF8 shl // [uint8 << 0xF8]
0x9C mstore // []

0x40 // [data_ptr]
[IMPL_SLOT] sload // [impl_addr, data_ptr]

CLONE(err, 0x00) // [instance]
0x00 mstore // []
0x20 0x00 return

err:
0x00 0x00 revert
}

/// @notice Creates an `ExampleClone` contract that has an immutable
/// uint256 array
#define macro CREATE_ARRAY_CLONE() = takes (0) returns (0) {
0x24 calldataload // [arr_len]
0x05 shl // [arr_len * 0x20]
0x20 add // [arr_len * 0x20 + 0x20]
0x24 // [0x24, arr_len * 0x20 + 0x20]
0x40 // [0x40, 0x24, arr_len * 0x20 + 0x20]
calldatacopy // []

// Set data length in bytes
0x40 dup1 // [0x40, 0x40]
mload // [arr_len, 0x40]
0x05 shl // [arr_len * 0x20, 0x40]
0x40 mstore // [0x40 (data_ptr)]

[IMPL_SLOT] sload // [impl_addr, data_ptr]

CLONE(err, 0x00) // [instance]
0x00 mstore // []
0x20 0x00 return

err:
0x00 0x00 revert
}

#define macro MAIN() = takes (0) returns (0) {
pc calldataload 0xE0 shr
dup1 __FUNC_SIG(createClone) eq clone jumpi
dup1 __FUNC_SIG(createArrClone) eq arr_clone jumpi

clone:
CREATE_CLONE()
arr_clone:
CREATE_ARRAY_CLONE()
}

#define macro CONSTRUCTOR() = takes (0) returns (0) {
0x20 // [size] - byte size to copy
0x20 codesize sub // [offset, size] - offset in the code to copy from
0x00 // [mem, offset, size] - offset in memory to copy to
codecopy // []

0x00 mload
[IMPL_SLOT] // [impl_ptr, impl_addr]
sstore // []
}
91 changes: 91 additions & 0 deletions src/mechanisms/huff-clones/HuffClone.huff
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/// @title HuffClone
/// @notice Clones with Immutable Args Library
/// Original implementation:
/// https://github.com/wighawag/clones-with-immutable-args/blob/master/src/Clone.sol
/// @author wighawag <https://github.com/wighawag>
/// @author zefram <https://github.com/boredGenius>
/// @author hari <https://github.com/hrkrshnn>
/// @author z0r0z <https://github.com/z0r0z>
/// @author clabby <https://github.com/clabby>

/// @notice Reads an immutable arg with type address
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
#define macro GET_ARG_ADDRESS() = takes (1) returns (1) {
// Initial Stack: // [argOffset]
GET_IMMUTABLE_ARGS_OFFSET() // [offset, argOffset]
add // [offset + argOffset]
calldataload // [cd]
0x60 // [0x60, cd]
shr // [cd >> 0x60]
}

/// @notice Reads an immutable arg with type uint256
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
#define macro GET_ARG_UINT_256() = takes (1) returns (1) {
// Initial Stack: // [argOffset]
GET_IMMUTABLE_ARGS_OFFSET() // [offset, argOffset]
add // [offset + argOffset]
calldataload // [cd]
}

/// @notice Reads a uint256 array stored in the immutable args.
/// @param argOffset The offset of the arg in the packed data
/// @param arrLen Number of elements in the array
/// @return arr The beginning location of the array in memory
#define macro GET_ARG_UINT_256_ARR(mem_ptr) = takes (2) returns (1) {
// Initial Stack: [argOffset, arrLen]
GET_IMMUTABLE_ARGS_OFFSET() // [offset, argOffset, arrLen]
add // [offset + argOffset, arrLen]

dup2 <mem_ptr> // [mem_ptr, arrLen, offset + argOffset, arrLen]
mstore // [offset + argOffset, arrLen]
swap1 0x05 shl swap1 // [offset + argOffset, arrLen * 0x20]
<mem_ptr> 0x20 add // [mem_ptr + 0x20, offset + argOffset, arrLen * 0x20]
calldatacopy // []

// Return the memory pointer of the array
<mem_ptr> // [mem_ptr]
}

/// @notice Reads an immutable arg with type uint64
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
#define macro GET_ARG_UINT_64() = takes (1) returns (1) {
// Initial Stack: // [argOffset]
GET_IMMUTABLE_ARGS_OFFSET() // [offset, argOffset]
add // [offset + argOffset]
calldataload // [cd]
0xC0 // [0xC0, cd]
shr // [cd >> 0xC0]
}

/// @notice Reads an immutable arg with type uint8
/// @param argOffset The offset of the arg in the packed data
/// @return The arg value
#define macro GET_ARG_UINT_8() = takes (1) returns (1) {
// Initial Stack: // [argOffset]
GET_IMMUTABLE_ARGS_OFFSET() // [offset, argOffset]
add // [offset + argOffset]
calldataload // [cd]
0xF8 // [0xF8, cd]
shr // [cd >> 0xF8]
}

/// @return The offset of the packed immutable args in calldata
#define macro GET_IMMUTABLE_ARGS_OFFSET() = takes (0) returns (1) {
0x02 // [0x02]
calldatasize // [calldatasize, 0x02]
sub // [calldatasize - 0x02]

calldataload // [cd]
0xF0 // [0xF0, cd]
shr // [cd >> 0xF0]

0x02 // [0x02, cd >> 0xF0]
add // [0x02 + cd >> 0xF0]

calldatasize // [calldatasize, 0x02 + cd >> 0xF0]
sub // [calldatasize - (0x02 + cd >> 0xF0)]
}
112 changes: 112 additions & 0 deletions src/mechanisms/huff-clones/HuffCloneLib.huff
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/// @title HuffCloneLib
/// @notice Clones with Immutable Args Library
/// Original implementation:
/// https://github.com/wighawag/clones-with-immutable-args/blob/master/src/ClonesWithImmutableArgs.sol
/// @author wighawag <https://github.com/wighawag>
/// @author zefram <https://github.com/boredGenius>
/// @author hari <https://github.com/hrkrshnn>
/// @author z0r0z <https://github.com/z0r0z>
/// @author clabby <https://github.com/clabby>

/// @notice Creates a clone proxy of the implementation contract, with immutable args
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param err The jumpdest to jump to if an error occurs
/// @param dest_ptr The memory pointer to store the creation code of the clone at
/// @param impl_addr The address of the implementation contract to clone
/// @param data_ptr Pointer to beginning of encoded immutable args in memory
/// @return instance The address of the created clone
#define macro CLONE(err, dest_ptr) = takes(2) returns (1) {
// Input Stack: [impl_addr, data_ptr]
dup2 mload // [data_len, impl_addr, data_ptr]
dup1 0x02 add // [extra_len, data_len, impl_addr, data_ptr]
dup1 0x41 add // [creation_size, extra_len, data_len, impl_addr, data_ptr]
0x0A dup2 sub // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

__RIGHTPAD(0x61) // [0x61..., run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
<dest_ptr> mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
dup1 0xF0 shl // [run_size << 0xF0, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
<dest_ptr> 0x01 add // [dest_ptr + 0x01, run_size << 0xF0, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

__RIGHTPAD(0x3d81600a3d39f33d3d3d3d363d3d3761)
<dest_ptr> 0x03 add // [dest_ptr + 0x03, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
dup3 0xF0 shl // [extra_len << 0xF0, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
<dest_ptr> 0x13 add // [dest_ptr + 0x13, extra_len << 0xF0, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

__RIGHTPAD(0x603736393661)
<dest_ptr> 0x15 add // [dest_ptr + 0x15, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
dup3 0xF0 shl // [extra_len << 0xF0, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
<dest_ptr> 0x1B add // [dest_ptr + 0x1B, extra_len << 0xF0, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

__RIGHTPAD(0x013d73)
<dest_ptr> 0x1D add // [dest_ptr + 0x1D, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
dup5 0x60 shl // [impl_addr << 0x60, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
<dest_ptr> 0x20 add // [dest_ptr + 0x20, impl_addr << 0x60, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

__RIGHTPAD(0x5af43d3d93803e603557fd5bf3)
<dest_ptr> 0x34 add // [dest_ptr + 0x34, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

////////////////////////////////
// APPENDED DATA //
////////////////////////////////

// Subtract 0x02 from extra_len
0x02 dup4 sub // [extra_len - 0x02, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
swap3 pop // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

// copy_ptr = data_ptr + 0x41
<dest_ptr> 0x41 add // [copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

// Increase data_ptr by 0x20
swap6 0x20 add // [data_ptr + 0x20, run_size, creation_size, extra_len, data_len, impl_addr, copy_ptr]
swap6 // [copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

// counter = extra_len
dup4 // [counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
memcopy_loop:
dup8 mload // [data, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
dup3 mstore // [counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

swap1 0x20 add // [copy_ptr + 0x20, counter, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
swap1 // [counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
swap7 0x20 add // [data_ptr + 0x20, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, counter]
swap7 // [counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

0x20 swap1 sub // [counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

dup1 0x20 gt // [0x20 > counter, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
iszero memcopy_loop jumpi
post_loop:
dup1 // [counter, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
0x01 // [0x01, counter, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
swap1 // [counter, 0x01, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
0x20 sub // [0x20 - counter, 0x01, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
0x100 exp // [0x100 ** (0x20 - counter), 0x01, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
sub not // [mask, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

dup9 mload and // [data & mask, counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
dup3 mstore // [counter, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

add // [copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
dup4 0xF0 shl // [extra_len << 0xF0, copy_ptr, run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]
swap1 mstore // [run_size, creation_size, extra_len, data_len, impl_addr, data_ptr]

pop <dest_ptr> // [dest_ptr, creation_size, extra_len, data_len, impl_addr, data_ptr]
0x00 create // [instance, extra_len, data_len, impl_addr, run_size]

// Revert if deployment failed
dup1 iszero err jumpi

// Clean stack:
swap4 // [run_size, extra_len, data_len, impl_addr, instance]
pop pop pop pop // [instance]

// Return stack: [instance]
}
Loading

0 comments on commit 3824157

Please sign in to comment.