Ethereum, tokens & smart contracts.

Notes on getting started Part 7. ERC20 Token Standard

Previous notes in case you are just joining us:
Part 1. Setting up.
Part 2. Web3.js/node.
Part 3. Solidity.
Part 4. Smart Contracts.
Part 5. Smarter Contracts.
Part 6. Tokens & Inheritance.

While you could use the minimal Token as the basis for your project, the good people at Ethereum have made available a more complex standard token which we will cover next, the erc20 token.

Note: Like everything in tech/blockchain development, things they are a changing, there is a new token standard proposal erc223 which might be implemented in the near future... and then something else will probably come along.

Sources:

In order to be part of the erc20 club, a token has to have the following functions and events (plus optional token information):

contract ERC20 { function totalSupply() constant returns (uint totalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns (bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint remaining);
// events: event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
// optional token information:string public constant name = "Token Name";
string public constant symbol = "SYM";
uint8 public constant decimals = 18;
}

We’ll get into them in detail later, for now let’s start where we left off last notes and start adding/replacing functions to our minimal Token and transform it into a beautiful ERC20 Token:

// FILE: DogToken.solpragma solidity ^0.4.0;contract DogToken {string public constant symbol = "DOG";
string public constant name = "Dutch Oven Gathering Token";
uint8 public constant decimals = 18;
uint256 totalSupply = 1000000;
address public owner;
mapping(address => uint256) balances;
mapping(address => mapping (address => uint256)) allowed;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
function DogToken() public{
owner = msg.sender;
balances[owner] = totalSupply;
}
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balances[_owner];
}
function transfer(address _to, uint256 _amount) public returns (bool success) {
if (balances[msg.sender] >= _amount
&& _amount > 0
&& balances[_to] + _amount > balances[_to]) {
balances[msg.sender] -= _amount;
balances[_to] += _amount;
Transfer(msg.sender, _to, _amount);
return true;
} else {
return false;
}
}
function transferFrom(
address _from,
address _to,
uint256 _amount
)
public returns (bool success) {
if (balances[_from] >= _amount
&& allowed[_from][msg.sender] >= _amount
&& _amount > 0
&& balances[_to] + _amount > balances[_to]) {
balances[_from] -= _amount;
allowed[_from][msg.sender] -= _amount;
balances[_to] += _amount;
Transfer(_from, _to, _amount);
return true;
} else {
return false;
}
}
function approve(address _spender, uint256 _amount) public returns (bool success) {
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}// And after deploying:txHash:0xa920719e6fe519a542595f6df06ec416ba83f6a428f8ce3d1f6b092f5ab772a2
Successfully deployed Contract with address: 0xa018bf627c0e388b5d8d8c13e3464bb7620672e5

Before getting into the details, let’s add this token to our Parity wallet , it is similar to other contracts, and there are 2 ways of adding it:

  1. Token : Parity and other wallets accept erc20 Tokens and have a special UI for them, to add our token in parity for instance:
Note: The contract abi is already provided for you, so no need for you or your users to add it.

2. Custom Contract : For more advanced or non erc20 Tokens, this option exposes functions like our minimal token, but you or your users will need to provide an abi:

Once you have added your token either way, you can start interacting with it, here is how it looks as a Token (with our balance):

Note that totalSupply is blank ?... The reason for this is that when we defined our variable, we omitted it as a public variable, so it got no getter, but you can provide it if you want.

And as a Custom Contract :

Which one you use or instruct your users to use is up to you and your specific requirements. Both have a dedicated screen to execute functions, we will start by transfering some DOGs to another account:

Result:

Let’s now go back to our code and go through the erc20 spec and functions:

Note: I replaced function totalSupply() constant returns (uint totalSupply);with:uint256 totalSupply = 1000000;
or
uint256 public totalSupply = 1000000;
The compiler kept on trowing an error, but it is simply a place to define the total supply of DOGs and I think also not break the spec.uint8 public constant decimals = 18;How divisable is your Token; ( 0 for indivisible ) for an in depth discussion as well as a great in depth technical overview of the ERC20 Standard check this post:https://medium.com/@jgm.orinoco/understanding-erc-20-token-contracts-a809a7310aa5function balanceOf(address _owner) public constant returns (uint256 balance) {
return balances[_owner];
}
We already used this function through parity when checking balances, it works in conjunction with our mapping:mapping(address => uint256) balances;which simply holds all the current and future balances, see mappings in the previous notes if it's unclear.function transfer(address _to, uint256 _amount) public returns (bool success) {
if (balances[msg.sender] >= _amount
&& _amount > 0
&& balances[_to] + _amount > balances[_to]) {
balances[msg.sender] -= _amount;
balances[_to] += _amount;
Transfer(msg.sender, _to, _amount);
return true;
} else {
return false;
}
}

The transfer function which we used to transfer 20,000 DOGs uses a conditional to check that the originating address has enough funds, is greater than 0 and importantly that there is no integer overflow as explained to me when asking about this line:
&& balances[_to] + _amount > balances[_to] "If _amount is very large, balances[_to] + _amount could overflow a 256 bit int. ( a very big number)Every time you add two numbers, you should check that they don't overflow, ditto for subtraction and multiplication." Nick Johnson@Arachnid
Then it just adds and removes the amount from each account.There is also an event attached which we defined earlier:event Transfer(address indexed _from, address indexed _to, uint _value);function transferFrom(
address _from,
address _to,
uint256 _amount
)
public returns (bool success) {
if (balances[_from] >= _amount
&& allowed[_from][msg.sender] >= _amount
&& _amount > 0
&& balances[_to] + _amount > balances[_to]) {
balances[_from] -= _amount;
allowed[_from][msg.sender] -= _amount;
balances[_to] += _amount;
Transfer(_from, _to, _amount);
return true;
} else {
return false;
}
}
function approve(address _spender, uint256 _amount) public returns (bool success) {
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}

These 3 functions (transferFrom, approve & allowance) work together to allow new and complex behavior which we’ll go into some detail next:

transferFrom allows a pre-approved address (via the approve function)to send tokens from one account to another account up to a specificed amount. (The approval and the amount are set in one operation). The allowance function serves to consult this allowance.

Let’s do a bit of setup by making 3 empty accounts and naming them (A1,A2,A3) :

I’ll start by giving them 40,000 DOG’s each using transfers from the main account:

