
In part 1, we explained the structure of DAICOVO. And in part 2, we talked about the deployment and operation flow. In this part 3 of the series, we will look at the most basic contract: the token contract.
Requirements of a Token
We have DaicovoStandardToken contract as a template of a token contract. This contract is ERC20 compatible token with some extended features from ERC223. You can either use this template to issue tokens or use other types of tokens to start your ICOs with the DAICOVO. However, keep in mind that your token needs to meet requirements below.
ERC20 Interface
1 2 3 4 5 6 7 8 9 10 |
/* ERC20 Interface*/ function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function transfer(address to, uint256 value) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function transferFrom(address from, address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); |
Mintable Interface
1 2 3 4 5 6 7 |
/* Mintable Interface */ bool public mintingFinished = false; function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) function finishMinting() onlyOwner canMint public returns (bool) event Mint(address indexed to, uint256 amount); event MintFinished(); |
Ownable Interface
1 2 3 4 5 |
/* Ownable Interface */ address public owner; function transferOwnership(address newOwner) public onlyOwner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); |
In the DAICOVO, tokens are issued(minted) through the TokenController: mint(address _to, uint256 _amount)
function will be called from theTokenController. As only the owner of the contract can call mint
, you need to transfer the ownership to the TokenController.
Structure of DaicovoStandardToken
The DaicovoStandardToken is the ERC20 compatible token contract template. We also borrowed some functions from the ERC223 token standard that eliminates the problem of lost tokens. On top of that, there are other functions and variables for flexibility. They also help us to solve known issues. You can see the members of the DaicovoStandardToken contract below.

