Commit a9b09bea by Francisco Giordano

Merge upstream openzeppelin-contracts into upstream-patched

parents d67594f6 7144ec8d
......@@ -2,9 +2,20 @@
## Unreleased
* `ERC20Votes`: add a new extension of the `ERC20` token with support for voting snapshots and delegation. This extension is compatible with Compound's `Comp` token interface. ([#2632](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2632))
* `ERC20Votes`: add a new extension of the `ERC20` token with support for voting snapshots and delegation. This extension is compatible with Compound's `Comp` token interface. ([#2632](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2632))
* Enumerables: Improve gas cost of removal in `EnumerableSet` and `EnumerableMap`.
* Enumerables: Improve gas cost of lookup in `EnumerableSet` and `EnumerableMap`.
* `Counter`: add a reset method. ([#2678](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2678))
* Tokens: Wrap definitely safe subtractions in `unchecked` blocks.
* `Math`: Add a `ceilDiv` method for performing ceiling division.
### Breaking Changes
* `ERC20FlashMint` is no longer a Draft ERC. ([#2673](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2673)))
**How to update:** Change your import paths by removing the `draft-` prefix from `@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20FlashMint.sol`.
> See [Releases and Stability: Drafts](https://docs.openzeppelin.com/contracts/4.x/releases-stability#drafts).
## 4.1.0 (2021-04-29)
......
......@@ -20,4 +20,8 @@ contract CountersImpl {
function decrement() public {
_counter.decrement();
}
function reset() public {
_counter.reset();
}
}
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../token/ERC20/extensions/draft-ERC20FlashMint.sol";
import "../token/ERC20/extensions/ERC20FlashMint.sol";
contract ERC20FlashMintMock is ERC20FlashMint {
constructor (
......
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "../token/ERC20/extensions/draft-ERC20Votes.sol";
import "../token/ERC20/extensions/ERC20Votes.sol";
contract ERC20VotesMock is ERC20Votes {
constructor (
......
......@@ -16,4 +16,8 @@ contract MathMock {
function average(uint256 a, uint256 b) public pure returns (uint256) {
return Math.average(a, b);
}
function ceilDiv(uint256 a, uint256 b) public pure returns (uint256) {
return Math.ceilDiv(a, b);
}
}
......@@ -187,7 +187,9 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
emit TransferSingle(operator, from, to, id, amount);
......@@ -228,7 +230,9 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
}
......@@ -327,7 +331,9 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][account] = accountBalance - amount;
}
emit TransferSingle(operator, account, address(0), id, amount);
}
......@@ -353,8 +359,10 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][account] = accountBalance - amount;
}
}
emit TransferBatch(operator, account, address(0), ids, amounts);
}
......
......@@ -150,7 +150,9 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
......@@ -189,7 +191,9 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
......@@ -216,7 +220,9 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
......@@ -259,8 +265,10 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
}
......
......@@ -34,7 +34,9 @@ abstract contract ERC20Burnable is Context, ERC20 {
function burnFrom(address account, uint256 amount) public virtual {
uint256 currentAllowance = allowance(account, _msgSender());
require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
unchecked {
_approve(account, _msgSender(), currentAllowance - amount);
}
_burn(account, amount);
}
}
......@@ -3,7 +3,7 @@
pragma solidity ^0.8.0;
import "./draft-ERC20Permit.sol";
import "./draft-IERC20Votes.sol";
import "./IERC20Votes.sol";
import "../../../utils/math/Math.sol";
import "../../../utils/math/SafeCast.sol";
import "../../../utils/cryptography/ECDSA.sol";
......
......@@ -98,8 +98,9 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
}
/**
* @dev Base URI for computing {tokenURI}. Empty by default, can be overriden
* in child contracts.
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overriden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
......
......@@ -51,7 +51,7 @@ contract ERC777 is Context, IERC777, IERC20 {
mapping(address => mapping(address => bool)) private _revokedDefaultOperators;
// ERC20-allowances
mapping (address => mapping (address => uint256)) private _allowances;
mapping(address => mapping(address => uint256)) private _allowances;
/**
* @dev `defaultOperators` may be an empty array.
......@@ -419,8 +419,10 @@ contract ERC777 is Context, IERC777, IERC20 {
// Update state variables
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC777: burn amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
_totalSupply -= amount;
}
emit Burned(operator, from, amount, data, operatorData);
emit Transfer(from, address(0), amount);
......@@ -440,7 +442,9 @@ contract ERC777 is Context, IERC777, IERC20 {
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC777: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Sent(operator, from, to, amount, userData, operatorData);
......
......@@ -5,7 +5,7 @@ pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
......@@ -35,4 +35,8 @@ library Counters {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}
......@@ -10,7 +10,7 @@ The {Address}, {Arrays} and {Strings} libraries provide more operations related
For new data types:
* {Counters}: a simple way to get a counter that can only be incremented or decremented. Very useful for ID generation, counting contract activity, among others.
* {Counters}: a simple way to get a counter that can only be incremented, decremented or reset. Very useful for ID generation, counting contract activity, among others.
* {EnumerableMap}: like Solidity's https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] type, but with key-value _enumeration_: this will let you know how many entries a mapping has, and iterate over them (which is not possible with `mapping`).
* {EnumerableSet}: like {EnumerableMap}, but for https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets]. Can be used to store privileged accounts, issued IDs, etc.
......
......@@ -28,4 +28,15 @@ library Math {
// (a + b) / 2 can overflow, so we distribute.
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}
......@@ -61,4 +61,24 @@ contract('Counters', function (accounts) {
});
});
});
describe('reset', function () {
context('null counter', function () {
it('does not throw', async function () {
await this.counter.reset();
expect(await this.counter.current()).to.be.bignumber.equal('0');
});
});
context('non null counter', function () {
beforeEach(async function () {
await this.counter.increment();
expect(await this.counter.current()).to.be.bignumber.equal('1');
});
it('reset to 0', async function () {
await this.counter.reset();
expect(await this.counter.current()).to.be.bignumber.equal('0');
});
});
});
});
const { BN } = require('@openzeppelin/test-helpers');
const { BN, constants } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const { MAX_UINT256 } = constants;
const MathMock = artifacts.require('MathMock');
......@@ -55,4 +55,29 @@ contract('Math', function (accounts) {
expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
});
});
describe('ceilDiv', function () {
it('does not round up on exact division', async function () {
const a = new BN('10');
const b = new BN('5');
expect(await this.math.ceilDiv(a, b)).to.be.bignumber.equal('2');
});
it('rounds up on division with remainders', async function () {
const a = new BN('42');
const b = new BN('13');
expect(await this.math.ceilDiv(a, b)).to.be.bignumber.equal('4');
});
it('does not overflow', async function () {
const b = new BN('2');
const result = new BN('1').shln(255);
expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(result);
});
it('correctly computes max uint256 divided by 1', async function () {
const b = new BN('1');
expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(MAX_UINT256);
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment