-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
3 changed files
with
103 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
= ERC-20 | ||
|
||
An ERC-20 token contract keeps track of xref:tokens.adoc#different-kinds-of-tokens[_fungible_ tokens]: any token is exactly equal to any other token; no token has a special right 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 provide many ERC20-related contracts for Arbitrum Stylus. | ||
On the https://docs.rs/openzeppelin_stylus/token/erc20/struct.Erc20.html[`API reference`] you'll find detailed information on their properties and usage. | ||
|
||
[[constructing-an-erc20-token-contract]] | ||
== Constructing an ERC-20 Token Contract | ||
|
||
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. | ||
|
||
[source,rust] | ||
---- | ||
sol_storage! { | ||
#[entrypoint] | ||
struct GLDToken { | ||
#[borrow] | ||
Erc20 erc20; | ||
#[borrow] | ||
Erc20Metadata metadata; | ||
} | ||
} | ||
#[external] | ||
#[inherit(Erc20, Erc20Metadata)] | ||
impl GLDToken {} | ||
---- | ||
|
||
Our contracts are often used via stylus-sdk https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#inheritance-inherit-and-borrow[inheritance], and here we're reusing https://docs.rs/openzeppelin_stylus/token/erc20/struct.Erc20.html[`ERC20`] for both the basic standard implementation and with optional extensions. | ||
|
||
[[a-note-on-decimals]] | ||
== A Note on `decimals` | ||
|
||
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, ERC20 provides a https://docs.rs/openzeppelin_stylus/token/erc20/extensions/metadata/trait.IErc20Metadata.html#tymethod.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. | ||
|
||
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 ERC-20 token contracts in use, unless you have an exceptional reason not to. | ||
When minting tokens or transferring them around, you will be actually sending the number `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. | ||
|
||
[source,rust] | ||
---- | ||
pub fn decimals(&self) -> u8 { | ||
16 | ||
} | ||
---- | ||
|
||
So if you want to send `5` tokens using a token contract with `18` decimals, the method to call will actually be: | ||
|
||
[source,rust] | ||
---- | ||
token.transfer(recipient, 5 * uint!(10_U256).pow(uint!(18_U256))); | ||
---- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
= Tokens | ||
|
||
Ah, the "token": blockchain's most powerful and most misunderstood tool. | ||
|
||
A token is a _representation of something in the blockchain_. This something can be money, time, services, shares in a company, a virtual pet, anything. By representing things as tokens, we can allow smart contracts to interact with them, exchange them, create or destroy them. | ||
|
||
[[but_first_coffee_a_primer_on_token_contracts]] | ||
== But First, [strikethrough]#Coffee# a Primer on Token Contracts | ||
|
||
Much of the confusion surrounding tokens comes from two concepts getting mixed up: _token contracts_ and the actual _tokens_. | ||
|
||
A _token contract_ is simply an Ethereum smart contract. "Sending tokens" actually means "calling a method on a smart contract that someone wrote and deployed". At the end of the day, a token contract is not much more than a mapping of addresses to balances, plus some methods to add and subtract from those balances. | ||
|
||
It is these balances that represent the _tokens_ themselves. Someone "has tokens" when their balance in the token contract is non-zero. That's it! These balances could be considered money, experience points in a game, deeds of ownership, or voting rights, and each of these tokens would be stored in different token contracts. | ||
|
||
[[different-kinds-of-tokens]] | ||
== Different Kinds of Tokens | ||
|
||
Note that there's a big difference between having two voting rights and two deeds of ownership: each vote is equal to all others, but houses usually are not! This is called https://en.wikipedia.org/wiki/Fungibility[fungibility]. _Fungible goods_ are equivalent and interchangeable, like Ether, fiat currencies, and voting rights. _Non-fungible_ goods are unique and distinct, like deeds of ownership, or collectibles. | ||
|
||
In a nutshell, when dealing with non-fungibles (like your house) you care about _which ones_ you have, while in fungible assets (like your bank account statement) what matters is _how much_ you have. | ||
|
||
== Standards | ||
|
||
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 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[ERC-20]: the most widespread token standard for fungible assets, albeit somewhat limited by its simplicity. |