We won’t mention members in ERC20 in this post. You can find more than enough articles about this standard online.
*The DaicovoStandardToken contract is based on the code from ERC20 compatible contract in OpenZeppelin and ERC223 compatible contract from Dexaran. Both of them are OSS under the MIT license.
Problems with ERC20
The problem with ERC20 is that tokens will be lost forever when ERC20 tokens are transferred to a contract address. This is why we borrowed some functions from ERC223 in the DaicovoStandardToken.
When you send ERC20 tokens in your balance, you use the transfer()
function. If the recipient were an EOA(Externaly Owned Account), they will know that they receive tokens because humans are in control.
However, a contract cannot notice that they have received tokens because it doesn’t check transactions autonomously. They do not even have the system to sync with the transfer()
function and execute the function in the contract. Unless the contract has the function to pull tokens out, the tokens will be locked up in the contract for good.
1 2 3 4 5 6 7 8 9 10 11 12 |
/* ERC20Standard.sol */ function transfer(address _to, uint256 _value) external returns (bool) { require(_to != address(0)); require(_value <= balances[msg.sender]); //the recipient doesn't sync with the transfer() function because it merely changes the balance balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); emit Transfer(msg.sender, _to, _value); return true; } |
Billions of dollars’ worth of tokens have been lost due to this flaw in ERC20.
We can imagine that this happens often when somebody uses the contract that takes some actions by receiving tokens. Let’s say you have ContractC with the function that exchanges ten TokenA with one TokenB. If UserX uses thetransfer()
function to send ten TokenA to the ContractC, the tokens will be locked up because the ContractC has no way of knowing that it has received the tokens. Here is a proper way to send the tokens:
- UserX gives permission to ContractC to spend on behalf of her with the
approve()
function in TokenA. - The UserX calls the function in the ContractC that exchanges ten TokenA with one TokenB. The ContractC spends ten TokenA on behalf of UserX with the
transferFrom()
function in TokenA. This is only possible because the ContractC was given the permission with theapprove()
function.
A lot of people didn’t know that these two transactions were required, and they ended up using only thetransfer()
function to send their tokens.
How ERC223 Works
ERC223 was proposed to solve the issue of lost tokens (But beware that this proposal is not officially accepted as the EIP yet.) .
Here is how the ERC223 works:
- When
transfer()
, check if the recipient of the token is an EOA or a contract - a.
transfer()
in case it is an EOA.
b. Invoke thetokenFallback()
function if the recipient were a contract. - a. The token transfer fails if the contract does not implement the
tokenFallback()
function or the fallback function to receive funds.
b. If the contract does implement thetokenFallback()
function, proceed to receive tokens.
This is a proposed implementation below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* ERC223_token.sol */ function transfer(address _to, uint _value, bytes _data) { uint codeLength; //check if the recipient of the token is an EOA or a contract assembly { codeLength := extcodesize(_to) } //transfer the balance balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); if(codeLength>0) { //invokes the <code class="markup--code markup--pre-code">tokenFallback()</code> function if the recipient is a contract. ERC223ReceivingContract receiver = ERC223ReceivingContract(_to); receiver.tokenFallback(msg.sender, _value, _data); } emit Transfer(msg.sender, _to, _value, _data); } |
As you can see, the transfer()
function in ERC223 send tokens only when 1) the recipient is an EOA or 2) the recipient is a contract that supports token receiving and handling.
This assembly code is checking if the recipient of the token is an EOA or a contract by retrieving the size of the code on a specific address. If the size were zero, the address is an EOA. If the size were larger than zero, the address is a contract.
1 2 3 |
assembly { codeLength := extcodesize(_to) } |
ERC223 Interface
The ERC223 doesn’t have any approve related functions below.
1 2 3 4 5 |
function allowance(address owner, address spender) external view returns (uint256); function transferFrom(address from, address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); |
This is because we don’t need the two steps that are required to send ERC20 tokens as I mentioned previously. We should note that ERC223 doesn’t have upward compatibility with ERC20 because these function has other usages.
*For example, you can give permission to your another address so that it can spend on behalf of you in case you lose your private key.
transfer(address _to, uint _value, bytes _data)
is the new function with three arguments in ERC223. You can use either this function or transfer(address _to, uint _value)
function from ERC20 to send tokens.
The third argument bytes _data
in transfer(address _to, uint _value, bytes _data)
can be attached to the tokenFallback()
function and then will be handled on a receiver contract. Since byte data is not fixed length, you need to encode all the argument data and bundle them as one argument.
I’d say this function is not usable because 1) users need to encode the data by following the specifics of contracts and 2) contracts need to have the logic to decode(there is no helpful library for this).
DaicovoStandardToken
The DaicovoStandardToken replaces the transfer()
function among the members of ERC20 with the one in ERC223. However, unlike ERC223, we still have approve related functions, and all the other interfaces are compatible with ERC20(ERC223 has some other different interfaces with ERC20).
On top of that, we have functions and variables below.
1 2 3 4 5 6 7 8 9 |
function forceTransfer(address _to, uint _value) external returns(bool); function increaseApproval(address _spender, uint _addedValue) external returns (bool); function decreaseApproval(address _spender, uint _subtractedValue) external returns (bool); string public name; string public symbol; uint8 public decimals; |
forceTransfer
forceTransfer()
is the same function with the ERC20 transfer()
function. It transfers tokens regardless of the recipients: either an EOA or a contract.
1 2 3 4 5 6 7 8 9 10 |
function forceTransfer(address _to, uint _value) external returns(bool) { require(_to != address(0x0)); require(_value <= balances[msg.sender]); //transfer without checking the recipient balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); emit Transfer(msg.sender, _to, _value); return true; } |
increaseApproval, decreaseApproval
increaseApproval()
and decreaseApproval()
are added as a solution to the problem associated with approve. We are taking the same approach with OpenZeppelin.
We don’t discuss the problem in detail in this post, but you can read more here. In a nutshell, tokens can be spent more than they should be because of double spending when the approve value has been changed.
approve()
is the function that directly change the allowance value(give permission to another address so that it can spend a certain amount of tokens). Essentially, you can avoid the double spending by allowing approve()
to change the value only by addition and subtraction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function increaseApproval(address _spender, uint _addedValue) external returns (bool) { allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } function decreaseApproval(address _spender, uint _subtractedValue) external returns (bool) { uint oldValue = allowed[msg.sender][_spender]; if (_subtractedValue > oldValue) { allowed[msg.sender][_spender] = 0; } else { allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); } emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } |
name, symbol, decimals
You are probably familiar with these three variables. name
is a name of a token. symbol
is a token symbol. decimals
is the number of decimal places. These variables are actually not defined officially in ERC20, but most of the ERC20 do have these. The DaicovoStandardToken has these variables by default considering this situation.
Again, as I mentioned at the beginning, this token template is what we recommend, but you don’t have to use it as long as you meet the requirements.
In the next post of this series, I will explain about TokenController contract that is in charge of token issuance.
Profile
Takuya Obata
Chief Operating Officer at ICOVO AG. And a blockchain engineer at the same time. He got MVP in Blockchain Hackathon Tokyo 2016. He is also experienced in natural language processing and chess like game programming.