Pārlūkot izejas kodu

Merge branch 'master' into release-v3.0.0

Nicolás Venturo 5 gadi atpakaļ
vecāks
revīzija
05085aa605
50 mainītis faili ar 727 papildinājumiem un 1003 dzēšanām
  1. 1 0
      .editorconfig
  2. 2 1
      CHANGELOG.md
  3. 8 71
      RELEASING.md
  4. 1 1
      contracts/GSN/IRelayHub.sol
  5. 1 1
      contracts/GSN/IRelayRecipient.sol
  6. 10 6
      contracts/access/AccessControl.sol
  7. 2 2
      contracts/introspection/ERC165Checker.sol
  8. 1 1
      contracts/introspection/IERC1820Registry.sol
  9. 0 4
      contracts/mocks/AccessControlMock.sol
  10. 0 4
      contracts/mocks/ERC20DecimalsMock.sol
  11. 8 0
      contracts/mocks/ERC20PausableMock.sol
  12. 1 1
      contracts/mocks/StringsMock.sol
  13. 6 2
      contracts/payment/PullPayment.sol
  14. 7 7
      contracts/presets/ERC20PresetMinterPauser.sol
  15. 30 15
      contracts/presets/ERC721PresetMinterPauserAutoId.sol
  16. 3 3
      contracts/presets/README.adoc
  17. 7 8
      contracts/token/ERC20/ERC20.sol
  18. 1 1
      contracts/token/ERC20/ERC20Burnable.sol
  19. 1 2
      contracts/token/ERC20/ERC20Pausable.sol
  20. 48 11
      contracts/token/ERC20/ERC20Snapshot.sol
  21. 43 14
      contracts/token/ERC721/ERC721.sol
  22. 12 2
      contracts/token/ERC721/ERC721Pausable.sol
  23. 1 1
      contracts/token/ERC721/IERC721.sol
  24. 7 3
      contracts/token/ERC777/ERC777.sol
  25. 1 1
      contracts/token/ERC777/IERC777.sol
  26. 2 2
      contracts/utils/Address.sol
  27. 24 0
      contracts/utils/EnumerableMap.sol
  28. 10 3
      contracts/utils/EnumerableSet.sol
  29. 33 9
      contracts/utils/README.adoc
  30. 1 2
      contracts/utils/Strings.sol
  31. 9 9
      docs/contract.hbs
  32. 2 3
      docs/modules/ROOT/nav.adoc
  33. 5 5
      docs/modules/ROOT/pages/access-control.adoc
  34. 8 4
      docs/modules/ROOT/pages/erc20-supply.adoc
  35. 4 4
      docs/modules/ROOT/pages/erc777.adoc
  36. 123 0
      docs/modules/ROOT/pages/extending-contracts.adoc
  37. 12 4
      docs/modules/ROOT/pages/gsn-strategies.adoc
  38. 2 2
      docs/modules/ROOT/pages/releases-stability.adoc
  39. 0 7
      docs/modules/ROOT/pages/using-hooks.adoc
  40. 167 729
      package-lock.json
  41. 3 3
      package.json
  42. 7 7
      scripts/gen-nav.js
  43. 1 3
      scripts/release/synchronize-versions.js
  44. 0 9
      test/access/AccessControl.test.js
  45. 5 5
      test/presets/ERC20PresetMinterPauser.test.js
  46. 34 21
      test/presets/ERC721PresetMinterPauserAutoId.js
  47. 0 6
      test/token/ERC20/ERC20.test.js
  48. 54 0
      test/token/ERC20/ERC20Pausable.test.js
  49. 2 2
      test/token/ERC721/ERC721.test.js
  50. 17 2
      test/token/ERC721/ERC721Pausable.test.js

+ 1 - 0
.editorconfig

@@ -9,6 +9,7 @@ end_of_line = lf
 indent_style = space
 insert_final_newline = true
 trim_trailing_whitespace = true
+max_line_length = 120
 
 [*.sol]
 indent_size = 4

+ 2 - 1
CHANGELOG.md

