How to write a smart contract for ICO in 5 minutes



Hello! In this article I will tell you how in 5 minutes and a few commands in the terminal to launch a smart contract to collect money for your ICO on Ethereum. This essay will potentially save you tens of thousands of US dollars, since any programmer — and not a programmer too — will be able to run an audited and secure smart contract (instead of paying $ 15,000 - $ 75,000 for development). In short, you can send money to this smart contract and get ERC20 tokens for it. You can say this article is a collection of all the experience I gained by launching an ICO for my project.

On the Internet, these are yours and so full of an article about smart contracts, but as soon as you start to write this, you are faced with the fact that the information is repeated everywhere, and the tutorials on how to run your ERC20 are either not there, or they are outdated and that is utterly impossible. By the way, in order for this article to remain relevant, I will try to indicate potential places where it might become outdated (and how to fix it). Go!

Solidity


This is the name of the main language that the kefir team developed to launch smart contracts. If you are a programmer, then just glance over the documentation of the language - it is indecently simple. By the way, they made it simple so that it was more difficult to make a mistake in writing a smart contract. So absolutely any programmer at least at the junior level can figure it out. Absolutely no sense in paying huge money to developers who know solidity - it will be cheaper to train an existing developer.

Smart contracts


... and everything you need to know about them. Skip this section if you are not a programmer. A smart contract is a piece of code. In principle, this is a class in solidity (OOP, yes), which has two types of functions: state changing and non changing. Well, to launch functions in a smart contract just by sending kefir to it, you need to mark this function payable .

The state is a data warehouse, blockchain, epta. Contracts can change the blockchain (state, storage) - but to change the blockchain you need to pay kefir to miners. How they will share kefir will not be analyzed in this article. Paying miners for running a code that changes state is called Gas. If someone from the outside throws kefir on the address of a smart contract with a call to a function marked payable but not labeled Constant , View or Pure , then the required amount of kefir will be deducted from the amount sent to pay for miners. Usually in ERC20 tokens are functions that either issue token senders as kefir, or transfer tokens from one token holder to another.

And if you mark a function in the contract with the words Constant or View (they mean the same thing, they allow only to read the state), or Pure (the same, only the state does not read), then even kefir will not have to spend on the execution of this function! I will say even more, these functions do not need to be called up by transactions - after all, any kefir client, theoretically, can execute it at home - and no one else needs to know about it (because nothing is written in the blockchain).

And there are two important things in solidity: multiple inheritance and function modifiers. About them, too, need to know.

The first is that contracts can be inherited simultaneously from several classes such as TimedCrowdsale , CappedCrowdsale , MintedCrowdsale , Ownable — while the functions of the constructors are also launched one after the other — but I will explain this later with an example.

The second is the ability to create functions that will then be inserted into other functions. It's like simple encapsulation, just a bit more flexible — it's literally a function template . When you create a modifier, you write a special _ character where you mean the code of the function using this modifier. That is, modifiers are not just an encapsulated functional that returns a value; this is the function template when the code from the modifier is literally inserted into the function using this modifier.

Let's go to practice.

Cooking environment


If you do not know what Terminal is - read this article here . If you are on the windows, set yourself a terminal through the WLS. If you are already familiar with the Terminal, continue. Also, immediately put yourself Node.js - it will be necessary for the next steps. It is better to set LTS, but, in fact, absolutely no difference, which of the modern versions of the node to put.

The first thing we will immediately put in and start the block synchronization process is geth . In short, this is a utility written in Go, which will allow us to run the ether node on the local computer and connect to the test and real network. You can install via installers , but I strongly advise you to geth geth to the Terminal right away, as described here . You can check whether your geth rules are geth running a command in the Terminal:

 geth version 

If you get the version of geth - everything is in openwork, we continue the tutorial. If not - bad, correct; It seems you will have to deal with love caress with the Terminal and your operating system - but you will not be the first to figure it out. How to install geth, run the following command in the Terminal:

 geth --testnet console 

This will start the process of synchronization of your node with the test server, the blocks of which can be viewed here . You can check whether you have synchronized with the network in the geth console by geth :

 eth.blockNumber #  0 —     eth.syncing #     false,     

The synchronization process took me from 1 to 4 hours - when how. Also, in addition to block synchronization, you will have to wait for state synchronization - this is more often longer than block synchronization. You can also use geth with the --light flag - then synchronization lasts from a few seconds to a minute and you can still deploy contracts.

All right, we have put the first utility - we put the following. We need to put an analogue of geth , only a very local simulation of the blockchain - testrpc . Yes, yes, we have 3 blockchains :


