
In this part 2 of the series, we will talk about a deployment and operation flow of the DAICOVO. If you were going to use our contracts as it is, you can operate it by simply following the operation flow below.
We will cover the whole process in order: before the token sale, during the token sale, after the token sale, voting period and after the approval to the proposal.
Until the Initial Token Distribution
- Deploy the DaicovoStandardToken contract.
1 2 3 4 5 6 7 |
/* DaicovoStandardToken.sol */ constructor(string _name, string _symbol, uint8 _decimals) public; //_name = the token name(例:ICOVO) //_symbol = the symbol of the token (例:OVO) //_decimals = the number of decimal places |
You may use your own ERC20 compatible token. But in that case, you need to inherit Mintable and Ownable contracts.
- Register your white paper to WhitepaperVersioning contract(optional).
This is optional. You don’t need to use this contract unless you want to execute your ICO on top of ICOVO platform. This contract prevents project owners from tempering with their white paper.
1 2 3 4 5 6 7 |
/* WhitepaperVersioning.sol */ function pushWhitepaper (Ownable _contract, uint256 _version, string _ipfsHash) public returns (bool); //_contract = the address of the token(the DaicovoStandardToken you have deployed) //_version = version info(ver.x.y.z is _version=x*10000 + y*100 + z) //_ipfsHash = the hash of the white paper in IPFS |
WhitepaperVersioning has an instance globally. You register your white paper on this instance. To be more specific, you register the white paper itself on IPFS and add the hash to the WhitepaperVersioning contract. This way, you can trace the change history. You can update the white paper every time you execute pushWhitepaper( )
.
_contract
should be Ownable and only the owner can register initially. For further updates, you can register from the address you registered first. This means that the initial registrar will have the authority to update for good even if the owner of _contract
change later on.
- Deploy the TokenController contract.
1 2 3 4 5 |
/* TokenController.sol */ constructor (MintableToken _targetToken) public; //_targetToken = the address of the token(The DaicovoStandardToken you have deployed) |
- Transfer the ownership of the token.
1 2 3 |
function transferOwnership(address newOwner) public onlyOwner; //newOwner = the address of the new owner(set up the address of the TokenController) |
You change the owner to the TokenController by executing thetransferOwnership()
function. The WhitepaperVersioning should be taken care of before this process because it changes the owner. After this change, the TokenController will be in charge of issuing tokens.
- Issue tokens via the TokenController contract.
They can distribute the tokens that will be allocated to their team members or marketing expenses. If you were to have closed sales, you can distribute them here.
*Beware that the project owner cannot distribute anymore once the crowd sale has started
1 2 3 4 5 6 |
/* TokenController.sol */ function mint (address to, uint256 amount) external returns (bool); // to = the addresses to distribute // amount = the number of tokens to distribute(with the smallest unit. If the smallest unit were 0.001 and you wanted to distribute 10, it should be 10000) |
If you want to lock up a certain amount of tokens, send them to the TimeLockPool.
Make sure you give permission to the TimeLockPool with approve()
function in the token contract before you deposit tokens to the TimeLockPool contract.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* TimeLockPool.sol */ function depositERC20 ( address tokenAddr, address account, uint256 amount, uint256 releaseTime ) external returns (bool); // tokenAddr = the token address to issue tokens(The DaicovoStandardToken you have deployed or else...) // account = the address(es) of distribution // amount = the amount of tokens(with the smallest unit. If the smallest unit were 0.001 and you distribute 10, it should be 10000) // releaseTime = the time of the release(timestamp) |
The Preparation for the Token Sale
- Deploy the DaicoPool contract.
1 2 3 4 5 6 7 |
/* DaicoPool.sol */ constructor(address _votingTokenAddr, uint256 tap_amount, uint256 _initialRelease) public; // _votingTokenAddr = the token you issue = the address of the token contract for voting // tap_amount = the tap amount // _initialRelease = the amount of wei for your initial release |
The Voting contract will be deployed in the constructor.
- Deploy the TokenSaleManager contract.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* TokenSaleManager.sol */ constructor ( address _tokenControllerAddr, address _timeLockPoolAddr, address _daicoPoolAddr, ERC20Interface _token ) public; // _tokenControllerAddr = the address of the TokenController // _timeLockPoolAddr = the address of the TimeLockPool // _daicoPoolAddr = the address of the DaicoPool // _token = the token address to issue tokens(the DaicovoStandardToken you have deployed or else...) |
- Set up your token sale in
TokenSaleManager.addTokenSale(…)
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* TokenSaleManager.sol */ function addTokenSale ( uint256 openingTime, uint256 closingTime, uint256 tokensCap, uint256 rate, bool carryover, uint256 timeLockRate, uint256 timeLockEnd, uint256 minAcceptableWei ) external onlyOwner // openingTime = the start date and time(timestamp) // closingTime = the end date and time(timestamp) // tokensCap = the mount of tokens for sale (it can be zero if you enable the carry-over from the previous sale) // rate = how many tokens to issue per 1 ETH // carryover = enable the carry-over or not(the carry-over of unsold tokens to the next sale) // timeLockRate = what percentage of tokens will be locked // timeLockEnd = the end date and time of locked-up tokens(timestamp) // minAcceptableWei = the minimum purchase amount(in wei). Reject anything smaller than this amount. |
Assign value to each parameter of the token sale.
initialize()
in the TokenSaleManager contract.
You can fix the setting of your token sale by running initialize()
.
1 2 3 |
/* TokenSaleManager.sol */ function initialize () external onlyOwner returns (bool); |
- Execute
OpenTokenSale()
in the TokenController contract.
This transfers the authority to issue tokens to the TokenSaleManager. The project owner cannot distribute tokens anymore.
1 2 3 4 5 |
/* TokenController.sol */ function openTokensale (address _tokensaleManagerAddr) external onlyOwner; // _tokensaleManagerAddr = the address of the TokenSaleManager you deployed |
Everybody can check the distribution of tokens before the sale. Thus, investors can make sure if the project owner keeps the promise written in the white paper before they participate in the sale.
- Whitelist
Whitelist the KYC’d addresses with TokenSaleManager.addToWhitelist
(or addManyToWhitelist
). Only the whitelisted users/addresses can purchase tokens(You can update the whitelist later).
1 2 3 4 5 6 7 8 9 |
/* TokenSaleManager.sol */ function addToWhitelist(address _beneficiary) external onlyOwner; // _beneficiary = the address function addManyToWhitelist(address[] _beneficiaries) external onlyOwner; // _beneficiaries = the list of addresses |
*We have designed the contract in a way that all the processes after the token sale will progress in a trustless manner: even the project owner cannot change the content of the sale or shut down the operation. The token sale will start in such a way that all the processes are clear to everybody.
During The Token Sale
- The sale starts
The sale will be open when the start date and time you set up in the first TokenSale comes.
- The investors buy tokens
The whitelisted investors can receive tokens in exchange with ETH by sending them to the TokenSale contract. The transaction fails if they don’t send more than the minimum amount.
1 2 3 |
/* Crowdsale.sol */ function () external payable; |
- Finalize the token sale
When the end date and time comes or when all the tokens were sold, you can finalize
the TokenSale with TokenSaleManager.finalize(uint256 index)
. Everybody can finalize
the sale.
1 2 3 4 5 |
/* TokenSale.sol */ function finalize (uint256 _indexTokenSale) external; // _indexTokenSale = the index of your token sale(the first sale = 0) |
The funds in the TokenSale contract will be sent to the DaicoPool contract with the finalization(You can finalize as many times as needed in case some funds are left in the TokenSale contract for some reasons). The finalization also initialize
the next TokenSale.
In case you set the carryover true, the unsold tokens will be carried over to the next TokenSale.
finalize
the TokenSaleManager.
You can execute TokenSaleManager.finalizeTokenSaleManager()
when all the TokenSale contracts were finalized. Everybody can execute this function just like the finalization of each TokenSale contract.
1 2 3 |
/* TokenSaleManager.sol */ function finalizeTokenSaleManager () external; |
The tap opens in the DaicoPool, which release the initial funds and start releasing the amount you set up as the tap amount. Also, token holders can make a proposal from this point.
After The Token Sale
- The project owner withdraws money from the pool
The amount of money you can withdraw increases every second depending on the amount of tap you set up. Only the project owner can withdraw this fund.
1 2 3 4 5 |
/* DaicoPool.sol */ function withdraw(uint256 _amount) public onlyOwner; // _amount = the amount of funds to withdraw[wei] |
- Make a proposal
At this point, everybody can make a proposal by sending a certain amount of ETH. There are two kinds of a proposal in the current version: the tap raising and the self-destruction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* Voting.sol */ // The proposal to raise a tap function addRaiseTapProposal ( string _reason, uint256 _tapMultiplierRate ) external payable returns(uint256); // _reason = the reason of your proposal(text). You can set up the URL as well. // _tapMultiplierRate = the tap change ratio(%). If you want to increase the tap by half, it's 150. // The proposal to self-destruct function addDestructionProposal ( string _reason ) external payable returns(uint256); // _reason = the reason of your proposal(text). You can set up the URL as well. |
Once the proposal is made, it immediately moves onto the voting period. The DaicoPool continues to release the funds during the period, and the project owner can still withdraw their funds.
During The Voting Period
The 14 days of voting period starts once somebody makes a proposal.
- Vote
The token holders can either agree or disagree with the proposal by depositing their tokens to the Voting contract. Before voting, they need to give permission to the Voting contract to spend on behalf of them with approve()
function.
1 2 3 4 5 6 |
/* Voting.sol */ function vote (bool agree, uint256 amount) external; // agree = agree(true) or disagree(false) // amount = the amount of tokens to deposit |
- finalize the voting.
When the voting period ends, everybody can finalize the Voting. When the conditions are met to pass the proposal, it sends either the RaiseTap or SelfDestruction signal to the DaicoPool contract. When the conditions are not met, nothing happens except the ending of the voting period.
1 2 3 |
/* Voting.sol */ function finalizeVoting () external; |
- Refund the tokens
After the finalization, the voters can get their tokens back. To be more specific, they can execute returnToken()
function in the Voting contract. Everybody can execute this function but it will always refund to the original token holders.
1 2 3 4 5 |
/* Voting.sol */ function returnToken (address account) external returns(bool); // account = the address that deposited the token(regardless of the msg.sender, it returns the specified address or deposited tokens to the address.) |
After The Self-destruction Proposal Is Passed
- Self-destruction
If the proposal of a self-destruction garners support from a majority, the DaicoPool contract immediately turns off the tap after it releases the fund of 30 days to the project owner. This fund is released to allow the project owner to pay expenses for a month. Once the tap is turned off, token holders can take back their money.
- More precisely, token holders can receive money from the funds left in exchange with their tokens.
You can calculate the money you will receive with the equation below.
The funds left in DaicoPool * the number of tokens you exchange / total supply
Before executing the refund()
, you need to give permission to the DaicoPoolcontract to spend on behalf of you with the approve()
function in the token contract.
1 2 3 4 5 |
/* DaicoPool.sol */ function refund(uint256 tokenAmount) external poolDestructed; // tokenAmount = the amount of tokens to refund |
For the upcoming posts, we will talk about the implementation of each contract.
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.