Статья представляет собой пример быстрого запуска своего смарт-контракта ICO на эфире. Мы не будем подробно разбирать все функции и с нуля писать смарт-контракт. Наша задача — взять готовый, сделать минимум изменений и запустить его.
- За основу возьмем смарт-контракт TenX. Он хорош тем, что там есть практически все необходимые функции.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578pragma solidity 0.4.11;/*** @title Ownable* @dev The Ownable contract has an owner address, and provides basic authorization control* functions, this simplifies the implementation of "user permissions".*/contract Ownable {address public owner;/*** @dev The Ownable constructor sets the original `owner` of the contract to the sender* account.*/function Ownable() {owner = msg.sender;}/*** @dev Throws if called by any account other than the owner.*/modifier onlyOwner() {if (msg.sender != owner) {throw;}_;}/*** @dev Allows the current owner to transfer control of the contract to a newOwner.* @param newOwner The address to transfer ownership to.*/function transferOwnership(address newOwner) onlyOwner {if (newOwner != address(0)) {owner = newOwner;}}}/*** @title Authorizable* @dev Allows to authorize access to certain function calls** ABI* [{"constant":true,"inputs":[{"name":"authorizerIndex","type":"uint256"}],"name":"getAuthorizer","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"addAuthorized","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"isAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]*/contract Authorizable {address[] authorizers;mapping(address => uint) authorizerIndex;/*** @dev Throws if called by any account tat is not authorized.*/modifier onlyAuthorized {require(isAuthorized(msg.sender));_;}/*** @dev Contructor that authorizes the msg.sender.*/function Authorizable() {authorizers.length = 2;authorizers[1] = msg.sender;authorizerIndex[msg.sender] = 1;}/*** @dev Function to get a specific authorizer* @param authorizerIndex index of the authorizer to be retrieved.* @return The address of the authorizer.*/function getAuthorizer(uint authorizerIndex) external constant returns(address) {return address(authorizers[authorizerIndex + 1]);}/*** @dev Function to check if an address is authorized* @param _addr the address to check if it is authorized.* @return boolean flag if address is authorized.*/function isAuthorized(address _addr) constant returns(bool) {return authorizerIndex[_addr] > 0;}/*** @dev Function to add a new authorizer* @param _addr the address to add as a new authorizer.*/function addAuthorized(address _addr) external onlyAuthorized {authorizerIndex[_addr] = authorizers.length;authorizers.length++;authorizers[authorizers.length - 1] = _addr;}}/*** @title ExchangeRate* @dev Allows updating and retrieveing of Conversion Rates for PAY tokens** ABI* [{"constant":false,"inputs":[{"name":"_symbol","type":"string"},{"name":"_rate","type":"uint256"}],"name":"updateRate","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"data","type":"uint256[]"}],"name":"updateRates","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_symbol","type":"string"}],"name":"getRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"rates","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"timestamp","type":"uint256"},{"indexed":false,"name":"symbol","type":"bytes32"},{"indexed":false,"name":"rate","type":"uint256"}],"name":"RateUpdated","type":"event"}]*/contract ExchangeRate is Ownable {event RateUpdated(uint timestamp, bytes32 symbol, uint rate);mapping(bytes32 => uint) public rates;/*** @dev Allows the current owner to update a single rate.* @param _symbol The symbol to be updated.* @param _rate the rate for the symbol.*/function updateRate(string _symbol, uint _rate) public onlyOwner {rates[sha3(_symbol)] = _rate;RateUpdated(now, sha3(_symbol), _rate);}/*** @dev Allows the current owner to update multiple rates.* @param data an array that alternates sha3 hashes of the symbol and the corresponding rate .*/function updateRates(uint[] data) public onlyOwner {if (data.length % 2 > 0)throw;uint i = 0;while (i < data.length / 2) {bytes32 symbol = bytes32(data[i * 2]);uint rate = data[i * 2 + 1];rates[symbol] = rate;RateUpdated(now, symbol, rate);i++;}}/*** @dev Allows the anyone to read the current rate.* @param _symbol the symbol to be retrieved.*/function getRate(string _symbol) public constant returns(uint) {return rates[sha3(_symbol)];}}/*** Math operations with safety checks*/library SafeMath {function mul(uint a, uint b) internal returns (uint) {uint c = a * b;assert(a == 0 || c / a == b);return c;}function div(uint a, uint b) internal returns (uint) {// assert(b > 0); // Solidity automatically throws when dividing by 0uint c = a / b;// assert(a == b * c + a % b); // There is no case in which this doesn't holdreturn c;}function sub(uint a, uint b) internal returns (uint) {assert(b <= a);return a - b;}function add(uint a, uint b) internal returns (uint) {uint c = a + b;assert(c >= a);return c;}function max64(uint64 a, uint64 b) internal constant returns (uint64) {return a >= b ? a : b;}function min64(uint64 a, uint64 b) internal constant returns (uint64) {return a < b ? a : b;}function max256(uint256 a, uint256 b) internal constant returns (uint256) {return a >= b ? a : b;}function min256(uint256 a, uint256 b) internal constant returns (uint256) {return a < b ? a : b;}function assert(bool assertion) internal {if (!assertion) {throw;}}}/*** @title ERC20Basic* @dev Simpler version of ERC20 interface* @dev see https://github.com/ethereum/EIPs/issues/20*/contract ERC20Basic {uint public totalSupply;function balanceOf(address who) constant returns (uint);function transfer(address to, uint value);event Transfer(address indexed from, address indexed to, uint value);}/*** @title ERC20 interface* @dev see https://github.com/ethereum/EIPs/issues/20*/contract ERC20 is ERC20Basic {function allowance(address owner, address spender) constant returns (uint);function transferFrom(address from, address to, uint value);function approve(address spender, uint value);event Approval(address indexed owner, address indexed spender, uint value);}/*** @title Basic token* @dev Basic version of StandardToken, with no allowances.*/contract BasicToken is ERC20Basic {using SafeMath for uint;mapping(address => uint) balances;/*** @dev Fix for the ERC20 short address attack.*/modifier onlyPayloadSize(uint size) {if(msg.data.length < size + 4) {throw;}_;}/*** @dev transfer token for a specified address* @param _to The address to transfer to.* @param _value The amount to be transferred.*/function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) {balances[msg.sender] = balances[msg.sender].sub(_value);balances[_to] = balances[_to].add(_value);Transfer(msg.sender, _to, _value);}/*** @dev Gets the balance of the specified address.* @param _owner The address to query the the balance of.* @return An uint representing the amount owned by the passed address.*/function balanceOf(address _owner) constant returns (uint balance) {return balances[_owner];}}/*** @title Standard ERC20 token** @dev Implemantation of the basic standart token.* @dev https://github.com/ethereum/EIPs/issues/20* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol*/contract StandardToken is BasicToken, ERC20 {mapping (address => mapping (address => uint)) allowed;/*** @dev Transfer tokens from one address to another* @param _from address The address which you want to send tokens from* @param _to address The address which you want to transfer to* @param _value uint the amout of tokens to be transfered*/function transferFrom(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) {var _allowance = allowed[_from][msg.sender];// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met// if (_value > _allowance) throw;balances[_to] = balances[_to].add(_value);balances[_from] = balances[_from].sub(_value);allowed[_from][msg.sender] = _allowance.sub(_value);Transfer(_from, _to, _value);}/*** @dev Aprove the passed address to spend the specified amount of tokens on beahlf of msg.sender.* @param _spender The address which will spend the funds.* @param _value The amount of tokens to be spent.*/function approve(address _spender, uint _value) {// To change the approve amount you first have to reduce the addresses`// allowance to zero by calling `approve(_spender, 0)` if it is not// already 0 to mitigate the race condition described here:// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;allowed[msg.sender][_spender] = _value;Approval(msg.sender, _spender, _value);}/*** @dev Function to check the amount of tokens than an owner allowed to a spender.* @param _owner address The address which owns the funds.* @param _spender address The address which will spend the funds.* @return A uint specifing the amount of tokens still avaible for the spender.*/function allowance(address _owner, address _spender) constant returns (uint remaining) {return allowed[_owner][_spender];}}/*** @title Mintable token* @dev Simple ERC20 Token example, with mintable token creation* @dev Issue: * https://github.com/OpenZeppelin/zeppelin-solidity/issues/120* Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol*/contract MintableToken is StandardToken, Ownable {event Mint(address indexed to, uint value);event MintFinished();bool public mintingFinished = false;uint public totalSupply = 0;modifier canMint() {if(mintingFinished) throw;_;}/*** @dev Function to mint tokens* @param _to The address that will recieve the minted tokens.* @param _amount The amount of tokens to mint.* @return A boolean that indicates if the operation was successful.*/function mint(address _to, uint _amount) onlyOwner canMint returns (bool) {totalSupply = totalSupply.add(_amount);balances[_to] = balances[_to].add(_amount);Mint(_to, _amount);return true;}/*** @dev Function to stop minting new tokens.* @return True if the operation was successful.*/function finishMinting() onlyOwner returns (bool) {mintingFinished = true;MintFinished();return true;}}/*** @title PayToken* @dev The main PAY token contract** ABI* [{"constant":true,"inputs":[],"name":"mintingFinished","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"startTrading","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tradingStarted","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[],"name":"MintFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]*/contract PayToken is MintableToken {string public name = "TenX Pay Token";string public symbol = "PAY";uint public decimals = 18;bool public tradingStarted = false;/*** @dev modifier that throws if trading has not started yet*/modifier hasStartedTrading() {require(tradingStarted);_;}/*** @dev Allows the owner to enable the trading. This can not be undone*/function startTrading() onlyOwner {tradingStarted = true;}/*** @dev Allows anyone to transfer the PAY tokens once trading has started* @param _to the recipient address of the tokens.* @param _value number of tokens to be transfered.*/function transfer(address _to, uint _value) hasStartedTrading {super.transfer(_to, _value);}/*** @dev Allows anyone to transfer the PAY tokens once trading has started* @param _from address The address which you want to send tokens from* @param _to address The address which you want to transfer to* @param _value uint the amout of tokens to be transfered*/function transferFrom(address _from, address _to, uint _value) hasStartedTrading {super.transferFrom(_from, _to, _value);}}/*** @title MainSale* @dev The main PAY token sale contract** ABI* [{"constant":false,"inputs":[{"name":"_multisigVault","type":"address"}],"name":"setMultisigVault","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"authorizerIndex","type":"uint256"}],"name":"getAuthorizer","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"exchangeRate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"altDeposits","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"tokens","type":"uint256"}],"name":"authorizedCreateTokens","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finishMinting","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_exchangeRate","type":"address"}],"name":"setExchangeRate","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"retrieveTokens","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"totalAltDeposits","type":"uint256"}],"name":"setAltDeposit","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"start","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"}],"name":"createTokens","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"addAuthorized","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"multisigVault","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_hardcap","type":"uint256"}],"name":"setHardCap","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_start","type":"uint256"}],"name":"setStart","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"isAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"ether_amount","type":"uint256"},{"indexed":false,"name":"pay_amount","type":"uint256"},{"indexed":false,"name":"exchangerate","type":"uint256"}],"name":"TokenSold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"pay_amount","type":"uint256"}],"name":"AuthorizedCreate","type":"event"},{"anonymous":false,"inputs":[],"name":"MainSaleClosed","type":"event"}]*/contract MainSale is Ownable, Authorizable {using SafeMath for uint;event TokenSold(address recipient, uint ether_amount, uint pay_amount, uint exchangerate);event AuthorizedCreate(address recipient, uint pay_amount);event MainSaleClosed();PayToken public token = new PayToken();address public multisigVault;uint hardcap = 200000 ether;ExchangeRate public exchangeRate;uint public altDeposits = 0;uint public start = 1498302000; //new Date("Jun 24 2017 11:00:00 GMT").getTime() / 1000/*** @dev modifier to allow token creation only when the sale IS ON*/modifier saleIsOn() {require(now > start && now < start + 28 days);_;}/*** @dev modifier to allow token creation only when the hardcap has not been reached*/modifier isUnderHardCap() {require(multisigVault.balance + altDeposits <= hardcap);_;}/*** @dev Allows anyone to create tokens by depositing ether.* @param recipient the recipient to receive tokens.*/function createTokens(address recipient) public isUnderHardCap saleIsOn payable {uint rate = exchangeRate.getRate("ETH");uint tokens = rate.mul(msg.value).div(1 ether);token.mint(recipient, tokens);require(multisigVault.send(msg.value));TokenSold(recipient, msg.value, tokens, rate);}/*** @dev Allows to set the toal alt deposit measured in ETH to make sure the hardcap includes other deposits* @param totalAltDeposits total amount ETH equivalent*/function setAltDeposit(uint totalAltDeposits) public onlyOwner {altDeposits = totalAltDeposits;}/*** @dev Allows authorized acces to create tokens. This is used for Bitcoin and ERC20 deposits* @param recipient the recipient to receive tokens.* @param tokens number of tokens to be created.*/function authorizedCreateTokens(address recipient, uint tokens) public onlyAuthorized {token.mint(recipient, tokens);AuthorizedCreate(recipient, tokens);}/*** @dev Allows the owner to set the hardcap.* @param _hardcap the new hardcap*/function setHardCap(uint _hardcap) public onlyOwner {hardcap = _hardcap;}/*** @dev Allows the owner to set the starting time.* @param _start the new _start*/function setStart(uint _start) public onlyOwner {start = _start;}/*** @dev Allows the owner to set the multisig contract.* @param _multisigVault the multisig contract address*/function setMultisigVault(address _multisigVault) public onlyOwner {if (_multisigVault != address(0)) {multisigVault = _multisigVault;}}/*** @dev Allows the owner to set the exchangerate contract.* @param _exchangeRate the exchangerate address*/function setExchangeRate(address _exchangeRate) public onlyOwner {exchangeRate = ExchangeRate(_exchangeRate);}/*** @dev Allows the owner to finish the minting. This will create the* restricted tokens and then close the minting.* Then the ownership of the PAY token contract is transfered* to this owner.*/function finishMinting() public onlyOwner {uint issuedTokenSupply = token.totalSupply();uint restrictedTokens = issuedTokenSupply.mul(49).div(51);token.mint(multisigVault, restrictedTokens);token.finishMinting();token.transferOwnership(owner);MainSaleClosed();}/*** @dev Allows the owner to transfer ERC20 tokens to the multi sig vault* @param _token the contract address of the ERC20 contract*/function retrieveTokens(address _token) public onlyOwner {ERC20 token = ERC20(_token);token.transfer(multisigVault, token.balanceOf(this));}/*** @dev Fallback function which receives ether and created the appropriate number of tokens for the* msg.sender.*/function() external payable {createTokens(msg.sender);}} - Ищем строчки
1234567...contract PayToken is MintableToken {string public name = "TenX Pay Token";string public symbol = "PAY";uint public decimals = 18;...
И заменяем название контракта токена на свое, пусть наш токен будет называться ICE. Тогда название контракта будет IceToken. Вместо «TenX Pay Token» пишем свое описание токена. Я написал просто «Ice Token». Как правило в описании указывают название своего ICO плюс название токена. Вместо «PAY» — пишем символ токена. Под этим эменем он потом будет у вас торговаться на бирже. Итак у меня получилось что-то такое:
1234567...contract IceToken is MintableToken {string public name = "Ice Token";string public symbol = "ICE";uint public decimals = 18;... - В 455 строчке заменяем PayToken на название контракта вашего токена. В моем случае вместо
1PayToken public token = new PayToken();
получаем
1ICEToken public token = new ICEToken(); - Смотрим строчки с 459 по 471 там будет код:
12345678910111213uint hardcap = 2000 ether;ExchangeRate public exchangeRate;uint public altDeposits = 0;uint public start = 1498302000; //new Date("Jun 24 2017 11:00:00 GMT").getTime() / 1000/*** @dev modifier to allow token creation only when the sale IS ON*/modifier saleIsOn() {require(now > start && now < start + 365 days);_;}
Тут нам нужно поменять дату начало ICO. Это поле start. Также поменяйте hardcap — это количество эфира, которое вы планируете собрать в течение ICO. 365 — количество дней в течение которого будет проходить ICO. Поля hardcap и start можно поменять и после того как зальете контракт в блокчейн, но мы это рассматривать не будем. - Заливаем код в блокчейн.
- Для этого открываем Ethereum Wallet и выбираем «contracts».
- Затем выбираем «Deploy new contract».
- В окно «SOLIDITY CONTRACT SOURCE CODE» вставляем наш код
- Ждем пока скомпилируется. И справа от окна будет будет выбор «select contract to deploy». В котором выбираем MainSale.
- И нажимаем кнопку «deploy» внизу, далее соглашаемся на все.
- Теперь нам надо залить в блокчейн контракт ExchangeRate. Поступаем также как и в пункте 5, только в самом последнем выбираем контракт ExchangeRate.
- Мы залили контракт ExchangeRate. Он у нас отвечает за курс ваших токенов. Т.е. в нем хранится информация о том сколько потребуется эфира для покупки вашего токена. Этот курс нам надо установить. Для этого необходимо вызвать функцию updateRate и указать для ETH курс. Так коэффициент 100000000000000000000 дает за один эфир 100 токенов. Для установки цены нужно вызвать updateRate с первым параметром ETH, а вторым — цена токена в wei, в нашем примере 100000000000000000000.
- Теперь нам необходимо указать адрес контракта ExchangeRate для MainSale. Для этого вызовем функцию setExchangeRate у MainSale и укажем адрес ExchangeRate.
- У контракта MainSale нужно указать адрес куда будет заливаться эфир, за который пользователи покупают ваши токены. Для этого вызовите у контракта MainSale функцию setMultisigVault с указанием вашего адреса.
После всех этих манипуляций ваш ICO должен работать. В случае если нужно поправить количество выпускаемых токенов — вызовите функцию setHardcap у MainSale. Если нужно поправить дату начала ICO — вызовите функцию setStart у MainSale.
Теперь если пользователь перешлет эфир на адрес контракта MainSale, то контракт MainSale рассчитает сколько токенов необходимо начислить в соответствии с указным в ExchangeRate коэффициентом. А полученый эфир перешлет на указанный в multisigVault адрес.
В соответствии с этим алгоритмом сделал свой пример ICO. Работоспособность такого контракта можно лицезреть на практике. Если отправить на адрес (проверяя свой ICO контракт — адрес замените на свой!): 0x2aa307aaf1617c301904e28bdcc672f909344609 эфир то вам зачислятся токены ICE (при этом Gas Limit нужно указать не менее 87518).
Вот пример транзакции — было отправлено 0.01 эфир на контракт MainSale:
Контракт MainSale в свою очередь начислил токены ICE в количестве 100 штук на адрес 0xea15adb66dc92a4bbccc8bf32fd25e2e86a2a770.
Убедится что токены начислены можно тут . Просто укажите в поле balanceOf адрес 0xea15adb66dc92a4bbccc8bf32fd25e2e86a2a770 и нажмите query. И контракт выведет количество начисленных токенов ICE.
Также работоспособность контракта можно проверить и в MyEtherWallet. Для этого достаточно добавить информацию о токене:
Если статья была полезна, то в качестве благодарности можно начислить немного эфира на 0x2aa307aaf1617c301904e28bdcc672f909344609 (при этом Gas Limit нужно указать не менее 87518), а взамен получите немного токенов ICE)
Все интересно. пытался скомпилировать в кошельке 0.9.2
Выдает ошибки тоже в Mist
тоже самое пробовал на версии 0.9.3 ошибка! Хелп.
Нужен программист смарт -контрактов в крупный проект
Ругается уже на номер версии.