If we use transferFrom(A1,A2,20000) no transfer would happen ( yet you would still burn some gas:

 check the tx for details: 0x5ca32d81247f65afb5150f69844f25edfe4c6ba6a640a4fdf5274987da05a87b

The reason is that we have yet to approve an Address, so let’s do that:

The owner of the account (A1) is approving (A2) to spend 20,000 DOGS. whenever A2 feels like it.

Note: Since the owner is the one posting the transaction, this account needs to supply the gas, in other words if account A1 doesn't have an Ether balance that can fulfill the transaction costs the approve function won't be successful.

So how does approve work in code ?

function approve(address _spender, uint256 _amount) public returns (bool success) {
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}

The main thing here is to populate this previously defined mapping:

mapping(address => mapping (address => uint256)) allowed;// This is a nested mapping, so it holds the relationship from one address with one or multiple addresses and their amounts.remember that a mapping is :mapping(_KeyType => _ValueType), so in this case it would be :mapping(_KeyType => mapping (_KeyType => _ValueType))after running approve(A1,A2,20000) it would look like this:key:A1 value: (key: A2 value: 20000)And more importantly after running another approve(A1,A3,10000) it would look like this:key:A1 value: (key: A2 value: 20000)
(key: A3 value: 10000)
What this allows us as we will see is to define and hold multiple nested relationships, like a list of approved addresses as well as the amounts they can spend from corresponding account owners.

The rest is pretty standard, it fires an Event…

event Approval(address indexed _owner, address indexed _spender, uint256 _value);return true;
// (bool success)

And returns true/success if the mapping was updated and the event was fired, else nothing happens ( the originating account does get charged gas for the failed transaction ) .

After running the approval function we can now query allowances ( basically our nested mapping in pretty UI form ) :

Now that we have approved spenders and allowances, let’s go back to the transferFrom() function and use it…

The results are as follows :

Note: same gas rules apply, the transaction poster A2 needs to supply the gas, so it has to have enough ether for this.

Can you transfer from a 3rd account ? ( for example):

Surprisingly the answer is yes, here are the results:

While the balances were affected as expected, I was expecting that the allowance in between (A1,A2) would be reduced, this is NOT the case, what ends up happening is that A3s Allowance was the one that got reduced to 5,000 from 10,000 since it was the one originating the transaction:

This is one way to send tokens from one account to another; these effects might or might not be what you want in your application or token, but seem to be provided as a catch all case for you to build on top.

One last case I would like to cover is that of overdrafts, let’s try over drafting an allowance (our allowance from A1-A3 is 5,000):

The result is that nothing happens ( but do check for failed transaction costs though). This line is responsible for the check:

allowed[_from][msg.sender] >= _amount

transferFrom allows accounts to approve other accounts to spend a set amount of tokens on their behalf, while this might seem complex, we use this every day when for instance we sign up for a monthly charge at the gym or video rentals, we approve them to spend a fixed amount every month from our funds in the bank, it is the same logic.

The last bit of code that is new or weird in the erc20 spec (at least to me ) is this one :

modifier onlyOwner {
require(msg.sender == owner);
_;
}

A modifier is a bit of code that adds some functionality to functions, hence the name modifier, the modifier itself in this case is a bit of a riddle, but this is what it does:

require(msg.sender == owner);// whoever is sending the transaction needs to be the owner of the Token, require which we also haven't talked about will evaluate to false and throw an exception, it will reverse any changes to state and exit the function._; This weird bit simply states continue running the function being modified if the msg.sender is in fact the owner.

Using a modifier once it has been defined is pretty simple, just add it to a function like so:

function approve(address _spender, uint256 _amount) onlyOwner public returns (bool success) {
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}

What this does in practice is restrict the approve function to the owner/creator of the token, modifiers in general give you flexibility to change the behavior of already existing functions.

This has been hopefully a short and sweet look at the erc20 Token standard, but it is by no means exhaustive, if you want to keep on learning about the erc20, you can start by reviewing the existing ones mentioned in the wiki and adding functionality to your own, our next notes will cover perhaps the most popular topic: ICOs and crowdfunding, for now let’s pause and recap:

  • Notes Part 1 : Setting up : Getting a wallet/client , connecting to a test Ethereum blockchain and getting some test ether.
  • Notes Part 2: web3.js/node : Interacting with the blockchain through web3.js and node for more convenience and ease of development.
  • Notes Part 3: Solidity : Installing solidity (Ethereums contract writing language ) , Creating a very basic contract, compiling it, deploying it to the blockchain ( the test one) and interacting with it.
  • Notes Part 4: Smart Contracts: We modified our simple contract so we could store and retrieve information (making it smart), in the process we also covered how to watch the blockchain and contracts and finally we took a look at gas and how to estimate it.
  • Notes Part 5: Smarter Contracts: In order to allow contracts more complex behavior (make them smarter) , we need to delve a bit deeper into contract creation via constructors and a few more complex types which we will use in making a token contract.
  • Notes Part 6: Tokens & Inheritance: We made our first Token and interacted with it by transfering some of it from one account to another, we also briefly covered inheritance which is used in more complex contracts.
  • Notes Part 7: ERC20 Token Standard (This post): We made and deployed an erc20 token which can be considered the parting standard for working sub currencies. We examined the code and talked about transfers in between tokens.
Now a book ! If you are looking for an introduction to Ethereum, Solidity and Smart Contracts these notes were edited and revised into a convenient book with easy to run code included !
Available in ebook and paperback:

https://www.amazon.com/dp/B078CQ8L7V

Cheers !

Keno

About the Author :

Born Eugenio Noyola Leon (Keno) I am a Designer,Web Developer/programmer, Artist and Inventor, currently living in Mexico City, you can find me at www.k3no.com

AI, Software Developer, Designer : www.k3no.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store