今天我將向你展現如何在以太坊區塊鏈上開發你本身的加密貨幣並將其出售!我將向你展現如何使用以太坊智能合約逐步建立本身的ERC-20代幣和衆籌銷售,如何測試智能合約,如何將智能合約部署到以太坊區塊鏈,以及如何構建ICO網站部署到網絡上。我還將解釋ERC-20代幣是什麼,以太坊代幣如何工做,初始代幣產品(ICO)如何工做。php
以太坊區塊鏈容許你建立本身的加密貨幣或代幣,能夠經過以太幣(以太坊區塊鏈的本地加密貨幣)購買。ERC-20只是一個標準,它指定了這些代幣的行爲方式,所以它們與加密貨幣交換等其餘平臺兼容。java
那怎麼作呢?讓咱們先來看看以太坊區塊鏈的工做原理。node
以太坊是像比特幣同樣的區塊鏈。與比特幣同樣,以太坊也會跟蹤擁有Ether的用戶餘額,以太坊的原生加密貨幣。與比特幣不一樣,以太坊也是一個平臺,容許你建立本身的代幣而無需建立新的區塊鏈。python
你可使用智能合約建立以太坊代幣。ERC-20是一個標準,用於指定此代幣智能合約應如何工做。android
讓咱們用一個例子來理解ERC-20代幣智能合約的工做原理。假設咱們想要建立一個名爲「My Token」的代幣,其符號爲「MTK」,而且存在100,000,000個這樣的代幣。git
首先,代幣智能合約跟蹤一些基本代幣屬性。例如,它記錄名稱「My Token」,你在加密貨幣交換中看到的符號,以及存在多少總代幣。程序員
它還跟蹤誰擁有「My Token」和多少。github
ERC-20代幣能夠做爲付款從一個賬戶轉移到另外一個賬戶,就像任何其餘加密貨幣同樣。web
它們也能夠在衆籌銷售中購買,如ICO,咱們將在下一節中進行討論。mongodb
它們也能夠在加密貨幣交易所買賣。
ERC-20代幣能夠以多種方式分發。一種流行的方法是舉行目標人羣促銷或初始代幣發行(ICO)。衆籌銷售是公司經過建立本身的ERC-20代幣來爲其業務籌集資金的一種方式,該代幣能夠由以太幣的投資者購買。
每當發生衆籌銷售時,公司就會以投資者支付的以太幣形式得到流動資金,並持有在衆籌銷售中出售的預留金額的ERC-20代幣。
爲了參與衆籌銷售,投資者必須使用賬戶鏈接到Etherum區塊鏈。此賬戶有一個能夠存儲以太幣的錢包地址,以及在衆籌銷售中購買的ERC-20代幣。
投資者必須訪問與智能合約談話的衆籌銷售網站。智能合約管理衆籌銷售如何運做的全部規則。
每當投資者在衆籌銷售網站上購買代幣時,他們就會將以太幣從他們的錢包發送到智能合約,而智能合約會當即將購買的代幣分發到他們的錢包中。
智能合約在衆籌銷售中設定代幣的價格並控制衆籌銷售的行爲方式。
衆籌銷售能夠採起各類形狀和大小。它們能夠具備多個層級或階段,例如Pre ICO,ICO和ICO Bonus階段。這些層中的每一層均可以在不一樣的時間點發生而且能夠表現不一樣。
他們還可使用白名單來限制哪些投資者能夠購買代幣。
他們還能夠擁有預約數量的代幣,這些代幣不會在衆籌銷售中出售。這些儲備一般留給每一個公司的特定成員,如創始人和顧問。這些儲備能夠是固定數量的代幣或百分比。
每當衆籌銷售結束時,它能夠由管理員最終肯定。每當發生這種狀況時,全部預留的代幣都將分發到相應的賬戶,衆籌銷售將正式結束。
正如我以前解釋的那樣,ERC-20代幣是使用以太坊智能合約建立的。什麼是智能合約?
以太坊容許開發人員使用智能合約編寫在區塊鏈上運行的應用程序,這些應用程序封裝了這些應用程序的全部業務邏輯。它們使咱們可以讀取和寫入區塊鏈的數據,以及執行代碼。智能合約使用名爲Solidity的編程語言編寫,看起來很像Javascript。它是一種完整的編程語言,它容許咱們執行Javascript所能提供的許多相同類型的事情,但因爲它的用例,它的行爲有點不一樣,咱們將在本教程中看到。
對於ERC-20代幣,智能合約管理有關代幣如何工做的全部行爲,並跟蹤代幣全部權和賬戶餘額。
ERC-20是關於如何構建以太坊代幣的API規範。它是一種社區採用的標準,容許在各類用例中支持代幣。咱們但願構建一個符合此標準的代幣,以便普遍接受。若是咱們沒有這樣的標準,咱們能夠有無盡的方式來建立代幣,它們可能彼此不兼容!
使用ERC-20標準可確保代幣符合如下用例(以及更多):
ERC-20規範基本上規定了智能合約必須響應的接口。它規定了智能合約的結構和智能合約必須具有的功能類型。它還提供了一些很好的建議功能,但最終是可選的。它規定了咱們的代幣必須具備的某些事件,例如transfer
事件。請注意,智能合約能夠發出消費者能夠訂閱的事件,而且使用此標準,咱們能夠訂閱告訴咱們什麼時候銷售代幣的事件。
如下是ERC-20標準指定的transfer
函數的示例實現。它是智能合約所要求的,而且管理某人如何將錢包中的ERC-20代幣發送到另外一個錢包。
contract ERC20Token { // ... function transfer(address _to, uint256 _value) public returns (bool success) { require(balanceOf[msg.sender] >= _value); balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; Transfer(msg.sender, _to, _value); return true; } // ... }
該函數經過如下方式實現ERC-20標準:
sell
事件。true
。若是全部這些尚未徹底有意義,請不要擔憂。你能夠直接在以太坊改進提案github存儲庫中閱讀有關ERC-20令牌標準的更多信息。這是圍繞以太坊標準進行的全部社區討論的地方。我強烈建議將該存儲庫加入書籤並閱讀提交內容,由於這是你能夠觀看以太坊技術實時增加和變化的地方!
我也推薦這篇維基百科文章。
咱們將創建一個ICO網站,與區塊鏈上的衆籌銷售智能合約進行對話。這個客戶端網站將有一個表格,用戶能夠在衆籌銷售中購買令牌。它將顯示衆籌銷售的進度,例如用戶購買了多少令牌,全部用戶購買了多少令牌,以及衆籌銷售中可用的令牌總數。它還會在「你的賬戶」下顯示咱們與區塊鏈相關聯的賬戶。
爲了構建咱們的ERC-20令牌和銷售,咱們首先須要一些依賴。
咱們須要的第一個依賴是節點包管理器,或NPM,它與Node.js一塊兒提供。你能夠經過你的終端並輸入如下內容來查看你是否已安裝節點:
$ node -v
下一個依賴是Truffle Framework,它容許咱們在以太坊區塊鏈上構建去中心化的應用程序。它提供了一套工具,容許咱們使用Solidity編程語言編寫智能合約。它還使咱們可以測試咱們的智能合約並將其部署到區塊鏈。它還爲咱們提供了開發客戶端應用程序的地方。
你能夠在命令行中使用NPM安裝Truffle,以下所示:
$ npm install -g truffle
下一個依賴項是Ganache,一個本地內存區塊鏈。你能夠經過從Truffle Framework網站下載來安裝Ganache。它將爲咱們提供10個外部賬戶,其中包含咱們當地以太坊區塊鏈的地址。每一個賬戶預裝100個測試Ether。
下一個依賴項是Google Chrome的Metamask擴展。爲了使用區塊鏈,咱們必須鏈接它(記住,我說塊鏈是一個網絡)。咱們必須安裝一個特殊的瀏覽器擴展才能使用以太坊區塊鏈。這就是Metamask的用武之地。咱們將可以經過咱們的我的帳戶鏈接到咱們當地的以太坊區塊鏈,並與咱們的智能合約進行交互。
咱們將在本教程中使用Metamask chrome擴展,所以若是你尚未安裝google chrome瀏覽器,則還須要安裝它。要安裝Metamask,請在Google Chrome網上應用店中搜索Metamask Chrome插件。安裝完成後,請確保在擴展列表中選中它。安裝後,你會在Chrome瀏覽器的右上角看到狐狸圖標。
依賴項是可選的,但建議使用。我建議爲Solidity編程語言安裝語法高亮顯示。大多數文本編輯器和IDE都沒有開箱即用的Solidity語法高亮顯示,所以你必須安裝一個軟件包才能支持此功能。我正在使用Sublime Text,我已經下載了「Ethereum」軟件包,它爲Solidity提供了很好的語法高亮顯示。
如今咱們已經安裝了依賴項,讓咱們開始構建咱們的ERC-20令牌!這是完整的ERC-20令牌智能合約Solidity代碼:
pragma solidity ^0.4.2; contract DappToken { string public name = "DApp Token"; string public symbol = "DAPP"; string public standard = "DApp Token v1.0"; uint256 public totalSupply; event Transfer( address indexed _from, address indexed _to, uint256 _value ); event Approval( address indexed _owner, address indexed _spender, uint256 _value ); mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; function DappToken (uint256 _initialSupply) public { balanceOf[msg.sender] = _initialSupply; totalSupply = _initialSupply; } function transfer(address _to, uint256 _value) public returns (bool success) { require(balanceOf[msg.sender] >= _value); balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; Transfer(msg.sender, _to, _value); return true; } function approve(address _spender, uint256 _value) public returns (bool success) { allowance[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { require(_value <= balanceOf[_from]); require(_value <= allowance[_from][msg.sender]); balanceOf[_from] -= _value; balanceOf[_to] += _value; allowance[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } }
讓咱們來看看這個智能合約的功能,以及它如何實現ERC-20標準:
string public name =「DApp Token」。
string public symbol =「DAPP」
。 uint256 public totalSupply
公共代幣總供應量。mapping(address => uint256) public balanceOf
的每一個賬戶的餘額。transfer
函數,容許用戶將代幣發送到另外一個賬戶。approve
函數,例如加密貨幣交換。這會更新allowance
映射,以查看賬戶能夠支出的金額。transferFrom
,容許其餘賬戶轉移令牌。你還能夠閱讀此智能合約的測試,以瞭解有關其工做原理的更多信息。這些測試確保這種智能合約的行爲符合咱們的預期。這是一個完整的測試套件,能夠檢查智能合約的全部行爲:
var DappToken = artifacts.require("./DappToken.sol"); contract('DappToken', function(accounts) { var tokenInstance; it('initializes the contract with the correct values', function() { return DappToken.deployed().then(function(instance) { tokenInstance = instance; return tokenInstance.name(); }).then(function(name) { assert.equal(name, 'DApp Token', 'has the correct name'); return tokenInstance.symbol(); }).then(function(symbol) { assert.equal(symbol, 'DAPP', 'has the correct symbol'); return tokenInstance.standard(); }).then(function(standard) { assert.equal(standard, 'DApp Token v1.0', 'has the correct standard'); }); }) it('allocates the initial supply upon deployment', function() { return DappToken.deployed().then(function(instance) { tokenInstance = instance; return tokenInstance.totalSupply(); }).then(function(totalSupply) { assert.equal(totalSupply.toNumber(), 1000000, 'sets the total supply to 1,000,000'); return tokenInstance.balanceOf(accounts[0]); }).then(function(adminBalance) { assert.equal(adminBalance.toNumber(), 1000000, 'it allocates the initial supply to the admin account'); }); }); it('transfers token ownership', function() { return DappToken.deployed().then(function(instance) { tokenInstance = instance; // Test `require` statement first by transferring something larger than the sender's balance return tokenInstance.transfer.call(accounts[1], 99999999999999999999999); }).then(assert.fail).catch(function(error) { assert(error.message.indexOf('revert') >= 0, 'error message must contain revert'); return tokenInstance.transfer.call(accounts[1], 250000, { from: accounts[0] }); }).then(function(success) { assert.equal(success, true, 'it returns true'); return tokenInstance.transfer(accounts[1], 250000, { from: accounts[0] }); }).then(function(receipt) { assert.equal(receipt.logs.length, 1, 'triggers one event'); assert.equal(receipt.logs[0].event, 'Transfer', 'should be the "Transfer" event'); assert.equal(receipt.logs[0].args._from, accounts[0], 'logs the account the tokens are transferred from'); assert.equal(receipt.logs[0].args._to, accounts[1], 'logs the account the tokens are transferred to'); assert.equal(receipt.logs[0].args._value, 250000, 'logs the transfer amount'); return tokenInstance.balanceOf(accounts[1]); }).then(function(balance) { assert.equal(balance.toNumber(), 250000, 'adds the amount to the receiving account'); return tokenInstance.balanceOf(accounts[0]); }).then(function(balance) { assert.equal(balance.toNumber(), 750000, 'deducts the amount from the sending account'); }); }); it('approves tokens for delegated transfer', function() { return DappToken.deployed().then(function(instance) { tokenInstance = instance; return tokenInstance.approve.call(accounts[1], 100); }).then(function(success) { assert.equal(success, true, 'it returns true'); return tokenInstance.approve(accounts[1], 100, { from: accounts[0] }); }).then(function(receipt) { assert.equal(receipt.logs.length, 1, 'triggers one event'); assert.equal(receipt.logs[0].event, 'Approval', 'should be the "Approval" event'); assert.equal(receipt.logs[0].args._owner, accounts[0], 'logs the account the tokens are authorized by'); assert.equal(receipt.logs[0].args._spender, accounts[1], 'logs the account the tokens are authorized to'); assert.equal(receipt.logs[0].args._value, 100, 'logs the transfer amount'); return tokenInstance.allowance(accounts[0], accounts[1]); }).then(function(allowance) { assert.equal(allowance.toNumber(), 100, 'stores the allowance for delegated trasnfer'); }); }); it('handles delegated token transfers', function() { return DappToken.deployed().then(function(instance) { tokenInstance = instance; fromAccount = accounts[2]; toAccount = accounts[3]; spendingAccount = accounts[4]; // Transfer some tokens to fromAccount return tokenInstance.transfer(fromAccount, 100, { from: accounts[0] }); }).then(function(receipt) { // Approve spendingAccount to spend 10 tokens form fromAccount return tokenInstance.approve(spendingAccount, 10, { from: fromAccount }); }).then(function(receipt) { // Try transferring something larger than the sender's balance return tokenInstance.transferFrom(fromAccount, toAccount, 9999, { from: spendingAccount }); }).then(assert.fail).catch(function(error) { assert(error.message.indexOf('revert') >= 0, 'cannot transfer value larger than balance'); // Try transferring something larger than the approved amount return tokenInstance.transferFrom(fromAccount, toAccount, 20, { from: spendingAccount }); }).then(assert.fail).catch(function(error) { assert(error.message.indexOf('revert') >= 0, 'cannot transfer value larger than approved amount'); return tokenInstance.transferFrom.call(fromAccount, toAccount, 10, { from: spendingAccount }); }).then(function(success) { assert.equal(success, true); return tokenInstance.transferFrom(fromAccount, toAccount, 10, { from: spendingAccount }); }).then(function(receipt) { assert.equal(receipt.logs.length, 1, 'triggers one event'); assert.equal(receipt.logs[0].event, 'Transfer', 'should be the "Transfer" event'); assert.equal(receipt.logs[0].args._from, fromAccount, 'logs the account the tokens are transferred from'); assert.equal(receipt.logs[0].args._to, toAccount, 'logs the account the tokens are transferred to'); assert.equal(receipt.logs[0].args._value, 10, 'logs the transfer amount'); return tokenInstance.balanceOf(fromAccount); }).then(function(balance) { assert.equal(balance.toNumber(), 90, 'deducts the amount from the sending account'); return tokenInstance.balanceOf(toAccount); }).then(function(balance) { assert.equal(balance.toNumber(), 10, 'adds the amount from the receiving account'); return tokenInstance.allowance(fromAccount, spendingAccount); }).then(function(allowance) { assert.equal(allowance.toNumber(), 0, 'deducts the amount from the allowance'); }); }); });
你可使用truffle從命令行運行測試,以下所示:
$ truffle test
如今咱們能夠創建一個衆籌銷售智能合約,容許投資者在最初的代幣產品(ICO)中購買代幣。這是完整的衆籌銷售智能合約Solidity代碼:
pragma solidity ^0.4.2; import "./DappToken.sol"; contract DappTokenSale { address admin; DappToken public tokenContract; uint256 public tokenPrice; uint256 public tokensSold; event Sell(address _buyer, uint256 _amount); function DappTokenSale(DappToken _tokenContract, uint256 _tokenPrice) public { admin = msg.sender; tokenContract = _tokenContract; tokenPrice = _tokenPrice; } function multiply(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x); } function buyTokens(uint256 _numberOfTokens) public payable { require(msg.value == multiply(_numberOfTokens, tokenPrice)); require(tokenContract.balanceOf(this) >= _numberOfTokens); require(tokenContract.transfer(msg.sender, _numberOfTokens)); tokensSold += _numberOfTokens; Sell(msg.sender, _numberOfTokens); } function endSale() public { require(msg.sender == admin); require(tokenContract.transfer(admin, tokenContract.balanceOf(this))); // Just transfer the balance to the admin admin.transfer(address(this).balance); } }
讓咱們來看看這個智能合約的功能,以及它如何進行衆籌銷售:
address admin
的地址管理員賬戶。DappToken public tokenContract
。uint256 public tokenPrice
。uint256 public tokensSold
。sell
事件,以便消費者能夠在出售代幣時收到通知。buyTokens
函數,容許用戶在衆籌銷售中購買代幣。endSale
函數,容許管理員結束衆籌銷售並收集銷售期間籌集的以太幣。var DappToken = artifacts.require('./DappToken.sol'); var DappTokenSale = artifacts.require('./DappTokenSale.sol'); contract('DappTokenSale', function(accounts) { var tokenInstance; var tokenSaleInstance; var admin = accounts[0]; var buyer = accounts[1]; var tokenPrice = 1000000000000000; // in wei var tokensAvailable = 750000; var numberOfTokens; it('initializes the contract with the correct values', function() { return DappTokenSale.deployed().then(function(instance) { tokenSaleInstance = instance; return tokenSaleInstance.address }).then(function(address) { assert.notEqual(address, 0x0, 'has contract address'); return tokenSaleInstance.tokenContract(); }).then(function(address) { assert.notEqual(address, 0x0, 'has token contract address'); return tokenSaleInstance.tokenPrice(); }).then(function(price) { assert.equal(price, tokenPrice, 'token price is correct'); }); }); it('facilitates token buying', function() { return DappToken.deployed().then(function(instance) { // Grab token instance first tokenInstance = instance; return DappTokenSale.deployed(); }).then(function(instance) { // Then grab token sale instance tokenSaleInstance = instance; // Provision 75% of all tokens to the token sale return tokenInstance.transfer(tokenSaleInstance.address, tokensAvailable, { from: admin }) }).then(function(receipt) { numberOfTokens = 10; return tokenSaleInstance.buyTokens(numberOfTokens, { from: buyer, value: numberOfTokens * tokenPrice }) }).then(function(receipt) { assert.equal(receipt.logs.length, 1, 'triggers one event'); assert.equal(receipt.logs[0].event, 'Sell', 'should be the "Sell" event'); assert.equal(receipt.logs[0].args._buyer, buyer, 'logs the account that purchased the tokens'); assert.equal(receipt.logs[0].args._amount, numberOfTokens, 'logs the number of tokens purchased'); return tokenSaleInstance.tokensSold(); }).then(function(amount) { assert.equal(amount.toNumber(), numberOfTokens, 'increments the number of tokens sold'); return tokenInstance.balanceOf(buyer); }).then(function(balance) { assert.equal(balance.toNumber(), numberOfTokens); return tokenInstance.balanceOf(tokenSaleInstance.address); }).then(function(balance) { assert.equal(balance.toNumber(), tokensAvailable - numberOfTokens); // Try to buy tokens different from the ether value return tokenSaleInstance.buyTokens(numberOfTokens, { from: buyer, value: 1 }); }).then(assert.fail).catch(function(error) { assert(error.message.indexOf('revert') >= 0, 'msg.value must equal number of tokens in wei'); return tokenSaleInstance.buyTokens(800000, { from: buyer, value: numberOfTokens * tokenPrice }) }).then(assert.fail).catch(function(error) { assert(error.message.indexOf('revert') >= 0, 'cannot purchase more tokens than available'); }); }); it('ends token sale', function() { return DappToken.deployed().then(function(instance) { // Grab token instance first tokenInstance = instance; return DappTokenSale.deployed(); }).then(function(instance) { // Then grab token sale instance tokenSaleInstance = instance; // Try to end sale from account other than the admin return tokenSaleInstance.endSale({ from: buyer }); }).then(assert.fail).catch(function(error) { assert(error.message.indexOf('revert' >= 0, 'must be admin to end sale')); // End sale as admin return tokenSaleInstance.endSale({ from: admin }); }).then(function(receipt) { return tokenInstance.balanceOf(admin); }).then(function(balance) { assert.equal(balance.toNumber(), 999990, 'returns all unsold dapp tokens to admin'); // Check that the contract has no balance balance = web3.eth.getBalance(tokenSaleInstance.address) assert.equal(balance.toNumber(), 0); }); }); });
恭喜!你已成功學習瞭如何在以太坊上創建了ERC-20代幣和衆籌銷售智能合約!
======================================================================
分享一些以太坊、EOS、比特幣等區塊鏈相關的交互式在線編程實戰教程:
- java以太坊開發教程,主要是針對java和android程序員進行區塊鏈以太坊開發的web3j詳解。
- python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
- php以太坊,主要是介紹使用php進行智能合約開發交互,進行帳號建立、交易、轉帳、代幣開發以及過濾器和交易等內容。
- 以太坊入門教程,主要介紹智能合約與dapp應用開發,適合入門。
- 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
- C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括帳戶管理、狀態與交易、智能合約開發與交互、過濾器和交易等。
- EOS教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、帳戶與錢包、發行代幣、智能合約開發與部署、使用代碼與智能合約交互等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
- java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Java代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
- php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Php代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
- tendermint區塊鏈開發詳解,本課程適合但願使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI接口、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操代碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。
匯智網原創翻譯,轉載請標明出處。這裏是原文在以太坊開發本身的加密貨幣