Update docs (#2168)

* Update docs for ERC20 and ERC721

* Add EnumerableMap to docs

* Update misc guides

* Apply suggestions from code review

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

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
This commit is contained in:
Nicolás Venturo
2020-04-03 13:17:24 -03:00
committed by GitHub
parent 6668a4d05c
commit 402c6ab4cc
11 changed files with 53 additions and 69 deletions

View File

@ -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";
@ -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() public {
constructor() ERC20("MyToken", "TKN") public {
// Grant the contract deployer the default admin role: it will be able
// to grant and revoke any roles
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -90,7 +90,7 @@ 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