|
@@ -68,60 +68,6 @@ The `super.revokeRole` statement at the end will invoke ``AccessControl``'s orig
|
|
|
|
|
|
NOTE: The same rule is implemented and extended in xref:api:access.adoc#AccessControlDefaultAdminRules[`AccessControlDefaultAdminRules`], an extension that also adds enforced security measures for the `DEFAULT_ADMIN_ROLE`.
|
|
|
|
|
|
-[[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.8.20;
|
|
|
-
|
|
|
-import {ERC20} from "@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.
|
|
|
-
|
|
|
-=== 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!
|
|
|
-
|
|
|
== Security
|
|
|
|
|
|
The maintainers of OpenZeppelin Contracts are mainly concerned with the correctness and security of the code as published in the library, and the combinations of base contracts with the official extensions from the library.
|