@@ -35,6 +35,7 @@
  * `ERC721`: added a constructor for `name` and `symbol`. ([#2160](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2160))
  * `ERC20Detailed`: this contract was removed and its functionality merged into `ERC20`. ([#2161](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2161))
  * `ERC20`: added a constructor for `name` and `symbol`. `decimals` now defaults to 18. ([#2161](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2161))
+ * `Strings`: renamed `fromUint256` to `toString` ([#2188](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2188))
 
 ## 2.5.0 (2020-02-04)
 
@@ -127,7 +128,7 @@ Refer to the table below to adjust your inheritance list.
 ### Improvements
  * Upgraded the minimum compiler version to v0.5.2: this removes many Solidity warnings that were false positives. ([#1606](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1606))
  * `ECDSA`: `recover` no longer accepts malleable signatures (those using upper-range values for `s`, or 0/1 for `v`). ([#1622](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1622))
- * `ERC721`'s transfers are now more gas efficient due to removal of unnecessary `SafeMath` calls. ([#1610](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1610))
+ * ``ERC721``'s transfers are now more gas efficient due to removal of unnecessary `SafeMath` calls. ([#1610](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1610))
  * Fixed variable shadowing issues. ([#1606](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1606))
 
 ### Bugfixes

+ 8 - 71
RELEASING.md

@@ -1,83 +1,20 @@
 # Releasing
 
-This document describes our release process, and contains the steps to be followed by an OpenZeppelin maintainer at the several stages of a release.
+> Visit the documentation for [details about release schedule].
 
-We release a new version of OpenZeppelin monthly. Release cycles are tracked in the [issue milestones](https://github.com/OpenZeppelin/openzeppelin-contracts/milestones).
+Start on an up-to-date `master` branch.
 
-Each release has at least one release candidate published first, intended for community review and any critical fixes that may come out of it. At the moment we leave 1 week between the first release candidate and the final release.
+Create the release branch with `npm run release start minor`.
 
-Before starting make sure to verify the following items.
-* Your local `master` branch is in sync with your `upstream` remote (it may have another name depending on your setup).
-* Your repo is clean, particularly with no untracked files in the contracts and tests directories. Verify with `git clean -n`.
+Publish a release candidate with `npm run release rc`.
 
+Publish the final release with `npm run release final`.
 
-## Creating the release branch
+Follow the general [OpenZeppelin release checklist].
 
-We'll refer to a release `vX.Y.Z`.
+[details about release schedule]: https://docs.openzeppelin.com/contracts/releases-stability
+[OpenZeppelin release checklist]: https://github.com/OpenZeppelin/code-style/blob/master/RELEASE_CHECKLIST.md
 
-```
-git checkout master
-git checkout -b release-vX.Y.Z
-```
-
-## Creating a release candidate
-
-Once in the release branch, change the version string in `package.json`, `package-lock.json` and `ethpm.json` to `X.Y.Z-rc.R`. (This will be `X.Y.Z-rc.1` for the first release candidate.) Commit these changes and tag the commit as `vX.Y.Z-rc.R`.
-
-```
-git add package.json package-lock.json ethpm.json
-git commit -m "Release candidate vX.Y.Z-rc.R"
-git tag -a vX.Y.Z-rc.R
-git push upstream release-vX.Y.Z
-git push upstream vX.Y.Z-rc.R
-```
-
-Draft the release notes in our [GitHub releases](https://github.com/OpenZeppelin/openzeppelin-contracts/releases). Make sure to mark it as a pre-release! Try to be consistent with our previous release notes in the title and format of the text. Release candidates don't need a detailed changelog, but make sure to include a link to GitHub's compare page.
-
-Once the CI run for the new tag is green, publish on npm under the `next` tag. You should see the contracts compile automatically.
-
-```
-npm publish --tag next
-```
-
-Publish the release notes on GitHub and the forum, and ask our community manager to announce the release candidate on at least Twitter.
-
-## Creating the final release
-
-Make sure to have the latest changes from `upstream` in your local release branch.
-
-```
-git checkout release-vX.Y.Z
-git pull upstream
-```
-
-Before starting the release process, make one final commit to CHANGELOG.md, including the date of the release.
-
-Change the version string in `package.json`, `package-lock.json` and `ethpm.json` removing the "-rc.R" suffix. Commit these changes and tag the commit as `vX.Y.Z`.
-
-```
-git add package.json package-lock.json ethpm.json
-git commit -m "Release vX.Y.Z"
-git tag -a vX.Y.Z
-git push upstream release-vX.Y.Z
-git push upstream vX.Y.Z
-```
-
-Draft the release notes in GitHub releases. Try to be consistent with our previous release notes in the title and format of the text. Make sure to include a detailed changelog.
-
-Once the CI run for the new tag is green, publish on npm. You should see the contracts compile automatically.
-
-```
-npm publish
-```
-
-Publish the release notes on GitHub and ask our community manager to announce the release!
-
-Delete the `next` tag in the npm package as there is no longer a release candidate.
-
-```
-npm dist-tag rm --otp $2FA_CODE @openzeppelin/contracts next
-```
 
 ## Merging the release branch
 

+ 1 - 1
contracts/GSN/IRelayHub.sol

@@ -250,7 +250,7 @@ interface IRelayHub {
     function penalizeRepeatedNonce(bytes calldata unsignedTx1, bytes calldata signature1, bytes calldata unsignedTx2, bytes calldata signature2) external;
 
     /**
-     * @dev Penalize a relay that sent a transaction that didn't target `RelayHub`'s {registerRelay} or {relayCall}.
+     * @dev Penalize a relay that sent a transaction that didn't target ``RelayHub``'s {registerRelay} or {relayCall}.
      */
     function penalizeIllegalTransaction(bytes calldata unsignedTx, bytes calldata signature) external;
 

+ 1 - 1
contracts/GSN/IRelayRecipient.sol

@@ -17,7 +17,7 @@ interface IRelayRecipient {
      *
      * The relay request was originated by `from` and will be served by `relay`. `encodedFunction` is the relayed call
      * calldata, so its first four bytes are the function selector. The relayed call will be forwarded `gasLimit` gas,
-     * and the transaction executed with a gas price of at least `gasPrice`. `relay`'s fee is `transactionFee`, and the
+     * and the transaction executed with a gas price of at least `gasPrice`. ``relay``'s fee is `transactionFee`, and the
      * recipient will be charged at most `maxPossibleCharge` (in wei). `nonce` is the sender's (`from`) nonce for
      * replay attack protection in {IRelayHub}, and `approvalData` is a optional parameter that can be used to hold a signature
      * over all or some of the previous values.

+ 10 - 6
contracts/access/AccessControl.sol

@@ -114,7 +114,7 @@ abstract contract AccessControl is Context {
      *
      * Requirements:
      *
-     * - the caller must have `role`'s admin role.
+     * - the caller must have ``role``'s admin role.
      */
     function grantRole(bytes32 role, address account) public virtual {
         require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
@@ -129,7 +129,7 @@ abstract contract AccessControl is Context {
      *
      * Requirements:
      *
-     * - the caller must have `role`'s admin role.
+     * - the caller must have ``role``'s admin role.
      */
     function revokeRole(bytes32 role, address account) public virtual {
         require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
@@ -164,17 +164,21 @@ abstract contract AccessControl is Context {
      * event. Note that unlike {grantRole}, this function doesn't perform any
      * checks on the calling account.
      *
-     * Requirements:
+     * [WARNING]
+     * ====
+     * This function should only be called from the constructor when setting
+     * up the initial roles for the system.
      *
-     * - this function can only be called from a constructor.
+     * Using this function in any other way is effectively circumventing the admin
+     * system imposed by {AccessControl}.
+     * ====
      */
     function _setupRole(bytes32 role, address account) internal virtual {
-        require(!address(this).isContract(), "AccessControl: roles cannot be setup after construction");
         _grantRole(role, account);
     }
 
     /**
-     * @dev Sets `adminRole` as `role`'s admin role.
+     * @dev Sets `adminRole` as ``role``'s admin role.
      */
     function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
         _roles[role].adminRole = adminRole;

+ 2 - 2
contracts/introspection/ERC165Checker.sol

@@ -1,4 +1,4 @@
-pragma solidity ^0.6.0;
+pragma solidity ^0.6.2;
 
 /**
  * @dev Library used to query support of an interface declared via {IERC165}.
@@ -97,7 +97,7 @@ library ERC165Checker {
         returns (bool, bool)
     {
         bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
-        (bool success, bytes memory result) = account.staticcall.gas(30000)(encodedParams);
+        (bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);
         if (result.length < 32) return (false, false);
         return (success, abi.decode(result, (bool)));
     }

+ 1 - 1
contracts/introspection/IERC1820Registry.sol

@@ -38,7 +38,7 @@ interface IERC1820Registry {
     function getManager(address account) external view returns (address);
 
     /**
-     * @dev Sets the `implementer` contract as `account`'s implementer for
+     * @dev Sets the `implementer` contract as ``account``'s implementer for
      * `interfaceHash`.
      *
      * `account` being the zero address is an alias for the caller's address.

+ 0 - 4
contracts/mocks/AccessControlMock.sol

@@ -10,8 +10,4 @@ contract AccessControlMock is AccessControl {
     function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public {
         _setRoleAdmin(roleId, adminRoleId);
     }
-
-    function setupRole(bytes32 roleId, address account) public {
-        _setupRole(roleId, account);
-    }
 }

+ 0 - 4
contracts/mocks/ERC20DecimalsMock.sol

@@ -6,8 +6,4 @@ contract ERC20DecimalsMock is ERC20 {
     constructor (string memory name, string memory symbol, uint8 decimals) public ERC20(name, symbol) {
         _setupDecimals(decimals);
     }
-
-    function setupDecimals(uint8 decimals) public {
-        _setupDecimals(decimals);
-    }
 }

+ 8 - 0
contracts/mocks/ERC20PausableMock.sol

@@ -20,4 +20,12 @@ contract ERC20PausableMock is ERC20Pausable {
     function unpause() external {
         _unpause();
     }
+
+    function mint(address to, uint256 amount) public {
+        _mint(to, amount);
+    }
+
+    function burn(address from, uint256 amount) public {
+        _burn(from, amount);
+    }
 }

+ 1 - 1
contracts/mocks/StringsMock.sol

@@ -4,6 +4,6 @@ import "../utils/Strings.sol";
 
 contract StringsMock {
     function fromUint256(uint256 value) public pure returns (string memory) {
-        return Strings.fromUint256(value);
+        return Strings.toString(value);
     }
 }

+ 6 - 2
contracts/payment/PullPayment.sol

@@ -1,4 +1,4 @@
-pragma solidity ^0.6.0;
+pragma solidity ^0.6.2;
 
 import "./escrow/Escrow.sol";
 
@@ -62,6 +62,10 @@ contract PullPayment {
      * @param amount The amount to transfer.
      */
     function _asyncTransfer(address dest, uint256 amount) internal virtual {
-        _escrow.deposit.value(amount)(dest);
+        // solhint-disable-previous-line no-unused-vars
+
+        // TODO: remove the previous linter directive once
+        // https://github.com/protofire/solhint/issues/170 is fixed
+        _escrow.deposit{ value: amount }(dest);
     }
 }

+ 7 - 7
contracts/deploy-ready/ERC20MinterPauser.sol → contracts/presets/ERC20PresetMinterPauser.sol

@@ -16,11 +16,11 @@ import "../token/ERC20/ERC20Pausable.sol";
  * This contract uses {AccessControl} to lock permissioned functions using the
  * different roles - head to its documentation for details.
  *
- * The account that deploys the contract will be granted the minter role, the
- * pauser role, and the default admin role, meaning it will be able to grant
- * both the minter and pauser roles.
+ * The account that deploys the contract will be granted the minter and pauser
+ * roles, as well as the default admin role, which will let it grant both minter
+ * and pauser roles to aother accounts
  */
-contract ERC20MinterPauser is Context, AccessControl, ERC20Burnable, ERC20Pausable {
+contract ERC20PresetMinterPauser is Context, AccessControl, ERC20Burnable, ERC20Pausable {
     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
     bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
 
@@ -47,7 +47,7 @@ contract ERC20MinterPauser is Context, AccessControl, ERC20Burnable, ERC20Pausab
      * - the caller must have the `MINTER_ROLE`.
      */
     function mint(address to, uint256 amount) public {
-        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20MinterPauser: must have minter role to mint");
+        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
         _mint(to, amount);
     }
 
@@ -61,7 +61,7 @@ contract ERC20MinterPauser is Context, AccessControl, ERC20Burnable, ERC20Pausab
      * - the caller must have the `PAUSER_ROLE`.
      */
     function pause() public {
-        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterPauser: must have pauser role to pause");
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
         _pause();
     }
 
@@ -75,7 +75,7 @@ contract ERC20MinterPauser is Context, AccessControl, ERC20Burnable, ERC20Pausab
      * - the caller must have the `PAUSER_ROLE`.
      */
     function unpause() public {
-        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20MinterPauser: must have pauser role to unpause");
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
         _unpause();
     }
 

+ 30 - 15
contracts/deploy-ready/ERC721MinterPauser.sol → contracts/presets/ERC721PresetMinterPauserAutoId.sol

@@ -2,6 +2,7 @@ pragma solidity ^0.6.0;
 
 import "../access/AccessControl.sol";
 import "../GSN/Context.sol";
+import "../utils/Counters.sol";
 import "../token/ERC721/ERC721.sol";
 import "../token/ERC721/ERC721Burnable.sol";
 import "../token/ERC721/ERC721Pausable.sol";
@@ -12,33 +13,43 @@ import "../token/ERC721/ERC721Pausable.sol";
  *  - ability for holders to burn (destroy) their tokens
  *  - a minter role that allows for token minting (creation)
  *  - a pauser role that allows to stop all token transfers
+ *  - token ID and URI autogeneration
  *
  * This contract uses {AccessControl} to lock permissioned functions using the
  * different roles - head to its documentation for details.
  *
- * The account that deploys the contract will be granted the minter role, the
- * pauser role, and the default admin role, meaning it will be able to grant
- * both the minter and pauser roles.
+ * The account that deploys the contract will be granted the minter and pauser
+ * roles, as well as the default admin role, which will let it grant both minter
+ * and pauser roles to aother accounts
  */
-contract ERC721MinterPauser is Context, AccessControl, ERC721Burnable, ERC721Pausable {
+contract ERC721PresetMinterPauserAutoId is Context, AccessControl, ERC721Burnable, ERC721Pausable {
+    using Counters for Counters.Counter;
+
     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
     bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
 
+    Counters.Counter private _tokenIdTracker;
+
     /**
-     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
-     * account that deploys the contract.
+     * @dev Grants `DEFAULT_ADMIN_ROLE` and `MINTER_ROLE`to the account that
+     * deploys the contract.
      *
-     * See {ERC721-constructor}.
+     * Token URIs will be autogenerated based on `baseURI` and their token IDs.
+     * See {ERC721-tokenURI}.
      */
-    constructor(string memory name, string memory symbol) public ERC721(name, symbol) {
+    constructor(string memory name, string memory symbol, string memory baseURI) public ERC721(name, symbol) {
         _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
 
         _setupRole(MINTER_ROLE, _msgSender());
         _setupRole(PAUSER_ROLE, _msgSender());
+
+        _setBaseURI(baseURI);
     }
 
     /**
-     * @dev Creates the `tokenId` tokens for `to`.
+     * @dev Creates a new token for `to`. Its token ID will be automatically
+     * assigned (and available on the emitted {Transfer} event), and the token
+     * URI autogenerated based on the base URI passed at construction.
      *
      * See {ERC721-_mint}.
      *
@@ -46,9 +57,13 @@ contract ERC721MinterPauser is Context, AccessControl, ERC721Burnable, ERC721Pau
      *
      * - the caller must have the `MINTER_ROLE`.
      */
-    function mint(address to, uint256 tokenId) public {
-        require(hasRole(MINTER_ROLE, _msgSender()), "ERC721MinterPauser: must have minter role to mint");
-        _mint(to, tokenId);
+    function mint(address to) public {
+        require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint");
+
+        // We can just use balanceOf to create the new tokenId because tokens
+        // can be burned (destroyed), so we need a separate counter.
+        _mint(to, _tokenIdTracker.current());
+        _tokenIdTracker.increment();
     }
 
     /**
@@ -61,21 +76,21 @@ contract ERC721MinterPauser is Context, AccessControl, ERC721Burnable, ERC721Pau
      * - the caller must have the `PAUSER_ROLE`.
      */
     function pause() public {
-        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721MinterPauser: must have pauser role to pause");
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause");
         _pause();
     }
 
     /**
      * @dev Unpauses all token transfers.
      *
-     * See {ERC20Pausable} and {Pausable-_unpause}.
+     * See {ERC721Pausable} and {Pausable-_unpause}.
      *
      * Requirements:
      *
      * - the caller must have the `PAUSER_ROLE`.
      */
     function unpause() public {
-        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721MinterPauser: must have pauser role to unpause");
+        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause");
         _unpause();
     }
 

+ 3 - 3
contracts/deploy-ready/README.adoc → contracts/presets/README.adoc

@@ -1,4 +1,4 @@
-= Deploy Ready
+= Presets
 
 These contracts integrate different Ethereum standards (ERCs) with custom extensions and modules, showcasing common configurations that are ready to deploy **without having to write any Solidity code**.
 
@@ -8,6 +8,6 @@ TIP: Intermediate and advanced users can use these as starting points when writi
 
 == Tokens
 
-{{ERC20MinterPauser}}
+{{ERC20PresetMinterPauser}}
 
-{{ERC721MinterPauser}}
+{{ERC721PresetMinterPauserAutoId}}

+ 7 - 8
contracts/token/ERC20/ERC20.sol

@@ -145,7 +145,7 @@ contract ERC20 is Context, IERC20 {
      * Requirements:
      * - `sender` and `recipient` cannot be the zero address.
      * - `sender` must have a balance of at least `amount`.
-     * - the caller must have allowance for `sender`'s tokens of at least
+     * - the caller must have allowance for ``sender``'s tokens of at least
      * `amount`.
      */
     function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
@@ -279,12 +279,11 @@ contract ERC20 is Context, IERC20 {
     /**
      * @dev Sets {decimals} to a value other than the default one of 18.
      *
-     * Requirements:
-     *
-     * - this function can only be called from a constructor.
+     * WARNING: This function should only be called from the constructor. Most
+     * applications that interact with token contracts will not expect
+     * {decimals} to ever change, and may work incorrectly if it does.
      */
     function _setupDecimals(uint8 decimals_) internal {
-        require(!address(this).isContract(), "ERC20: decimals cannot be changed after construction");
         _decimals = decimals_;
     }
 
@@ -294,13 +293,13 @@ contract ERC20 is Context, IERC20 {
      *
      * Calling conditions:
      *
-     * - when `from` and `to` are both non-zero, `amount` of `from`'s tokens
+     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
      * will be to transferred to `to`.
      * - when `from` is zero, `amount` tokens will be minted for `to`.
-     * - when `to` is zero, `amount` of `from`'s tokens will be burned.
+     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
      * - `from` and `to` are never both zero.
      *
-     * To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
      */
     function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
 }

+ 1 - 1
contracts/token/ERC20/ERC20Burnable.sol

@@ -26,7 +26,7 @@ abstract contract ERC20Burnable is Context, ERC20 {
      *
      * Requirements:
      *
-     * - the caller must have allowance for `accounts`'s tokens of at least
+     * - the caller must have allowance for ``accounts``'s tokens of at least
      * `amount`.
      */
     function burnFrom(address account, uint256 amount) public virtual {

+ 1 - 2
contracts/token/ERC20/ERC20Pausable.sol

@@ -4,8 +4,7 @@ import "./ERC20.sol";
 import "../../utils/Pausable.sol";
 
 /**
- * @title Pausable token
- * @dev ERC20 with pausable transfers and allowances.
+ * @dev ERC20 token with pausable token transfers, minting and burning.
  *
  * Useful for scenarios such as preventing trades until the end of an evaluation
  * period, or having an emergency switch for freezing all token transfers in the

+ 48 - 11
contracts/token/ERC20/ERC20Snapshot.sol

@@ -6,16 +6,28 @@ import "../../utils/Counters.sol";
 import "./ERC20.sol";
 
 /**
- * @dev ERC20 token with snapshots.
+ * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and
+ * total supply at the time are recorded for later access.
  *
- * When a snapshot is made, the balances and total supply at the time of the snapshot are recorded for later
- * access.
+ * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting.
+ * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different
+ * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be
+ * used to create an efficient ERC20 forking mechanism.
  *
- * To make a snapshot, call the {snapshot} function, which will emit the {Snapshot} event and return a snapshot id.
- * To get the total supply from a snapshot, call the function {totalSupplyAt} with the snapshot id.
- * To get the balance of an account from a snapshot, call the {balanceOfAt} function with the snapshot id and the
- * account address.
- * @author Validity Labs AG <info@validitylabs.org>
+ * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a
+ * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot
+ * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id
+ * and the account address.
+ *
+ * ==== Gas Costs
+ *
+ * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log
+ * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much
+ * smaller since identical balances in subsequent snapshots are stored as a single entry.
+ *
+ * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is
+ * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent
+ * transfers will have normal cost until the next snapshot, and so on.
  */
 abstract contract ERC20Snapshot is ERC20 {
     // Inspired by Jordi Baylina's MiniMeToken to record historical balances:
@@ -38,12 +50,31 @@ abstract contract ERC20Snapshot is ERC20 {
     // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid.
     Counters.Counter private _currentSnapshotId;
 
+    /**
+     * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created.
+     */
     event Snapshot(uint256 id);
 
     /**
-     * @dev Creates a new snapshot id. Balances are only stored in snapshots on demand: unless a snapshot was taken, a
-     * balance change will not be recorded. This means the extra added cost of storing snapshotted balances is only paid
-     * when required, but is also flexible enough that it allows for e.g. daily snapshots.
+     * @dev Creates a new snapshot and returns its snapshot id.
+     *
+     * Emits a {Snapshot} event that contains the same id.
+     *
+     * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a
+     * set of accounts, for example using {AccessControl}, or it may be open to the public.
+     *
+     * [WARNING]
+     * ====
+     * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking,
+     * you must consider that it can potentially be used by attackers in two ways.
+     *
+     * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow
+     * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target
+     * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs
+     * section above.
+     *
+     * We haven't measured the actual numbers; if this is something you're interested in please reach out to us.
+     * ====
      */
     function _snapshot() internal virtual returns (uint256) {
         _currentSnapshotId.increment();
@@ -53,12 +84,18 @@ abstract contract ERC20Snapshot is ERC20 {
         return currentId;
     }
 
+    /**
+     * @dev Retrieves the balance of `account` at the time `snapshotId` was created.
+     */
     function balanceOfAt(address account, uint256 snapshotId) public view returns (uint256) {
         (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]);
 
         return snapshotted ? value : balanceOf(account);
     }
 
+    /**
+     * @dev Retrieves the total supply at the time `snapshotId` was created.
+     */
     function totalSupplyAt(uint256 snapshotId) public view returns(uint256) {
         (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots);
 

+ 43 - 14
contracts/token/ERC721/ERC721.sol

@@ -10,6 +10,7 @@ import "../../math/SafeMath.sol";
 import "../../utils/Address.sol";
 import "../../utils/EnumerableSet.sol";
 import "../../utils/EnumerableMap.sol";
+import "../../utils/Strings.sol";
 
 /**
  * @title ERC721 Non-Fungible Token Standard basic implementation
@@ -20,6 +21,7 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
     using Address for address;
     using EnumerableSet for EnumerableSet.UintSet;
     using EnumerableMap for EnumerableMap.UintToAddressMap;
+    using Strings for uint256;
 
     // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
     // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
@@ -132,29 +134,55 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
     /**
      * @dev Returns the URI for a given token ID. May return an empty string.
      *
-     * If the token's URI is non-empty and a base URI was set (via
-     * {_setBaseURI}), it will be added to the token ID's URI as a prefix.
+     * If a base URI is set (via {_setBaseURI}), it is added as a prefix to the
+     * token's own URI (via {_setTokenURI}).
      *
-     * Reverts if the token ID does not exist.
+     * If there is a base URI but no token URI, the token's ID will be used as
+     * its URI when appending it to the base URI. This pattern for autogenerated
+     * token URIs can lead to large gas savings.
+     *
+     * .Examples
+     * |===
+     * |`_setBaseURI()` |`_setTokenURI()` |`tokenURI()`
+     * | ""
+     * | ""
+     * | ""
+     * | ""
+     * | "token.uri/123"
+     * | "token.uri/123"
+     * | "token.uri/"
+     * | "123"
+     * | "token.uri/123"
+     * | "token.uri/"
+     * | ""
+     * | "token.uri/<tokenId>"
+     * |===
+     *
+     * Requirements:
+     *
+     * - `tokenId` must exist.
      */
     function tokenURI(uint256 tokenId) public view override returns (string memory) {
         require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
 
         string memory _tokenURI = _tokenURIs[tokenId];
 
-        // Even if there is a base URI, it is only appended to non-empty token-specific URIs
-        if (bytes(_tokenURI).length == 0) {
-            return "";
-        } else {
-            // abi.encodePacked is being used to concatenate strings
+        // If there is no base URI, return the token URI.
+        if (bytes(_baseURI).length == 0) {
+            return _tokenURI;
+        }
+        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
+        if (bytes(_tokenURI).length > 0) {
             return string(abi.encodePacked(_baseURI, _tokenURI));
         }
+        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
+        return string(abi.encodePacked(_baseURI, tokenId.toString()));
     }
 
     /**
     * @dev Returns the base URI set via {_setBaseURI}. This will be
-    * automatically added as a preffix in {tokenURI} to each token's URI, when
-    * they are non-empty.
+    * automatically added as a prefix in {tokenURI} to each token's URI, or
+    * to the token ID if no specific URI is set for that token ID.
     */
     function baseURI() public view returns (string memory) {
         return _baseURI;
@@ -444,7 +472,8 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
 
     /**
      * @dev Internal function to set the base URI for all token IDs. It is
-     * automatically added as a prefix to the value returned in {tokenURI}.
+     * automatically added as a prefix to the value returned in {tokenURI},
+     * or to the token ID if {tokenURI} is empty.
      */
     function _setBaseURI(string memory baseURI_) internal virtual {
         _baseURI = baseURI_;
@@ -501,13 +530,13 @@ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable
      *
      * Calling conditions:
      *
-     * - when `from` and `to` are both non-zero, `from`'s `tokenId` will be
+     * - when `from` and `to` are both non-zero, ``from``'s `tokenId` will be
      * transferred to `to`.
      * - when `from` is zero, `tokenId` will be minted for `to`.
-     * - when `to` is zero, `from`'s `tokenId` will be burned.
+     * - when `to` is zero, ``from``'s `tokenId` will be burned.
      * - `from` and `to` are never both zero.
      *
-     * To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
      */
     function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
 }

+ 12 - 2
contracts/token/ERC721/ERC721Pausable.sol

@@ -4,10 +4,20 @@ import "./ERC721.sol";
 import "../../utils/Pausable.sol";
 
 /**
- * @title ERC721 Non-Fungible Pausable token
- * @dev ERC721 modified with pausable transfers.
+ * @dev ERC721 token with pausable token transfers, minting and burning.
+ *
+ * Useful for scenarios such as preventing trades until the end of an evaluation
+ * period, or having an emergency switch for freezing all token transfers in the
+ * event of a large bug.
  */
 abstract contract ERC721Pausable is ERC721, Pausable {
+    /**
+     * @dev See {ERC721-_beforeTokenTransfer}.
+     *
+     * Requirements:
+     *
+     * - the contract must not be paused.
+     */
     function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {
         super._beforeTokenTransfer(from, to, tokenId);
 

+ 1 - 1
contracts/token/ERC721/IERC721.sol

@@ -11,7 +11,7 @@ interface IERC721 is IERC165 {
     event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
 
     /**
-     * @dev Returns the number of NFTs in `owner`'s account.
+     * @dev Returns the number of NFTs in ``owner``'s account.
      */
     function balanceOf(address owner) external view returns (uint256 balance);
 

+ 7 - 3
contracts/token/ERC777/ERC777.sol

@@ -322,6 +322,8 @@ contract ERC777 is Context, IERC777, IERC20 {
 
         address operator = _msgSender();
 
+        _beforeTokenTransfer(operator, address(0), account, amount);
+
         // Update state variables
         _totalSupply = _totalSupply.add(amount);
         _balances[account] = _balances[account].add(amount);
@@ -382,6 +384,8 @@ contract ERC777 is Context, IERC777, IERC20 {
 
         address operator = _msgSender();
 
+        _beforeTokenTransfer(operator, from, address(0), amount);
+
         _callTokensToSend(operator, from, address(0), amount, data, operatorData);
 
         // Update state variables
@@ -482,13 +486,13 @@ contract ERC777 is Context, IERC777, IERC20 {
      *
      * Calling conditions:
      *
-     * - when `from` and `to` are both non-zero, `from`'s `tokenId` will be
+     * - when `from` and `to` are both non-zero, ``from``'s `tokenId` will be
      * transferred to `to`.
      * - when `from` is zero, `tokenId` will be minted for `to`.
-     * - when `to` is zero, `from`'s `tokenId` will be burned.
+     * - when `to` is zero, ``from``'s `tokenId` will be burned.
      * - `from` and `to` are never both zero.
      *
-     * To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
      */
     function _beforeTokenTransfer(address operator, address from, address to, uint256 tokenId) internal virtual { }
 }

+ 1 - 1
contracts/token/ERC777/IERC777.sol

@@ -96,7 +96,7 @@ interface IERC777 {
     function authorizeOperator(address operator) external;
 
     /**
-     * @dev Make an account an operator of the caller.
+     * @dev Revoke an account's operator status for the caller.
      *
      * See {isOperatorFor} and {defaultOperators}.
      *

+ 2 - 2
contracts/utils/Address.sol

@@ -1,4 +1,4 @@
-pragma solidity ^0.6.0;
+pragma solidity ^0.6.2;
 
 /**
  * @dev Collection of functions related to the address type
@@ -52,7 +52,7 @@ library Address {
         require(address(this).balance >= amount, "Address: insufficient balance");
 
         // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
-        (bool success, ) = recipient.call.value(amount)("");
+        (bool success, ) = recipient.call{ value: amount }("");
         require(success, "Address: unable to send value, recipient may have reverted");
     }
 }

+ 24 - 0
contracts/utils/EnumerableMap.sol

@@ -1,5 +1,29 @@
 pragma solidity ^0.6.0;
 
+/**
+ * @dev Library for managing an enumerable variant of Solidity's
+ * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
+ * type.
+ *
+ * Maps have the following properties:
+ *
+ * - Entries are added, removed, and checked for existence in constant time
+ * (O(1)).
+ * - Entries are enumerated in O(n). No guarantees are made on the ordering.
+ *
+ * ```
+ * contract Example {
+ *     // Add the library methods
+ *     using EnumerableMap for EnumerableMap.UintToAddressMap;
+ *
+ *     // Declare a set state variable
+ *     EnumerableMap.UintToAddressMap private myMap;
+ * }
+ * ```
+ *
+ * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
+ * supported.
+ */
 library EnumerableMap {
     // To implement this library for multiple types with as little code
     // repetition as possible, we write it in terms of a generic Map type with

+ 10 - 3
contracts/utils/EnumerableSet.sol

@@ -11,11 +11,18 @@ pragma solidity ^0.6.0;
  * (O(1)).
  * - Elements are enumerated in O(n). No guarantees are made on the ordering.
  *
- * As of v2.5.0, only `address` sets are supported.
+ * ```
+ * contract Example {
+ *     // Add the library methods
+ *     using EnumerableSet for EnumerableSet.AddressSet;
  *
- * Include with `using EnumerableSet for EnumerableSet.AddressSet;`.
+ *     // Declare a set state variable
+ *     EnumerableSet.AddressSet private mySet;
+ * }
+ * ```
  *
- * @author Alberto Cuesta Cañada
+ * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
+ * (`UintSet`) are supported.
  */
 library EnumerableSet {
     // To implement this library for multiple types with as little code

+ 33 - 9
contracts/utils/README.adoc

@@ -1,25 +1,49 @@
 = Utilities
 
-Miscellaneous contracts containing utility functions, often related to working with different data types.
+Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives.
+
+Security tools include:
+
+ * {Pausable}: provides a simple way to halt activity in your contracts (often in reponse to an external threat).
+ * {ReentrancyGuard}: protects you from https://blog.openzeppelin.com/reentrancy-after-istanbul/[reentrant calls].
+
+The {Address}, {Arrays} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types.
+
+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.
+ * {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.
+
+[NOTE]
+====
+Because Solidity does not support generic types, {EnumerableMap} and {EnumerableSet} are specialized to a limited number of key-value types.
+
+As of v3.0, {EnumerableMap} supports `uint256 -> address` (`UintToAddressMap`), and {EnumerableSet} supports `address` and `uint256` (`AddressSet` and `UintSet`).
+====
+
+Finally, {Create2} contains all necessary utilities to safely use the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode], without having to deal with low-level assembly.
 
 == Contracts
 
-{{Address}}
+{{Pausable}}
 
-{{SafeCast}}
+{{ReentrancyGuard}}
+
+== Libraries
+
+{{Address}}
 
 {{Arrays}}
 
 {{Counters}}
 
-{{Strings}}
-
-{{EnumerableSet}}
+{{Create2}}
 
 {{EnumerableMap}}
 
-{{Create2}}
+{{EnumerableSet}}
 
-{{ReentrancyGuard}}
+{{SafeCast}}
 
-{{Pausable}}
+{{Strings}}

+ 1 - 2
contracts/utils/Strings.sol

@@ -1,14 +1,13 @@
 pragma solidity ^0.6.0;
 
 /**
- * @title Strings
  * @dev String operations.
  */
 library Strings {
     /**
      * @dev Converts a `uint256` to its ASCII `string` representation.
      */
-    function fromUint256(uint256 value) internal pure returns (string memory) {
+    function toString(uint256 value) internal pure returns (string memory) {
         // Inspired by OraclizeAPI's implementation - MIT licence
         // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
 

+ 9 - 9
docs/contract.hbs

@@ -1,14 +1,14 @@
 {{~#*inline "typed-variable-array"~}}
-{{#each .}}[.var-type]#{{typeName}}#{{#if name}} [.var-name]#{{name}}#{{/if}}{{#unless @last}}, {{/unless}}{{/each}}
+{{#each .}}++[.var-type]#++{{typeName}}++#++{{#if name}} ++[.var-name]#++{{name}}++#++{{/if}}{{#unless @last}}, {{/unless}}{{/each}}
 {{~/inline~}}
 
 {{#each linkable}}
-:{{name}}: pass:normal[xref:#{{anchor}}[`{{name}}`]]
+:{{name}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]]
 {{/each}}
 
 [.contract]
 [[{{anchor}}]]
-=== `{{name}}`
+=== `++{{name}}++`
 
 {{natspec.devdoc}}
 
@@ -22,7 +22,7 @@
 .{{contract.name}}
 {{/unless}}
 {{#each modifiers}}
-* {xref-{{slug fullName~}} }[`{{name}}({{args.names}})`]
+* {xref-{{slug fullName~}} }[`++{{name}}({{args.names}})++`]
 {{/each}}
 
 {{/each}}
@@ -39,7 +39,7 @@
 .{{contract.name}}
 {{/unless}}
 {{#each functions}}
-* {xref-{{slug fullName~}} }[`{{name}}({{args.names}})`]
+* {xref-{{slug fullName~}} }[`++{{name}}({{args.names}})++`]
 {{/each}}
 
 {{/each}}
@@ -56,7 +56,7 @@
 .{{contract.name}}
 {{/unless}}
 {{#each events}}
-* {xref-{{slug fullName~}} }[`{{name}}({{args.names}})`]
+* {xref-{{slug fullName~}} }[`++{{name}}({{args.names}})++`]
 {{/each}}
 
 {{/each}}
@@ -66,7 +66,7 @@
 {{#each ownModifiers}}
 [.contract-item]
 [[{{anchor}}]]
-==== `{{name}}({{> typed-variable-array args}})` [.item-kind]#modifier#
+==== `++{{name}}({{> typed-variable-array args}})++` [.item-kind]#modifier#
 
 {{natspec.devdoc}}
 
@@ -75,7 +75,7 @@
 {{#each ownFunctions}}
 [.contract-item]
 [[{{anchor}}]]
-==== `{{name}}({{> typed-variable-array args}}){{#if outputs}} → {{> typed-variable-array outputs}}{{/if}}` [.item-kind]#{{visibility}}#
+==== `++{{name}}({{> typed-variable-array args}}){{#if outputs}} → {{> typed-variable-array outputs}}{{/if}}++` [.item-kind]#{{visibility}}#
 
 {{natspec.devdoc}}
 
@@ -84,7 +84,7 @@
 {{#each ownEvents}}
 [.contract-item]
 [[{{anchor}}]]
-==== `{{name}}({{> typed-variable-array args}})` [.item-kind]#event#
+==== `++{{name}}({{> typed-variable-array args}})++` [.item-kind]#event#
 
 {{natspec.devdoc}}
 

+ 2 - 3
docs/modules/ROOT/nav.adoc

@@ -8,12 +8,11 @@
 ** xref:erc721.adoc[ERC721]
 ** xref:erc777.adoc[ERC777]
 
-// xref:using-hooks.adoc[Using Hooks]
-
 * xref:gsn.adoc[Gas Station Network]
 ** xref:gsn-strategies.adoc[Strategies]
 
-* xref:utilities.adoc[Utilities]
+* xref:extending-contracts.adoc[Extending Contracts]
 
+* xref:utilities.adoc[Utilities]
 
 * xref:releases-stability.adoc[Releases & Stability]

+ 5 - 5
docs/modules/ROOT/pages/access-control.adoc

@@ -67,7 +67,7 @@ contract MyToken is ERC20, AccessControl {
     // Create a new role identifier for the minter role
     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
 
-    constructor(address minter) public {
+    constructor(address minter) public ERC20("MyToken", "TKN") {
         // Grant the minter role to a specified account
         _setupRole(MINTER_ROLE, minter);
     }
@@ -97,7 +97,7 @@ contract MyToken is ERC20, AccessControl {
     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
     bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
 
-    constructor(address minter, address burner) public {
+    constructor(address minter, address burner) public ERC20("MyToken", "TKN") {
         _setupRole(MINTER_ROLE, minter);
         _setupRole(BURNER_ROLE, burner);
     }
@@ -109,7 +109,7 @@ contract MyToken is ERC20, AccessControl {
 
     function burn(address from, uint256 amount) public {
         require(hasRole(BURNER_ROLE, msg.sender), "Caller is not a burner");
-       _burn(from, amount);
+        _burn(from, amount);
     }
 }
 ----
@@ -140,7 +140,7 @@ contract MyToken is ERC20, AccessControl {
     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
     bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
 
-    constructor() ERC20("MyToken", "TKN") public {
+    constructor() public ERC20("MyToken", "TKN") {
         // Grant the contract deployer the default admin role: it will be able
         // to grant and revoke any roles
         _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
@@ -153,7 +153,7 @@ contract MyToken is ERC20, AccessControl {
 
     function burn(address from, uint256 amount) public {
         require(hasRole(BURNER_ROLE, msg.sender), "Caller is not a burner");
-       _burn(from, amount);
+        _burn(from, amount);
     }
 }
 ----

+ 8 - 4
docs/modules/ROOT/pages/erc20-supply.adoc

@@ -26,7 +26,7 @@ Starting with Contracts v2 this pattern is not only discouraged, but disallowed.
 [source,solidity]
 ----
 contract ERC20FixedSupply is ERC20 {
-    constructor() public {
+    constructor() public ERC20("Fixed", "FIX") {
         _mint(msg.sender, 1000);
     }
 }
@@ -44,6 +44,8 @@ The mechanism we will implement is a token reward for the miners that produce Et
 [source,solidity]
 ----
 contract ERC20WithMinerReward is ERC20 {
+    constructor() public ERC20("Reward", "RWD") {}
+
     function mintMinerReward() public {
         _mint(block.coinbase, 1000);
     }
@@ -64,9 +66,9 @@ The accounts with the minter role don't need to be externally owned, though, and
 [source,solidity]
 ----
 contract MinerRewardMinter {
-    ERC20DeployReady _token;
+    ERC20MinterPauser _token;
 
-    constructor(ERC20DeployReady token) public {
+    constructor(ERC20MinterPauser token) public {
         _token = token;
     }
 
@@ -90,11 +92,13 @@ Adding to our previous supply mechanism, we can use this to mint a miner reward
 [source,solidity]
 ----
 contract ERC20WithAutoMinerReward is ERC20 {
+    constructor() public ERC20("Reward", "RWD") {}
+
     function _mintMinerReward() internal {
         _mint(block.coinbase, 1000);
     }
 
-    function _transfer(address from, address to, uint256 value) internal {
+    function _transfer(address from, address to, uint256 value) internal override {
         _mintMinerReward();
         super._transfer(from, to, value);
     }

+ 4 - 4
docs/modules/ROOT/pages/erc777.adoc

@@ -18,7 +18,7 @@ We will replicate the `GLD` example of the xref:erc20.adoc#constructing-an-erc20
 
 [source,solidity]
 ----
-pragma solidity ^0.5.0;
+pragma solidity ^0.6.0;
 
 import "@openzeppelin/contracts/token/ERC777/ERC777.sol";
 
@@ -30,14 +30,14 @@ contract GLDToken is ERC777 {
         ERC777("Gold", "GLD", defaultOperators)
         public
     {
-        _mint(msg.sender, msg.sender, initialSupply, "", "");
+        _mint(msg.sender, initialSupply, "", "");
     }
 }
 ----
 
 In this case, we'll be extending from the xref:api:token/ERC777.adoc#ERC777[`ERC777`] contract, which provides an implementation with compatibility support for ERC20. The API is quite similar to that of xref:api:token/ERC777.adoc#ERC777[`ERC777`], and we'll once again make use of xref:api:token/ERC777.adoc#ERC777-_mint-address-address-uint256-bytes-bytes-[`_mint`] to assign the `initialSupply` to the deployer account. Unlike xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[ERC20's `_mint`], this one includes some extra parameters, but you can safely ignore those for now.
 
-You'll notice both xref:api:token/ERC777.adoc#IERC777-name--[`name`] and xref:api:token/ERC777.adoc#IERC777-symbol--[`symbol`] are assigned, but not xref:api:token/ERC777.adoc#ERC777-decimals--[`decimals`]. The ERC777 specification makes it mandatory to include support for these functions (unlike ERC20, where it is optional and we had to include xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`]), but also mandates that `decimals` always returns a fixed value of `18`, so there's no need to set it ourselves. For a review of `decimals`'s role and importance, refer back to our xref:erc20.adoc#a-note-on-decimals[ERC20 guide].
+You'll notice both xref:api:token/ERC777.adoc#IERC777-name--[`name`] and xref:api:token/ERC777.adoc#IERC777-symbol--[`symbol`] are assigned, but not xref:api:token/ERC777.adoc#ERC777-decimals--[`decimals`]. The ERC777 specification makes it mandatory to include support for these functions (unlike ERC20, where it is optional and we had to include xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`]), but also mandates that `decimals` always returns a fixed value of `18`, so there's no need to set it ourselves. For a review of ``decimals``'s role and importance, refer back to our xref:erc20.adoc#a-note-on-decimals[ERC20 guide].
 
 Finally, we'll need to set the xref:api:token/ERC777.adoc#IERC777-defaultOperators--[`defaultOperators`]: special accounts (usually other smart contracts) that will be able to transfer tokens on behalf of their holders. If you're not planning on using operators in your token, you can simply pass an empty array. _Stay tuned for an upcoming in-depth guide on ERC777 operators!_
 
@@ -49,7 +49,7 @@ That's it for a basic token contract! We can now deploy it, and use the same xre
 1000
 ----
 
-To move tokens from one account to another, we can use both xref:api:token/ERC777.adoc#ERC777-transfer-address-uint256-[`ERC20`'s `transfer`] method, or the new xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[`ERC777`'s `send`], which fulfills a very similar role, but adds an optional `data` field:
+To move tokens from one account to another, we can use both xref:api:token/ERC777.adoc#ERC777-transfer-address-uint256-[``ERC20``'s `transfer`] method, or the new xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[``ERC777``'s `send`], which fulfills a very similar role, but adds an optional `data` field:
 
 [source,javascript]
 ----

+ 123 - 0
docs/modules/ROOT/pages/extending-contracts.adoc

@@ -0,0 +1,123 @@
+= Extending Contracts
+
+Most of the OpenZeppelin Contracts are expected to be used via https://solidity.readthedocs.io/en/latest/contracts.html#inheritance[inheritance]: you will _inherit_ from them when writing your own contracts.
+
+This is the commonly found `is` syntax, like in `contract MyToken is ERC20`.
+
+[NOTE]
+====
+Unlike ``contract``s, Solidity ``library``s are not inherited from and instead rely on the https://solidity.readthedocs.io/en/latest/contracts.html#using-for[`using for`] syntax.
+
+OpenZeppelin Contracts has some ``library``s: most are in the xref:api:utils.adoc[Utils] directory.
+====
+
+== Overriding
+
+Inheritance is often used to add the parent contract's functionality to your own contract, but that's not all it can do. You can also _change_ how some parts of the parent behave using _overrides_.
+
+For example, imagine you want to change xref:api:access.adoc#AccessControl[`AccessControl`] so that xref:api:access.adoc#AccessControl-revokeRole-bytes32-address-[`revokeRole`] can no longer be called. This can be achieved using overrides:
+
+```solidity
+pragma solidity ^0.6.0;
+
+import "@openzeppelin/contracts/access/AccessControl.sol";
+
+contract ModifiedAccessControl is AccessControl {
+    // Override the revokeRole function
+    function revokeRole(bytes32, address) public override {
+        revert("ModifiedAccessControl: cannot revoke roles");
+    }
+}
+```
+
+The old `revokeRole` is then replaced by our override, and any calls to it will immediately revert. We cannot _remove_ the function from the contract, but reverting on all calls is good enough.
+
+=== Calling `super`
+
+Sometimes you want to _extend_ a parent's behavior, instead of outright changing it to something else. This is where `super` comes in.
+
+The `super` keyword will let you call functions defined in a parent contract, even if they are overridden. This mechanism can be used to add additional checks to a function, emit events, or otherwise add functionality as you see fit.
+
+TIP: For more information on how overrides work, head over to the https://solidity.readthedocs.io/en/latest/contracts.html#index-17[official Solidity documentation].
+
+Here is a modified version of xref:api:access.adoc#AccessControl[`AccessControl`] where xref:api:access.adoc#AccessControl-revokeRole-bytes32-address-[`revokeRole`] cannot be used to revoke the `DEFAULT_ADMIN_ROLE`:
+
+
+```solidity
+pragma solidity ^0.6.0;
+
+import "@openzeppelin/contracts/access/AccessControl.sol";
+
+contract ModifiedAccessControl is AccessControl {
+    function revokeRole(bytes32 role, address account) public override {
+        require(
+            role != DEFAULT_ADMIN_ROLE,
+            "ModifiedAccessControl: cannot revoke default admin role"
+        );
+
+        super.revokeRole(role, account);
+    }
+}
+```
+
+The `super.revokeRole` statement at the end will invoke ``AccessControl``'s original version of `revokeRole`, the same code that would've run if there were no overrides in place.
+
+NOTE: As of v3.0.0, `view` functions are not `virtual` in OpenZeppelin, and therefore cannot be overriden. We're considering https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2154[lifting this restriction] in an upcoming release. Let us know if this is something you care about!
+
+[[using-hooks]]
+== Using Hooks
+
+Sometimes, in order to extend a parent contract you will need to override multiple related functions, which leads to code duplication and increased likelihood of bugs.
+
+For example, consider implementing safe xref:api:token/ERC20.adoc#ERC20[`ERC20`] transfers in the style of xref:api:token/ERC721.adoc#IERC721Receiver[`IERC721Receiver`]. You may think overriding xref:api:token/ERC20.adoc#ERC20-transfer-address-uint256-[`transfer`] and xref:api:token/ERC20.adoc#ERC20-transferFrom-address-address-uint256-[`transferFrom`] would be enough, but what about  xref:api:token/ERC20.adoc#ERC20-_transfer-address-address-uint256-[`_transfer`] and xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[`_mint`]? To prevent you from having to deal with these details, we introduced **hooks**.
+
+Hooks are simply functions that are called before or after some action takes place. They provide a centralized point to _hook into_ and extend the original behavior.
+
+Here's how you would implement the `IERC721Receiver` pattern in `ERC20`, using the xref:api:token/ERC20.adoc#ERC20-_beforeTokenTransfer-address-address-uint256-[`_beforeTokenTransfer`] hook:
+
+```solidity
+pragma solidity ^0.6.0;
+
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+
+contract ERC20WithSafeTransfer is ERC20 {
+    function _beforeTokenTransfer(address from, address to, uint256 amount)
+        internal virtual override
+    {
+        super._beforeTokenTransfer(from, to, amount);
+
+        require(_validRecipient(to), "ERC20WithSafeTransfer: invalid recipient");
+    }
+
+    function _validRecipient(address to) private view returns (bool) {
+        ...
+    }
+```
+
+Using hooks this way leads to cleaner and safer code, without having to rely on a deep understanding of the parent's internals.
+
+[NOTE]
+====
+Hooks are a new feature of OpenZeppelin Contracts v3.0.0, and we're eager to learn how you plan to use them!
+
+So far, the only available hook is `_beforeTransferHook`, in all of xref:api:token/ERC20.adoc#ERC20-_beforeTokenTransfer-address-address-uint256-[`ERC20`], xref:api:token/ERC721.adoc#ERC721-_beforeTokenTransfer-address-address-uint256-[ERC721] and xref:api:token/ERC777.adoc#ERC777-_beforeTokenTransfer-address-address-address-uint256-[ERC777]. If you have ideas for new hooks, let us know!
+====
+
+=== Rules of Hooks
+
+There's a few guidelines you should follow when writing code that uses hooks in order to prevent issues. They are very simple, but do make sure you follow them:
+
+1. Whenever you override a parent's hook, re-apply the `virtual` attribute to the hook. That will allow child contracts to add more functionality to the hook.
+2. **Always** call the parent's hook in your override using `super`. This will make sure all hooks in the inheritance tree are called: contracts like xref:api:token/ERC20.adoc#ERC20Pausable[`ERC20Pausable`] rely on this behavior.
+
+```solidity
+contract MyToken is ERC20 {
+    function _beforeTokenTransfer(address from, address to, uint256 amount)
+        internal virtual override // Add virtual here!
+    {
+        super._beforeTokenTransfer(from, to, amount); // Call parent hook
+        ...
+    }
+}
+```
+That's it! Enjoy simpler code using hooks!

+ 12 - 4
docs/modules/ROOT/pages/gsn-strategies.adoc

@@ -59,7 +59,7 @@ Instead of using `GSNRecipient` directly, your GSN recipient contract will inste
 
 [source,solidity]
 ----
-import "@openzeppelin/contracts/GSN/GSNRecipientSignature";
+import "@openzeppelin/contracts/GSN/GSNRecipientSignature.sol";
 
 contract MyContract is GSNRecipientSignature {
     constructor(address trustedSigner) public GSNRecipientSignature(trustedSigner) {
@@ -106,8 +106,8 @@ Your GSN recipient contract needs to inherit from `GSNRecipientERC20Fee` along w
 
 [source,solidity]
 ----
-import "@openzeppelin/contracts/GSN/GSNRecipientERC20Fee";
-import "@openzeppelin/contracts/access/AccessControl";
+import "@openzeppelin/contracts/GSN/GSNRecipientERC20Fee.sol";
+import "@openzeppelin/contracts/access/AccessControl.sol";
 
 contract MyContract is GSNRecipientERC20Fee, AccessControl {
     bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
@@ -116,8 +116,16 @@ contract MyContract is GSNRecipientERC20Fee, AccessControl {
         _setupRole(MINTER_ROLE, _msgSender());
     }
 
+    function _msgSender() internal view override(Context, GSNRecipient) returns (address payable) {
+        return GSNRecipient._msgSender();
+    }
+
+    function _msgData() internal view override(Context, GSNRecipient) returns (bytes memory) {
+        return GSNRecipient._msgData();
+    }
+
     function mint(address account, uint256 amount) public {
-        require(hasRole(MINTER_ROLE, _msgSender()));
+        require(hasRole(MINTER_ROLE, _msgSender()), "Caller is not a minter");
         _mint(account, amount);
     }
 }

+ 2 - 2
docs/modules/ROOT/pages/releases-stability.adoc

@@ -12,7 +12,7 @@ OpenZeppelin Contracts follows a <<versioning-scheme, semantic versioning scheme
 [[minor-releases]]
 === Minor Releases
 
-OpenZeppelin Contracts has a *5 week release cycle*. This means that every five weeks a new release is published.
+OpenZeppelin Contracts aims for a new minor release every 1 or 2 months.
 
 At the beginning of the release cycle we decide which issues we want to prioritize, and assign them to https://github.com/OpenZeppelin/openzeppelin-contracts/milestones[a milestone on GitHub]. During the next five weeks, they are worked on and fixed.
 
@@ -21,7 +21,7 @@ Once the milestone is complete, we publish a feature-frozen release candidate. T
 [[major-releases]]
 === Major Releases
 
-Every several months a new major release may come out. These are not scheduled, but will be based on the need to release breaking changes such as a redesign of a core feature of the library (e.g. https://github.com/OpenZeppelin/openzeppelin-contracts/issues/1146[roles] in 2.0). Since we value stability, we aim for these to happen infrequently (expect no less than six months between majors). However, we may be forced to release one when there are big changes to the Solidity language.
+After several months or a year a new major release may come out. These are not scheduled, but will be based on the need to release breaking changes such as a redesign of a core feature of the library (e.g. https://github.com/OpenZeppelin/openzeppelin-contracts/pulls/2112[access control] in 3.0). Since we value stability, we aim for these to happen infrequently (expect no less than six months between majors). However, we may be forced to release one when there are big changes to the Solidity language.
 
 [[api-stability]]
 == API Stability

+ 0 - 7
docs/modules/ROOT/pages/using-hooks.adoc

@@ -1,7 +0,0 @@
-# Using Hooks
-
-is a good idea.
-
-using hooks:
-  super.hook() must _always_ be called
-  if you want to cancel the process, revert. works both in before and after

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 167 - 729
package-lock.json


+ 3 - 3
package.json

@@ -44,11 +44,11 @@
   },
   "homepage": "https://openzeppelin.com/contracts/",
   "devDependencies": {
-    "@openzeppelin/cli": "^2.8.0",
+    "@openzeppelin/cli": "^2.8.2",
     "@openzeppelin/docs-utils": "^0.1.0",
     "@openzeppelin/gsn-helpers": "^0.2.3",
     "@openzeppelin/gsn-provider": "^0.1.10",
-    "@openzeppelin/test-environment": "^0.1.3",
+    "@openzeppelin/test-environment": "^0.1.4",
     "@openzeppelin/test-helpers": "^0.5.5",
     "chai": "^4.2.0",
     "eslint": "^6.5.1",
@@ -64,7 +64,7 @@
     "lodash.zip": "^4.2.0",
     "micromatch": "^4.0.2",
     "mocha": "^7.1.1",
-    "solhint": "^3.0.0-rc.6",
+    "solhint": "^3.0.0-rc.8",
     "solidity-coverage": "github:rotcivegaf/solidity-coverage#5875f5b7bc74d447f3312c9c0e9fc7814b482477",
     "solidity-docgen": "^0.4.1"
   },

+ 7 - 7
scripts/gen-nav.js

@@ -13,13 +13,13 @@ const files = proc.execFileSync(
 console.log('.API');
 
 const links = files.map((file) => {
-    const doc = file.replace(baseDir, '');
-    const title = path.parse(file).name;
+  const doc = file.replace(baseDir, '');
+  const title = path.parse(file).name;
 
-    return {
-      xref: `* xref:${doc}[${startCase(title)}]`,
-      title,
-    };
+  return {
+    xref: `* xref:${doc}[${startCase(title)}]`,
+    title,
+  };
 });
 
 // Case-insensitive sort based on titles (so 'token/ERC20' gets sorted as 'erc20')
@@ -28,5 +28,5 @@ const sortedLinks = links.sort(function (a, b) {
 });
 
 for (const link of sortedLinks) {
-   console.log(link.xref);
+  console.log(link.xref);
 }

+ 1 - 3
scripts/release/synchronize-versions.js

@@ -1,14 +1,12 @@
 #!/usr/bin/env node
 
-// Synchronizes the versions in ethpm.json and contracts/package.json with the
-// one in package.json.
+// Synchronizes the version in contracts/package.json with the one in package.json.
 // This is run automatically when npm version is run.
 
 const fs = require('fs');
 const cp = require('child_process');
 
 setVersion('contracts/package.json');
-setVersion('ethpm.json');
 
 function setVersion (file) {
   const json = JSON.parse(fs.readFileSync(file));

+ 0 - 9
test/access/AccessControl.test.js

@@ -17,15 +17,6 @@ describe('AccessControl', function () {
     this.accessControl = await AccessControlMock.new({ from: admin });
   });
 
-  describe('_setupRole', function () {
-    it('cannot be called outside the constructor', async function () {
-      await expectRevert(
-        this.accessControl.setupRole(OTHER_ROLE, other),
-        'AccessControl: roles cannot be setup after construction'
-      );
-    });
-  });
-
   describe('default admin', function () {
     it('deployer has default admin role', async function () {
       expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true);

+ 5 - 5
test/deploy-ready/ERC20MinterPauser.test.js → test/presets/ERC20PresetMinterPauser.test.js

@@ -5,9 +5,9 @@ const { ZERO_ADDRESS } = constants;
 
 const { expect } = require('chai');
 
-const ERC20MinterPauser = contract.fromArtifact('ERC20MinterPauser');
+const ERC20PresetMinterPauser = contract.fromArtifact('ERC20PresetMinterPauser');
 
-describe('ERC20MinterPauser', function () {
+describe('ERC20PresetMinterPauser', function () {
   const [ deployer, other ] = accounts;
 
   const name = 'MinterPauserToken';
@@ -20,7 +20,7 @@ describe('ERC20MinterPauser', function () {
   const PAUSER_ROLE = web3.utils.soliditySha3('PAUSER_ROLE');
 
   beforeEach(async function () {
-    this.token = await ERC20MinterPauser.new(name, symbol, { from: deployer });
+    this.token = await ERC20PresetMinterPauser.new(name, symbol, { from: deployer });
   });
 
   it('deployer has the default admin role', async function () {
@@ -54,7 +54,7 @@ describe('ERC20MinterPauser', function () {
     it('other accounts cannot mint tokens', async function () {
       await expectRevert(
         this.token.mint(other, amount, { from: other }),
-        'ERC20MinterPauser: must have minter role to mint'
+        'ERC20PresetMinterPauser: must have minter role to mint'
       );
     });
   });
@@ -86,7 +86,7 @@ describe('ERC20MinterPauser', function () {
     });
 
     it('other accounts cannot pause', async function () {
-      await expectRevert(this.token.pause({ from: other }), 'ERC20MinterPauser: must have pauser role to pause');
+      await expectRevert(this.token.pause({ from: other }), 'ERC20PresetMinterPauser: must have pauser role to pause');
     });
   });
 

+ 34 - 21
test/deploy-ready/ERC721MinterPauser.test.js → test/presets/ERC721PresetMinterPauserAutoId.js

@@ -5,22 +5,32 @@ const { ZERO_ADDRESS } = constants;
 
 const { expect } = require('chai');
 
-const ERC721MinterPauser = contract.fromArtifact('ERC721MinterPauser');
+const ERC721PresetMinterPauserAutoId = contract.fromArtifact('ERC721PresetMinterPauserAutoId');
 
-describe('ERC721MinterPauser', function () {
+describe('ERC721PresetMinterPauserAutoId', function () {
   const [ deployer, other ] = accounts;
 
-  const name = 'MinterPauserToken';
-  const symbol = 'DRT';
-
-  const tokenId = new BN('1337');
+  const name = 'MinterAutoIDToken';
+  const symbol = 'MAIT';
+  const baseURI = 'my.app/';
 
   const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000';
   const MINTER_ROLE = web3.utils.soliditySha3('MINTER_ROLE');
-  const PAUSER_ROLE = web3.utils.soliditySha3('PAUSER_ROLE');
 
   beforeEach(async function () {
-    this.token = await ERC721MinterPauser.new(name, symbol, { from: deployer });
+    this.token = await ERC721PresetMinterPauserAutoId.new(name, symbol, baseURI, { from: deployer });
+  });
+
+  it('token has correct name', async function () {
+    expect(await this.token.name()).to.equal(name);
+  });
+
+  it('token has correct symbol', async function () {
+    expect(await this.token.symbol()).to.equal(symbol);
+  });
+
+  it('token has correct base URI', async function () {
+    expect(await this.token.baseURI()).to.equal(baseURI);
   });
 
   it('deployer has the default admin role', async function () {
@@ -33,29 +43,27 @@ describe('ERC721MinterPauser', function () {
     expect(await this.token.getRoleMember(MINTER_ROLE, 0)).to.equal(deployer);
   });
 
-  it('deployer has the pauser role', async function () {
-    expect(await this.token.getRoleMemberCount(PAUSER_ROLE)).to.be.bignumber.equal('1');
-    expect(await this.token.getRoleMember(PAUSER_ROLE, 0)).to.equal(deployer);
-  });
-
-  it('minter and pauser role admin is the default admin', async function () {
+  it('minter role admin is the default admin', async function () {
     expect(await this.token.getRoleAdmin(MINTER_ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
-    expect(await this.token.getRoleAdmin(PAUSER_ROLE)).to.equal(DEFAULT_ADMIN_ROLE);
   });
 
   describe('minting', function () {
     it('deployer can mint tokens', async function () {
-      const receipt = await this.token.mint(other, tokenId, { from: deployer });
+      const tokenId = new BN('0');
+
+      const receipt = await this.token.mint(other, { from: deployer });
       expectEvent(receipt, 'Transfer', { from: ZERO_ADDRESS, to: other, tokenId });
 
       expect(await this.token.balanceOf(other)).to.be.bignumber.equal('1');
       expect(await this.token.ownerOf(tokenId)).to.equal(other);
+
+      expect(await this.token.tokenURI(tokenId)).to.equal(baseURI + tokenId);
     });
 
     it('other accounts cannot mint tokens', async function () {
       await expectRevert(
-        this.token.mint(other, tokenId, { from: other }),
-        'ERC721MinterPauser: must have minter role to mint'
+        this.token.mint(other, { from: other }),
+        'ERC721PresetMinterPauserAutoId: must have minter role to mint'
       );
     });
   });
@@ -81,19 +89,24 @@ describe('ERC721MinterPauser', function () {
       await this.token.pause({ from: deployer });
 
       await expectRevert(
-        this.token.mint(other, tokenId, { from: deployer }),
+        this.token.mint(other, { from: deployer }),
         'ERC721Pausable: token transfer while paused'
       );
     });
 
     it('other accounts cannot pause', async function () {
-      await expectRevert(this.token.pause({ from: other }), 'ERC721MinterPauser: must have pauser role to pause');
+      await expectRevert(
+        this.token.pause({ from: other }),
+        'ERC721PresetMinterPauserAutoId: must have pauser role to pause'
+      );
     });
   });
 
   describe('burning', function () {
     it('holders can burn their tokens', async function () {
-      await this.token.mint(other, tokenId, { from: deployer });
+      const tokenId = new BN('0');
+
+      await this.token.mint(other, { from: deployer });
 
       const receipt = await this.token.burn(tokenId, { from: other });
 

+ 0 - 6
test/token/ERC20/ERC20.test.js

@@ -44,12 +44,6 @@ describe('ERC20', function () {
       const token = await ERC20DecimalsMock.new(name, symbol, decimals);
       expect(await token.decimals()).to.be.bignumber.equal(decimals);
     });
-
-    it('reverts if setting decimals after construction', async function () {
-      const token = await ERC20DecimalsMock.new(name, symbol, decimals);
-
-      await expectRevert(token.setupDecimals(decimals.addn(1)), 'ERC20: decimals cannot be changed after construction');
-    });
   });
 
   shouldBehaveLikeERC20('ERC20', initialSupply, initialHolder, recipient, anotherAccount);

+ 54 - 0
test/token/ERC20/ERC20Pausable.test.js

@@ -78,5 +78,59 @@ describe('ERC20Pausable', function () {
         );
       });
     });
+
+    describe('mint', function () {
+      const amount = new BN('42');
+
+      it('allows to mint when unpaused', async function () {
+        await this.token.mint(recipient, amount);
+
+        expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount);
+      });
+
+      it('allows to mint when paused and then unpaused', async function () {
+        await this.token.pause();
+        await this.token.unpause();
+
+        await this.token.mint(recipient, amount);
+
+        expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount);
+      });
+
+      it('reverts when trying to mint when paused', async function () {
+        await this.token.pause();
+
+        await expectRevert(this.token.mint(recipient, amount),
+          'ERC20Pausable: token transfer while paused'
+        );
+      });
+    });
+
+    describe('burn', function () {
+      const amount = new BN('42');
+
+      it('allows to burn when unpaused', async function () {
+        await this.token.burn(holder, amount);
+
+        expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(amount));
+      });
+
+      it('allows to burn when paused and then unpaused', async function () {
+        await this.token.pause();
+        await this.token.unpause();
+
+        await this.token.burn(holder, amount);
+
+        expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(amount));
+      });
+
+      it('reverts when trying to burn when paused', async function () {
+        await this.token.pause();
+
+        await expectRevert(this.token.burn(holder, amount),
+          'ERC20Pausable: token transfer while paused'
+        );
+      });
+    });
   });
 });

+ 2 - 2
test/token/ERC721/ERC721.test.js

@@ -92,10 +92,10 @@ describe('ERC721', function () {
         expect(await this.token.tokenURI(firstTokenId)).to.be.equal(newBaseURI + sampleUri);
       });
 
-      it('token URI is empty for tokens with no URI but with base URI', async function () {
+      it('tokenId is appended to base URI for tokens with no URI', async function () {
         await this.token.setBaseURI(baseURI);
 
-        expect(await this.token.tokenURI(firstTokenId)).to.be.equal('');
+        expect(await this.token.tokenURI(firstTokenId)).to.be.equal(baseURI + firstTokenId);
       });
 
       it('tokens with URI can be burnt ', async function () {

+ 17 - 2
test/token/ERC721/ERC721Pausable.test.js

@@ -19,7 +19,8 @@ describe('ERC721Pausable', function () {
 
   context('when token is paused', function () {
     const firstTokenId = new BN(1);
-    const mintedTokens = new BN(1);
+    const secondTokenId = new BN(1337);
+
     const mockData = '0x42';
 
     beforeEach(async function () {
@@ -49,6 +50,20 @@ describe('ERC721Pausable', function () {
       );
     });
 
+    it('reverts when trying to mint', async function () {
+      await expectRevert(
+        this.token.mint(receiver, secondTokenId),
+        'ERC721Pausable: token transfer while paused'
+      );
+    });
+
+    it('reverts when trying to burn', async function () {
+      await expectRevert(
+        this.token.burn(firstTokenId),
+        'ERC721Pausable: token transfer while paused'
+      );
+    });
+
     describe('getApproved', function () {
       it('returns approved address', async function () {
         const approvedAccount = await this.token.getApproved(firstTokenId);
@@ -59,7 +74,7 @@ describe('ERC721Pausable', function () {
     describe('balanceOf', function () {
       it('returns the amount of tokens owned by the given address', async function () {
         const balance = await this.token.balanceOf(owner);
-        expect(balance).to.be.bignumber.equal(mintedTokens);
+        expect(balance).to.be.bignumber.equal('1');
       });
     });
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels