Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to pass non-zero msg.value directly to proxy constructor #21

Merged
merged 2 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions src/ClonesWithImmutableArgs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,24 @@ library ClonesWithImmutableArgs {
function clone(address implementation, bytes memory data)
internal
returns (address payable instance)
{
return clone(implementation, data, 0);
}

/// @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
/// @param value The amount of wei to transfer to the created clone
/// @return instance The address of the created clone
function clone(address implementation, bytes memory data, uint256 value)
internal
returns (address payable instance)
{
bytes memory creationcode = getCreationBytecode(implementation, data);
// solhint-disable-next-line no-inline-assembly
assembly {
instance := create(0, add(creationcode, 0x20), mload(creationcode))
instance := create(value, add(creationcode, 0x20), mload(creationcode))
}
if (instance == address(0)) {
revert CreateFail();
Expand All @@ -52,11 +65,25 @@ library ClonesWithImmutableArgs {
function clone2(address implementation, bytes memory data)
internal
returns (address payable instance)
{
return clone2(implementation, data, 0);
}

/// @notice Creates a clone proxy of the implementation contract, with immutable args,
/// using CREATE2
/// @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
/// @param value The amount of wei to transfer to the created clone
/// @return instance The address of the created clone
function clone2(address implementation, bytes memory data, uint256 value)
internal
returns (address payable instance)
{
bytes memory creationcode = getCreationBytecode(implementation, data);
// solhint-disable-next-line no-inline-assembly
assembly {
instance := create2(0, add(creationcode, 0x20), mload(creationcode), 0)
instance := create2(value, add(creationcode, 0x20), mload(creationcode), 0)
}
if (instance == address(0)) {
revert CreateFail();
Expand Down Expand Up @@ -219,11 +246,29 @@ library ClonesWithImmutableArgs {
/// @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
/// @param salt The salt used by the CREATE3 deployment
/// @return deployed The address of the created clone
function clone3(
address implementation,
bytes memory data,
bytes32 salt
) internal returns (address deployed) {
return clone3(implementation, data, salt, 0);
}

/// @notice Creates a clone proxy of the implementation contract, with immutable args. Uses CREATE3
/// to implement deterministic deployment.
/// @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
/// @param salt The salt used by the CREATE3 deployment
/// @param value The amount of wei to transfer to the created clone
/// @return deployed The address of the created clone
function clone3(
address implementation,
bytes memory data,
bytes32 salt,
uint256 value
) internal returns (address deployed) {
// unrealistic for memory ptr or data length to exceed 256 bits
unchecked {
Expand Down Expand Up @@ -386,7 +431,7 @@ library ClonesWithImmutableArgs {
call(
gas(), // Gas remaining.
proxy, // Proxy's address.
0, // Ether value.
value, // Ether value.
ptr, // Pointer to the creation code
creationSize, // Size of the creation code
0x00, // Offset of output.
Expand Down
12 changes: 6 additions & 6 deletions src/ExampleCloneFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ contract ExampleCloneFactory {
uint256 param2,
uint64 param3,
uint8 param4
) external returns (ExampleClone clone) {
) external payable returns (ExampleClone clone) {
bytes memory data = abi.encodePacked(param1, param2, param3, param4);
clone = ExampleClone(address(implementation).clone(data));
clone = ExampleClone(address(implementation).clone(data, msg.value));
}

function createClone2(
address param1,
uint256 param2,
uint64 param3,
uint8 param4
) external returns (ExampleClone clone) {
) external payable returns (ExampleClone clone) {
bytes memory data = abi.encodePacked(param1, param2, param3, param4);
clone = ExampleClone(address(implementation).clone2(data));
clone = ExampleClone(address(implementation).clone2(data, msg.value));
}

function addressOfClone2(
Expand All @@ -49,9 +49,9 @@ contract ExampleCloneFactory {
uint64 param3,
uint8 param4,
bytes32 salt
) external returns (ExampleClone clone) {
) external payable returns (ExampleClone clone) {
bytes memory data = abi.encodePacked(param1, param2, param3, param4);
clone = ExampleClone(address(implementation).clone3(data, salt));
clone = ExampleClone(address(implementation).clone3(data, salt, msg.value));
}

function addressOfClone3(bytes32 salt) external view returns (address) {
Expand Down
57 changes: 57 additions & 0 deletions src/test/ExampleCloneFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,24 @@ contract ExampleCloneFactoryTest is DSTest {
assertEq(clone.param4(), param4);
}

function testCorrectness_clone_value(
address param1,
uint256 param2,
uint64 param3,
uint8 param4
) public {
ExampleClone clone = factory.createClone{ value: 1 wei }(
param1,
param2,
param3,
param4
);
assertEq(clone.param1(), param1);
assertEq(clone.param2(), param2);
assertEq(clone.param3(), param3);
assertEq(clone.param4(), param4);
}

function testCorrectness_clone2(
address param1,
uint256 param2,
Expand All @@ -80,6 +98,24 @@ contract ExampleCloneFactoryTest is DSTest {
assertEq(clone.param4(), param4);
}

function testCorrectness_clone2_value(
address param1,
uint256 param2,
uint64 param3,
uint8 param4
) public {
ExampleClone clone = factory.createClone2{ value: 1 wei }(
param1,
param2,
param3,
param4
);
assertEq(clone.param1(), param1);
assertEq(clone.param2(), param2);
assertEq(clone.param3(), param3);
assertEq(clone.param4(), param4);
}

function testCorrectness_clone3(
address param1,
uint256 param2,
Expand All @@ -101,6 +137,27 @@ contract ExampleCloneFactoryTest is DSTest {
assertEq(address(clone), factory.addressOfClone3(salt));
}

function testCorrectness_clone3_value(
address param1,
uint256 param2,
uint64 param3,
uint8 param4,
bytes32 salt
) public {
ExampleClone clone = factory.createClone3{ value: 1 wei }(
param1,
param2,
param3,
param4,
salt
);
assertEq(clone.param1(), param1);
assertEq(clone.param2(), param2);
assertEq(clone.param3(), param3);
assertEq(clone.param4(), param4);
assertEq(address(clone), factory.addressOfClone3(salt));
}

function testCorrectness_addressOfClone2(
address param1,
uint256 param2,
Expand Down
Loading