I must admit that learning eosio has been no walk in the park and I can see why many people say that it has a steep learning curve. As the eosio software continues to undergo heavy rapid development there is limited documentation and few working examples to refer to. I have gotten stumped quite a few times and would like to help improve the experience for the next developer that comes along. In this article, I will go over the ‘eosio.token’ contract by breaking it down into separate parts.
## What Is The eosio.token Contract?
The eosio.token contract allows for the creation of many different tokens. This gives anyone the ability to create and transfer a token. Every token must be issued by an `issuer` account. Since accounts can include multiple parties you can use a normal account with owner and active permissions or a custom configured account to create and manage a token. Each token is an asset type and looks like the following:
`1000000000.0000 SYS`
`1.0000 SYMBOL`
`0.10 SYS`
The `asset` type is a number (that if I recall correctly can go up to 18 decimal places) and a symbol that can be between 1–7 uppercase letters. This contract has three actions that you can use to interact with it. They are: create, issue, and transfer.
Create is used to define the characteristics of a new token. This includes the token (`asset`) symbol, the maximum supply, and the account allowed to issue the token. Create will also persist the new token configuration onto the blockchain. This means that the storage for the new token configuration must be staked by someone. As you will see later on, the account this contract gets deployed on (in our case ‘eosio.token’) will also pay for the tokens configuration storage.
Issue is used to increase the active supply of the token. Tokens can be continually issued until the maximum supply is reached. The issuer defined during the token creation must approve this action in order for it to succeed.
Transfer lets one account transfer tokens to another.
## Deploying The Contract
The first thing you should know is that every eosio smart contract belongs to a single eosio account. A contract is basically an object that other accounts can interact with. Contracts contain “actions” that executes code on the blockchain. Contracts have direct access to store, remove, and update data on the blockchain.
Pushing an action to a contract requires the authorization of at least one account. Further accounts and permissions can be required depending on contract complexity. An account can be made of a single, or many, individuals set up in a permission based configuration. Smart contracts can only be run by a single account, and a single account can only have a single smart contract. It is best practice to give both the account and contract the same (lowercase) name.
Before you can interact with the eosio.token contract you will need to make an account with the same name and deploy the contract onto that account.
Start off by creating an account
`$cleos create account eosio eosio.token <OWNER-KEY> <ACTIVE-KEY>`
Then compile the contract
`$cd eos/contract/eosio.token`
`$eosiocpp -o eosio.token.wast eosio.token.cpp`
Finally deploy the contract onto the account
`$cleos set contract eosio.token ../eosio.token`
You can verify the contract was deployed with
`$cleos get code eosio.token`
## Contract Architecture
The contract is broken up into two files ‘eosio.token.cpp’ and ‘eosio.token.hpp’. The ‘.hpp’ file defines the contract class, actions and tables, while the ‘.cpp’ file implements the action logic. Let’s first look at the contract class which will be used to instantiate the contract object. (I have removed some left-over code from ‘eosio.token.hpp’)
```c++
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <eosiolib/asset.hpp>
#include <eosiolib/eosio.hpp>
#include <string>
namespace eosiosystem {
class system_contract;
}
namespace eosio {
using std::string;
class token : public contract {
public:
token( account_name self ):contract(self){}
void create( account_name issuer,
asset maximum_supply);
void issue( account_name to, asset quantity, string memo );
void transfer( account_name from,
account_name to,
asset quantity,
string memo );
private:
friend eosiosystem::system_contract;
inline asset get_supply( symbol_name sym )const;
inline asset get_balance( account_name owner, symbol_name sym )const;
struct account {
asset balance;
uint64_t primary_key()const { return balance.symbol.name(); }
};
struct currency_stats {
asset supply;
asset max_supply;
account_name issuer;
uint64_t primary_key()const { return supply.symbol.name(); }
};
typedef eosio::multi_index<N(accounts), account> accounts;
typedef eosio::multi_index<N(stat), currency_stats> stats;
void sub_balance( account_name owner, asset value );
void add_balance( account_name owner, asset value, account_name ram_payer );
};
asset token::get_supply( symbol_name sym )const
{
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
return st.supply;
}
asset token::get_balance( account_name owner, symbol_name sym )const
{
accounts accountstable( _self, owner );
const auto& ac = accountstable.get( sym );
return ac.balance;
}
} /// namespace eosio
```
The constructor and actions are defined as public member functions. The constructor takes an account name (which will be the account that the contract is deployed on, aka eosio.token) and sets it to the `contract` variable. Note that this class is inheriting from the ‘eosio::contract’.
The tables and helper functions are provided as private members. Two inline functions are defined at the bottom yet never used. This leaves us with the important functions `sub_balance()` and `add_balance()`. These will get called by the transfer action.
## Tables
The two tables defined are `accounts` and `stat`. The `accounts` table is made up of different `account` objects each holding the balance for a different token. The `stat` table is made up of `currency_stats` objects (defined by `struct currency_stats`) that holds a supply, a max_supply, and an issuer. Before continuing on, it is important to know that this contract will hold data into two different scopes. The `accounts` table is scoped to an eosio account, and the `stat` table is scoped to a token symbol name.
*According to the ‘eosio::multi_index’ definitions, `code` is the name of the account which has write permission and the `scope` is the account where the data gets stored.*
The scope is essentially a way to compartmentalize data in a contract so that it is only accessible within a defined space. In the token contract, each eosio account is used as the scope of the `accounts` table. The `accounts` table is a multi-index container which holds multiple `account` objects. Each `account` object is indexed by its token symbol and contains the token balance. When you query a users’ `accounts` table using their scope, a list of all the tokens that user has an existing balance for is returned.
Here is how I visualize it.

