Skip to content

Commit e831429

Browse files
k06aAmxxernestognw
authored
Add ability to create clones with initial value in Clones.sol (#4936)
Co-authored-by: Hadrien Croubois <[email protected]> Co-authored-by: ernestognw <[email protected]>
1 parent 8b2f29c commit e831429

19 files changed

+182
-102
lines changed

.changeset/chilled-walls-develop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Clones`: Add version of `clone` and `cloneDeterministic` that support sending value at creation.

.changeset/strong-singers-talk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`Errors`: New library of common custom errors.

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
### Custom error changes
4+
5+
This version comes with changes to the custom error identifiers. Contracts previously depending on the following errors should be replaced accordingly:
6+
7+
- Replace `Address.FailedInnerCall` with `Errors.FailedCall`
8+
- Replace `Address.AddressInsufficientBalance` with `Errors.InsufficientBalance`
9+
- Replace `Clones.Create2InsufficientBalance` with `Errors.InsufficientBalance`
10+
- Replace `Clones.ERC1167FailedCreateClone` with `Errors.FailedDeployment`
11+
- Replace `Clones.Create2FailedDeployment` with `Errors.FailedDeployment`
312

413
## 5.0.2 (2024-02-29)
514

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ $ npm install @openzeppelin/contracts
4141
$ forge install OpenZeppelin/openzeppelin-contracts
4242
```
4343

44-
Add `@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/` in `remappings.txt.`
44+
Add `@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/` in `remappings.txt.`
4545

4646
### Usage
4747

contracts/metatx/ERC2771Forwarder.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {ECDSA} from "../utils/cryptography/ECDSA.sol";
88
import {EIP712} from "../utils/cryptography/EIP712.sol";
99
import {Nonces} from "../utils/Nonces.sol";
1010
import {Address} from "../utils/Address.sol";
11+
import {Errors} from "../utils/Errors.sol";
1112

1213
/**
1314
* @dev A forwarder compatible with ERC-2771 contracts. See {ERC2771Context}.
@@ -132,7 +133,7 @@ contract ERC2771Forwarder is EIP712, Nonces {
132133
}
133134

134135
if (!_execute(request, true)) {
135-
revert Address.FailedInnerCall();
136+
revert Errors.FailedCall();
136137
}
137138
}
138139

contracts/proxy/Clones.sol

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
pragma solidity ^0.8.20;
55

6+
import {Errors} from "../utils/Errors.sol";
7+
68
/**
79
* @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
810
* deploying minimal proxy contracts, also known as "clones".
@@ -15,17 +17,26 @@ pragma solidity ^0.8.20;
1517
* deterministic method.
1618
*/
1719
library Clones {
18-
/**
19-
* @dev A clone instance deployment failed.
20-
*/
21-
error ERC1167FailedCreateClone();
22-
2320
/**
2421
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
2522
*
2623
* This function uses the create opcode, which should never revert.
2724
*/
2825
function clone(address implementation) internal returns (address instance) {
26+
return clone(implementation, 0);
27+
}
28+
29+
/**
30+
* @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
31+
* to the new contract.
32+
*
33+
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
34+
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
35+
*/
36+
function clone(address implementation, uint256 value) internal returns (address instance) {
37+
if (address(this).balance < value) {
38+
revert Errors.InsufficientBalance(address(this).balance, value);
39+
}
2940
/// @solidity memory-safe-assembly
3041
assembly {
3142
// Stores the bytecode after address
@@ -34,10 +45,10 @@ library Clones {
3445
mstore(0x11, implementation)
3546
// Packs the first 3 bytes of the `implementation` address with the bytecode before the address.
3647
mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
37-
instance := create(0, 0x09, 0x37)
48+
instance := create(value, 0x09, 0x37)
3849
}
3950
if (instance == address(0)) {
40-
revert ERC1167FailedCreateClone();
51+
revert Errors.FailedDeployment();
4152
}
4253
}
4354

@@ -49,6 +60,24 @@ library Clones {
4960
* the clones cannot be deployed twice at the same address.
5061
*/
5162
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
63+
return cloneDeterministic(implementation, salt, 0);
64+
}
65+
66+
/**
67+
* @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
68+
* a `value` parameter to send native currency to the new contract.
69+
*
70+
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
71+
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
72+
*/
73+
function cloneDeterministic(
74+
address implementation,
75+
bytes32 salt,
76+
uint256 value
77+
) internal returns (address instance) {
78+
if (address(this).balance < value) {
79+
revert Errors.InsufficientBalance(address(this).balance, value);
80+
}
5281
/// @solidity memory-safe-assembly
5382
assembly {
5483
// Stores the bytecode after address
@@ -57,10 +86,10 @@ library Clones {
5786
mstore(0x11, implementation)
5887
// Packs the first 3 bytes of the `implementation` address with the bytecode before the address.
5988
mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
60-
instance := create2(0, 0x09, 0x37, salt)
89+
instance := create2(value, 0x09, 0x37, salt)
6190
}
6291
if (instance == address(0)) {
63-
revert ERC1167FailedCreateClone();
92+
revert Errors.FailedDeployment();
6493
}
6594
}
6695

contracts/utils/Address.sol

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,17 @@
33

44
pragma solidity ^0.8.20;
55

6+
import {Errors} from "./Errors.sol";
7+
68
/**
79
* @dev Collection of functions related to the address type
810
*/
911
library Address {
10-
/**
11-
* @dev The ETH balance of the account is not enough to perform the operation.
12-
*/
13-
error AddressInsufficientBalance(address account);
14-
1512
/**
1613
* @dev There's no code at `target` (it is not a contract).
1714
*/
1815
error AddressEmptyCode(address target);
1916

20-
/**
21-
* @dev A call to an address target failed. The target may have reverted.
22-
*/
23-
error FailedInnerCall();
24-
2517
/**
2618
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
2719
* `recipient`, forwarding all available gas and reverting on errors.
@@ -40,12 +32,12 @@ library Address {
4032
*/
4133
function sendValue(address payable recipient, uint256 amount) internal {
4234
if (address(this).balance < amount) {
43-
revert AddressInsufficientBalance(address(this));
35+
revert Errors.InsufficientBalance(address(this).balance, amount);
4436
}
4537

4638
(bool success, ) = recipient.call{value: amount}("");
4739
if (!success) {
48-
revert FailedInnerCall();
40+
revert Errors.FailedCall();
4941
}
5042
}
5143

@@ -57,7 +49,7 @@ library Address {
5749
* If `target` reverts with a revert reason or custom error, it is bubbled
5850
* up by this function (like regular Solidity function calls). However, if
5951
* the call reverted with no returned reason, this function reverts with a
60-
* {FailedInnerCall} error.
52+
* {Errors.FailedCall} error.
6153
*
6254
* Returns the raw returned data. To convert to the expected return value,
6355
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
@@ -82,7 +74,7 @@ library Address {
8274
*/
8375
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
8476
if (address(this).balance < value) {
85-
revert AddressInsufficientBalance(address(this));
77+
revert Errors.InsufficientBalance(address(this).balance, value);
8678
}
8779
(bool success, bytes memory returndata) = target.call{value: value}(data);
8880
return verifyCallResultFromTarget(target, success, returndata);
@@ -108,8 +100,8 @@ library Address {
108100

109101
/**
110102
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
111-
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
112-
* unsuccessful call.
103+
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
104+
* of an unsuccessful call.
113105
*/
114106
function verifyCallResultFromTarget(
115107
address target,
@@ -130,7 +122,7 @@ library Address {
130122

131123
/**
132124
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
133-
* revert reason or with a default {FailedInnerCall} error.
125+
* revert reason or with a default {Errors.FailedCall} error.
134126
*/
135127
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
136128
if (!success) {
@@ -141,7 +133,7 @@ library Address {
141133
}
142134

143135
/**
144-
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
136+
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
145137
*/
146138
function _revert(bytes memory returndata) private pure {
147139
// Look for revert reason and bubble it up if present
@@ -153,7 +145,7 @@ library Address {
153145
revert(add(32, returndata), returndata_size)
154146
}
155147
} else {
156-
revert FailedInnerCall();
148+
revert Errors.FailedCall();
157149
}
158150
}
159151
}

contracts/utils/Create2.sol

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
pragma solidity ^0.8.20;
55

6+
import {Errors} from "./Errors.sol";
7+
68
/**
79
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
810
* `CREATE2` can be used to compute in advance the address where a smart
@@ -13,21 +15,11 @@ pragma solidity ^0.8.20;
1315
* information.
1416
*/
1517
library Create2 {
16-
/**
17-
* @dev Not enough balance for performing a CREATE2 deploy.
18-
*/
19-
error Create2InsufficientBalance(uint256 balance, uint256 needed);
20-
2118
/**
2219
* @dev There's no code to deploy.
2320
*/
2421
error Create2EmptyBytecode();
2522

26-
/**
27-
* @dev The deployment failed.
28-
*/
29-
error Create2FailedDeployment();
30-
3123
/**
3224
* @dev Deploys a contract using `CREATE2`. The address where the contract
3325
* will be deployed can be known in advance via {computeAddress}.
@@ -44,7 +36,7 @@ library Create2 {
4436
*/
4537
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
4638
if (address(this).balance < amount) {
47-
revert Create2InsufficientBalance(address(this).balance, amount);
39+
revert Errors.InsufficientBalance(address(this).balance, amount);
4840
}
4941
if (bytecode.length == 0) {
5042
revert Create2EmptyBytecode();
@@ -54,7 +46,7 @@ library Create2 {
5446
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
5547
}
5648
if (addr == address(0)) {
57-
revert Create2FailedDeployment();
49+
revert Errors.FailedDeployment();
5850
}
5951
}
6052

contracts/utils/Errors.sol

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
/**
6+
* @dev Collection of common custom errors used in multiple contracts
7+
*
8+
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
9+
* It is recommended to avoid relying on the error API for critical functionality.
10+
*/
11+
library Errors {
12+
/**
13+
* @dev The ETH balance of the account is not enough to perform the operation.
14+
*/
15+
error InsufficientBalance(uint256 balance, uint256 needed);
16+
17+
/**
18+
* @dev A call to an address target failed. The target may have reverted.
19+
*/
20+
error FailedCall();
21+
22+
/**
23+
* @dev The deployment failed.
24+
*/
25+
error FailedDeployment();
26+
}

scripts/upgradeable/upgradeable.patch

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ index ff596b0c3..000000000
5959
-<!-- Make sure that you have reviewed the OpenZeppelin Contracts Contributor Guidelines. -->
6060
-<!-- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CONTRIBUTING.md -->
6161
diff --git a/README.md b/README.md
62-
index 35083bc6e..05cf4fc27 100644
62+
index fa7b4e31e..4799b6376 100644
6363
--- a/README.md
6464
+++ b/README.md
6565
@@ -19,6 +19,9 @@
@@ -89,8 +89,8 @@ index 35083bc6e..05cf4fc27 100644
8989
+$ forge install OpenZeppelin/openzeppelin-contracts-upgradeable
9090
```
9191

92-
-Add `@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/` in `remappings.txt.`
93-
+Add `@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/` in `remappings.txt.`
92+
-Add `@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/` in `remappings.txt.`
93+
+Add `@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/` in `remappings.txt.`
9494

9595
### Usage
9696

@@ -110,15 +110,15 @@ index 35083bc6e..05cf4fc27 100644
110110
}
111111
```
112112
diff --git a/contracts/package.json b/contracts/package.json
113-
index 6ab89138a..ece834a44 100644
113+
index 845e8c403..8dc181b91 100644
114114
--- a/contracts/package.json
115115
+++ b/contracts/package.json
116116
@@ -1,5 +1,5 @@
117117
{
118118
- "name": "@openzeppelin/contracts",
119119
+ "name": "@openzeppelin/contracts-upgradeable",
120120
"description": "Secure Smart Contract library for Solidity",
121-
"version": "5.0.1",
121+
"version": "5.0.2",
122122
"files": [
123123
@@ -13,7 +13,7 @@
124124
},
@@ -307,7 +307,7 @@ index 77c4c8990..602467f40 100644
307307
}
308308
}
309309
diff --git a/package.json b/package.json
310-
index ec2c44ced..46eedc98f 100644
310+
index c4b358e10..96ab2559c 100644
311311
--- a/package.json
312312
+++ b/package.json
313313
@@ -32,7 +32,7 @@
@@ -328,7 +328,7 @@ index 304d1386a..a1cd63bee 100644
328328
+@openzeppelin/contracts-upgradeable/=contracts/
329329
+@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
330330
diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js
331-
index 166038b36..268e0d29d 100644
331+
index 2b6e7fa97..268e0d29d 100644
332332
--- a/test/utils/cryptography/EIP712.test.js
333333
+++ b/test/utils/cryptography/EIP712.test.js
334334
@@ -47,27 +47,6 @@ describe('EIP712', function () {
@@ -346,7 +346,7 @@ index 166038b36..268e0d29d 100644
346346
- const clone = await factory
347347
- .$clone(this.eip712)
348348
- .then(tx => tx.wait())
349-
- .then(receipt => receipt.logs.find(ev => ev.fragment.name == 'return$clone').args.instance)
349+
- .then(receipt => receipt.logs.find(ev => ev.fragment.name == 'return$clone_address').args.instance)
350350
- .then(address => ethers.getContractAt('$EIP712Verifier', address));
351351
-
352352
- const expectedDomain = { ...this.domain, verifyingContract: clone.target };

0 commit comments

Comments
 (0)