今天我將向你展示如何在以太坊區(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í)增長和變化的地方熟呛!
我也推薦這篇維基百科文章宽档。
我們要建立的網(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ā)自己的加密貨幣