Compare commits

..

36 Commits

Author SHA1 Message Date
b7452960be 3.0.0 2020-04-20 20:37:38 -03:00
32f55009af Remove outdated notes to v2.x docs 2020-04-20 20:37:20 -03:00
a4320108d5 Remove prerelease tag from docs 2020-04-20 20:24:24 -03:00
05085aa605 Merge branch 'master' into release-v3.0.0 2020-04-20 20:21:14 -03:00
528c23d679 Remove ethpm.json and update releasing docs (#2205)
* remove ethpm.json

* update releasing document

* fix release documentation to be more realistic
2020-04-20 20:10:30 -03:00
715ec806f0 ERC721 deploy ready fixes (#2192)
* Add baseURI to ERC721MinterPauser constructor

* Add tokenURI to mint

* Remove tokenId and tokenURI from ERC721 deploy ready

* Rename ERC721MinterPauser to ERC721MinterAutoID, remove Pausable mechanisms

* Restore pausing to ERC721

* Rename deploy ready to presets

* Rename ERC721 preset
2020-04-20 20:05:48 -03:00
5bb8d0245b Improve Hooks documentation (#2199)
* Improve Hooks docs

* Improve Utils docs

* Apply suggestions from code review

Co-Authored-By: Francisco Giordano <frangio.1@gmail.com>

* Add enumerable code samples

* Remove import statement from example

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
2020-04-20 16:43:05 -03:00
c986dfb256 [Security] Bump acorn from 6.4.0 to 6.4.1 (#2204)
Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1. **This update includes security fixes.**
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-20 14:17:12 -03:00
6d5ef3ef5f Bump solhint from 3.0.0-rc.7 to 3.0.0-rc.8 (#2202)
Bumps [solhint](https://github.com/protofire/solhint) from 3.0.0-rc.7 to 3.0.0-rc.8.
- [Release notes](https://github.com/protofire/solhint/releases)
- [Changelog](https://github.com/protofire/solhint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/protofire/solhint/compare/v3.0.0-rc.7...v3.0.0-rc.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-20 14:02:33 -03:00
2663c5ee0f Bump @openzeppelin/test-environment from 0.1.3 to 0.1.4 (#2201)
Bumps [@openzeppelin/test-environment](https://github.com/OpenZeppelin/openzeppelin-test-environment) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/OpenZeppelin/openzeppelin-test-environment/releases)
- [Changelog](https://github.com/OpenZeppelin/openzeppelin-test-environment/blob/master/CHANGELOG.md)
- [Commits](https://github.com/OpenZeppelin/openzeppelin-test-environment/compare/v0.1.3...v0.1.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-20 13:43:00 -03:00
1075898b06 Bump @openzeppelin/cli from 2.8.1 to 2.8.2 (#2200)
Bumps [@openzeppelin/cli](https://github.com/OpenZeppelin/openzeppelin-sdk) from 2.8.1 to 2.8.2.
- [Release notes](https://github.com/OpenZeppelin/openzeppelin-sdk/releases)
- [Commits](https://github.com/OpenZeppelin/openzeppelin-sdk/compare/v2.8.1...v2.8.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-20 12:33:00 -03:00
c7705712ba Remove in-constructor requirements (#2195)
* Remove isConstructor requirement from _setupRole

* Remove isConstructor requirement from _setupDecimals

* Update contracts/access/AccessControl.sol

Co-Authored-By: Francisco Giordano <frangio.1@gmail.com>

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
2020-04-17 17:00:30 -03:00
13e113df81 Improve usage of toString 2020-04-17 11:37:58 -03:00
cca71ab709 Fix deprecation warnings (#2115)
* Fix deprecation warnings

* Workaround linter bug
2020-04-16 12:13:49 -03:00
8b10cb38d8 Rename Strings.fromUint256 to Strings.toString (#2188)
* rename Strings.fromUint256 to Strings.toString

* add changelog entry

* fix conflict with js toString method
2020-04-15 18:04:00 -03:00
3e139baa50 Add missing hook to ERC777, fix relevant documentation (#2191)
* Improve ERC20/721 Pausable docs

* Add ERC20Pausable mint and burn tests

* Add ERC721Pausable mint and burn tests

* Add _beforeTransfer hook in ERC777 to mint and burn
2020-04-15 17:58:24 -03:00
b6513f6ad7 provide 'automatic' token URI by appending the token ID to the base URI (#2174)
* provide 'automatic' token URI by appending the token ID to the base URI, if a base is set but no token-specific URI is available

* make the three cases more explicit, avoid else after return

* adjust comments to reflect reality
2020-04-15 15:39:33 -03:00
4ca719bf8b Fix typo 2020-04-15 14:35:27 -03:00
3216fd9729 Configure line length in .editorconfig 2020-04-15 13:33:49 -03:00
b734bf3fa5 Tweak wording in ERC20Snapshot docs 2020-04-15 13:33:24 -03:00
3b10205c8e Improve ERC20Snapshot documentation (#2186)
Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com>
2020-04-14 19:06:51 -03:00
d2ab599bd3 Update IERC777.sol (#2184) 2020-04-14 14:41:33 -03:00
281bcb747e Bump @openzeppelin/cli from 2.8.0 to 2.8.1 (#2182)
Bumps [@openzeppelin/cli](https://github.com/OpenZeppelin/openzeppelin-sdk) from 2.8.0 to 2.8.1.
- [Release notes](https://github.com/OpenZeppelin/openzeppelin-sdk/releases)
- [Commits](https://github.com/OpenZeppelin/openzeppelin-sdk/compare/v2.8.0...v2.8.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-13 13:30:27 -03:00
e03c05774b Bump solhint from 3.0.0-rc.6 to 3.0.0-rc.7 (#2183)
Bumps [solhint](https://github.com/protofire/solhint) from 3.0.0-rc.6 to 3.0.0-rc.7.
- [Release notes](https://github.com/protofire/solhint/releases)
- [Changelog](https://github.com/protofire/solhint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/protofire/solhint/compare/v3.0.0-rc.6...v3.0.0-rc.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-13 13:21:09 -03:00
05d1618d01 Update Contracts docs to make examples compile (#2170)
* Update access-control.adoc to make compile

Add call to `ERC20("MyToken", "TKN")` in `MyToken` constructor

* Update access-control.adoc to make compile

Add call to `ERC20("MyToken", "TKN")` in `MyToken` constructor

* Update access-control.adoc MyToken formatting

* Update erc20-supply.adoc to make compile

Add call to `ERC20("MyToken", "TKN")` in `ERC20FixedSupply` constructor

* Update erc20-supply.adoc to make compile

Add constructor to `ERC20WithMinerReward`

* Update erc20-supply.adoc to make compile

In `MinerRewardMinter` use `ERC20MinterPauser`

* Update erc20-supply.adoc to make compile

Add constructor and override to `ERC20WithAutoMinerReward`

* Update erc777.adoc to make compile

* Update gsn-strategies.adoc to make compile

* Update gsn-strategies.adoc to make compile

Fix imports, add overrides, and revert reason to `MyContract`
2020-04-07 08:45:48 -03:00
885378e421 Fix some of the code formatting in docs 2020-04-03 21:13:55 -03:00
0292d793f3 add passthrough delimiters to avoid italics from underscores (#2169)
Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com>
2020-04-03 20:51:14 -03:00
f4566aaade Fix rc generator 2020-04-03 17:55:36 -03:00
d8a5ffeee9 Fix prerelease marker 2020-04-03 17:23:57 -03:00
9edee8a7a8 Fix linter errors 2020-04-03 17:15:40 -03:00
bbb245fc43 Add rc marker to docs 2020-04-03 17:13:56 -03:00
cd6f52997e Merge branch 'master' into release-v3.0.0 2020-04-03 17:06:39 -03:00
c4e5daff86 Fix Pausable docs 2020-04-03 17:06:33 -03:00
96a7113a16 Fix broken links 2020-04-03 17:05:36 -03:00
21ceabc77c Improve API docgen sorting 2020-04-03 17:02:04 -03:00
7c19c56844 Improve API docgen sorting 2020-04-03 17:01:29 -03:00
55 changed files with 786 additions and 1139 deletions

View File

@ -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

View File

@ -1,6 +1,6 @@
# Changelog
## 3.0.0 (unreleased)
## 3.0.0 (2020-04-20)
### New features
* `AccessControl`: new contract for managing permissions in a system, replacement for `Ownable` and `Roles`. ([#2112](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2112))
@ -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

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -1,7 +1,5 @@
= Gas Station Network (GSN)
_Available since v2.4.0._
This set of contracts provide all the tools required to make a contract callable via the https://gsn.openzeppelin.com[Gas Station Network].
TIP: If you're new to the GSN, head over to our xref:learn::sending-gasless-transactions.adoc[overview of the system] and basic guide to xref:ROOT:gsn.adoc[creating a GSN-capable contract].

View File

@ -28,7 +28,7 @@ import "../GSN/Context.sol";
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRoke}.
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
@ -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;

View File

@ -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)));
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@openzeppelin/contracts",
"version": "3.0.0-rc.1",
"version": "3.0.0",
"description": "Secure Smart Contract library for Solidity",
"files": [
"**/*.sol",

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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}}

View File

@ -10,7 +10,7 @@ import "../../utils/Address.sol";
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
* For a generic mechanism see {ERC20MinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
@ -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 { }
}

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -1,8 +1,7 @@
pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**

View File

@ -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 { }
}

View File

@ -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);

View File

@ -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);

View File

@ -97,7 +97,7 @@ contract ERC777 is Context, IERC777, IERC20 {
}
/**
* @dev See {ERC20Detailed-decimals}.
* @dev See {ERC20-decimals}.
*
* Always returns 18, as per the
* [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility).
@ -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 { }
}

View File

@ -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}.
*

View File

@ -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");
}
}

View File

@ -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

View File

@ -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

View File

@ -13,20 +13,19 @@ import "../GSN/Context.sol";
*/
contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by a pauser (`account`).
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by a pauser (`account`).
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state. Assigns the Pauser role
* to the deployer.
* @dev Initializes the contract in unpaused state.
*/
constructor () internal {
_paused = false;
@ -56,7 +55,7 @@ contract Pausable is Context {
}
/**
* @dev Called by a pauser to pause, triggers stopped state.
* @dev Triggers stopped state.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
@ -64,7 +63,7 @@ contract Pausable is Context {
}
/**
* @dev Called by a pauser to unpause, returns to normal state.
* @dev Returns to normal state.
*/
function _unpause() internal virtual whenPaused {
_paused = false;

View File

@ -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}}

View File

@ -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

View File

@ -1,7 +1,6 @@
name: contracts
title: Contracts
version: 3.x
prerelease: true
nav:
- modules/ROOT/nav.adoc
- modules/api/nav.adoc

View File

@ -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}}

View File

@ -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:extending-contracts.adoc[Extending Contracts]
* xref:utilities.adoc[Utilities]
* xref:releases-stability.adoc[Releases & Stability]

View File

@ -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);
}
}
----

View File

@ -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);
}

View File

@ -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]
----

View File

@ -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!

View File

@ -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);
}
}
@ -135,7 +143,7 @@ Not all GSN strategies use `_preRelayedCall` and `_postRelayedCall` (though they
`_preRelayedCall` should take the maximum possible charge, with `_postRelayedCall` refunding any difference from the actual charge once the relayed call has been made.
When returning `_approveRelayedCall` to approve the relayed call, the end users address, `maxPossibleCharge`, `transactionFee` and `gasPrice` data can also be returned so that the data can be used in `_preRelayedCall` and `_postRelayedCall`.
See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/GSN/GSNRecipientERC20Fee.sol[the code for `GSNRecipientERC20Fee`] as an example implementation.
See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/GSN/GSNRecipientERC20Fee.sol[the code for `GSNRecipientERC20Fee`] as an example implementation.
Once your strategy is ready, all your GSN recipient needs to do is inherit from it:

View File

@ -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

View File

@ -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

1057
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "openzeppelin-solidity",
"version": "3.0.0-rc.1",
"version": "3.0.0",
"description": "Secure Smart Contract library for Solidity",
"files": [
"/contracts/**/*.sol",
@ -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"
},

View File

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

View File

@ -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));

View File

@ -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);

View File

@ -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');
});
});

View File

@ -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 });

View File

@ -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);

View File

@ -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'
);
});
});
});
});

View File

@ -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 () {

View File

@ -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');
});
});