Skip to content

Commit 29d68b3

Browse files
authored
Merge branch 'master' into optimize-clones
2 parents ae25cd4 + 6d8017d commit 29d68b3

File tree

8 files changed

+81
-34
lines changed

8 files changed

+81
-34
lines changed

.github/workflows/checks.yml

+11
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,14 @@ jobs:
7676
- name: Set up environment
7777
uses: ./.github/actions/setup
7878
- uses: crytic/slither-action@v0.1.1
79+
80+
codespell:
81+
if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable'
82+
runs-on: ubuntu-latest
83+
steps:
84+
- uses: actions/checkout@v3
85+
- name: Run CodeSpell
86+
uses: codespell-project/actions-codespell@v1.0
87+
with:
88+
check_filenames: true
89+
skip: package-lock.json

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
* `ERC721`: optimize transfers by making approval clearing implicit instead of emitting an event. ([#3481](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3481))
1313
* `ERC721`: optimize burn by making approval clearing implicit instead of emitting an event. ([#3538](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3538))
1414
* `ERC721`: Fix balance accounting when a custom `_beforeTokenTransfer` hook results in a transfer of the token under consideration. ([#3611](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3611))
15+
* `ERC721`: use unchecked arithmetic for balance updates. ([#3524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3524))
1516
* `ReentrancyGuard`: Reduce code size impact of the modifier by using internal functions. ([#3515](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3515))
1617
* `SafeCast`: optimize downcasting of signed integers. ([#3565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3565))
1718
* `VestingWallet`: remove unused library `Math.sol`. ([#3605](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3605))
1819
* `ECDSA`: Remove redundant check on the `v` value. ([#3591](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3591))
1920
* `VestingWallet`: add `releasable` getters. ([#3580](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3580))
21+
* `Create2`: optimize address computation by using assembly instead of `abi.encodePacked`. ([#3600](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3600))
2022
* `Clones`: optimized the assembly to use only the scratch space during deployments, and optimized `predictDeterministicAddress` to use lesser operations. ([#3640](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3640))
2123

2224
### Deprecations
@@ -549,7 +551,7 @@ Refer to the table below to adjust your inheritance list.
549551
* Now conforming to a 4-space indentation code style. ([1508](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1508))
550552
* `ERC20`: more gas efficient due to removed redundant `require`s. ([#1409](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1409))
551553
* `ERC721`: fixed a bug that prevented internal data structures from being properly cleaned, missing potential gas refunds. ([#1539](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1539) and [#1549](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1549))
552-
* `ERC721`: general gas savings on `transferFrom`, `_mint` and `_burn`, due to redudant `require`s and `SSTORE`s. ([#1549](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1549))
554+
* `ERC721`: general gas savings on `transferFrom`, `_mint` and `_burn`, due to redundant `require`s and `SSTORE`s. ([#1549](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1549))
553555

554556
### Bugfixes
555557

certora/specs/GovernorBase.spec

+15-16
Original file line numberDiff line numberDiff line change
@@ -98,26 +98,26 @@ function helperFunctionsWithRevert(uint256 proposalId, method f, env e) {
9898
// To use env with general preserved block disable type checking [--disableLocalTypeChecking]
9999
invariant startAndEndDatesNonZero(uint256 pId)
100100
proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0
101-
{ preserved with (env e){
101+
{ preserved with (env e){
102102
require e.block.number > 0;
103103
}}
104-
104+
105105

106106
/*
107-
* If a proposal is canceled it must have a start and an end date
107+
* If a proposal is canceled it must have a start and an end date
108108
*/
109109
// To use env with general preserved block disable type checking [--disableLocalTypeChecking]
110110
invariant canceledImplyStartAndEndDateNonZero(uint pId)
111111
isCanceled(pId) => proposalSnapshot(pId) != 0
112-
{preserved with (env e){
112+
{preserved with (env e){
113113
require e.block.number > 0;
114114
}}
115115

116116

117117
/*
118-
* If a proposal is executed it must have a start and an end date
118+
* If a proposal is executed it must have a start and an end date
119119
*/
120-
// To use env with general preserved block disable type checking [--disableLocalTypeChecking]
120+
// To use env with general preserved block disable type checking [--disableLocalTypeChecking]
121121
invariant executedImplyStartAndEndDateNonZero(uint pId)
122122
isExecuted(pId) => proposalSnapshot(pId) != 0
123123
{ preserved with (env e){
@@ -143,7 +143,7 @@ invariant voteStartBeforeVoteEnd(uint256 pId)
143143
/*
144144
* A proposal cannot be both executed and canceled simultaneously.
145145
*/
146-
invariant noBothExecutedAndCanceled(uint256 pId)
146+
invariant noBothExecutedAndCanceled(uint256 pId)
147147
!isExecuted(pId) || !isCanceled(pId)
148148

149149

@@ -154,10 +154,10 @@ rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){
154154
bool isExecutedBefore = isExecuted(pId);
155155
bool quorumReachedBefore = _quorumReached(e, pId);
156156
bool voteSucceededBefore = _voteSucceeded(pId);
157-
157+
158158
calldataarg args;
159159
f(e, args);
160-
160+
161161
bool isExecutedAfter = isExecuted(pId);
162162
assert (!isExecutedBefore && isExecutedAfter) => (quorumReachedBefore && voteSucceededBefore), "quorum was changed";
163163
}
@@ -177,16 +177,16 @@ rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){
177177
// the fact that the 3 functions themselves makes no changes, but rather call an internal function to execute.
178178
// That means that we do not check those 3 functions directly, however for castVote & castVoteWithReason it is quite trivial
179179
// to understand why this is ok. For castVoteBySig we basically assume that the signature referendum is correct without checking it.
180-
// We could check each function separately and pass the rule, but that would have uglyfied the code with no concrete
180+
// We could check each function separately and pass the rule, but that would have uglyfied the code with no concrete
181181
// benefit, as it is evident that nothing is happening in the first 2 functions (calling a view function), and we do not desire to check the signature verification.
182182
rule doubleVoting(uint256 pId, uint8 sup, method f) {
183183
env e;
184-
address user = e.msg.sender;
184+
address user = e.msg.sender;
185185
bool votedCheck = hasVoted(e, pId, user);
186186

187187
castVote@withrevert(e, pId, sup);
188188

189-
assert votedCheck => lastReverted, "double voting accured";
189+
assert votedCheck => lastReverted, "double voting occurred";
190190
}
191191

192192

@@ -207,7 +207,7 @@ rule immutableFieldsAfterProposalCreation(uint256 pId, method f) {
207207
uint256 _voteEnd = proposalDeadline(pId);
208208

209209
require proposalCreated(pId); // startDate > 0
210-
210+
211211
env e; calldataarg arg;
212212
f(e, arg);
213213

@@ -226,7 +226,7 @@ rule noStartBeforeCreation(uint256 pId) {
226226
// This line makes sure that we see only cases where start date is changed from 0, i.e. creation of proposal
227227
// We proved in immutableFieldsAfterProposalCreation that once dates set for proposal, it cannot be changed
228228
require !proposalCreated(pId); // previousStart == 0;
229-
229+
230230
env e; calldataarg args;
231231
propose(e, args);
232232

@@ -273,7 +273,7 @@ rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){
273273
* All proposal specific (non-view) functions should revert if proposal is executed
274274
*/
275275
// In this rule we show that if a function is executed, i.e. execute() was called on the proposal ID,
276-
// non of the proposal specific functions can make changes again. In executedOnlyAfterExecuteFunc
276+
// non of the proposal specific functions can make changes again. In executedOnlyAfterExecuteFunc
277277
// we connected the executed attribute to the execute() function, showing that only execute() can
278278
// change it, and that it will always change it.
279279
rule allFunctionsRevertIfExecuted(method f) filtered { f ->
@@ -331,4 +331,3 @@ rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] c
331331
bool executedAfter = isExecuted(pId);
332332
assert(executedAfter != executedBefore => f.selector == execute(address[], uint256[], bytes[], bytes32).selector, "isExecuted only changes in the execute method");
333333
}
334-

contracts/governance/extensions/GovernorVotesQuorumFraction.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ abstract contract GovernorVotesQuorumFraction is GovernorVotes {
5555
return latest._value;
5656
}
5757

58-
// Otherwize, do the binary search
58+
// Otherwise, do the binary search
5959
return _quorumNumeratorHistory.getAtBlock(blockNumber);
6060
}
6161

contracts/token/ERC20/extensions/ERC4626.sol

+3-3
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ abstract contract ERC4626 is ERC20, IERC4626 {
147147
* @dev Internal conversion function (from assets to shares) with support for rounding direction.
148148
*
149149
* Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset
150-
* would represent an infinite amout of shares.
150+
* would represent an infinite amount of shares.
151151
*/
152152
function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {
153153
uint256 supply = totalSupply();
@@ -182,7 +182,7 @@ abstract contract ERC4626 is ERC20, IERC4626 {
182182
// calls the vault, which is assumed not malicious.
183183
//
184184
// Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
185-
// assets are transfered and before the shares are minted, which is a valid state.
185+
// assets are transferred and before the shares are minted, which is a valid state.
186186
// slither-disable-next-line reentrancy-no-eth
187187
SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
188188
_mint(receiver, shares);
@@ -209,7 +209,7 @@ abstract contract ERC4626 is ERC20, IERC4626 {
209209
// calls the vault, which is assumed not malicious.
210210
//
211211
// Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
212-
// shares are burned and after the assets are transfered, which is a valid state.
212+
// shares are burned and after the assets are transferred, which is a valid state.
213213
_burn(owner, shares);
214214
SafeERC20.safeTransfer(_asset, receiver, assets);
215215

contracts/token/ERC721/ERC721.sol

+24-6
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,14 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
285285
// Check that tokenId was not minted by `_beforeTokenTransfer` hook
286286
require(!_exists(tokenId), "ERC721: token already minted");
287287

288-
_balances[to] += 1;
288+
unchecked {
289+
// Will not overflow unless all 2**256 token ids are minted to the same owner.
290+
// Given that tokens are minted one by one, it is impossible in practice that
291+
// this ever happens. Might change if we allow batch minting.
292+
// The ERC fails to describe this case.
293+
_balances[to] += 1;
294+
}
295+
289296
_owners[tokenId] = to;
290297

291298
emit Transfer(address(0), to, tokenId);
@@ -309,13 +316,17 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
309316

310317
_beforeTokenTransfer(owner, address(0), tokenId);
311318

312-
// Update ownership in case tokenId was transfered by `_beforeTokenTransfer` hook
319+
// Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
313320
owner = ERC721.ownerOf(tokenId);
314321

315322
// Clear approvals
316323
delete _tokenApprovals[tokenId];
317324

318-
_balances[owner] -= 1;
325+
unchecked {
326+
// Cannot overflow, as that would require more tokens to be burned/transferred
327+
// out than the owner initially received through minting and transferring in.
328+
_balances[owner] -= 1;
329+
}
319330
delete _owners[tokenId];
320331

321332
emit Transfer(owner, address(0), tokenId);
@@ -344,14 +355,21 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
344355

345356
_beforeTokenTransfer(from, to, tokenId);
346357

347-
// Check that tokenId was not transfered by `_beforeTokenTransfer` hook
358+
// Check that tokenId was not transferred by `_beforeTokenTransfer` hook
348359
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
349360

350361
// Clear approvals from the previous owner
351362
delete _tokenApprovals[tokenId];
352363

353-
_balances[from] -= 1;
354-
_balances[to] += 1;
364+
unchecked {
365+
// `_balances[from]` cannot overflow for the same reason as described in `_burn`:
366+
// `from`'s balance is the number of token held, which is at least one before the current
367+
// transfer.
368+
// `_balances[to]` could overflow in the conditions described in `_mint`. That would require
369+
// all 2**256 token ids to be minted, which in practice is impossible.
370+
_balances[from] -= 1;
371+
_balances[to] += 1;
372+
}
355373
_owners[tokenId] = to;
356374

357375
emit Transfer(from, to, tokenId);

contracts/utils/Create2.sol

+23-6
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,14 @@ library Create2 {
3131
uint256 amount,
3232
bytes32 salt,
3333
bytes memory bytecode
34-
) internal returns (address) {
35-
address addr;
34+
) internal returns (address addr) {
3635
require(address(this).balance >= amount, "Create2: insufficient balance");
3736
require(bytecode.length != 0, "Create2: bytecode length is zero");
3837
/// @solidity memory-safe-assembly
3938
assembly {
4039
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
4140
}
4241
require(addr != address(0), "Create2: Failed on deploy");
43-
return addr;
4442
}
4543

4644
/**
@@ -59,8 +57,27 @@ library Create2 {
5957
bytes32 salt,
6058
bytes32 bytecodeHash,
6159
address deployer
62-
) internal pure returns (address) {
63-
bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash));
64-
return address(uint160(uint256(_data)));
60+
) internal pure returns (address addr) {
61+
/// @solidity memory-safe-assembly
62+
assembly {
63+
let ptr := mload(0x40) // Get free memory pointer
64+
65+
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
66+
// |-------------------|---------------------------------------------------------------------------|
67+
// | bytecodeHash | CCCCCCCCCCCCC...CC |
68+
// | salt | BBBBBBBBBBBBB...BB |
69+
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
70+
// | 0xFF | FF |
71+
// |-------------------|---------------------------------------------------------------------------|
72+
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
73+
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
74+
75+
mstore(add(ptr, 0x40), bytecodeHash)
76+
mstore(add(ptr, 0x20), salt)
77+
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
78+
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
79+
mstore8(start, 0xff)
80+
addr := keccak256(start, 85)
81+
}
6582
}
6683
}

test/token/ERC20/extensions/ERC20FlashMint.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ contract('ERC20FlashMint', function (accounts) {
9797
const receiverInitialBalance = new BN(200000);
9898
const flashFee = new BN(5000);
9999

100-
beforeEach('init reciever balance & set flash fee', async function () {
100+
beforeEach('init receiver balance & set flash fee', async function () {
101101
this.receiver = await ERC3156FlashBorrowerMock.new(true, true);
102102
const receipt = await this.token.mint(this.receiver.address, receiverInitialBalance);
103103
await expectEvent(receipt, 'Transfer', { from: ZERO_ADDRESS, to: this.receiver.address, value: receiverInitialBalance });

0 commit comments

Comments
 (0)