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:
@ -2,48 +2,40 @@
|
|||||||
|
|
||||||
This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-20[ERC20 Token Standard].
|
This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-20[ERC20 Token Standard].
|
||||||
|
|
||||||
TIP: For an overview of ERC20 tokens and a walkthrough on how to create a token contract read our xref:ROOT:tokens.adoc#ERC20[ERC20 guide].
|
TIP: For an overview of ERC20 tokens and a walkthrough on how to create a token contract read our xref:ROOT:erc20.adoc[ERC20 guide].
|
||||||
|
|
||||||
There a few core contracts that implement the behavior specified in the EIP:
|
There a few core contracts that implement the behavior specified in the EIP:
|
||||||
|
|
||||||
* {IERC20}: the interface all ERC20 implementations should conform to
|
* {IERC20}: the interface all ERC20 implementations should conform to.
|
||||||
* {ERC20}: the base implementation of the ERC20 interface
|
* {ERC20}: the implementation of the ERC20 interface, including the <<ERC20-name,`name`>>, <<ERC20-symbol,`symbol`>> and <<ERC20-decimals,`decimals`>> optional standard extension to the base interface.
|
||||||
* {ERC20Detailed}: includes the <<ERC20Detailed-name,`name`>>,
|
|
||||||
<<ERC20Detailed-symbol,`symbol`>> and <<ERC20Detailed-decimals,`decimals`>>
|
|
||||||
optional standard extension to the base interface
|
|
||||||
|
|
||||||
Additionally there are multiple custom extensions, including:
|
Additionally there are multiple custom extensions, including:
|
||||||
|
|
||||||
* designation of addresses that can create token supply ({ERC20Mintable}), with an optional maximum cap ({ERC20Capped})
|
* designation of addresses that can pause token transfers for all users ({ERC20Pausable}).
|
||||||
* destruction of own tokens ({ERC20Burnable})
|
* efficient storage of past token balances to be later queried at any point in time ({ERC20Snapshot}).
|
||||||
* designation of addresses that can pause token operations for all users ({ERC20Pausable}).
|
* destruction of own tokens ({ERC20Burnable}).
|
||||||
|
* enforcement of a cap to the total supply when minting tokens ({ERC20Capped}).
|
||||||
|
|
||||||
Finally, there are some utilities to interact with ERC20 contracts in various ways.
|
Finally, there are some utilities to interact with ERC20 contracts in various ways.
|
||||||
|
|
||||||
* {SafeERC20} is a wrapper around the interface that eliminates the need to handle boolean return values.
|
* {SafeERC20} is a wrapper around the interface that eliminates the need to handle boolean return values.
|
||||||
* {TokenTimelock} can hold tokens for a beneficiary until a specified time.
|
* {TokenTimelock} can hold tokens for a beneficiary until a specified time.
|
||||||
|
|
||||||
NOTE: This page is incomplete. We're working to improve it for the next release. Stay tuned!
|
|
||||||
|
|
||||||
== Core
|
== Core
|
||||||
|
|
||||||
{{IERC20}}
|
{{IERC20}}
|
||||||
|
|
||||||
{{ERC20}}
|
{{ERC20}}
|
||||||
|
|
||||||
{{ERC20Detailed}}
|
|
||||||
|
|
||||||
== Extensions
|
== Extensions
|
||||||
|
|
||||||
{{ERC20Mintable}}
|
{{ERC20Snapshot}}
|
||||||
|
|
||||||
{{ERC20Burnable}}
|
|
||||||
|
|
||||||
{{ERC20Pausable}}
|
{{ERC20Pausable}}
|
||||||
|
|
||||||
{{ERC20Capped}}
|
{{ERC20Burnable}}
|
||||||
|
|
||||||
{{ERC20Snapshot}}
|
{{ERC20Capped}}
|
||||||
|
|
||||||
== Utilities
|
== Utilities
|
||||||
|
|
||||||
|
|||||||
@ -2,54 +2,37 @@
|
|||||||
|
|
||||||
This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-721[ERC721 Non-Fungible Token Standard].
|
This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-721[ERC721 Non-Fungible Token Standard].
|
||||||
|
|
||||||
TIP: For a walkthrough on how to create an ERC721 token read our xref:ROOT:tokens.adoc#ERC721[ERC721 guide].
|
TIP: For a walkthrough on how to create an ERC721 token read our xref:ROOT:erc721.adoc[ERC721 guide].
|
||||||
|
|
||||||
The EIP consists of three interfaces, found here as {IERC721}, {IERC721Metadata}, and {IERC721Enumerable}. Only the first one is required in a contract to be ERC721 compliant.
|
The EIP consists of three interfaces, found here as {IERC721}, {IERC721Metadata}, and {IERC721Enumerable}. Only the first one is required in a contract to be ERC721 compliant. However, all three are implemented in {ERC721}.
|
||||||
|
|
||||||
Each interface is implemented separately in {ERC721}, {ERC721Metadata}, and {ERC721Enumerable}. You can choose the subset of functionality you would like to support in your token by combining the
|
|
||||||
desired subset through inheritance.
|
|
||||||
|
|
||||||
The fully featured token implementing all three interfaces is prepackaged as {ERC721Full}.
|
|
||||||
|
|
||||||
Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <<IERC721-safeTransferFrom,`safeTransferFrom`>>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface.
|
Additionally, {IERC721Receiver} can be used to prevent tokens from becoming forever locked in contracts. Imagine sending an in-game item to an exchange address that can't send it back!. When using <<IERC721-safeTransferFrom,`safeTransferFrom`>>, the token contract checks to see that the receiver is an {IERC721Receiver}, which implies that it knows how to handle {ERC721} tokens. If you're writing a contract that needs to receive {ERC721} tokens, you'll want to include this interface.
|
||||||
|
|
||||||
Finally, some custom extensions are also included:
|
Finally, some custom extensions are also included:
|
||||||
|
|
||||||
* {ERC721Mintable} — like the ERC20 version, this allows certain addresses to mint new tokens
|
Additionally there are multiple custom extensions, including:
|
||||||
* {ERC721Pausable} — like the ERC20 version, this allows addresses to freeze transfers of tokens
|
|
||||||
|
|
||||||
NOTE: This page is incomplete. We're working to improve it for the next release. Stay tuned!
|
* designation of addresses that can pause token transfers for all users ({ERC721Pausable}).
|
||||||
|
* destruction of own tokens ({ERC721Burnable}).
|
||||||
|
|
||||||
== Core
|
== Core
|
||||||
|
|
||||||
{{IERC721}}
|
{{IERC721}}
|
||||||
|
|
||||||
{{ERC721}}
|
|
||||||
|
|
||||||
{{IERC721Metadata}}
|
{{IERC721Metadata}}
|
||||||
|
|
||||||
{{ERC721Metadata}}
|
|
||||||
|
|
||||||
{{ERC721Enumerable}}
|
|
||||||
|
|
||||||
{{IERC721Enumerable}}
|
{{IERC721Enumerable}}
|
||||||
|
|
||||||
{{IERC721Full}}
|
{{ERC721}}
|
||||||
|
|
||||||
{{ERC721Full}}
|
|
||||||
|
|
||||||
{{IERC721Receiver}}
|
{{IERC721Receiver}}
|
||||||
|
|
||||||
== Extensions
|
== Extensions
|
||||||
|
|
||||||
{{ERC721Mintable}}
|
{{ERC721Pausable}}
|
||||||
|
|
||||||
{{ERC721MetadataMintable}}
|
|
||||||
|
|
||||||
{{ERC721Burnable}}
|
{{ERC721Burnable}}
|
||||||
|
|
||||||
{{ERC721Pausable}}
|
|
||||||
|
|
||||||
== Convenience
|
== Convenience
|
||||||
|
|
||||||
{{ERC721Holder}}
|
{{ERC721Holder}}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
= ERC 777
|
= ERC 777
|
||||||
This set of interfaces and contracts are all related to the [ERC777 token standard](https://eips.ethereum.org/EIPS/eip-777).
|
This set of interfaces and contracts are all related to the [ERC777 token standard](https://eips.ethereum.org/EIPS/eip-777).
|
||||||
|
|
||||||
TIP: For an overview of ERC777 tokens and a walkthrough on how to create a token contract read our xref:ROOT:tokens.adoc#ERC777[ERC777 guide].
|
TIP: For an overview of ERC777 tokens and a walkthrough on how to create a token contract read our xref:ROOT:erc777.adoc[ERC777 guide].
|
||||||
|
|
||||||
The token behavior itself is implemented in the core contracts: {IERC777}, {ERC777}.
|
The token behavior itself is implemented in the core contracts: {IERC777}, {ERC777}.
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,8 @@ Miscellaneous contracts containing utility functions, often related to working w
|
|||||||
|
|
||||||
{{EnumerableSet}}
|
{{EnumerableSet}}
|
||||||
|
|
||||||
|
{{EnumerableMap}}
|
||||||
|
|
||||||
{{Create2}}
|
{{Create2}}
|
||||||
|
|
||||||
{{ReentrancyGuard}}
|
{{ReentrancyGuard}}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ OpenZeppelin provides xref:api:access.adoc#Ownable[`Ownable`] for implementing o
|
|||||||
|
|
||||||
[source,solidity]
|
[source,solidity]
|
||||||
----
|
----
|
||||||
pragma solidity ^0.5.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
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 MINTER_ROLE = keccak256("MINTER_ROLE");
|
||||||
bytes32 public constant BURNER_ROLE = keccak256("BURNER_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
|
// Grant the contract deployer the default admin role: it will be able
|
||||||
// to grant and revoke any roles
|
// to grant and revoke any roles
|
||||||
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||||
|
|||||||
@ -55,7 +55,7 @@ As we can see, `_mint` makes it super easy to do this correctly.
|
|||||||
[[modularizing-the-mechanism]]
|
[[modularizing-the-mechanism]]
|
||||||
== 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].
|
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]
|
[source,solidity]
|
||||||
----
|
----
|
||||||
contract MinerRewardMinter {
|
contract MinerRewardMinter {
|
||||||
ERC20Mintable _token;
|
ERC20DeployReady _token;
|
||||||
|
|
||||||
constructor(ERC20Mintable token) public {
|
constructor(ERC20DeployReady token) public {
|
||||||
_token = token;
|
_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]]
|
||||||
== Automating the Reward
|
== Automating the Reward
|
||||||
|
|||||||
@ -13,19 +13,18 @@ Here's what our GLD token might look like.
|
|||||||
|
|
||||||
[source,solidity]
|
[source,solidity]
|
||||||
----
|
----
|
||||||
pragma solidity ^0.5.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";
|
|
||||||
|
|
||||||
contract GLDToken is ERC20, ERC20Detailed {
|
contract GLDToken is ERC20 {
|
||||||
constructor(uint256 initialSupply) ERC20Detailed("Gold", "GLD", 18) public {
|
constructor(uint256 initialSupply) ERC20("Gold", "GLD") public {
|
||||||
_mint(msg.sender, initialSupply);
|
_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].
|
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`.
|
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.
|
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`.
|
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:
|
So if you want to send `5` tokens using a token contract with 18 decimals, the the method to call will actually be:
|
||||||
|
|
||||||
```solidity
|
```solidity
|
||||||
|
|||||||
@ -12,16 +12,16 @@ Here's what a contract for tokenized items might look like:
|
|||||||
|
|
||||||
[source,solidity]
|
[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";
|
import "@openzeppelin/contracts/utils/Counters.sol";
|
||||||
|
|
||||||
contract GameItem is ERC721Full {
|
contract GameItem is ERC721 {
|
||||||
using Counters for Counters.Counter;
|
using Counters for Counters.Counter;
|
||||||
Counters.Counter private _tokenIds;
|
Counters.Counter private _tokenIds;
|
||||||
|
|
||||||
constructor() ERC721Full("GameItem", "ITM") public {
|
constructor() ERC721("GameItem", "ITM") public {
|
||||||
}
|
}
|
||||||
|
|
||||||
function awardItem(address player, string memory tokenURI) public returns (uint256) {
|
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.
|
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`
|
=== 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]
|
[source,solidity]
|
||||||
----
|
----
|
||||||
import "@openzeppelin/contracts/GSN/GSNRecipientERC20Fee";
|
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") {
|
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);
|
_mint(account, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,13 +26,12 @@ Once installed, you can use the contracts in the library by importing them:
|
|||||||
|
|
||||||
[source,solidity]
|
[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/token/ERC721/ERC721Mintable.sol";
|
|
||||||
|
|
||||||
contract MyNFT is ERC721Full, ERC721Mintable {
|
contract MyNFT is ERC721 {
|
||||||
constructor() ERC721Full("MyNFT", "MNFT") public {
|
constructor() ERC721("MyNFT", "MNFT") public {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|||||||
@ -90,7 +90,7 @@ If you want to Escrow some funds, check out xref:api:payment.adoc#Escrow[`Escrow
|
|||||||
[[collections]]
|
[[collections]]
|
||||||
== 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]]
|
||||||
== Misc
|
== Misc
|
||||||
|
|||||||
Reference in New Issue
Block a user