Use ERC-XXX syntax (#4730)
Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com> Co-authored-by: ernestognw <ernestognw@gmail.com>
This commit is contained in:
@ -8,11 +8,11 @@
|
||||
* xref:access-control.adoc[Access Control]
|
||||
|
||||
* xref:tokens.adoc[Tokens]
|
||||
** xref:erc20.adoc[ERC20]
|
||||
** xref:erc20.adoc[ERC-20]
|
||||
*** xref:erc20-supply.adoc[Creating Supply]
|
||||
** xref:erc721.adoc[ERC721]
|
||||
** xref:erc1155.adoc[ERC1155]
|
||||
** xref:erc4626.adoc[ERC4626]
|
||||
** xref:erc721.adoc[ERC-721]
|
||||
** xref:erc1155.adoc[ERC-1155]
|
||||
** xref:erc4626.adoc[ERC-4626]
|
||||
|
||||
* xref:governance.adoc[Governance]
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ Most software uses access control systems that are role-based: some users are re
|
||||
OpenZeppelin Contracts provides xref:api:access.adoc#AccessControl[`AccessControl`] for implementing role-based access control. Its usage is straightforward: for each role that you want to define,
|
||||
you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role.
|
||||
|
||||
Here's a simple example of using `AccessControl` in an xref:erc20.adoc[`ERC20` token] to define a 'minter' role, which allows accounts that have it create new tokens:
|
||||
Here's a simple example of using `AccessControl` in an xref:erc20.adoc[ERC-20 token] to define a 'minter' role, which allows accounts that have it create new tokens:
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
@ -54,7 +54,7 @@ NOTE: Make sure you fully understand how xref:api:access.adoc#AccessControl[`Acc
|
||||
|
||||
While clear and explicit, this isn't anything we wouldn't have been able to achieve with `Ownable`. Indeed, where `AccessControl` shines is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles.
|
||||
|
||||
Let's augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens, and by using the `onlyRole` modifier:
|
||||
Let's augment our ERC-20 token example by also defining a 'burner' role, which lets accounts destroy tokens, and by using the `onlyRole` modifier:
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
@ -66,7 +66,7 @@ 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 assigning roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts?
|
||||
The ERC-20 token example above uses `_grantRole`, an `internal` function that is useful when programmatically assigning 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_.
|
||||
|
||||
@ -76,7 +76,7 @@ This mechanism can be used to create complex permissioning structures resembling
|
||||
|
||||
Since it is the admin for all roles by default, and in fact it is also its own admin, this role carries significant risk. To mitigate this risk we provide xref:api:access.adoc#AccessControlDefaultAdminRules[`AccessControlDefaultAdminRules`], a recommended extension of `AccessControl` that adds a number of enforced security measures for this role: the admin is restricted to a single account, with a 2-step transfer procedure with a delay in between steps.
|
||||
|
||||
Let's take a look at the ERC20 token example, this time taking advantage of the default admin role:
|
||||
Let's take a look at the ERC-20 token example, this time taking advantage of the default admin role:
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
@ -165,7 +165,7 @@ OpenZeppelin Contracts provides xref:api:access.adoc#AccessManager[`AccessManage
|
||||
|
||||
In order to restrict access to some functions of your contract, you should inherit from the xref:api:access.adoc#AccessManaged[`AccessManaged`] contract provided along with the manager. This provides the `restricted` modifier that can be used to protect any externally facing function. Note that you will have to specify the address of the AccessManager instance (xref:api:access.adoc#AccessManaged-constructor-address-[`initialAuthority`]) in the constructor so the `restricted` modifier knows which manager to use for checking permissions.
|
||||
|
||||
Here's a simple example of an xref:tokens.adoc#ERC20[`ERC20` token] that defines a `mint` function that is restricted by an xref:api:access.adoc#AccessManager[`AccessManager`]:
|
||||
Here's a simple example of an xref:tokens.adoc#ERC20[ERC-20 token] that defines a `mint` function that is restricted by an xref:api:access.adoc#AccessManager[`AccessManager`]:
|
||||
|
||||
```solidity
|
||||
include::api:example$access-control/AccessManagedERC20MintBase.sol[]
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
= ERC1155
|
||||
= ERC-1155
|
||||
|
||||
ERC1155 is a novel token standard that aims to take the best from previous standards to create a xref:tokens.adoc#different-kinds-of-tokens[*fungibility-agnostic*] and *gas-efficient* xref:tokens.adoc#but_first_coffee_a_primer_on_token_contracts[token contract].
|
||||
ERC-1155 is a novel token standard that aims to take the best from previous standards to create a xref:tokens.adoc#different-kinds-of-tokens[*fungibility-agnostic*] and *gas-efficient* xref:tokens.adoc#but_first_coffee_a_primer_on_token_contracts[token contract].
|
||||
|
||||
TIP: ERC1155 draws ideas from all of xref:erc20.adoc[ERC20], xref:erc721.adoc[ERC721], and https://eips.ethereum.org/EIPS/eip-777[ERC777]. If you're unfamiliar with those standards, head to their guides before moving on.
|
||||
TIP: ERC-1155 draws ideas from all of xref:erc20.adoc[ERC-20], xref:erc721.adoc[ERC-721], and https://eips.ethereum.org/EIPS/eip-777[ERC-777]. If you're unfamiliar with those standards, head to their guides before moving on.
|
||||
|
||||
[[multi-token-standard]]
|
||||
== Multi Token Standard
|
||||
|
||||
The distinctive feature of ERC1155 is that it uses a single smart contract to represent multiple tokens at once. This is why its xref:api:token/ERC1155.adoc#IERC1155-balanceOf-address-uint256-[`balanceOf`] function differs from ERC20's and ERC777's: it has an additional `id` argument for the identifier of the token that you want to query the balance of.
|
||||
The distinctive feature of ERC-1155 is that it uses a single smart contract to represent multiple tokens at once. This is why its xref:api:token/ERC1155.adoc#IERC1155-balanceOf-address-uint256-[`balanceOf`] function differs from ERC-20's and ERC-777's: it has an additional `id` argument for the identifier of the token that you want to query the balance of.
|
||||
|
||||
This is similar to how ERC721 does things, but in that standard a token `id` has no concept of balance: each token is non-fungible and exists or doesn't. The ERC721 xref:api:token/ERC721.adoc#IERC721-balanceOf-address-[`balanceOf`] function refers to _how many different tokens_ an account has, not how many of each. On the other hand, in ERC1155 accounts have a distinct balance for each token `id`, and non-fungible tokens are implemented by simply minting a single one of them.
|
||||
This is similar to how ERC-721 does things, but in that standard a token `id` has no concept of balance: each token is non-fungible and exists or doesn't. The ERC-721 xref:api:token/ERC721.adoc#IERC721-balanceOf-address-[`balanceOf`] function refers to _how many different tokens_ an account has, not how many of each. On the other hand, in ERC-1155 accounts have a distinct balance for each token `id`, and non-fungible tokens are implemented by simply minting a single one of them.
|
||||
|
||||
This approach leads to massive gas savings for projects that require multiple tokens. Instead of deploying a new contract for each token type, a single ERC1155 token contract can hold the entire system state, reducing deployment costs and complexity.
|
||||
This approach leads to massive gas savings for projects that require multiple tokens. Instead of deploying a new contract for each token type, a single ERC-1155 token contract can hold the entire system state, reducing deployment costs and complexity.
|
||||
|
||||
[[batch-operations]]
|
||||
== Batch Operations
|
||||
@ -20,13 +20,13 @@ Because all state is held in a single contract, it is possible to operate over m
|
||||
|
||||
In the spirit of the standard, we've also included batch operations in the non-standard functions, such as xref:api:token/ERC1155.adoc#ERC1155-_mintBatch-address-uint256---uint256---bytes-[`_mintBatch`].
|
||||
|
||||
== Constructing an ERC1155 Token Contract
|
||||
== Constructing an ERC-1155 Token Contract
|
||||
|
||||
We'll use ERC1155 to track multiple items in our game, which will each have their own unique attributes. We mint all items to the deployer of the contract, which we can later transfer to players. Players are free to keep their tokens or trade them with other people as they see fit, as they would any other asset on the blockchain!
|
||||
We'll use ERC-1155 to track multiple items in our game, which will each have their own unique attributes. We mint all items to the deployer of the contract, which we can later transfer to players. Players are free to keep their tokens or trade them with other people as they see fit, as they would any other asset on the blockchain!
|
||||
|
||||
For simplicity, we will mint all items in the constructor, but you could add minting functionality to the contract to mint on demand to players.
|
||||
|
||||
TIP: For an overview of minting mechanisms, check out xref:erc20-supply.adoc[Creating ERC20 Supply].
|
||||
TIP: For an overview of minting mechanisms, check out xref:erc20-supply.adoc[Creating ERC-20 Supply].
|
||||
|
||||
Here's what a contract for tokenized items might look like:
|
||||
|
||||
@ -59,7 +59,7 @@ Note that for our Game Items, Gold is a fungible token whilst Thor's Hammer is a
|
||||
|
||||
The xref:api:token/ERC1155.adoc#ERC1155[`ERC1155`] contract includes the optional extension xref:api:token/ERC1155.adoc#IERC1155MetadataURI[`IERC1155MetadataURI`]. That's where the xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`uri`] function comes from: we use it to retrieve the metadata uri.
|
||||
|
||||
Also note that, unlike ERC20, ERC1155 lacks a `decimals` field, since each token is distinct and cannot be partitioned.
|
||||
Also note that, unlike ERC-20, ERC-1155 lacks a `decimals` field, since each token is distinct and cannot be partitioned.
|
||||
|
||||
Once deployed, we will be able to query the deployer’s balance:
|
||||
[source,javascript]
|
||||
@ -114,7 +114,7 @@ For more information about the metadata JSON Schema, check out the https://githu
|
||||
|
||||
NOTE: You'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game!
|
||||
|
||||
TIP: If you'd like to put all item information on-chain, you can extend ERC721 to do so (though it will be rather costly) by providing a xref:utilities.adoc#base64[`Base64`] Data URI with the JSON schema encoded. You could also leverage IPFS to store the URI information, but these techniques are out of the scope of this overview guide
|
||||
TIP: If you'd like to put all item information on-chain, you can extend ERC-721 to do so (though it will be rather costly) by providing a xref:utilities.adoc#base64[`Base64`] Data URI with the JSON schema encoded. You could also leverage IPFS to store the URI information, but these techniques are out of the scope of this overview guide
|
||||
|
||||
[[sending-to-contracts]]
|
||||
== Sending Tokens to Contracts
|
||||
@ -123,12 +123,12 @@ A key difference when using xref:api:token/ERC1155.adoc#IERC1155-safeTransferFro
|
||||
|
||||
[source,text]
|
||||
----
|
||||
ERC1155: transfer to non ERC1155Receiver implementer
|
||||
ERC-1155: transfer to non ERC-1155 Receiver implementer
|
||||
----
|
||||
|
||||
This is a good thing! It means that the recipient contract has not registered itself as aware of the ERC1155 protocol, so transfers to it are disabled to *prevent tokens from being locked forever*. As an example, https://etherscan.io/token/0xa74476443119A942dE498590Fe1f2454d7D4aC0d?a=0xa74476443119A942dE498590Fe1f2454d7D4aC0d[the Golem contract currently holds over 350k `GNT` tokens], worth multiple tens of thousands of dollars, and lacks methods to get them out of there. This has happened to virtually every ERC20-backed project, usually due to user error.
|
||||
This is a good thing! It means that the recipient contract has not registered itself as aware of the ERC-1155 protocol, so transfers to it are disabled to *prevent tokens from being locked forever*. As an example, https://etherscan.io/token/0xa74476443119A942dE498590Fe1f2454d7D4aC0d?a=0xa74476443119A942dE498590Fe1f2454d7D4aC0d[the Golem contract currently holds over 350k `GNT` tokens], worth multiple tens of thousands of dollars, and lacks methods to get them out of there. This has happened to virtually every ERC20-backed project, usually due to user error.
|
||||
|
||||
In order for our contract to receive ERC1155 tokens we can inherit from the convenience contract xref:api:token/ERC1155.adoc#ERC1155Holder[`ERC1155Holder`] which handles the registering for us. Though we need to remember to implement functionality to allow tokens to be transferred out of our contract:
|
||||
In order for our contract to receive ERC-1155 tokens we can inherit from the convenience contract xref:api:token/ERC1155.adoc#ERC1155Holder[`ERC1155Holder`] which handles the registering for us. Though we need to remember to implement functionality to allow tokens to be transferred out of our contract:
|
||||
|
||||
[source,solidity]
|
||||
----
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
= Creating ERC20 Supply
|
||||
= Creating ERC-20 Supply
|
||||
|
||||
In this guide, you will learn how to create an ERC20 token with a custom supply mechanism. We will showcase two idiomatic ways to use OpenZeppelin Contracts for this purpose that you will be able to apply to your smart contract development practice.
|
||||
In this guide, you will learn how to create an ERC-20 token with a custom supply mechanism. We will showcase two idiomatic ways to use OpenZeppelin Contracts for this purpose that you will be able to apply to your smart contract development practice.
|
||||
|
||||
The standard interface implemented by tokens built on Ethereum is called ERC20, and Contracts includes a widely used implementation of it: the aptly named xref:api:token/ERC20.adoc[`ERC20`] contract. This contract, like the standard itself, is quite simple and bare-bones. In fact, if you try to deploy an instance of `ERC20` as-is it will be quite literally useless... it will have no supply! What use is a token with no supply?
|
||||
The standard interface implemented by tokens built on Ethereum is called ERC-20, and Contracts includes a widely used implementation of it: the aptly named xref:api:token/ERC20.adoc[`ERC20`] contract. This contract, like the standard itself, is quite simple and bare-bones. In fact, if you try to deploy an instance of `ERC20` as-is it will be quite literally useless... it will have no supply! What use is a token with no supply?
|
||||
|
||||
The way that supply is created is not defined in the ERC20 document. Every token is free to experiment with its own mechanisms, ranging from the most decentralized to the most centralized, from the most naive to the most researched, and more.
|
||||
The way that supply is created is not defined in the ERC-20 document. Every token is free to experiment with its own mechanisms, ranging from the most decentralized to the most centralized, from the most naive to the most researched, and more.
|
||||
|
||||
[[fixed-supply]]
|
||||
== Fixed Supply
|
||||
@ -37,7 +37,7 @@ Encapsulating state like this makes it safer to extend contracts. For instance,
|
||||
[[rewarding-miners]]
|
||||
== Rewarding Miners
|
||||
|
||||
The internal xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[`_mint`] function is the key building block that allows us to write ERC20 extensions that implement a supply mechanism.
|
||||
The internal xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[`_mint`] function is the key building block that allows us to write ERC-20 extensions that implement a supply mechanism.
|
||||
|
||||
The mechanism we will implement is a token reward for the miners that produce Ethereum blocks. In Solidity, we can access the address of the current block's miner in the global variable `block.coinbase`. We will mint a token reward to this address whenever someone calls the function `mintMinerReward()` on our token. The mechanism may sound silly, but you never know what kind of dynamic this might result in, and it's worth analyzing and experimenting with!
|
||||
|
||||
@ -68,4 +68,4 @@ include::api:example$ERC20WithAutoMinerReward.sol[]
|
||||
[[wrapping-up]]
|
||||
== Wrapping Up
|
||||
|
||||
We've seen how to implement a ERC20 supply mechanism: internally through `_mint`. Hopefully this has helped you understand how to use OpenZeppelin Contracts and some of the design principles behind it, and you can apply them to your own smart contracts.
|
||||
We've seen how to implement a ERC-20 supply mechanism: internally through `_mint`. Hopefully this has helped you understand how to use OpenZeppelin Contracts and some of the design principles behind it, and you can apply them to your own smart contracts.
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
= ERC20
|
||||
= ERC-20
|
||||
|
||||
An ERC20 token contract keeps track of xref:tokens.adoc#different-kinds-of-tokens[_fungible_ tokens]: any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a *medium of exchange currency*, *voting rights*, *staking*, and more.
|
||||
An ERC-20 token contract keeps track of xref:tokens.adoc#different-kinds-of-tokens[_fungible_ tokens]: any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC-20 tokens useful for things like a *medium of exchange currency*, *voting rights*, *staking*, and more.
|
||||
|
||||
OpenZeppelin Contracts provides many ERC20-related contracts. On the xref:api:token/ERC20.adoc[`API reference`] you'll find detailed information on their properties and usage.
|
||||
|
||||
[[constructing-an-erc20-token-contract]]
|
||||
== Constructing an ERC20 Token Contract
|
||||
== Constructing an ERC-20 Token Contract
|
||||
|
||||
Using Contracts, we can easily create our own ERC20 token contract, which will be used to track _Gold_ (GLD), an internal currency in a hypothetical game.
|
||||
Using Contracts, we can easily create our own ERC-20 token contract, which will be used to track _Gold_ (GLD), an internal currency in a hypothetical game.
|
||||
|
||||
Here's what our GLD token might look like.
|
||||
|
||||
@ -28,7 +28,7 @@ contract GLDToken is ERC20 {
|
||||
|
||||
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 ERC-20 supply mechanisms, see xref:erc20-supply.adoc[Creating ERC-20 Supply].
|
||||
|
||||
That's it! Once deployed, we will be able to query the deployer's balance:
|
||||
|
||||
@ -60,7 +60,7 @@ How can this be achieved? It's actually very simple: a token contract can use la
|
||||
|
||||
It is important to understand that `decimals` is _only used for display purposes_. All arithmetic inside the contract is still performed on integers, and it is the different user interfaces (wallets, exchanges, etc.) that must adjust the displayed values according to `decimals`. The total token supply and balance of each account are not specified in `GLD`: you need to divide by `10 ** decimals` to get the actual `GLD` amount.
|
||||
|
||||
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 ERC-20 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 override the `decimals()` function in your contract.
|
||||
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
= ERC4626
|
||||
= ERC-4626
|
||||
:stem: latexmath
|
||||
|
||||
https://eips.ethereum.org/EIPS/eip-4626[ERC4626] is an extension of xref:erc20.adoc[ERC20] that proposes a standard interface for token vaults. This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault.
|
||||
https://eips.ethereum.org/EIPS/eip-4626[ERC-4626] is an extension of xref:erc20.adoc[ERC-20] that proposes a standard interface for token vaults. This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault.
|
||||
|
||||
We provide a base implementation of ERC4626 that includes a simple vault. This contract is designed in a way that allows developers to easily re-configure the vault's behavior, with minimal overrides, while staying compliant. In this guide, we will discuss some security considerations that affect ERC4626. We will also discuss common customizations of the vault.
|
||||
We provide a base implementation of ERC-4626 that includes a simple vault. This contract is designed in a way that allows developers to easily re-configure the vault's behavior, with minimal overrides, while staying compliant. In this guide, we will discuss some security considerations that affect ERC-4626. We will also discuss common customizations of the vault.
|
||||
|
||||
[[inflation-attack]]
|
||||
== Security concern: Inflation attack
|
||||
|
||||
=== Visualizing the vault
|
||||
|
||||
In exchange for the assets deposited into an ERC4626 vault, a user receives shares. These shares can later be burned to redeem the corresponding underlying assets. The number of shares a user gets depends on the amount of assets they put in and on the exchange rate of the vault. This exchange rate is defined by the current liquidity held by the vault.
|
||||
In exchange for the assets deposited into an ERC-4626 vault, a user receives shares. These shares can later be burned to redeem the corresponding underlying assets. The number of shares a user gets depends on the amount of assets they put in and on the exchange rate of the vault. This exchange rate is defined by the current liquidity held by the vault.
|
||||
|
||||
- If a vault has 100 tokens to back 200 shares, then each share is worth 0.5 assets.
|
||||
- If a vault has 200 tokens to back 100 shares, then each share is worth 2.0 assets.
|
||||
@ -195,7 +195,7 @@ stem:[\delta = 6], stem:[a_0 = 1], stem:[a_1 = 10^5]
|
||||
[[fees]]
|
||||
== Custom behavior: Adding fees to the vault
|
||||
|
||||
In an ERC4626 vaults, fees can be captured during the deposit/mint and/or during the withdraw/redeem steps. In both cases it is essential to remain compliant with the ERC4626 requirements with regard to the preview functions.
|
||||
In an ERC-4626 vaults, fees can be captured during the deposit/mint and/or during the withdraw/redeem steps. In both cases it is essential to remain compliant with the ERC-4626 requirements with regard to the preview functions.
|
||||
|
||||
For example, if calling `deposit(100, receiver)`, the caller should deposit exactly 100 underlying tokens, including fees, and the receiver should receive a number of shares that matches the value returned by `previewDeposit(100)`. Similarly, `previewMint` should account for the fees that the user will have to pay on top of share's cost.
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
= ERC721
|
||||
= ERC-721
|
||||
|
||||
We've discussed how you can make a _fungible_ token using xref:erc20.adoc[ERC20], but what if not all tokens are alike? This comes up in situations like *real estate*, *voting rights*, or *collectibles*, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of xref:tokens.adoc#different-kinds-of-tokens[_non-fungible_ tokens], that is, where each token is unique.
|
||||
We've discussed how you can make a _fungible_ token using xref:erc20.adoc[ERC-20], but what if not all tokens are alike? This comes up in situations like *real estate*, *voting rights*, or *collectibles*, where some items are valued more than others, due to their usefulness, rarity, etc. ERC-721 is a standard for representing ownership of xref:tokens.adoc#different-kinds-of-tokens[_non-fungible_ tokens], that is, where each token is unique.
|
||||
|
||||
ERC721 is a more complex standard than ERC20, with multiple optional extensions, and is split across a number of contracts. The OpenZeppelin Contracts provide flexibility regarding how these are combined, along with custom useful extensions. Check out the xref:api:token/ERC721.adoc[API Reference] to learn more about these.
|
||||
ERC-721 is a more complex standard than ERC-20, with multiple optional extensions, and is split across a number of contracts. The OpenZeppelin Contracts provide flexibility regarding how these are combined, along with custom useful extensions. Check out the xref:api:token/ERC721.adoc[API Reference] to learn more about these.
|
||||
|
||||
== Constructing an ERC721 Token Contract
|
||||
== Constructing an ERC-721 Token Contract
|
||||
|
||||
We'll use ERC721 to track items in our game, which will each have their own unique attributes. Whenever one is to be awarded to a player, it will be minted and sent to them. Players are free to keep their token or trade it with other people as they see fit, as they would any other asset on the blockchain! Please note any account can call `awardItem` to mint items. To restrict what accounts can mint items we can add xref:access-control.adoc[Access Control].
|
||||
We'll use ERC-721 to track items in our game, which will each have their own unique attributes. Whenever one is to be awarded to a player, it will be minted and sent to them. Players are free to keep their token or trade it with other people as they see fit, as they would any other asset on the blockchain! Please note any account can call `awardItem` to mint items. To restrict what accounts can mint items we can add xref:access-control.adoc[Access Control].
|
||||
|
||||
Here's what a contract for tokenized items might look like:
|
||||
|
||||
@ -36,9 +36,9 @@ contract GameItem is ERC721URIStorage {
|
||||
}
|
||||
----
|
||||
|
||||
The xref:api:token/ERC721.adoc#ERC721URIStorage[`ERC721URIStorage`] contract is an implementation of ERC721 that includes the metadata standard extensions (xref:api:token/ERC721.adoc#IERC721Metadata[`IERC721Metadata`]) as well as a mechanism for per-token metadata. 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.
|
||||
The xref:api:token/ERC721.adoc#ERC721URIStorage[`ERC721URIStorage`] contract is an implementation of ERC-721 that includes the metadata standard extensions (xref:api:token/ERC721.adoc#IERC721Metadata[`IERC721Metadata`]) as well as a mechanism for per-token metadata. 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 ERC-20, ERC-721 lacks a `decimals` field, since each token is distinct and cannot be partitioned.
|
||||
|
||||
New items can be created:
|
||||
|
||||
@ -72,8 +72,8 @@ This `tokenURI` should resolve to a JSON document that might look something like
|
||||
}
|
||||
----
|
||||
|
||||
For more information about the `tokenURI` metadata JSON Schema, check out the https://eips.ethereum.org/EIPS/eip-721[ERC721 specification].
|
||||
For more information about the `tokenURI` metadata JSON Schema, check out the https://eips.ethereum.org/EIPS/eip-721[ERC-721 specification].
|
||||
|
||||
NOTE: You'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game!
|
||||
|
||||
TIP: If you'd like to put all item information on-chain, you can extend ERC721 to do so (though it will be rather costly) by providing a xref:utilities.adoc#base64[`Base64`] Data URI with the JSON schema encoded. You could also leverage IPFS to store the tokenURI information, but these techniques are out of the scope of this overview guide.
|
||||
TIP: If you'd like to put all item information on-chain, you can extend ERC-721 to do so (though it will be rather costly) by providing a xref:utilities.adoc#base64[`Base64`] Data URI with the JSON schema encoded. You could also leverage IPFS to store the tokenURI information, but these techniques are out of the scope of this overview guide.
|
||||
|
||||
@ -16,7 +16,7 @@ OpenZeppelin’s Governor system was designed with a concern for compatibility w
|
||||
|
||||
=== ERC20Votes & ERC20VotesComp
|
||||
|
||||
The ERC20 extension to keep track of votes and vote delegation is one such case. The shorter one is the more generic version because it can support token supplies greater than 2^96, while the “Comp” variant is limited in that regard, but exactly fits the interface of the COMP token that is used by GovernorAlpha and Bravo. Both contract variants share the same events, so they are fully compatible when looking at events only.
|
||||
The ERC-20 extension to keep track of votes and vote delegation is one such case. The shorter one is the more generic version because it can support token supplies greater than 2^96, while the “Comp” variant is limited in that regard, but exactly fits the interface of the COMP token that is used by GovernorAlpha and Bravo. Both contract variants share the same events, so they are fully compatible when looking at events only.
|
||||
|
||||
=== Governor & GovernorStorage
|
||||
|
||||
@ -40,7 +40,7 @@ In the rest of this guide, we will focus on a fresh deploy of the vanilla OpenZe
|
||||
|
||||
=== Token
|
||||
|
||||
The voting power of each account in our governance setup will be determined by an ERC20 token. The token has to implement the ERC20Votes extension. This extension will keep track of historical balances so that voting power is retrieved from past snapshots rather than current balance, which is an important protection that prevents double voting.
|
||||
The voting power of each account in our governance setup will be determined by an ERC-20 token. The token has to implement the ERC20Votes extension. This extension will keep track of historical balances so that voting power is retrieved from past snapshots rather than current balance, which is an important protection that prevents double voting.
|
||||
|
||||
```solidity
|
||||
include::api:example$governance/MyToken.sol[]
|
||||
@ -52,7 +52,7 @@ If your project already has a live token that does not include ERC20Votes and is
|
||||
include::api:example$governance/MyTokenWrapped.sol[]
|
||||
```
|
||||
|
||||
NOTE: The only other source of voting power available in OpenZeppelin Contracts currently is xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`]. ERC721 tokens that don't provide this functionality can be wrapped into a voting tokens using a combination of xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`] and xref:api:token/ERC721Wrapper.adoc#ERC721Wrapper[`ERC721Wrapper`].
|
||||
NOTE: The only other source of voting power available in OpenZeppelin Contracts currently is xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`]. ERC-721 tokens that don't provide this functionality can be wrapped into a voting tokens using a combination of xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`] and xref:api:token/ERC721Wrapper.adoc#ERC721Wrapper[`ERC721Wrapper`].
|
||||
|
||||
NOTE: The internal clock used by the token to store voting balances will dictate the operating mode of the Governor contract attached to it. By default, block numbers are used. Since v4.9, developers can override the xref:api:interfaces.adoc#IERC6372[IERC6372] clock to use timestamps instead of block numbers.
|
||||
|
||||
@ -60,7 +60,7 @@ NOTE: The internal clock used by the token to store voting balances will dictate
|
||||
|
||||
Initially, we will build a Governor without a timelock. The core logic is given by the Governor contract, but we still need to choose: 1) how voting power is determined, 2) how many votes are needed for quorum, 3) what options people have when casting a vote and how those votes are counted, and 4) what type of token should be used to vote. Each of these aspects is customizable by writing your own module, or more easily choosing one from OpenZeppelin Contracts.
|
||||
|
||||
For 1) we will use the GovernorVotes module, which hooks to an IVotes instance to determine the voting power of an account based on the token balance they hold when a proposal becomes active. This module requires as a constructor parameter the address of the token. This module also discovers the clock mode (ERC6372) used by the token and applies it to the Governor.
|
||||
For 1) we will use the GovernorVotes module, which hooks to an IVotes instance to determine the voting power of an account based on the token balance they hold when a proposal becomes active. This module requires as a constructor parameter the address of the token. This module also discovers the clock mode (ERC-6372) used by the token and applies it to the Governor.
|
||||
|
||||
For 2) we will use GovernorVotesQuorumFraction which works together with ERC20Votes to define quorum as a percentage of the total supply at the block a proposal’s voting power is retrieved. This requires a constructor parameter to set the percentage. Most Governors nowadays use 4%, so we will initialize the module with parameter 4 (this indicates the percentage, resulting in 4%).
|
||||
|
||||
@ -100,7 +100,7 @@ A proposal is a sequence of actions that the Governor contract will perform if i
|
||||
|
||||
=== Create a Proposal
|
||||
|
||||
Let’s say we want to create a proposal to give a team a grant, in the form of ERC20 tokens from the governance treasury. This proposal will consist of a single action where the target is the ERC20 token, calldata is the encoded function call `transfer(<team wallet>, <grant amount>)`, and with 0 ETH attached.
|
||||
Let’s say we want to create a proposal to give a team a grant, in the form of ERC-20 tokens from the governance treasury. This proposal will consist of a single action where the target is the ERC-20 token, calldata is the encoded function call `transfer(<team wallet>, <grant amount>)`, and with 0 ETH attached.
|
||||
|
||||
Generally a proposal will be created with the help of an interface such as Tally or Defender. Here we will show how to create the proposal using Ethers.js.
|
||||
|
||||
@ -172,7 +172,7 @@ await governor.execute(
|
||||
);
|
||||
```
|
||||
|
||||
Executing the proposal will transfer the ERC20 tokens to the chosen recipient. To wrap up: we set up a system where a treasury is controlled by the collective decision of the token holders of a project, and all actions are executed via proposals enforced by on-chain votes.
|
||||
Executing the proposal will transfer the ERC-20 tokens to the chosen recipient. To wrap up: we set up a system where a treasury is controlled by the collective decision of the token holders of a project, and all actions are executed via proposals enforced by on-chain votes.
|
||||
|
||||
== Timestamp based governance
|
||||
|
||||
@ -235,6 +235,6 @@ contract MyGovernor is Governor, GovernorCountingSimple, GovernorVotes, Governor
|
||||
|
||||
=== Disclaimer
|
||||
|
||||
Timestamp based voting is a recent feature that was formalized in EIP-6372 and EIP-5805, and introduced in v4.9. At the time this feature is released, governance tooling such as https://www.tally.xyz[Tally] does not support it yet. While support for timestamps should come soon, users can expect invalid reporting of deadlines & durations. This invalid reporting by offchain tools does not affect the onchain security and functionality of the governance contract.
|
||||
Timestamp based voting is a recent feature that was formalized in ERC-6372 and ERC-5805, and introduced in v4.9. At the time this feature is released, governance tooling such as https://www.tally.xyz[Tally] does not support it yet. While support for timestamps should come soon, users can expect invalid reporting of deadlines & durations. This invalid reporting by offchain tools does not affect the onchain security and functionality of the governance contract.
|
||||
|
||||
Governors with timestamp support (v4.9 and above) are compatible with old tokens (before v4.9) and will operate in "block number" mode (which is the mode all old tokens operate on). On the other hand, old Governor instances (before v4.9) are not compatible with new tokens operating using timestamps. If you update your token code to use timestamps, make sure to also update your Governor code.
|
||||
|
||||
@ -24,8 +24,8 @@ In a nutshell, when dealing with non-fungibles (like your house) you care about
|
||||
|
||||
Even though the concept of a token is simple, they have a variety of complexities in the implementation. Because everything in Ethereum is just a smart contract, and there are no rules about what smart contracts have to do, the community has developed a variety of *standards* (called EIPs or ERCs) for documenting how a contract can interoperate with other contracts.
|
||||
|
||||
You've probably heard of the ERC20 or ERC721 token standards, and that's why you're here. Head to our specialized guides to learn more about these:
|
||||
You've probably heard of the ERC-20 or ERC-721 token standards, and that's why you're here. Head to our specialized guides to learn more about these:
|
||||
|
||||
* xref:erc20.adoc[ERC20]: the most widespread token standard for fungible assets, albeit somewhat limited by its simplicity.
|
||||
* xref:erc721.adoc[ERC721]: the de-facto solution for non-fungible tokens, often used for collectibles and games.
|
||||
* xref:erc1155.adoc[ERC1155]: a novel standard for multi-tokens, allowing for a single contract to represent multiple fungible and non-fungible tokens, along with batched operations for increased gas efficiency.
|
||||
* xref:erc20.adoc[ERC-20]: the most widespread token standard for fungible assets, albeit somewhat limited by its simplicity.
|
||||
* xref:erc721.adoc[ERC-721]: the de-facto solution for non-fungible tokens, often used for collectibles and games.
|
||||
* xref:erc1155.adoc[ERC-1155]: a novel standard for multi-tokens, allowing for a single contract to represent multiple fungible and non-fungible tokens, along with batched operations for increased gas efficiency.
|
||||
|
||||
@ -36,10 +36,10 @@ xref:api:utils.adoc#MerkleProof[`MerkleProof`] provides:
|
||||
[[introspection]]
|
||||
== Introspection
|
||||
|
||||
In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC165 is a standard that helps do runtime interface detection. Contracts provide helpers both for implementing ERC165 in your contracts and querying other contracts:
|
||||
In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC-165 is a standard that helps do runtime interface detection. Contracts provide helpers both for implementing ERC-165 in your contracts and querying other contracts:
|
||||
|
||||
* xref:api:utils.adoc#IERC165[`IERC165`] — this is the ERC165 interface that defines xref:api:utils.adoc#IERC165-supportsInterface-bytes4-[`supportsInterface`]. When implementing ERC165, you'll conform to this interface.
|
||||
* xref:api:utils.adoc#ERC165[`ERC165`] — inherit this contract if you'd like to support interface detection using a lookup table in contract storage. You can register interfaces using xref:api:utils.adoc#ERC165-_registerInterface-bytes4-[`_registerInterface(bytes4)`]: check out example usage as part of the ERC721 implementation.
|
||||
* xref:api:utils.adoc#IERC165[`IERC165`] — this is the ERC-165 interface that defines xref:api:utils.adoc#IERC165-supportsInterface-bytes4-[`supportsInterface`]. When implementing ERC-165, you'll conform to this interface.
|
||||
* xref:api:utils.adoc#ERC165[`ERC165`] — inherit this contract if you'd like to support interface detection using a lookup table in contract storage. You can register interfaces using xref:api:utils.adoc#ERC165-_registerInterface-bytes4-[`_registerInterface(bytes4)`]: check out example usage as part of the ERC-721 implementation.
|
||||
* xref:api:utils.adoc#ERC165Checker[`ERC165Checker`] — ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about.
|
||||
* include with `using ERC165Checker for address;`
|
||||
* xref:api:utils.adoc#ERC165Checker-_supportsInterface-address-bytes4-[`myAddress._supportsInterface(bytes4)`]
|
||||
@ -53,7 +53,7 @@ contract MyContract {
|
||||
bytes4 private InterfaceId_ERC721 = 0x80ac58cd;
|
||||
|
||||
/**
|
||||
* @dev transfer an ERC721 token from this contract to someone else
|
||||
* @dev transfer an ERC-721 token from this contract to someone else
|
||||
*/
|
||||
function transferERC721(
|
||||
address token,
|
||||
@ -118,9 +118,9 @@ The `Enumerable*` structures are similar to mappings in that they store and remo
|
||||
|
||||
xref:api:utils.adoc#Base64[`Base64`] util allows you to transform `bytes32` data into its Base64 `string` representation.
|
||||
|
||||
This is especially useful for building URL-safe tokenURIs for both xref:api:token/ERC721.adoc#IERC721Metadata-tokenURI-uint256-[`ERC721`] or xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`ERC1155`]. This library provides a clever way to serve URL-safe https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs/[Data URI] compliant strings to serve on-chain data structures.
|
||||
This is especially useful for building URL-safe tokenURIs for both xref:api:token/ERC721.adoc#IERC721Metadata-tokenURI-uint256-[`ERC-721`] or xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`ERC-1155`]. This library provides a clever way to serve URL-safe https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs/[Data URI] compliant strings to serve on-chain data structures.
|
||||
|
||||
Here is an example to send JSON Metadata through a Base64 Data URI using an ERC721:
|
||||
Here is an example to send JSON Metadata through a Base64 Data URI using an ERC-721:
|
||||
|
||||
[source, solidity]
|
||||
----
|
||||
@ -147,7 +147,7 @@ contract My721Token is ERC721 {
|
||||
bytes memory dataURI = abi.encodePacked(
|
||||
'{',
|
||||
'"name": "My721Token #', tokenId.toString(), '"',
|
||||
// Replace with extra ERC721 Metadata properties
|
||||
// Replace with extra ERC-721 Metadata properties
|
||||
'}'
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user