forked from wighawag/clones-with-immutable-args
-
Notifications
You must be signed in to change notification settings - Fork 2
/
ClonesWithImmutableArgs.sol
159 lines (129 loc) · 8.36 KB
/
ClonesWithImmutableArgs.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// SPDX-License-Identifier: BSD
pragma solidity ^0.8.4;
/// @title ClonesWithImmutableArgs
/// @author wighawag, zefram.eth, Saw-mon & Natalie
/// @notice Enables creating clone contracts with immutable args
library ClonesWithImmutableArgs {
uint256 private constant FREE_MEMORY_POINTER_SLOT = 0x40;
uint256 private constant BOOTSTRAP_LENGTH = 0x3f;
uint256 private constant ONE_WORD = 0x20;
uint256 private constant MAX_UINT256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
bytes32 private constant CREATE_FAIL_ERROR = 0xebfef18800000000000000000000000000000000000000000000000000000000;
/// @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 implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @return instance The address of the created clone
function clone(address implementation, bytes memory data)
internal
returns (address payable instance)
{
// unrealistic for memory ptr or data length to exceed 256 bits
// solhint-disable-next-line no-inline-assembly
assembly {
let extraLength := add(mload(data), 2) // +2 bytes for telling how much data there is appended to the call
let creationSize := add(extraLength, BOOTSTRAP_LENGTH)
let runSize := sub(creationSize, 0x0a)
// free memory pointer
let ptr := mload(FREE_MEMORY_POINTER_SLOT)
// -------------------------------------------------------------------------------------------------------------
// CREATION (10 bytes)
// -------------------------------------------------------------------------------------------------------------
// 61 runtime | PUSH2 runtime (r) | r | –
// 3d | RETURNDATASIZE | 0 r | –
// 81 | DUP2 | r 0 r | –
// 60 offset | PUSH1 offset (o) | o r 0 r | –
// 3d | RETURNDATASIZE | 0 o r 0 r | –
// 39 | CODECOPY | 0 r | [0 - runSize): runtime code
// f3 | RETURN | | [0 - runSize): runtime code
// -------------------------------------------------------------------------------------------------------------
// RUNTIME (53 bytes + extraLength)
// -------------------------------------------------------------------------------------------------------------
// --- copy calldata to memmory ---
// 36 | CALLDATASIZE | cds | –
// 3d | RETURNDATASIZE | 0 cds | –
// 3d | RETURNDATASIZE | 0 0 cds | –
// 37 | CALLDATACOPY | | [0 - cds): calldata
// --- keep some values in stack ---
// 3d | RETURNDATASIZE | 0 | [0 - cds): calldata
// 3d | RETURNDATASIZE | 0 0 | [0 - cds): calldata
// 3d | RETURNDATASIZE | 0 0 0 | [0 - cds): calldata
// 3d | RETURNDATASIZE | 0 0 0 0 | [0 - cds): calldata
// 61 extra | PUSH2 extra (e) | e 0 0 0 0 | [0 - cds): calldata
// --- copy extra data to memory ---
// 80 | DUP1 | e e 0 0 0 0 | [0 - cds): calldata
// 60 0x35 | PUSH1 0x35 | 0x35 e e 0 0 0 0 | [0 - cds): calldata
// 36 | CALLDATASIZE | cds 0x35 e e 0 0 0 0 | [0 - cds): calldata
// 39 | CODECOPY | e 0 0 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// --- delegate call to the implementation contract ---
// 36 | CALLDATASIZE | cds e 0 0 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// 01 | ADD | cds+e 0 0 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// 3d | RETURNDATASIZE | 0 cds+e 0 0 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// 73 addr | PUSH20 addr | addr 0 cds+e 0 0 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// 5a | GAS | gas addr 0 cds+e 0 0 0 0| [0 - cds): calldata, [cds - cds + e): extraData
// f4 | DELEGATECALL | success 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// --- copy return data to memory ---
// 3d | RETURNDATASIZE | rds success 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// 3d | RETURNDATASIZE | rds rds success 0 0 | [0 - cds): calldata, [cds - cds + e): extraData
// 93 | SWAP4 | 0 rds success 0 rds | [0 - cds): calldata, [cds - cds + e): extraData
// 80 | DUP1 | 0 0 rds success 0 rds | [0 - cds): calldata, [cds - cds + e): extraData
// 3e | RETURNDATACOPY | success 0 rds | [0 - rds): returndata, ... the rest might be dirty
// 60 0x33 | PUSH1 0x33 | 0x33 success 0 rds | [0 - rds): returndata, ... the rest might be dirty
// 57 | JUMPI | 0 rds | [0 - rds): returndata, ... the rest might be dirty
// --- revert ---
// fd | REVERT | | [0 - rds): returndata, ... the rest might be dirty
// --- return ---
// 5b | JUMPDEST | 0 rds | [0 - rds): returndata, ... the rest might be dirty
// f3 | RETURN | | [0 - rds): returndata, ... the rest might be dirty
mstore(
ptr,
or(
hex"610000_3d_81_600a_3d_39_f3_36_3d_3d_37_3d_3d_3d_3d_610000_80_6035_36_39_36_01_3d_73",
or(
shl(0xe8, runSize),
shl(0x58, extraLength)
)
)
)
mstore(
add(ptr, 0x1e),
shl(0x60, implementation)
)
mstore(
add(ptr, 0x32),
hex"5a_f4_3d_3d_93_80_3e_6033_57_fd_5b_f3"
)
// -------------------------------------------------------------------------------------------------------------
// APPENDED DATA (Accessible from extcodecopy)
// (but also send as appended data to the delegatecall)
// -------------------------------------------------------------------------------------------------------------
let counter := mload(data)
let copyPtr := add(ptr, BOOTSTRAP_LENGTH)
let dataPtr := add(data, ONE_WORD)
for {} true {} {
if lt(counter, ONE_WORD) {
break
}
mstore(copyPtr, mload(dataPtr))
copyPtr := add(copyPtr, ONE_WORD)
dataPtr := add(dataPtr, ONE_WORD)
counter := sub(counter, ONE_WORD)
}
let mask := shl(
shl(3, sub(ONE_WORD, counter)),
MAX_UINT256
)
mstore(copyPtr, and(mload(dataPtr), mask))
copyPtr := add(copyPtr, counter)
mstore(copyPtr, shl(0xf0, extraLength))
instance := create(0, ptr, creationSize)
if iszero(instance) {
// revert CreateFail()
mstore(0, CREATE_FAIL_ERROR)
revert(0, ONE_WORD)
}
// Update free memory pointer
mstore(FREE_MEMORY_POINTER_SLOT, add(ptr, creationSize))
}
}
}