Commit b8f8da84 by Francisco Giordano

Merge upstream openzeppelin-contracts into upstream-patched

parents 0502cc31 508a879e
...@@ -20,23 +20,35 @@ ...@@ -20,23 +20,35 @@
## Unreleased ## Unreleased
* `IERC20Metadata`: add a new extended interface that includes the optional `name()`, `symbol()` and `decimals()` functions. ([#2561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2561))
* `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552))
* `ERC20Permit`: add a `_useNonce` to enable further usage of ERC712 signatures. ([#2565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2565))
## Unreleased
* Now targeting the 0.8.x line of Solidity compilers. For 0.6.x (resp 0.7.x) support, use version 3.4.0 (resp 3.4.0-solc-0.7) of OpenZeppelin. * Now targeting the 0.8.x line of Solidity compilers. For 0.6.x (resp 0.7.x) support, use version 3.4.0 (resp 3.4.0-solc-0.7) of OpenZeppelin.
* `Context`: making `_msgData` return `bytes calldata` instead of `bytes memory` ([#2492](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2492)) * `Context`: making `_msgData` return `bytes calldata` instead of `bytes memory` ([#2492](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2492))
* `ERC20`: Removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502)) * `ERC20`: removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502))
* `Strings`: addition of a `toHexString` function. ([#2504](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2504)) * `Strings`: addition of a `toHexString` function. ([#2504](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2504))
* `EnumerableMap`: change implementation to optimize for `key → value` lookups instead of enumeration. ([#2518](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2518)) * `EnumerableMap`: change implementation to optimize for `key → value` lookups instead of enumeration. ([#2518](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2518))
* `GSN`: Deprecate GSNv1 support in favor of upcomming support for GSNv2. ([#2521](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2521)) * `GSN`: deprecate GSNv1 support in favor of upcoming support for GSNv2. ([#2521](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2521))
* `ERC165`: Remove uses of storage in the base ERC165 implementation. ERC165 based contracts now use storage-less virtual functions. Old behaviour remains available in the `ERC165Storage` extension. ([#2505](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2505)) * `ERC165`: remove uses of storage in the base ERC165 implementation. ERC165 based contracts now use storage-less virtual functions. Old behavior remains available in the `ERC165Storage` extension. ([#2505](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2505))
* `Initializable`: Make initializer check stricter during construction. ([#2531](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2531)) * `Initializable`: make initializer check stricter during construction. ([#2531](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2531))
* `ERC721`: remove enumerability of tokens from the base implementation. This feature is now provided separately through the `ERC721Enumerable` extension. ([#2511](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2511)) * `ERC721`: remove enumerability of tokens from the base implementation. This feature is now provided separately through the `ERC721Enumerable` extension. ([#2511](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2511))
* `AccessControl`: removed enumerability by default for a more lightweight contract. It is now opt-in through `AccessControlEnumerable`. ([#2512](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2512)) * `AccessControl`: removed enumerability by default for a more lightweight contract. It is now opt-in through `AccessControlEnumerable`. ([#2512](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2512))
* Meta Transactions: add `ERC2771Context` and a `MinimalForwarder` for meta-transactions. ([#2508](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2508)) * Meta Transactions: add `ERC2771Context` and a `MinimalForwarder` for meta-transactions. ([#2508](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2508))
* Overall reorganisation of the contract folder to improve clarity and discoverability. ([#2503](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2503)) * Overall reorganization of the contract folder to improve clarity and discoverability. ([#2503](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2503))
* `ERC20Capped`: optimize gas usage of by enforcing te check directly in `_mint`. ([#2524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2524)) * `ERC20Capped`: optimize gas usage by enforcing the check directly in `_mint`. ([#2524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2524))
* Rename `UpgradeableProxy` to `ERC1967Proxy`. ([#2547](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2547)) * Rename `UpgradeableProxy` to `ERC1967Proxy`. ([#2547](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2547))
* `ERC777`: Optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551)) * `ERC777`: optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551))
* `ERC721URIStorage`: Add a new extension that implements the `_setTokenURI` behavior as it was available in 3.4.0. ([#2555](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2555)) * `ERC721URIStorage`: add a new extension that implements the `_setTokenURI` behavior as it was available in 3.4.0. ([#2555](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2555))
* `AccessControl`: Added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562)) * `AccessControl`: added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562))
* `ERC1155`: make `uri` public so overloading function can call it using super. ([#2576](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2576))
### Bug fixes for beta releases
* `AccessControlEnumerable`: Fixed `renounceRole` not updating enumerable set of addresses for a role. ([#2572](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2572))
### How to upgrade from 3.x ### How to upgrade from 3.x
...@@ -48,6 +60,10 @@ npx openzeppelin-contracts-migrate-imports ...@@ -48,6 +60,10 @@ npx openzeppelin-contracts-migrate-imports
Make sure you're using git or another version control system to be able to recover from any potential error in our script. Make sure you're using git or another version control system to be able to recover from any potential error in our script.
### How to upgrade from 4.0-beta.x
Some further changes have been done between the different beta iterations. Transitions made during this period are configured in the `migrate-imports` script. Consequently, you can upgrade from any previous 4.0-beta.x version using the same script as described in the *How to upgrade from 3.x* section.
## 3.4.0 (2021-02-02) ## 3.4.0 (2021-02-02)
* `BeaconProxy`: added new kind of proxy that allows simultaneous atomic upgrades. ([#2411](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2411)) * `BeaconProxy`: added new kind of proxy that allows simultaneous atomic upgrades. ([#2411](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2411))
......
...@@ -70,6 +70,14 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon ...@@ -70,6 +70,14 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon
} }
/** /**
* @dev Overload {renounceRole} to track enumerable memberships
*/
function renounceRole(bytes32 role, address account) public virtual override {
super.renounceRole(role, account);
_roleMembers[role].remove(account);
}
/**
* @dev Overload {_setupRole} to track enumerable memberships * @dev Overload {_setupRole} to track enumerable memberships
*/ */
function _setupRole(bytes32 role, address account) internal virtual override { function _setupRole(bytes32 role, address account) internal virtual override {
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "./Address.sol"; import "../utils/Address.sol";
import "./Context.sol"; import "../utils/Context.sol";
import "./math/SafeMath.sol"; import "../utils/math/SafeMath.sol";
/** /**
* @title PaymentSplitter * @title PaymentSplitter
......
= Finance
[.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance
This directory includes primitives for financial systems. We currently only offer the {PaymentSplitter} contract, but we want to grow this directory so we welcome ideas.
== PaymentSplitter
{{PaymentSplitter}}
= Governance = Governance
[.readme-notice] [.readme-notice]
NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance
This directory includes primitives for on-chain governance. We currently only offer the {TimelockController} contract, that can be used as a component in a governance systems to introduce a delay between a proposal and its execution. This directory includes primitives for on-chain governance. We currently only offer the {TimelockController} contract, that can be used as a component in a governance systems to introduce a delay between a proposal and its execution.
......
...@@ -27,6 +27,16 @@ contract ERC777Mock is Context, ERC777 { ...@@ -27,6 +27,16 @@ contract ERC777Mock is Context, ERC777 {
_mint(to, amount, userData, operatorData); _mint(to, amount, userData, operatorData);
} }
function mintInternalExtended (
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
) public {
_mint(to, amount, userData, operatorData, requireReceptionAck);
}
function approveInternal(address holder, address spender, uint256 value) public { function approveInternal(address holder, address spender, uint256 value) public {
_approve(holder, spender, value); _approve(holder, spender, value);
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../utils/Initializable.sol"; import "../proxy/utils/Initializable.sol";
/** /**
* @title InitializableMock * @title InitializableMock
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../utils/Initializable.sol"; import "../proxy/utils/Initializable.sol";
// Sample contracts showing upgradeability with multiple inheritance. // Sample contracts showing upgradeability with multiple inheritance.
// Child contract inherits from Father and Mother contracts, and Father extends from Gramps. // Child contract inherits from Father and Mother contracts, and Father extends from Gramps.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../utils/Initializable.sol"; import "../proxy/utils/Initializable.sol";
contract Implementation1 is Initializable { contract Implementation1 is Initializable {
uint internal _value; uint internal _value;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../utils/Initializable.sol"; import "../proxy/utils/Initializable.sol";
/** /**
* @title MigratableMockV1 * @title MigratableMockV1
......
...@@ -40,3 +40,7 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th ...@@ -40,3 +40,7 @@ CAUTION: Using upgradeable proxies correctly and securely is a difficult task th
== Minimal Clones == Minimal Clones
{{Clones}} {{Clones}}
== Utils
{{Initializable}}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// solhint-disable-next-line compiler-version // solhint-disable-next-line compiler-version
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "./Address.sol"; import "../../utils/Address.sol";
/** /**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
......
...@@ -55,7 +55,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { ...@@ -55,7 +55,7 @@ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
* Clients calling this function must replace the `\{id\}` substring with the * Clients calling this function must replace the `\{id\}` substring with the
* actual token type ID. * actual token type ID.
*/ */
function uri(uint256) external view virtual override returns (string memory) { function uri(uint256) public view virtual override returns (string memory) {
return _uri; return _uri;
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "./IERC20.sol"; import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol"; import "../../utils/Context.sol";
/** /**
...@@ -29,7 +30,7 @@ import "../../utils/Context.sol"; ...@@ -29,7 +30,7 @@ import "../../utils/Context.sol";
* functions have been added to mitigate the well-known issues around setting * functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}. * allowances. See {IERC20-approve}.
*/ */
contract ERC20 is Context, IERC20 { contract ERC20 is Context, IERC20, IERC20Metadata {
mapping (address => uint256) private _balances; mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances; mapping (address => mapping (address => uint256)) private _allowances;
...@@ -56,7 +57,7 @@ contract ERC20 is Context, IERC20 { ...@@ -56,7 +57,7 @@ contract ERC20 is Context, IERC20 {
/** /**
* @dev Returns the name of the token. * @dev Returns the name of the token.
*/ */
function name() public view virtual returns (string memory) { function name() public view virtual override returns (string memory) {
return _name; return _name;
} }
...@@ -64,7 +65,7 @@ contract ERC20 is Context, IERC20 { ...@@ -64,7 +65,7 @@ contract ERC20 is Context, IERC20 {
* @dev Returns the symbol of the token, usually a shorter version of the * @dev Returns the symbol of the token, usually a shorter version of the
* name. * name.
*/ */
function symbol() public view virtual returns (string memory) { function symbol() public view virtual override returns (string memory) {
return _symbol; return _symbol;
} }
...@@ -81,7 +82,7 @@ contract ERC20 is Context, IERC20 { ...@@ -81,7 +82,7 @@ contract ERC20 is Context, IERC20 {
* no way affects any of the arithmetic of the contract, including * no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}. * {IERC20-balanceOf} and {IERC20-transfer}.
*/ */
function decimals() public view virtual returns (uint8) { function decimals() public view virtual override returns (uint8) {
return 18; return 18;
} }
......
...@@ -10,6 +10,7 @@ TIP: For an overview of ERC20 tokens and a walk through on how to create a token ...@@ -10,6 +10,7 @@ TIP: For an overview of ERC20 tokens and a walk through on how to create a token
There a few core contracts that implement the behavior specified in the EIP: There a few core contracts that implement the behavior specified in the EIP:
* {IERC20}: the interface all ERC20 implementations should conform to. * {IERC20}: the interface all ERC20 implementations should conform to.
* {IERC20Metadata}: the extended ERC20 interface including the <<ERC20-name,`name`>>, <<ERC20-symbol,`symbol`>> and <<ERC20-decimals,`decimals`>> functions.
* {ERC20}: the implementation of the ERC20 interface, including the <<ERC20-name,`name`>>, <<ERC20-symbol,`symbol`>> and <<ERC20-decimals,`decimals`>> optional standard extension to the base interface. * {ERC20}: the implementation of the ERC20 interface, including the <<ERC20-name,`name`>>, <<ERC20-symbol,`symbol`>> and <<ERC20-decimals,`decimals`>> optional standard extension to the base interface.
Additionally there are multiple custom extensions, including: Additionally there are multiple custom extensions, including:
...@@ -36,6 +37,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel ...@@ -36,6 +37,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
{{IERC20}} {{IERC20}}
{{IERC20Metadata}}
{{ERC20}} {{ERC20}}
== Extensions == Extensions
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
...@@ -47,7 +47,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { ...@@ -47,7 +47,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
owner, owner,
spender, spender,
value, value,
_nonces[owner].current(), _useNonce(owner),
deadline deadline
) )
); );
...@@ -57,14 +57,13 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { ...@@ -57,14 +57,13 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
address signer = ECDSA.recover(hash, v, r, s); address signer = ECDSA.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature"); require(signer == owner, "ERC20Permit: invalid signature");
_nonces[owner].increment();
_approve(owner, spender, value); _approve(owner, spender, value);
} }
/** /**
* @dev See {IERC20Permit-nonces}. * @dev See {IERC20Permit-nonces}.
*/ */
function nonces(address owner) public view override returns (uint256) { function nonces(address owner) public view virtual override returns (uint256) {
return _nonces[owner].current(); return _nonces[owner].current();
} }
...@@ -75,4 +74,13 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { ...@@ -75,4 +74,13 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
function DOMAIN_SEPARATOR() external view override returns (bytes32) { function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4(); return _domainSeparatorV4();
} }
/**
* @dev "Consume a nonce": return the current value and increment.
*/
function _useNonce(address owner) internal virtual returns (uint256 current) {
Counters.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
} }
...@@ -223,7 +223,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { ...@@ -223,7 +223,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
* @dev Safely mints `tokenId` and transfers it to `to`. * @dev Safely mints `tokenId` and transfers it to `to`.
* *
* Requirements: * Requirements:
d* *
* - `tokenId` must not exist. * - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
* *
......
...@@ -313,6 +313,37 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -313,6 +313,37 @@ contract ERC777 is Context, IERC777, IERC20 {
internal internal
virtual virtual
{ {
_mint(account, amount, userData, operatorData, true);
}
/**
* @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* If `requireReceptionAck` is set to true, and if a send hook is
* registered for `account`, the corresponding function will be called with
* `operator`, `data` and `operatorData`.
*
* See {IERC777Sender} and {IERC777Recipient}.
*
* Emits {Minted} and {IERC20-Transfer} events.
*
* Requirements
*
* - `account` cannot be the zero address.
* - if `account` is a contract, it must implement the {IERC777Recipient}
* interface.
*/
function _mint(
address account,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
internal
virtual
{
require(account != address(0), "ERC777: mint to the zero address"); require(account != address(0), "ERC777: mint to the zero address");
address operator = _msgSender(); address operator = _msgSender();
...@@ -323,7 +354,7 @@ contract ERC777 is Context, IERC777, IERC20 { ...@@ -323,7 +354,7 @@ contract ERC777 is Context, IERC777, IERC20 {
_totalSupply += amount; _totalSupply += amount;
_balances[account] += amount; _balances[account] += amount;
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, true); _callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck);
emit Minted(operator, account, amount, userData, operatorData); emit Minted(operator, account, amount, userData, operatorData);
emit Transfer(address(0), account, amount); emit Transfer(address(0), account, amount);
......
...@@ -40,6 +40,14 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl ...@@ -40,6 +40,14 @@ Finally, {Create2} contains all necessary utilities to safely use the https://bl
{{EIP712}} {{EIP712}}
== Escrow
{{ConditionalEscrow}}
{{Escrow}}
{{RefundEscrow}}
== Introspection == Introspection
This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_. This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_.
...@@ -84,7 +92,3 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar ...@@ -84,7 +92,3 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar
{{Counters}} {{Counters}}
{{Strings}} {{Strings}}
== Other
{{Initializable}}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -67,8 +67,10 @@ ...@@ -67,8 +67,10 @@
"ethereumjs-wallet": "^1.0.1", "ethereumjs-wallet": "^1.0.1",
"hardhat": "^2.0.6", "hardhat": "^2.0.6",
"hardhat-gas-reporter": "^1.0.4", "hardhat-gas-reporter": "^1.0.4",
"keccak256": "^1.0.2",
"lodash.startcase": "^4.4.0", "lodash.startcase": "^4.4.0",
"lodash.zip": "^4.2.0", "lodash.zip": "^4.2.0",
"merkletreejs": "^0.2.13",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"mocha": "^8.0.1", "mocha": "^8.0.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
...@@ -76,6 +78,5 @@ ...@@ -76,6 +78,5 @@
"solidity-coverage": "^0.7.11", "solidity-coverage": "^0.7.11",
"solidity-docgen": "^0.5.3", "solidity-docgen": "^0.5.3",
"web3": "^1.3.0" "web3": "^1.3.0"
}, }
"dependencies": {}
} }
...@@ -30,7 +30,8 @@ const pathUpdates = { ...@@ -30,7 +30,8 @@ const pathUpdates = {
'payment/escrow/ConditionalEscrow.sol': 'utils/escrow/ConditionalEscrow.sol', 'payment/escrow/ConditionalEscrow.sol': 'utils/escrow/ConditionalEscrow.sol',
'payment/escrow/Escrow.sol': 'utils/escrow/Escrow.sol', 'payment/escrow/Escrow.sol': 'utils/escrow/Escrow.sol',
'payment/escrow/RefundEscrow.sol': 'utils/escrow/RefundEscrow.sol', 'payment/escrow/RefundEscrow.sol': 'utils/escrow/RefundEscrow.sol',
'payment/PaymentSplitter.sol': 'utils/PaymentSplitter.sol', 'payment/PaymentSplitter.sol': 'finance/PaymentSplitter.sol',
'utils/PaymentSplitter.sol': 'finance/PaymentSplitter.sol',
'payment/PullPayment.sol': 'security/PullPayment.sol', 'payment/PullPayment.sol': 'security/PullPayment.sol',
'presets/ERC1155PresetMinterPauser.sol': 'token/ERC1155/presets/ERC1155PresetMinterPauser.sol', 'presets/ERC1155PresetMinterPauser.sol': 'token/ERC1155/presets/ERC1155PresetMinterPauser.sol',
'presets/ERC20PresetFixedSupply.sol': 'token/ERC20/presets/ERC20PresetFixedSupply.sol', 'presets/ERC20PresetFixedSupply.sol': 'token/ERC20/presets/ERC20PresetFixedSupply.sol',
...@@ -40,7 +41,8 @@ const pathUpdates = { ...@@ -40,7 +41,8 @@ const pathUpdates = {
// 'proxy/BeaconProxy.sol': 'proxy/beacon/BeaconProxy.sol', // 'proxy/BeaconProxy.sol': 'proxy/beacon/BeaconProxy.sol',
// 'proxy/Clones.sol': undefined, // 'proxy/Clones.sol': undefined,
// 'proxy/IBeacon.sol': 'proxy/beacon/IBeacon.sol', // 'proxy/IBeacon.sol': 'proxy/beacon/IBeacon.sol',
'proxy/Initializable.sol': 'utils/Initializable.sol', 'proxy/Initializable.sol': 'proxy/utils/Initializable.sol',
'utils/Initializable.sol': 'proxy/utils/Initializable.sol',
// 'proxy/ProxyAdmin.sol': 'proxy/transparent/ProxyAdmin.sol', // 'proxy/ProxyAdmin.sol': 'proxy/transparent/ProxyAdmin.sol',
// 'proxy/Proxy.sol': undefined, // 'proxy/Proxy.sol': undefined,
// 'proxy/TransparentUpgradeableProxy.sol': 'proxy/transparent/TransparentUpgradeableProxy.sol', // 'proxy/TransparentUpgradeableProxy.sol': 'proxy/transparent/TransparentUpgradeableProxy.sol',
......
...@@ -179,6 +179,13 @@ function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized ...@@ -179,6 +179,13 @@ function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized
expect(bearers).to.have.members([authorized, otherAuthorized]); expect(bearers).to.have.members([authorized, otherAuthorized]);
}); });
it('role enumeration should be in sync after renounceRole call', async function () {
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
await this.accessControl.grantRole(ROLE, admin, { from: admin });
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('1');
await this.accessControl.renounceRole(ROLE, admin, { from: admin });
expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0');
});
}); });
} }
......
const { keccak256, keccakFromString, bufferToHex } = require('ethereumjs-util');
class MerkleTree {
constructor (elements) {
// Filter empty strings and hash elements
this.elements = elements.filter(el => el).map(el => keccakFromString(el));
// Sort elements
this.elements.sort(Buffer.compare);
// Deduplicate elements
this.elements = this.bufDedup(this.elements);
// Create layers
this.layers = this.getLayers(this.elements);
}
getLayers (elements) {
if (elements.length === 0) {
return [['']];
}
const layers = [];
layers.push(elements);
// Get next layer until we reach the root
while (layers[layers.length - 1].length > 1) {
layers.push(this.getNextLayer(layers[layers.length - 1]));
}
return layers;
}
getNextLayer (elements) {
return elements.reduce((layer, el, idx, arr) => {
if (idx % 2 === 0) {
// Hash the current element with its pair element
layer.push(this.combinedHash(el, arr[idx + 1]));
}
return layer;
}, []);
}
combinedHash (first, second) {
if (!first) { return second; }
if (!second) { return first; }
return keccak256(this.sortAndConcat(first, second));
}
getRoot () {
return this.layers[this.layers.length - 1][0];
}
getHexRoot () {
return bufferToHex(this.getRoot());
}
getProof (el) {
let idx = this.bufIndexOf(el, this.elements);
if (idx === -1) {
throw new Error('Element does not exist in Merkle tree');
}
return this.layers.reduce((proof, layer) => {
const pairElement = this.getPairElement(idx, layer);
if (pairElement) {
proof.push(pairElement);
}
idx = Math.floor(idx / 2);
return proof;
}, []);
}
getHexProof (el) {
const proof = this.getProof(el);
return this.bufArrToHexArr(proof);
}
getPairElement (idx, layer) {
const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1;
if (pairIdx < layer.length) {
return layer[pairIdx];
} else {
return null;
}
}
bufIndexOf (el, arr) {
let hash;
// Convert element to 32 byte hash if it is not one already
if (el.length !== 32 || !Buffer.isBuffer(el)) {
hash = keccakFromString(el);
} else {
hash = el;
}
for (let i = 0; i < arr.length; i++) {
if (hash.equals(arr[i])) {
return i;
}
}
return -1;
}
bufDedup (elements) {
return elements.filter((el, idx) => {
return idx === 0 || !elements[idx - 1].equals(el);
});
}
bufArrToHexArr (arr) {
if (arr.some(el => !Buffer.isBuffer(el))) {
throw new Error('Array is not an array of buffers');
}
return arr.map(el => '0x' + el.toString('hex'));
}
sortAndConcat (...args) {
return Buffer.concat([...args].sort(Buffer.compare));
}
}
module.exports = {
MerkleTree,
};
...@@ -171,6 +171,133 @@ contract('ERC777', function (accounts) { ...@@ -171,6 +171,133 @@ contract('ERC777', function (accounts) {
shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData);
}); });
}); });
describe('mint (internal extended)', function () {
const amount = new BN('5');
context('to anyone', function () {
beforeEach(async function () {
this.recipient = anyone;
});
context('with default operator', function () {
const operator = defaultOperatorA;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
);
});
});
context('with non operator', function () {
const operator = newOperator;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
);
});
});
});
context('to non ERC777TokensRecipient implementer', function () {
beforeEach(async function () {
this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new();
this.recipient = this.tokensRecipientImplementer.address;
});
context('with default operator', function () {
const operator = defaultOperatorA;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await expectRevert(
this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
),
'ERC777: token recipient contract has no implementer for ERC777TokensRecipient',
);
});
});
context('with non operator', function () {
const operator = newOperator;
it('without requireReceptionAck', async function () {
await this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
false,
{ from: operator },
);
});
it('with requireReceptionAck', async function () {
await expectRevert(
this.token.mintInternalExtended(
this.recipient,
amount,
data,
operatorData,
true,
{ from: operator },
),
'ERC777: token recipient contract has no implementer for ERC777TokensRecipient',
);
});
});
});
});
}); });
describe('operator management', function () { describe('operator management', function () {
......
require('@openzeppelin/test-helpers'); require('@openzeppelin/test-helpers');
const { MerkleTree } = require('../../helpers/merkleTree.js'); const { MerkleTree } = require('merkletreejs');
const { keccakFromString, bufferToHex } = require('ethereumjs-util'); const keccak256 = require('keccak256');
const { expect } = require('chai'); const { expect } = require('chai');
...@@ -15,43 +15,43 @@ contract('MerkleProof', function (accounts) { ...@@ -15,43 +15,43 @@ contract('MerkleProof', function (accounts) {
describe('verify', function () { describe('verify', function () {
it('returns true for a valid Merkle proof', async function () { it('returns true for a valid Merkle proof', async function () {
const elements = ['a', 'b', 'c', 'd']; const elements = ['a', 'b', 'c', 'd'];
const merkleTree = new MerkleTree(elements); const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true });
const root = merkleTree.getHexRoot(); const root = merkleTree.getHexRoot();
const proof = merkleTree.getHexProof(elements[0]); const leaf = keccak256(elements[0]);
const leaf = bufferToHex(keccakFromString(elements[0])); const proof = merkleTree.getHexProof(leaf);
expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true); expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true);
}); });
it('returns false for an invalid Merkle proof', async function () { it('returns false for an invalid Merkle proof', async function () {
const correctElements = ['a', 'b', 'c']; const correctElements = ['a', 'b', 'c'];
const correctMerkleTree = new MerkleTree(correctElements); const correctMerkleTree = new MerkleTree(correctElements, keccak256, { hashLeaves: true });
const correctRoot = correctMerkleTree.getHexRoot(); const correctRoot = correctMerkleTree.getHexRoot();
const correctLeaf = bufferToHex(keccakFromString(correctElements[0])); const correctLeaf = keccak256(correctElements[0]);
const badElements = ['d', 'e', 'f']; const badElements = ['d', 'e', 'f'];
const badMerkleTree = new MerkleTree(badElements); const badMerkleTree = new MerkleTree(badElements);
const badProof = badMerkleTree.getHexProof(badElements[0]); const badProof = badMerkleTree.getHexProof(badElements[0], keccak256, { hashLeaves: true });
expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false); expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false);
}); });
it('returns false for a Merkle proof of invalid length', async function () { it('returns false for a Merkle proof of invalid length', async function () {
const elements = ['a', 'b', 'c']; const elements = ['a', 'b', 'c'];
const merkleTree = new MerkleTree(elements); const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true });
const root = merkleTree.getHexRoot(); const root = merkleTree.getHexRoot();
const proof = merkleTree.getHexProof(elements[0]); const leaf = keccak256(elements[0]);
const badProof = proof.slice(0, proof.length - 5);
const leaf = bufferToHex(keccakFromString(elements[0])); const proof = merkleTree.getHexProof(leaf);
const badProof = proof.slice(0, proof.length - 5);
expect(await this.merkleProof.verify(badProof, root, leaf)).to.equal(false); expect(await this.merkleProof.verify(badProof, root, leaf)).to.equal(false);
}); });
......
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