In this article, we are going to discuss how basic crowdsale works and how can we develop an EOS Crowdsale smart contract. We will try to understand in what ways EOS smart contract differs from Ethereum smart contract in such basic contracts. Also, why do we need inter contract communication to manage crowdsale in EOS and how we can do it.
Before reading this, we would recommend you to read the previous articles in this series so as to have an in-depth understanding of eosio.token system smart contract. So let’s get started!
Crowdsale is required in blockchain space to have faith while raising funds, unlike traditional revenue-raising methods. An initial coin offering (ICO), or digital token crowdsale, is a method of blockchain-based crowdfunding based on the exchange of a project’s new and unique cryptocurrency tokens for established cryptocurrencies like ETH, EOS, etc. Now, Crowdsale smart contract defines the rules and makes sure that there is transparency while raising these funds.
In this article, we are going to develop a crowdsale contract in EOS. QUI token crowdsale would be there in exchange for SYS token. The smart contract will have the following action functions:
ACTION init(eosio::name recipient, eosio::time_point_sec start, eosio::time_point_sec finish);
ACTION buyquill(eosio::name from, eosio::name to, eosio::asset quantity, std::string memo);
ACTION transfer(eosio::name from, eosio::name to, eosio::asset quantity, std::string memo);
ACTION withdraw()
ACTION pause();
We will be issuing QUI token in exchange for SYS token of eosio.token contract after applying the rates. One can configure for the rates that apply after the crowdsale gets over.
Now, one main thing to note in this is that unlike ethereum the smart contract does not have its own address, but is deployed on particular EOS account address, say Alice. So, any transfer of tokens one does, is upon that address only. For convenience sake, we use the same name as contract for the address where account is deployed i.e. for crowdsale smart contract, we have crowdsale named account address.
Code - Refers to an account_name where a contract has been published. We also define the scope of the contract in the constructor itself.
Now, let's first quickly revise what are Multi-Index Tables. They are a way to cache state and/or data in RAM for fast access. Multi-index table supports CRUD operations i.e. create, read, update and delete operations, something which the blockchain doesn't support (it only supports create and read.)
The multi-index table we will be using in crowdsale will be to hold the deposits of each account and its SYS and QUI holdings. The primary key would be the account name.
Conceptually, Multi-index tables are stored on the EOSIO RAM cache. Smart contracts using a multi-index table reserve a partition of the RAM cache and accesses to each partition is controlled using tablename, code, and scope.
In the code, when we emplace or modify the row in the table, we need to specify the ram payer as first argument payer.
deposits.emplace( payer, [&]( auto& deposit ) {
deposit.account = investor;
deposit.tokens = entire_tokens;
});
Note, in inter-contract communication, we cannot charge RAM to other accounts during notify normally. That is pretty obvious else anyone will make anyone pay for the ram.
Also, one more important note, you can always upgrade your smart contract but you can’t change the fields of the table.
Now, let's learn about inter contract communications and what is the need for it?
Inter contract is the communication between 2 external smart contracts. eosio.token contract is the system contract that holds all our tokens QUI, SYS, etc.
Any transfer of tokens or issuance of new tokens is done by this contract only. We have learned in detail about the contract in the previous article.
The crowdsale contract needs to issue QUI tokens in exchange for SYS tokens. To have that strategy, we require to have a communication in between 2 contracts, whenever transfer action is called upon we need to have transfer of SYS from participant to the recipient (of crowdsale) and also issue new QUI tokens to the participant.
In order to notify both the account from and account to, that there is some transfer between them, the standard eosio.token contract uses the following in its transfer action:
require_recipient( from );
require_recipient( to );
Now, we need to listen to these receipts of eosio.token contract. So, a custom dispatcher is made that handles QUI transfers from eosio.token contract. Hence, when transfer action of eosio.token is called then the transfer action of crowdsale is invoked as a result which handles the calculation of QUI as per the rate and transfers QUI tokens back to the same participant account. It is done using an instance of the action sender by giving the required permissions.
A custom dispatcher code snippet is shown below:
extern "C" void apply(uint64_t receiver, uint64_t code, uint64_t action)
{
if (code == eosio::name("eosio.token").value && action == eosio::name("transfer").value) // handle actions from eosio.token contract
eosio::execute_action(eosio::name(receiver), eosio::name(code), &crowdsale::transfer;
else if (code == receiver) // for other direct actions
switch (action)
EOSIO_DISPATCH_HELPER(crowdsaler, (init)(transfer)(pause)(rate)(checkgoal)(withdraw));
}
Here, we are listening from the eosio.token code (account where eosio.token contract is deployed default) and its transfer action. We invoke crowdsale’s transfer function when condition is satisfied using execute_action. If this condition is not met, and any crowdsale action is called explicitly, the EOSIO_DISPATCH_HELPER is used. Note, we have not done any changes in standard eosio.token contract. When you deploy it on Jungle testnet or any other testnet, you wont need to deploy eosio.token as such, it being a system contract. Before deploying on testnet, you need to create an account and have some EOS (from faucet) and then buy some ram required for deploying and updating the tables. It seems you need about 10 times the size of your contract for ram-allocation. The EOS required can be calculated from https://www.eosrp.io/#calc .
Now let's get back to the contract. For the issuance of QUI tokens to the same participant account action sender is used. An action constructor has 4 parameters:
So, issue action of eosio.token needs to be invoked. It is shown in the code snippet below:
eosio::action issue_action = eosio::action(
eosio::permission_level(this->_self, "active"_n),
eosio::name("eosio.token"),
eosio::name("issue"),
issue{to, quantity, memo});
issue_action.send();
}
Similarly, we make an action construct in crowdsale for transfer action of eosio.token.
eosio::action transfer_action = eosio::action(
eosio::permission_level(_self, eosio::name("active")),
eosio::name("eosio.token"), // name of the contract
eosio::name("transfer"),
transfer{from, to, quantity, memo});
transfer_action.send();
Now, the inter-contract communication is established, we need to write the logic of crowdsale actions to do basic functionalities like init, pause, rate, checkgoal, withdraw and transfer.
Now, in order to test if all the actions are working as expected, we use the eosfactory. The test cases for all basic actions are written in python. The installation instructions can be found at:
http://eosfactory.io/build/html/tutorials/01.InstallingEOSFactory.html
The complete smart contract of EOS Crowdsale along with the test cases can be found at https://github.com/Quillhash/Crowdsale_EOS
So, we learned here how inter-contract communication is so amazing. We can listen to the transactions of 1 contract and apply our logic to do another one. Please note, we cannot as such change the fields of standard eosio.token i.e. you cannot take EOS from anyone’s wallet or issue new tokens, its just if they are giving you the EOS you can provide QUI back to them in exchange from another contract, say quilltoken. That's why we try not to change much in token contracts and keep it the same as standard eosio.token only.
Summary
We went full path from writing EOS crowdsale smart contract and learnt about eosio.token smart contract to have our own token and transfer tokens out to other accounts using inter-contract communication. We also did see about EOS Jungle Testnet (which is almost identical to Mainnet) and the major differences between EOS and Ethereum smart contract.
Stay tuned for the next part…
~~Part 2 — EOS Smart Contracts Audit checklist to Keep In mind Before Development~~
~~Part 3- Understanding fundamental concepts for writing dApps on EOS.~~
~~Part 5- Develop a basic crowd sale application with EOS~~
Part 6- EOS for high performance dApps — Games on EOS!
Disclaimer: The views expressed by the author above do not necessarily represent the views of EOS GO. EOS GO is a community where EOS GO Blog being a platform for authors to express their diverse ideas and perspectives. To learn more about EOS GO and EOS, please join us on our social medias.
EOS GO Telegram - EOS News Channel - Twitter
EOS GO is funded by EOS ASIA and powered by YOU. Join the community and begin contributing to the movement by adding eos go to your name and joining the EOS GO telegram group.