From 6746516da38171d1450663b837a35567debc9f77 Mon Sep 17 00:00:00 2001 From: ronhuafeng Date: Fri, 9 Sep 2022 17:09:16 +0100 Subject: [PATCH] Use unchecked arithmetic in "_transfer", "_mint" and "_burn" (#3513) --- CHANGELOG.md | 1 + contracts/token/ERC20/ERC20.sol | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaa3c9e478b..8c2edb59785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * `Address`: optimize `functionCall` by calling `functionCallWithValue` directly. ([#3468](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3468)) * `Address`: optimize `functionCall` functions by checking contract size only if there is no returned data. ([#3469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3469)) * `GovernorCompatibilityBravo`: remove unused `using` statements ([#3506](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3506)) + * `ERC20`: optimize `_transfer`, `_mint` and `_burn` by using `unchecked` arithmetic when possible. ([#3513](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3513)) ## 4.7.0 (2022-06-29) diff --git a/contracts/token/ERC20/ERC20.sol b/contracts/token/ERC20/ERC20.sol index ed2cd5dec5f..083b58c7197 100644 --- a/contracts/token/ERC20/ERC20.sol +++ b/contracts/token/ERC20/ERC20.sol @@ -237,8 +237,10 @@ contract ERC20 is Context, IERC20, IERC20Metadata { require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; + // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by + // decrementing then incrementing. + _balances[to] += amount; } - _balances[to] += amount; emit Transfer(from, to, amount); @@ -260,7 +262,10 @@ contract ERC20 is Context, IERC20, IERC20Metadata { _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; - _balances[account] += amount; + unchecked { + // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. + _balances[account] += amount; + } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); @@ -286,8 +291,9 @@ contract ERC20 is Context, IERC20, IERC20Metadata { require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; + // Overflow not possible: amount <= accountBalance <= totalSupply. + _totalSupply -= amount; } - _totalSupply -= amount; emit Transfer(account, address(0), amount);