Accordingly, we will begin the test of contracts with testrpc , then we will get into geth --testnet , and then we will start right in geth .

testrpc by running the following command:

 npm install -g ethereumjs-testrpc 

Well, or get up immediately with a truffle, as now testrpc under the wing of the truffle is called ganache-cli . Although the devil knows, I have everything and vanilla testrpc worked. And if it works, don't touch it, as they taught me at the intergalactic academy. You can also launch it to check the installation, by registering a truffle in the console, but we are already synchronizing the test blockchain - we will not disturb it.

Well, have you dealt with blockchains? Now there is a node and the test is even synchronized? We put a convenient utility for working with smart contracts on kefir - truffle , with the following command:

 npm install -g truffle truffle version #  ,  ,   

A truffle is a tool that allows you to keep smart contracts in different files, import other files, and also compiles your smart contract code into one big byte code (unreadable by human), automatically finds your locally running geth (test and real ) or testrpc , deploy your smart contract to this network. Also, it checks your smart contract code for errors and, more recently, completed transactions help debug . Mast of, shorter.

At this stage, you should have installed: testrpc , geth , truffle - if one of these things is missing or the version is not spat out to the console upon request, then correct this; otherwise you will not succeed.

Also, I nakidal simple bash-skriptik , which installs everything for you. Called like this:

 source <(curl -s https://raw.githubusercontent.com/backmeupplz/eth-installer/master/install.sh) 

- but I have never tested it yet, so I'm not sure about its efficiency. However, pull requests will be only happy.

Figa contract


Everything has been thought out and written for you - this is good. A little smut will not care - but I will try to minimize it to you. We will use ready-made ERC20 contracts from OpenZeppelin - this is now the industry standard, they have been audited, and indeed all their code is used. Thanks to them so much for their contribution to the open-sence.

Make a cd in some safe folder and then write:

 mkdir contract && cd contract 

In this folder we will work. Create a stub here for our smart contract:

 truffle init 

Be hurt, clearly. We now have two very important folders into which we will climb: contracts and migrations . The first is the code of our contracts, the second is the code for truffle, so that we know what to do with the total number of contracts in the blockchain.

Next we need to pick up the current smart contract code from npm and, strictly speaking, start the project itself:

 npm init -y #     ( -y) npm install -E openzeppelin-solidity #       ( -E) 

Great, the smart contract code from OpenZeppelin is in our pocket in the node_modules/openzeppelin-solidity/contracts folder. Now go to the main contracts folder, delete all the files there and add the files MyToken.sol and MyCrowdsale.sol - naturally, you will call your contracts differently. The first will be the contract for our ERC20 Token, and the second - by the contract of our ICO, which will receive kefir and distribute to people MyToken . This article may be out of date, but you can always see how OpenZeppelin suggests that you create contracts in their repository . This is how MyToken.sol will look MyToken.sol :

 pragma solidity ^0.4.23; // Imports import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; // Main token smart contract contract MyToken is MintableToken { string public constant name = "My Token"; string public constant symbol = "MTKN"; uint8 public constant decimals = 18; } 

Nice - you have a smart contract of your own token (just change the names in the constants)! You can see that there is inheritance from MintableToken - but everything is as simple as possible. This is a token that can be released (from the English “Mint” - minted), and only the owner has the right to release it, since the MintableToken is also inherited from Ownable . Also, MintableToken is also inherited from the ERC20 classes of tokens written by OpenZeppelin, in which the ERC20 interface is implemented:

 contract ERC20Basic { function totalSupply() public view returns (uint256); function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } 

Yeah, here's the whole ERC20 interface. Complicated? I do not think. It gives an opportunity to see how many tokens were issued, check the address balance and transfer tokens to another address by spitting a translation event for kefir light clients into the network. And all this you get a MyToken.sol in your MyToken.sol thanks to the work of OpenZeppelin - they are great.

And now let's move on to the main part of our ICO - we need to take yogurt and distribute MyToken ! This is how your MyCrowdsale.sol will look like:

 pragma solidity ^0.4.23; // Imports import "../node_modules/openzeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol"; import "../node_modules/openzeppelin-solidity/contracts/crowdsale/distribution/RefundableCrowdsale.sol"; import "../node_modules/openzeppelin-solidity/contracts/crowdsale/validation/CappedCrowdsale.sol"; import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; contract MyCrowdsale is CappedCrowdsale, RefundableCrowdsale, MintedCrowdsale { constructor( uint256 _openingTime, uint256 _closingTime, uint256 _rate, address _wallet, uint256 _cap, MintableToken _token, uint256 _goal ) public Crowdsale(_rate, _wallet, _token) CappedCrowdsale(_cap) TimedCrowdsale(_openingTime, _closingTime) RefundableCrowdsale(_goal) { //   ,  ,    // ,     require(_goal <= _cap); } } 

So, so, so, what is here with us? What, guys, smart contracts? Our public sale of tokens inherits three of the most popular properties: it has a hard cap, more than which it will not work; soft cap, without having collected which ethers come back; time to start and end sales of tokens. In fact, what else is needed for happiness?

Programmers notice how the constructors of the classes of multiple inheritance are lined up and get arguments from the main designer of MyCrowdsale . Also, we check that our hardkep is higher than softkep - ales gut! Also, do not be intimidated by the parameters cloud in the MyCrowdsale constructor - we will pass them on at the stage of contract deployment in truffle.

That's all - you have ready-made contracts of your own ERC20 token and even an ICO smart contract that is customized according to your wishes and distributes your tokens for kefir. Also, it is supported by all ERC20 wallets - lyapota! We turn to the manual tests and Deploy.

Migrations


As I said earlier, we will be testing on three blockchain networks consistently, but the testing process will always be the same. Let's start with testrpc , then move on to geth --testnet and on to geth . Sow, we just wrote the code, let's try to compile it. In the project folder, type:

 truffle compile 

If everything compiles without problems, then you will have a daddy build , which will contain krakozyabra truffle, so that he could put the byte code of your smart contracts into the blockchain. Before deploying smart contracts, we need to tell the truffle what to do. The smart contracts contract in a truffle is called migration - well, well, we will stick to this terminology. Go to migrations/1_initial_migration.js and change it in the following way:

 const token = artifacts.require("../contracts/MyToken.sol"); const crowdsale = artifacts.require("../contracts/MyCrowdsale.sol"); module.exports = function(deployer, network, accounts) { const openingTime = 1514764800; // 15  2018 const closingTime = 1561939200; // 1  2019 const rate = new web3.BigNumber(1); // 1   1  const wallet = '0x281055afc982d96fab65b3a49cac8b878184cb16'; // - const cap = 200 * 1000000; //  const goal = 100 * 1000000; //  return deployer .then(() => { return deployer.deploy(token); }) .then(() => { return deployer.deploy( crowdsale, openingTime, closingTime, rate, wallet, cap, token.address, goal ); }) .then(() => { // Crowdsale    var tokenContract = web3.eth.contract(token.abi).at(token.address); web3.eth.defaultAccount = web3.eth.accounts[0]; tokenContract.transferOwnership(crowdsale.address); }); }; 

This is the file that truffles will use to deploy contracts. So what are we doing with this? First, we requested the compiled MyToken and MyCrowdsale . After, we set constants with all the arguments of our ICO - set the start and end time; how many tokens people will receive for 1 wei kefir (0.000000000000000001 eth = 1 wei; setting decimals indicates how many orders wei need to get 1 of your new token); a purse where kefirs received on sale will come; hard-cap and soft-cap. Note that the openingTime should always be after the time of the current block in the blockchain - otherwise your smart contract will not fail due to checking the conditions in TimedCrowdsale . I attacked this rake, but failed transactions can not be debugged at all. Change these constants on your own.

The next step is to deploy smart contracts. There is nothing interesting here: we have a deployer object that deploit smart contract artifacts and passes arguments there. Notice that MyToken is MyToken first, and only then MyCrowdsale - and the argument of the first is passed to the second.

Then the most interesting thing is what they do not write about either in the documentation or in books. When you create a MyToken from a wallet, this wallet becomes the owner of MyToken in the Ownable superclass - the same happens with MyCrowdsale . If you dig deep into MintableToken , you can see that only the Owner can mint coins! And who is the owner of MyToken ? That's right: the address that zadloplo it. And who will send requests for minting coins? That's right: MyCrowdsale smart contract. Let me remind you that the address that created MyToken and the address for MyCrowdsale are two different addresses.

Therefore, we add a non-Orthodox third step of deployment, where the address that has contracted contracts ( web3.eth.accounts[0] ) calls the transferOwnership function on the MyToken contract MyToken that MyCrowdsale owns the MyToken and can mint coins. And MyCrowdsale is still under the control of web3.eth.accounts[0] - so everything is a bundle.

A note about web3.eth.accounts[0] : when deploying a smart contract, make sure that geth or testrpc have the correct wallet in web3.eth.accounts[0] - do not lose the private key to it, even though it doesn’t hurt you, but suddenly the owner will need to do something later, but the key is gone?
In testrpc , as a rule, accounts are created immediately upon startup and they are immediately unlocked; However, it’s worth creating an account on personal.newAccount() on the test and real blockchain of the air through the Faucet on the test blockchain or real kefir on the real blockchain. Do not lose your password and private keys.
Also, you can add an already existing wallet to your accounts by calling web3.personal.importRawKey('pvt_key', 'password') , but you need to call geth with the additional parameter --rpcapi="db,eth,net,web3,personal,web3" . I think you figure it out.

Testing and Deploy


Yes, the contracts are ready, the migrations are written, it remains only to enclose and check. Both geth (test and real) and testrpc controlled the same way through the truffle console - so I’ll describe the test method for testrpc and just tell you how to enable geth after. And so, we launch a test local kefir blockchain:

 testrpc 

Um ... that's it. You have a local simulation of the kefir blockchain.

And in order to plug into the test blockchain of ether, you will do geth --testnet --rpc instead of this command. And in order to block the real blockchain of the ether, you simply geth --rpc . The --rpc flag --rpc needed so that the truffle can connect. The following steps of deployment and test are more or less the same for all three types of blockchain. The only thing is that after you start the test or real blockchain through geth , it will start synchronizing the blocks - and this can take up to 4-5 hours on a good Internet connection. A remark about this one was at the very beginning of the article. Before deploying smart contracts, I recommend waiting for full synchronization. Also, the blockchain weighs around 60-100 gigabytes, so prepare disk space for this.
Also also, make sure that web3.eth.accounts[0] unlocked. Usually, you can type in the testrpc console, which opens immediately, or in a separate Terminal window in the console, which opens via geth console : eth.unlockAccount(eth.accounts[0], ", ", 24*3600) - this will unlock your account, which should create a smart contract

Now we open a new Terminal window ( testrpc not closed - it should work) and write in the project folder:

 truffle migrate --reset 

This magic command will compile a smart contract (that is, do not need to write truffle compile each time) and deposit it on the micro-server of the blockchain found locally open. It should be noted that if testrpc does it instantly, then the test and real blockchains will include the transaction in the following blocks much longer. After that, you should spit out something similar in the console:

 Using network 'development'. Running migration: 1_initial_migration.js Running step... Replacing MyToken... ... 0x86a7090b0a279f8befc95b38fa8bee6918df30928dda0a3c48416454e2082b65 MyToken: 0x2dc35f255e56f06bd2935f5a49a0033548d85477 Replacing MyCrowdsale... ... 0xf0aab5d550f363478ac426dc2aff570302a576282c6c2c4e91205a7a3dea5d72 MyCrowdsale: 0xaac611907f12d5ebe89648d6459c1c81eca78151 ... 0x459303aa0b79be2dc2c8041dd48493f2d0e109fac19588f50c0ac664f34c7e30 Saving artifacts... 

I think you already understood that the console gave you smart contract MyToken and MyCrowdsale . Everything! The smart contract is secured on the blockchain, whose micro-server is open. It remains only to verify that the tokens are indeed distributed to users who send kefir to the smart contract MyCrowdsale . We register the following in the Terminal to enter the truffle console:

 truffle console 

We register the following in the now truffle (no comment only):

 //   - t="0x2dc35f255e56f06bd2935f5a49a0033548d85477" //     MyToken ="0xaac611907f12d5ebe89648d6459c1c81eca78151" //     MyCrowdsale //   - token=MyToken.at(t) crowdsale=MyCrowdsale.at(c) //       account=web3.eth.accounts[0] // ,      token.balanceOf(account) //   0 //    - web3.eth.sendTransaction({from: account, to:c, value: web3.toWei(0.1, 'ether'), gas: 900000}) 

testrpc , , — , , . ? MyToken :

 // ,      token.balanceOf(account) //     

That's all! testrpc , geth --testnet , geth . ICO! . , OpenZeppelin, , . truffle — . , , - — . -, , .

Conclusion


, ! , - , . , , ICO — $75,000 -, -, , - 25 .

-! Any questions? — .

Bonus


, , ? , rate OpenZeppelin, - ? - getTokenAmount :

 function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256) { if (block.timestamp < 1533081600) { // August 1st, 2018 rate = rate * 4; } else if (block.timestamp < 1546300800) { // January 1st, 2019 rate = rate * 2; } return _weiAmount.mul(rate); } 

, — , . - — !

Source: https://habr.com/ru/post/414493/


All Articles