Skip to content

Commit

Permalink
feat(docs): document erc20 (#218)
Browse files Browse the repository at this point in the history
Resolves #214
  • Loading branch information
qalisander committed Aug 1, 2024
1 parent fba80ac commit a546c63
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
* xref:access-control.adoc[Access Control]
* xref:crypto.adoc[Cryptography]
* xref:tokens.adoc[Tokens]
** xref:erc20.adoc[ERC-20]
** xref:erc721.adoc[ERC-721]
70 changes: 70 additions & 0 deletions docs/modules/ROOT/pages/erc20.adoc
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)));
----
29 changes: 29 additions & 0 deletions docs/modules/ROOT/pages/tokens.adoc
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.

0 comments on commit a546c63

Please sign in to comment.