
I will talk about the TokenController contract in this post.
The TokenController contract manages the issuance of tokens: it mints tokens as the owner of the token contract. It also serves as a controller that manages permission to mint tokens in each three phases of the token sale.
This is the constructor:
1 2 3 4 5 6 7 8 9 10 |
/* TokenController.sol */ constructor ( MintableToken _targetToken ) public { targetToken = MintableToken(_targetToken); state = State.Init; } // _targetToken = the target token contract this controller contract manages the rights to mint |
State Transition
The TokenContoller changes its state in this order below:
- before the token sale
- during the token sale
- after the token sale
This is how the states are written in the contract:
1 2 3 4 5 6 7 8 |
/* TokenController.sol */ State public state; enum State { Init, // before the token sale Tokensale, // during the token sale Public // after the token sale } |
Mint Permission
Here is how the TokenController manages permission to mint tokens:
- Before the token sale, only the project owner(the owner of the TokenController contract) can mint tokens.
- During the token sale, only the TokenSaleManager can issue tokens. When it receives ETH from an investor, the TokenController mints tokens via the TokenSaleManager. The project owner can no longer mint tokens at this state.
- After the token sale, nobody can issue tokens anymore. However, in the upcoming updates, we might make it possible to issue tokens when the proposal to issue extra tokens garners support from a majority. In this case, only the Voting contract will have the permission.
This permission management is implemented in the mint()
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* TokenController.sol */ function mint (address to, uint256 amount) external returns (bool) { /* being called from voting contract will be available in the future ex. if (state == State.Public && msg.sender == votingAddr) */ if ((state == State.Init && msg.sender == owner) || (state == State.Tokensale && msg.sender == tokensaleManagerAddr)) { return targetToken.mint(to, amount); } revert(); } |
Operation Flow
Here is the operation flow of the TokenController. You can check the whole operation flow in the part 2 of this series.
- Deploy the TokenController (state: “before the token sale”)
- Transfer the ownership of the token contract to the TokenController
- The project owner(the owner of the TokenController) distributes tokens before the token sale with
TokenController.mint()
function. - Change the state to “during the token sale” with
TokenController.openTokenSale()
. - The TokenSaleManager executes the token sale.
- Finalize the TokenSaleManager, which calls
closeTokenSale()
in the TokenController. The state changes to “after the token sale”.
State Transition Functions
Firstly, the constructor()
changes the state to Init
(before the token sale). Secondly, the openTokenSale()
changes the state to the TokenSale
(during the token sale). Finally, the closeTokenSale()
changes the state to Public
(after the token sale).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* TokenController.sol */ //You give the address of the TokenSaleManager when you change the state to "during the token sale". function openTokensale (address _tokensaleManagerAddr) external onlyOwner returns (bool) { /* check if the owner of the target token is set to this contract */ require(MintableToken(targetToken).owner() == address(this)); require(state == State.Init); require(_tokensaleManagerAddr != address(0x0)); tokensaleManagerAddr = _tokensaleManagerAddr; state = State.Tokensale; return true; } //Only the TokenSaleManager can change the state to Public(after the token sale) function closeTokensale () external returns (bool) { require(state == State.Tokensale && msg.sender == tokensaleManagerAddr); state = State.Public; return true; } |
Other Getter Functions
There are getter functions to check the current state:
isStateInit
isStateTokensale
isStatePublic
enum State
is internally unit
, and thus it returns
assigned unit value when it returns
the State
. Since this is problematic from maintainability point of view, we prepared the isStateXXXX functions that return the boolean value instead of having the function to return the State.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* TokenController.sol */ function isStateInit () external view returns (bool) { return (state == State.Init); } function isStateTokensale () external view returns (bool) { return (state == State.Tokensale); } function isStatePublic () external view returns (bool) { return (state == State.Public); } |
That is all for the TokenController!
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.