In the above picture, there is an eosio account named tom who has his own scope. Within his scope is a table called `accounts`. Within that table is a separate `account` object for each token he holds, ‘SYS’ and ‘EOS’.
There is also a second table called `stat`. This table will contain the status for an existing token. New tokens get created in their own symbol name scope. Within the scope is a `stat` table that holds a `currency_stats` object. Unlike the `accounts` table, which contains many different `account` objects, the `stat` table only holds a single `currency_stats` object for the given token symbol.

## Actions
Actions are implemented in the ‘.cpp’ file. In order to create a new token, the create action must be sent. Create takes two parameters; the issuer, and the maximum supply for the new token. The `issuer` is the only one allowed to increase the new token supply. The `issuer` cannot issue more than the maximum supply.
```
void token::create( account_name issuer,
asset maximum_supply )
{
```
The first line of code simply requires the authorization of the contract account itself. This can be given with the command line flag `-p eosio.token` when pushing the action.
```
require_auth( _self );
```
The next few lines extract the symbol for the `maximum_supply` asset passed in and performs some error handling. If any of the `eosio_assert`’s fail, then all the code will be rolled back and the transaction will not be pushed onto the blockchain.
```
auto sym = maximum_supply.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( maximum_supply.is_valid(), "invalid supply");
eosio_assert( maximum_supply.amount > 0, "max-supply must be positive");
```
A `stat` table called `statstable` is constructed using the symbols name (token symbol) as its scope. The code checks to see if the token already exists. If it does not, then it creates and saves the new token status onto the blockchain. The first parameter `_self` in the `emplace` function means that this contracts account ‘eosio.token’ will pay for the staked storage.
```
stats statstable( _self, sym.name() );
auto existing = statstable.find( sym.name() );
eosio_assert( existing == statstable.end(), "token with symbol already exists" );
statstable.emplace( _self, [&]( auto& s ) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
});
}
```
Note that the `supply`'s symbol is saved since it is used as the key for locating the table row, but the supplies amount has not been issued yet.
You can now perform the next action, issue. Issue will take the account who will receive the issued tokens, the token quantity being issued, and a memo. The issue action performs two actions in one because it will both modify the created tokens supply and call the transfer action to send the issued tokens. Again, the first few lines extract the token symbol and perform error checking.
```
void token::issue( account_name to, asset quantity, string memo )
{
auto sym = quantity.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" );
```
The following code section will construct the `stat` table using the symbol name as the scope. This is used as the key to find the token that was created previously with the create action.
```
auto sym_name = sym.name();
stats statstable( _self, sym_name );
auto existing = statstable.find( sym_name );
eosio_assert( existing != statstable.end(), "token with symbol does not exist, create token before issue" );
const auto& st = *existing;
```
Note that the `existing` currency_stat returned from `statstable.find()` is an iterator which points to the found object. For brevity, a new variable called `st` is declared and set to the actual object that the `existing` iterator is pointing to. This lets us use the `.` operator to access the member variables instead of pointer notation `->`.
The `issuer` for the created token is required to sign this transaction, and more error handling is performed.
```
require_auth( st.issuer );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must issue positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
eosio_assert( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
```
Finally, the currency_stats `st` for our existing token is modified and the issued `quantity` is added to the `supply`. The `issuer` will also have the `supply` added to their balance so that the initial supply can be traced back to their account.
```
statstable.modify( st, 0, [&]( auto& s ) {
s.supply += quantity;
});
add_balance( st.issuer, quantity, st.issuer );
if( to != st.issuer ) {
SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} );
}
}
```
Immediately after, the transfer function is called via the `SEND_INLINE_ACTION()` macro which will transfer the funds. The arguments are as follows:
1. `*this` - the contract code the action belongs to
2. `transfer` - the anme of the action
3. `{st.issuer, N(active)}` - the permissions required for the action
4. `{st.issuer, to, quantity, memo}` - the arguments for the action itself
This brings us to our final action transfer. Transfer will take four input arguments `from`, `to`, `quantity`, and `memo`. `from` is whom will be sending the tokens, thus the `quantity` will be subtracted from their balance. `to` is whom will be receiving the tokens and thus the `quantity` will be added to their balance. `quantity` is the amount of tokens being sent, and `memo` is a string that you can send along with the transaction. The `memo` is not used or stored within this contract.
The action starts off by requiring the `from` accounts permission and performing error handling on the `from` and `to` accounts. The symbol is extracted from the `quantity` and used to get the `currency_stats` for the token.
```
void token::transfer( account_name from,
account_name to,
asset quantity,
string memo )
{
eosio_assert( from != to, "cannot transfer to self" );
require_auth( from );
eosio_assert( is_account( to ), "to account does not exist");
auto sym = quantity.symbol.name();
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
```
The `require_recipient()` function will notify both the sender and receiver upon action completion.
```
require_recipient( from );
require_recipient( to );
```
More error handling is done and finally the two private functions `sub_balance()` and `add_balance()` are called to subtract the token balance from the sender and increase the token balance for the receiver.
Here is the full ‘eosio.token.cpp’ file
```
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include "eosio.token.hpp"
namespace eosio {
void token::create( account_name issuer,
asset maximum_supply )
{
require_auth( _self );
auto sym = maximum_supply.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( maximum_supply.is_valid(), "invalid supply");
eosio_assert( maximum_supply.amount > 0, "max-supply must be positive");
stats statstable( _self, sym.name() );
auto existing = statstable.find( sym.name() );
eosio_assert( existing == statstable.end(), "token with symbol already exists" );
statstable.emplace( _self, [&]( auto& s ) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
});
}
void token::issue( account_name to, asset quantity, string memo )
{
auto sym = quantity.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" );
auto sym_name = sym.name();
stats statstable( _self, sym_name );
auto existing = statstable.find( sym_name );
eosio_assert( existing != statstable.end(), "token with symbol does not exist, create token before issue" );
const auto& st = *existing;
require_auth( st.issuer );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must issue positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
eosio_assert( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
statstable.modify( st, 0, [&]( auto& s ) {
s.supply += quantity;
});
add_balance( st.issuer, quantity, st.issuer );
if( to != st.issuer ) {
SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} );
}
}
void token::transfer( account_name from,
account_name to,
asset quantity,
string memo )
{
eosio_assert( from != to, "cannot transfer to self" );
require_auth( from );
eosio_assert( is_account( to ), "to account does not exist");
auto sym = quantity.symbol.name();
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
require_recipient( from );
require_recipient( to );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must transfer positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" );
sub_balance( from, quantity );
add_balance( to, quantity, from );
}
void token::sub_balance( account_name owner, asset value ) {
accounts from_acnts( _self, owner );
const auto& from = from_acnts.get( value.symbol.name(), "no balance object found" );
eosio_assert( from.balance.amount >= value.amount, "overdrawn balance" );
if( from.balance.amount == value.amount ) {
from_acnts.erase( from );
} else {
from_acnts.modify( from, owner, [&]( auto& a ) {
a.balance -= value;
});
}
}
void token::add_balance( account_name owner, asset value, account_name ram_payer )
{
accounts to_acnts( _self, owner );
auto to = to_acnts.find( value.symbol.name() );
if( to == to_acnts.end() ) {
to_acnts.emplace( ram_payer, [&]( auto& a ){
a.balance = value;
});
} else {
to_acnts.modify( to, 0, [&]( auto& a ) {
a.balance += value;
});
}
}
} /// namespace eosio
EOSIO_ABI( eosio::token, (create)(issue)(transfer) )
```
### Example commands:
```
$cleos push action eosio.token create '["usera","21000000.0000 DEMO"]' -p eosio.token usera
$cleos push action eosio.token issue '["usera","21000000.0000 DEMO","issuance"]' -p usera
$cleos push action eosio.token tranfser '["usera","userb","1000000.0000 DEMO","here you go"]' -p usera
```
### Table commands:
```
$cleos get table eosio.token DEMO stat
{
"rows": [{
"supply": "21000000.0000 DEMO"
"max_supply": "2100000000.0000 DEMO"
"issuer": "usera"
}
],
"more": false
}
$cleos get table eosio.token usera accounts
{
"rows": [{
"balance": "20000000.0000 DEMO"
}
],
"more": false
}
$cleos get table eosio.token userb accounts
{
"rows": [{
"balance": "10000000.0000 DEMO"
}
],
"more": false
}
```
<br/>
*Note: This article was written at the time of the Dawn4.1 code release*