Merge branch 'master' into release-v3.0.0
This commit is contained in:
@ -11,7 +11,7 @@ OpenZeppelin provides xref:api:access.adoc#Ownable[`Ownable`] for implementing o
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
pragma solidity ^0.5.0;
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
@ -69,7 +69,7 @@ contract MyToken is ERC20, AccessControl {
|
||||
|
||||
constructor(address minter) public {
|
||||
// Grant the minter role to a specified account
|
||||
_grantRole(MINTER_ROLE, minter);
|
||||
_setupRole(MINTER_ROLE, minter);
|
||||
}
|
||||
|
||||
function mint(address to, uint256 amount) public {
|
||||
@ -98,8 +98,8 @@ contract MyToken is ERC20, AccessControl {
|
||||
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
|
||||
|
||||
constructor(address minter, address burner) public {
|
||||
_grantRole(MINTER_ROLE, minter);
|
||||
_grantRole(BURNER_ROLE, burner);
|
||||
_setupRole(MINTER_ROLE, minter);
|
||||
_setupRole(BURNER_ROLE, burner);
|
||||
}
|
||||
|
||||
function mint(address to, uint256 amount) public {
|
||||
@ -119,11 +119,11 @@ So clean! By splitting concerns this way, more granular levels of permission may
|
||||
[[granting-and-revoking]]
|
||||
=== Granting and Revoking Roles
|
||||
|
||||
The ERC20 token example above uses `\_grantRole`, an `internal` function that is useful when programmatically asigning roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts?
|
||||
The ERC20 token example above uses `\_setupRole`, an `internal` function that is useful when programmatically asigning roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts?
|
||||
|
||||
By default, **accounts with a role cannot grant it or revoke it from other accounts**: all having a role does is making the `hasRole` check pass. To grant and revoke roles dynamically, you will need help from the _role's admin_.
|
||||
|
||||
Every role has an associated admin role, which grants permission to call the `grantRole` and `revokeRole` `external` functions. A role can be granted or revoked by using these if the calling account has the corresponding admin role. Multiple roles may have the same admin role to make management easier. A role's admin can even be the same role itself, which would cause accounts with that role to be able to also grant and revoke it.
|
||||
Every role has an associated admin role, which grants permission to call the `grantRole` and `revokeRole` functions. A role can be granted or revoked by using these if the calling account has the corresponding admin role. Multiple roles may have the same admin role to make management easier. A role's admin can even be the same role itself, which would cause accounts with that role to be able to also grant and revoke it.
|
||||
|
||||
This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also provides an easy way to manage simpler applications. `AccessControl` includes a special role, called `DEFAULT_ADMIN_ROLE`, which acts as the **default admin role for all roles**. An account with this role will be able to manage any other role, unless `\_setRoleAdmin` is used to select a new admin role.
|
||||
|
||||
@ -140,10 +140,10 @@ contract MyToken is ERC20, AccessControl {
|
||||
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
|
||||
|
||||
constructor() public {
|
||||
constructor() ERC20("MyToken", "TKN") public {
|
||||
// Grant the contract deployer the default admin role: it will be able
|
||||
// to grant and revoke any roles
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||
}
|
||||
|
||||
function mint(address to, uint256 amount) public {
|
||||
|
||||
@ -55,7 +55,7 @@ As we can see, `_mint` makes it super easy to do this correctly.
|
||||
[[modularizing-the-mechanism]]
|
||||
== Modularizing the Mechanism
|
||||
|
||||
There is one supply mechanism already included in Contracts: xref:api:token/ERC20.adoc#ERC20Mintable[`ERC20Mintable`]. This is a generic mechanism in which a set of accounts is assigned the `minter` role, granting them the permission to call a xref:api:token/ERC20.adoc#ERC20Mintable-mint-address-uint256-[`mint`] function, an external version of `_mint`.
|
||||
There is one supply mechanism already included in Contracts: `ERC20DeployReady`. This is a generic mechanism in which a set of accounts is assigned the `minter` role, granting them the permission to call a `mint` function, an external version of `_mint`.
|
||||
|
||||
This can be used for centralized minting, where an externally owned account (i.e. someone with a pair of cryptographic keys) decides how much supply to create and to whom. There are very legitimate use cases for this mechanism, such as https://medium.com/reserve-currency/why-another-stablecoin-866f774afede#3aea[traditional asset-backed stablecoins].
|
||||
|
||||
@ -64,9 +64,9 @@ The accounts with the minter role don't need to be externally owned, though, and
|
||||
[source,solidity]
|
||||
----
|
||||
contract MinerRewardMinter {
|
||||
ERC20Mintable _token;
|
||||
ERC20DeployReady _token;
|
||||
|
||||
constructor(ERC20Mintable token) public {
|
||||
constructor(ERC20DeployReady token) public {
|
||||
_token = token;
|
||||
}
|
||||
|
||||
@ -76,7 +76,9 @@ contract MinerRewardMinter {
|
||||
}
|
||||
----
|
||||
|
||||
This contract, when initialized with an `ERC20Mintable` instance, will result in exactly the same behavior implemented in the previous section. What is interesting about using `ERC20Mintable` is that we can easily combine multiple supply mechanisms by assigning the role to multiple contracts, and moreover that we can do this dynamically.
|
||||
This contract, when initialized with an `ERC20DeployReady` instance, will result in exactly the same behavior implemented in the previous section. What is interesting about using `ERC20DeployReady` is that we can easily combine multiple supply mechanisms by assigning the role to multiple contracts, and moreover that we can do this dynamically.
|
||||
|
||||
TIP: To learn more about roles and permissioned systems, head to our xref:access-control.adoc[Access Control guide].
|
||||
|
||||
[[automating-the-reward]]
|
||||
== Automating the Reward
|
||||
|
||||
@ -13,19 +13,18 @@ Here's what our GLD token might look like.
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
pragma solidity ^0.5.0;
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";
|
||||
|
||||
contract GLDToken is ERC20, ERC20Detailed {
|
||||
constructor(uint256 initialSupply) ERC20Detailed("Gold", "GLD", 18) public {
|
||||
contract GLDToken is ERC20 {
|
||||
constructor(uint256 initialSupply) ERC20("Gold", "GLD") public {
|
||||
_mint(msg.sender, initialSupply);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Our contracts are often used via https://solidity.readthedocs.io/en/latest/contracts.html#inheritance[inheritance], and here we're reusing xref:api:token/ERC20.adoc#erc20[`ERC20`] for the basic standard implementation and xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`] to get the xref:api:token/ERC20.adoc#ERC20Detailed-name--[`name`], xref:api:token/ERC20.adoc#ERC20Detailed-symbol--[`symbol`], and xref:api:token/ERC20.adoc#ERC20Detailed-decimals--[`decimals`] properties. Additionally, we're creating an `initialSupply` of tokens, which will be assigned to the address that deploys the contract.
|
||||
Our contracts are often used via https://solidity.readthedocs.io/en/latest/contracts.html#inheritance[inheritance], and here we're reusing xref:api:token/ERC20.adoc#erc20[`ERC20`] for both the basic standard implementation and the xref:api:token/ERC20.adoc#ERC20-name--[`name`], xref:api:token/ERC20.adoc#ERC20-symbol--[`symbol`], and xref:api:token/ERC20.adoc#ERC20-decimals--[`decimals`] optional extensions. Additionally, we're creating an `initialSupply` of tokens, which will be assigned to the address that deploys the contract.
|
||||
|
||||
TIP: For a more complete discussion of ERC20 supply mechanisms, see xref:erc20-supply.adoc[Creating ERC20 Supply].
|
||||
|
||||
@ -53,7 +52,7 @@ We can also xref:api:token/ERC20.adoc#IERC20-transfer-address-uint256-[transfer]
|
||||
|
||||
Often, you'll want to be able to divide your tokens into arbitrary amounts: say, if you own `5 GLD`, you may want to send `1.5 GLD` to a friend, and keep `3.5 GLD` to yourself. Unfortunately, Solidity and the EVM do not support this behavior: only integer (whole) numbers can be used, which poses an issue. You may send `1` or `2` tokens, but not `1.5`.
|
||||
|
||||
To work around this, xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`] provides a xref:api:token/ERC20.adoc#ERC20Detailed-decimals--[`decimals`] field, which is used to specify how many decimal places a token has. To be able to transfer `1.5 GLD`, `decimals` must be at least `1`, since that number has a single decimal place.
|
||||
To work around this, xref:api:token/ERC20.adoc#ERC20[`ERC20`] provides a xref:api:token/ERC20.adoc#ERC20-decimals--[`decimals`] field, which is used to specify how many decimal places a token has. To be able to transfer `1.5 GLD`, `decimals` must be at least `1`, since that number has a single decimal place.
|
||||
|
||||
How can this be achieved? It's actually very simple: a token contract can use larger integer values, so that a balance of `50` will represent `5 GLD`, a transfer of `15` will correspond to `1.5 GLD` being sent, and so on.
|
||||
|
||||
@ -61,6 +60,8 @@ It is important to understand that `decimals` is _only used for display purposes
|
||||
|
||||
You'll probably want to use a `decimals` value of `18`, just like Ether and most ERC20 token contracts in use, unless you have a very special reason not to. When minting tokens or transferring them around, you will be actually sending the number `num GLD * 10^decimals`.
|
||||
|
||||
NOTE: By default, `ERC20` uses a value of `18` for `decimals`. To use a different value, you will need to call xref:api:token/ERC20.adoc#ERC20-_setupDecimals-uint8-[_setupDecimals] in your constructor.
|
||||
|
||||
So if you want to send `5` tokens using a token contract with 18 decimals, the the method to call will actually be:
|
||||
|
||||
```solidity
|
||||
|
||||
@ -12,16 +12,16 @@ Here's what a contract for tokenized items might look like:
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
pragma solidity ^0.5.0;
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||
import "@openzeppelin/contracts/utils/Counters.sol";
|
||||
|
||||
contract GameItem is ERC721Full {
|
||||
contract GameItem is ERC721 {
|
||||
using Counters for Counters.Counter;
|
||||
Counters.Counter private _tokenIds;
|
||||
|
||||
constructor() ERC721Full("GameItem", "ITM") public {
|
||||
constructor() ERC721("GameItem", "ITM") public {
|
||||
}
|
||||
|
||||
function awardItem(address player, string memory tokenURI) public returns (uint256) {
|
||||
@ -36,7 +36,7 @@ contract GameItem is ERC721Full {
|
||||
}
|
||||
----
|
||||
|
||||
The xref:api:token/ERC721.adoc#ERC721Full[`ERC721Full`] contract includes all standard extensions, and is probably the one you want to use. In particular, it includes xref:api:token/ERC721.adoc#ERC721Metadata[`ERC721Metadata`], which provides the xref:api:token/ERC721.adoc#ERC721Metadata-_setTokenURI-uint256-string-[`_setTokenURI`] method we use to store an item's metadata.
|
||||
The xref:api:token/ERC721.adoc#ERC721[`ERC721`] contract includes all standard extensions (xref:api:token/ERC721.adoc#IERC721Metadata[`IERC721Metadata`] and xref:api:token/ERC721.adoc#IERC721Enumerable[`IERC721Enumerable`]). That's where the xref:api:token/ERC721.adoc#ERC721-_setTokenURI-uint256-string-[`_setTokenURI`] method comes from: we use it to store an item's metadata.
|
||||
|
||||
Also note that, unlike ERC20, ERC721 lacks a `decimals` field, since each token is distinct and cannot be partitioned.
|
||||
|
||||
|
||||
@ -102,17 +102,22 @@ NOTE: Always use `_preRelayedCall` and `_postRelayedCall` functions. Internal `
|
||||
|
||||
=== How to Use `GSNRecipientERC20Fee`
|
||||
|
||||
Your GSN recipient contract needs to inherit from `GSNRecipientERC20Fee` along with appropriate xref:access-control.adoc[access control] (for token minting), set the token details in the constructor of `GSNRecipientERC20Fee` and create a public `mint` function suitably protected by your chosen access control as per the following sample code (which uses the xref:api:access.adoc#MinterRole[MinterRole]):
|
||||
Your GSN recipient contract needs to inherit from `GSNRecipientERC20Fee` along with appropriate xref:access-control.adoc[access control] (for token minting), set the token details in the constructor of `GSNRecipientERC20Fee` and create a public `mint` function suitably protected by your chosen access control as per the following sample code (which uses xref:api:access.adoc#AccessControl[`AccessControl`]):
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
import "@openzeppelin/contracts/GSN/GSNRecipientERC20Fee";
|
||||
import "@openzeppelin/contracts/access/AccessControl";
|
||||
|
||||
contract MyContract is GSNRecipientERC20Fee, AccessControl {
|
||||
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||
|
||||
contract MyContract is GSNRecipientERC20Fee, MinterRole {
|
||||
constructor() public GSNRecipientERC20Fee("FeeToken", "FEE") {
|
||||
_setupRole(MINTER_ROLE, _msgSender());
|
||||
}
|
||||
|
||||
function mint(address account, uint256 amount) public onlyMinter {
|
||||
function mint(address account, uint256 amount) public {
|
||||
require(hasRole(MINTER_ROLE, _msgSender()));
|
||||
_mint(account, amount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,13 +26,12 @@ Once installed, you can use the contracts in the library by importing them:
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
pragma solidity ^0.5.0;
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721Mintable.sol";
|
||||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||
|
||||
contract MyNFT is ERC721Full, ERC721Mintable {
|
||||
constructor() ERC721Full("MyNFT", "MNFT") public {
|
||||
contract MyNFT is ERC721 {
|
||||
constructor() ERC721("MyNFT", "MNFT") public {
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@ -28,7 +28,7 @@ Every several months a new major release may come out. These are not scheduled,
|
||||
|
||||
On the https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v2.0.0[OpenZeppelin 2.0 release], we committed ourselves to keeping a stable API. We aim to more precisely define what we understand by _stable_ and _API_ here, so users of the library can understand these guarantees and be confident their project won't break unexpectedly.
|
||||
|
||||
In a nutshell, the API being stable means _if your project is working today, it will continue to do so_. New contracts and features will be added in minor releases, but only in a backwards compatible way. The exception to this rule are contracts in the xref:api:drafts.adoc[Drafts] category, which should be considered unstable.
|
||||
In a nutshell, the API being stable means _if your project is working today, it will continue to do so_. New contracts and features will be added in minor releases, but only in a backwards compatible way.
|
||||
|
||||
[[versioning-scheme]]
|
||||
=== Versioning Scheme
|
||||
@ -54,7 +54,7 @@ Finally, sometimes language limitations will force us to e.g. make `internal` a
|
||||
[[libraries]]
|
||||
=== Libraries
|
||||
|
||||
Some of our Solidity libraries use `struct`s to handle internal data that should not be accessed directly (e.g. `Roles`). There's an https://github.com/ethereum/solidity/issues/4637[open issue] in the Solidity repository requesting a language feature to prevent said access, but it looks like it won't be implemented any time soon. Because of this, we will use leading underscores and mark said `struct` s to make it clear to the user that its contents and layout are _not_ part of the API.
|
||||
Some of our Solidity libraries use ``struct``s to handle internal data that should not be accessed directly (e.g. `Roles`). There's an https://github.com/ethereum/solidity/issues/4637[open issue] in the Solidity repository requesting a language feature to prevent said access, but it looks like it won't be implemented any time soon. Because of this, we will use leading underscores and mark said `struct` s to make it clear to the user that its contents and layout are _not_ part of the API.
|
||||
|
||||
[[events]]
|
||||
=== Events
|
||||
|
||||
@ -90,11 +90,11 @@ If you want to Escrow some funds, check out xref:api:payment.adoc#Escrow[`Escrow
|
||||
[[collections]]
|
||||
== Collections
|
||||
|
||||
If you need support for more powerful collections than Solidity's native arrays and mappings, take a look at xref:api:utils.adoc#EnumerableSet[`EnumerableSet`]. It is similar to a mapping in that it stores and removes elements in constant time and doesn't allow for repeated entries, but it also supports _enumeration_, which means you can easily query all elements of the set both on and off-chain.
|
||||
If you need support for more powerful collections than Solidity's native arrays and mappings, take a look at xref:api:utils.adoc#EnumerableSet[`EnumerableSet`] and xref:api:utils.adoc#EnumerableMap[`EnumerableMap`]. They are similar to mappings in that they store and remove elements in constant time and don't allow for repeated entries, but they also supports _enumeration_, which means you can easily query all stored entries both on and off-chain.
|
||||
|
||||
[[misc]]
|
||||
== Misc
|
||||
|
||||
Want to check if an address is a contract? Use xref:api:utils.adoc#Address[`Address`] and xref:api:utils.adoc#Address-isContract-address-[`Address.isContract()`].
|
||||
|
||||
Want to keep track of some numbers that increment by 1 every time you want another one? Check out xref:api:drafts.adoc#Counter[`Counter`]. This is useful for lots of things, like creating incremental identifiers, as shown on the xref:721.adoc[ERC721 guide].
|
||||
Want to keep track of some numbers that increment by 1 every time you want another one? Check out xref:api:utils.adoc#Counters[`Counters`]. This is useful for lots of things, like creating incremental identifiers, as shown on the xref:erc721.adoc[ERC721 guide].
|
||||
|
||||
Reference in New Issue
Block a user