今天我將向你展示如何在以太坊區(qū)塊鏈上開發(fā)你自己的加密貨幣并將其出售!我將向你展示如何使用以太坊智能合約逐步創(chuàng)建自己的ERC-20代幣和眾籌銷售,如何測(cè)試智能合約,如何將智能合約部署到以太坊區(qū)塊鏈,以及如何構(gòu)建ICO網(wǎng)站部署到網(wǎng)絡(luò)上。我還將解釋ERC-20代幣是什么,以太坊代幣如何工作,初始代幣產(chǎn)品(ICO)如何工作。
什么是ERC-20代幣?
以太坊區(qū)塊鏈允許你創(chuàng)建自己的加密貨幣或代幣,可以通過以太幣(以太坊區(qū)塊鏈的本地加密貨幣)購買。ERC-20只是一個(gè)標(biāo)準(zhǔn),它指定了這些代幣的行為方式,因此它們與加密貨幣交換等其他平臺(tái)兼容。
那怎么做呢?讓我們先來看看以太坊區(qū)塊鏈的工作原理。
以太坊是像比特幣一樣的區(qū)塊鏈。與比特幣一樣,以太坊也會(huì)跟蹤擁有Ether的用戶余額,以太坊的原生加密貨幣。與比特幣不同,以太坊也是一個(gè)平臺(tái),允許你創(chuàng)建自己的代幣而無需創(chuàng)建新的區(qū)塊鏈。
你可以使用智能合約創(chuàng)建以太坊代幣。ERC-20是一個(gè)標(biāo)準(zhǔn),用于指定此代幣智能合約應(yīng)如何工作。
讓我們用一個(gè)例子來理解ERC-20代幣智能合約的工作原理。假設(shè)我們想要?jiǎng)?chuàng)建一個(gè)名為“My Token”的代幣,其符號(hào)為“MTK”,并且存在100,000,000個(gè)這樣的代幣。
首先,代幣智能合約跟蹤一些基本代幣屬性。例如,它記錄名稱“My Token”,你在加密貨幣交換中看到的符號(hào),以及存在多少總代幣。
它還跟蹤誰擁有“My Token”和多少。
ERC-20代幣可以作為付款從一個(gè)帳戶轉(zhuǎn)移到另一個(gè)帳戶,就像任何其他加密貨幣一樣。
它們也可以在眾籌銷售中購買,如ICO,我們將在下一節(jié)中進(jìn)行討論。
它們也可以在加密貨幣交易所買賣。
ICO如何運(yùn)作
ERC-20代幣可以以多種方式分發(fā)。一種流行的方法是舉行目標(biāo)人群促銷或初始代幣發(fā)行(ICO)。眾籌銷售是公司通過創(chuàng)建自己的ERC-20代幣來為其業(yè)務(wù)籌集資金的一種方式,該代幣可以由以太幣的投資者購買。
每當(dāng)發(fā)生眾籌銷售時(shí),公司就會(huì)以投資者支付的以太幣形式獲得流動(dòng)資金,并持有在眾籌銷售中出售的預(yù)留金額的ERC-20代幣。
為了參與眾籌銷售,投資者必須使用帳戶連接到Etherum區(qū)塊鏈。此帳戶有一個(gè)可以存儲(chǔ)以太幣的錢包地址,以及在眾籌銷售中購買的ERC-20代幣。
投資者必須訪問與智能合約談話的眾籌銷售網(wǎng)站。智能合約管理眾籌銷售如何運(yùn)作的所有規(guī)則。
每當(dāng)投資者在眾籌銷售網(wǎng)站上購買代幣時(shí),他們就會(huì)將以太幣從他們的錢包發(fā)送到智能合約,而智能合約會(huì)立即將購買的代幣分發(fā)到他們的錢包中。
智能合約在眾籌銷售中設(shè)定代幣的價(jià)格并控制眾籌銷售的行為方式。
眾籌銷售可以采取各種形狀和大小。它們可以具有多個(gè)層級(jí)或階段,例如Pre ICO,ICO和ICO Bonus階段。這些層中的每一層都可以在不同的時(shí)間點(diǎn)發(fā)生并且可以表現(xiàn)不同。
他們還可以使用白名單來限制哪些投資者可以購買代幣。
他們還可以擁有預(yù)定數(shù)量的代幣,這些代幣不會(huì)在眾籌銷售中出售。這些儲(chǔ)備通常留給每個(gè)公司的特定成員,如創(chuàng)始人和顧問。這些儲(chǔ)備可以是固定數(shù)量的代幣或百分比。
每當(dāng)眾籌銷售結(jié)束時(shí),它可以由管理員最終確定。每當(dāng)發(fā)生這種情況時(shí),所有預(yù)留的代幣都將分發(fā)到相應(yīng)的帳戶,眾籌銷售將正式結(jié)束。
ERC-20代幣的工作原理
正如我之前解釋的那樣,ERC-20代幣是使用以太坊智能合約創(chuàng)建的。什么是智能合約?
以太坊允許開發(fā)人員使用智能合約編寫在區(qū)塊鏈上運(yùn)行的應(yīng)用程序,這些應(yīng)用程序封裝了這些應(yīng)用程序的所有業(yè)務(wù)邏輯。它們使我們能夠讀取和寫入?yún)^(qū)塊鏈的數(shù)據(jù),以及執(zhí)行代碼。智能合約使用名為Solidity的編程語言編寫,看起來很像Javascript。它是一種完整的編程語言,它允許我們執(zhí)行Javascript所能提供的許多相同類型的事情,但由于它的用例,它的行為有點(diǎn)不同,我們將在本教程中看到。
對(duì)于ERC-20代幣,智能合約管理有關(guān)代幣如何工作的所有行為,并跟蹤代幣所有權(quán)和帳戶余額。
ERC-20是關(guān)于如何構(gòu)建以太坊代幣的API規(guī)范。它是一種社區(qū)采用的標(biāo)準(zhǔn),允許在各種用例中支持代幣。我們希望構(gòu)建一個(gè)符合此標(biāo)準(zhǔn)的代幣,以便廣泛接受。如果我們沒有這樣的標(biāo)準(zhǔn),我們可以有無盡的方式來創(chuàng)建代幣,它們可能彼此不兼容!
使用ERC-20標(biāo)準(zhǔn)可確保代幣符合以下用例(以及更多):
- 電子錢包轉(zhuǎn)帳 - 將代幣從一個(gè)帳戶發(fā)送到另一個(gè)帳戶
- 在加密貨幣交易所買賣
- 在眾籌銷售(ICO)中購買代幣,就像我們將在本教程中演示一樣
ERC-20規(guī)范基本上規(guī)定了智能合約必須響應(yīng)的接口。它規(guī)定了智能合約的結(jié)構(gòu)和智能合約必須具備的功能類型。它還提供了一些很好的建議功能,但最終是可選的。它規(guī)定了我們的代幣必須具有的某些事件,例如transfer
事件。請(qǐng)注意,智能合約可以發(fā)出消費(fèi)者可以訂閱的事件,并且使用此標(biāo)準(zhǔn),我們可以訂閱告訴我們何時(shí)銷售代幣的事件。
以下是ERC-20標(biāo)準(zhǔn)指定的transfer
函數(shù)的示例實(shí)現(xiàn)。它是智能合約所要求的,并且管理某人如何將錢包中的ERC-20代幣發(fā)送到另一個(gè)錢包。
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;
}
// ...
}
該函數(shù)通過以下方式實(shí)現(xiàn)ERC-20標(biāo)準(zhǔn):
- 該功能存在。
- 它接受正確的參數(shù)。
- 如果用戶沒有足夠的代幣進(jìn)行支付,即余額不足,則失敗。
- 它將余額從發(fā)件人的帳戶轉(zhuǎn)移到收款人的帳戶。
- 它會(huì)觸發(fā)
sell
事件。 - 它返回正確的值,例如
true
。
如果所有這些還沒有完全有意義,請(qǐng)不要擔(dān)心。你可以直接在以太坊改進(jìn)提案github存儲(chǔ)庫中閱讀有關(guān)ERC-20令牌標(biāo)準(zhǔn)的更多信息。這是圍繞以太坊標(biāo)準(zhǔn)進(jìn)行的所有社區(qū)討論的地方。我強(qiáng)烈建議將該存儲(chǔ)庫加入書簽并閱讀提交內(nèi)容,因?yàn)檫@是你可以觀看以太坊技術(shù)實(shí)時(shí)增長(zhǎng)和變化的地方!
我也推薦這篇維基百科文章。
我們要建立的網(wǎng)站
我們將建立一個(gè)ICO網(wǎng)站,與區(qū)塊鏈上的眾籌銷售智能合約進(jìn)行對(duì)話。這個(gè)客戶端網(wǎng)站將有一個(gè)表格,用戶可以在眾籌銷售中購買令牌。它將顯示眾籌銷售的進(jìn)度,例如用戶購買了多少令牌,所有用戶購買了多少令牌,以及眾籌銷售中可用的令牌總數(shù)。它還會(huì)在“你的帳戶”下顯示我們與區(qū)塊鏈相關(guān)聯(lián)的帳戶。
安裝依賴項(xiàng)
為了構(gòu)建我們的ERC-20令牌和銷售,我們首先需要一些依賴。
節(jié)點(diǎn)包管理器(NPM)
我們需要的第一個(gè)依賴是節(jié)點(diǎn)包管理器,或NPM,它與Node.js一起提供。你可以通過你的終端并輸入以下內(nèi)容來查看你是否已安裝節(jié)點(diǎn):
$ node -v
Truffle框架
下一個(gè)依賴是Truffle Framework,它允許我們?cè)谝蕴粎^(qū)塊鏈上構(gòu)建去中心化的應(yīng)用程序。它提供了一套工具,允許我們使用Solidity編程語言編寫智能合約。它還使我們能夠測(cè)試我們的智能合約并將其部署到區(qū)塊鏈。它還為我們提供了開發(fā)客戶端應(yīng)用程序的地方。
你可以在命令行中使用NPM安裝Truffle,如下所示:
$ npm install -g truffle
Ganache
下一個(gè)依賴項(xiàng)是Ganache,一個(gè)本地內(nèi)存區(qū)塊鏈。你可以通過從Truffle Framework網(wǎng)站下載來安裝Ganache。它將為我們提供10個(gè)外部帳戶,其中包含我們當(dāng)?shù)匾蕴粎^(qū)塊鏈的地址。每個(gè)帳戶預(yù)裝100個(gè)測(cè)試Ether。
Metamask
下一個(gè)依賴項(xiàng)是Google Chrome的Metamask擴(kuò)展。為了使用區(qū)塊鏈,我們必須連接它(記住,我說塊鏈?zhǔn)且粋€(gè)網(wǎng)絡(luò))。我們必須安裝一個(gè)特殊的瀏覽器擴(kuò)展才能使用以太坊區(qū)塊鏈。這就是Metamask的用武之地。我們將能夠通過我們的個(gè)人賬戶連接到我們當(dāng)?shù)氐囊蕴粎^(qū)塊鏈,并與我們的智能合約進(jìn)行交互。
我們將在本教程中使用Metamask chrome擴(kuò)展,因此如果你還沒有安裝google chrome瀏覽器,則還需要安裝它。要安裝Metamask,請(qǐng)?jiān)贕oogle Chrome網(wǎng)上應(yīng)用店中搜索Metamask Chrome插件。安裝完成后,請(qǐng)確保在擴(kuò)展列表中選中它。安裝后,你會(huì)在Chrome瀏覽器的右上角看到狐貍圖標(biāo)。
語法高亮顯示
依賴項(xiàng)是可選的,但建議使用。我建議為Solidity編程語言安裝語法高亮顯示。大多數(shù)文本編輯器和IDE都沒有開箱即用的Solidity語法高亮顯示,因此你必須安裝一個(gè)軟件包才能支持此功能。我正在使用Sublime Text,我已經(jīng)下載了“Ethereum”軟件包,它為Solidity提供了很好的語法高亮顯示。
ERC-20令牌智能合約
現(xiàn)在我們已經(jīng)安裝了依賴項(xiàng),讓我們開始構(gòu)建我們的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;
}
}
讓我們來看看這個(gè)智能合約的功能,以及它如何實(shí)現(xiàn)ERC-20標(biāo)準(zhǔn):
- 它存儲(chǔ)代幣名稱
string public name =“DApp Token”。
- 它存儲(chǔ)用于加密貨幣交換的代幣符號(hào)
string public symbol =“DAPP”
。 - 它存儲(chǔ)了
uint256 public totalSupply
公共代幣總供應(yīng)量。 - 它使用Solidity映射來存儲(chǔ)擁有代幣映射
mapping(address => uint256) public balanceOf
的每個(gè)帳戶的余額。 - 它實(shí)現(xiàn)了一個(gè)
transfer
函數(shù),允許用戶將代幣發(fā)送到另一個(gè)帳戶。 - 它實(shí)現(xiàn)了一個(gè)允許其他帳戶使用代幣的
approve
函數(shù),例如加密貨幣交換。這會(huì)更新allowance
映射,以查看帳戶可以支出的金額。 - 它實(shí)現(xiàn)了
transferFrom
,允許其他帳戶轉(zhuǎn)移令牌。
你還可以閱讀此智能合約的測(cè)試,以了解有關(guān)其工作原理的更多信息。這些測(cè)試確保這種智能合約的行為符合我們的預(yù)期。這是一個(gè)完整的測(cè)試套件,可以檢查智能合約的所有行為:
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從命令行運(yùn)行測(cè)試,如下所示:
$ truffle test
眾籌銷售智能合約
現(xiàn)在我們可以建立一個(gè)眾籌銷售智能合約,允許投資者在最初的代幣產(chǎn)品(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);
}
}
讓我們來看看這個(gè)智能合約的功能,以及它如何進(jìn)行眾籌銷售:
- 它存儲(chǔ)眾籌銷售
address admin
的地址管理員帳戶。 - 它引用了ERC-20代幣智能合約
DappToken public tokenContract
。 - 它存儲(chǔ)代幣價(jià)格
uint256 public tokenPrice
。 - 它存儲(chǔ)了代幣銷售的數(shù)量
uint256 public tokensSold
。 - 它實(shí)現(xiàn)了一個(gè)
sell
事件,以便消費(fèi)者可以在出售代幣時(shí)收到通知。 - 它實(shí)現(xiàn)了
buyTokens
函數(shù),允許用戶在眾籌銷售中購買代幣。 - 它實(shí)現(xiàn)了一個(gè)
endSale
函數(shù),允許管理員結(jié)束眾籌銷售并收集銷售期間籌集的以太幣。
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);
});
});
});
恭喜!你已成功學(xué)習(xí)了如何在以太坊上建立了ERC-20代幣和眾籌銷售智能合約!
======================================================================
分享一些以太坊、EOS、比特幣等區(qū)塊鏈相關(guān)的交互式在線編程實(shí)戰(zhàn)教程:
- java以太坊開發(fā)教程,主要是針對(duì)java和android程序員進(jìn)行區(qū)塊鏈以太坊開發(fā)的web3j詳解。
- python以太坊,主要是針對(duì)python工程師使用web3.py進(jìn)行區(qū)塊鏈以太坊開發(fā)的詳解。
- php以太坊,主要是介紹使用php進(jìn)行智能合約開發(fā)交互,進(jìn)行賬號(hào)創(chuàng)建、交易、轉(zhuǎn)賬、代幣開發(fā)以及過濾器和交易等內(nèi)容。
- 以太坊入門教程,主要介紹智能合約與dapp應(yīng)用開發(fā),適合入門。
- 以太坊開發(fā)進(jìn)階教程,主要是介紹使用node.js、mongodb、區(qū)塊鏈、ipfs實(shí)現(xiàn)去中心化電商DApp實(shí)戰(zhàn),適合進(jìn)階。
- C#以太坊,主要講解如何使用C#開發(fā)基于.Net的以太坊應(yīng)用,包括賬戶管理、狀態(tài)與交易、智能合約開發(fā)與交互、過濾器和交易等。
- EOS教程,本課程幫助你快速入門EOS區(qū)塊鏈去中心化應(yīng)用的開發(fā),內(nèi)容涵蓋EOS工具鏈、賬戶與錢包、發(fā)行代幣、智能合約開發(fā)與部署、使用代碼與智能合約交互等核心知識(shí)點(diǎn),最后綜合運(yùn)用各知識(shí)點(diǎn)完成一個(gè)便簽DApp的開發(fā)。
- java比特幣開發(fā)教程,本課程面向初學(xué)者,內(nèi)容即涵蓋比特幣的核心概念,例如區(qū)塊鏈存儲(chǔ)、去中心化共識(shí)機(jī)制、密鑰與腳本、交易與UTXO等,同時(shí)也詳細(xì)講解如何在Java代碼中集成比特幣支持功能,例如創(chuàng)建地址、管理錢包、構(gòu)造裸交易等,是Java工程師不可多得的比特幣開發(fā)學(xué)習(xí)課程。
- php比特幣開發(fā)教程,本課程面向初學(xué)者,內(nèi)容即涵蓋比特幣的核心概念,例如區(qū)塊鏈存儲(chǔ)、去中心化共識(shí)機(jī)制、密鑰與腳本、交易與UTXO等,同時(shí)也詳細(xì)講解如何在Php代碼中集成比特幣支持功能,例如創(chuàng)建地址、管理錢包、構(gòu)造裸交易等,是Php工程師不可多得的比特幣開發(fā)學(xué)習(xí)課程。
- tendermint區(qū)塊鏈開發(fā)詳解,本課程適合希望使用tendermint進(jìn)行區(qū)塊鏈開發(fā)的工程師,課程內(nèi)容即包括tendermint應(yīng)用開發(fā)模型中的核心概念,例如ABCI接口、默克爾樹、多版本狀態(tài)庫等,也包括代幣發(fā)行等豐富的實(shí)操代碼,是go語言工程師快速入門區(qū)塊鏈開發(fā)的最佳選擇。
匯智網(wǎng)原創(chuàng)翻譯,轉(zhuǎn)載請(qǐng)標(biāo)明出處。這里是原文在以太坊開發(fā)自己的加密貨幣