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