ERC-20
An ERC-20 token contract keeps track of 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 API reference
you’ll find detailed information on their properties and usage.
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.
sol_storage! {
#[entrypoint]
struct GLDToken {
#[borrow]
Erc20 erc20;
#[borrow]
Erc20Metadata metadata;
}
}
#[public]
#[inherit(Erc20, Erc20Metadata)]
impl GLDToken {}
Our contracts are often used via stylus-sdk inheritance, and here we’re reusing ERC20
for both the basic standard implementation and with optional extensions.
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 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)
.
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. For example, to use 16
decimals, you would do:
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:
token.transfer(recipient, 5 * uint!(10_U256).pow(uint!(18_U256)));
Extensions
Additionally, there are multiple custom extensions, including:
-
ERC-20 Burnable: destruction of own tokens.
-
ERC-20 Capped: enforcement of a cap to the total supply when minting tokens.
-
ERC-20 Metadata: the extended ERC20 interface including the name, symbol, and decimals functions.
-
ERC-20 Pausable: ability to pause token transfers.
-
ERC-20 Permit: gasless approval of tokens (standardized as
EIP-2612
).