上一篇中我們已寫好並部署完成了簡單的加密代幣🔒💵合約。在閱讀完本文後,你將學會建立一個可以放到乙太幣錢包👛 的加密代幣🔒💵。
開發前的準備
延續上一篇的內容,在開發的過程中,我們將繼續使用testrpc
1工具在電腦上模擬智能合約所需的乙太坊區塊鏈測試環境。
首先確保已啟動 testrpc。若尚未啟動,可以使用以下命令啟動:
$ testrpc
...
這邊有個值得一學的小技巧:在啟動 testrpc 時加上--seed
參數,例如
testrpc --seed apple banana cherry
這樣之後重新啟動 testrpc 時可以產生一樣的帳號 (accounts) 和私鑰 (private key)。
ERC20 標準
建立的代幣若要能透過乙太幣錢包👛 來收送,必須相容於以太坊的 ERC20 標準2。ERC20 標準定義了支援錢包所必須的合約介面。
本篇將使用OpenZeppelin
2函式庫來簡化建立加密代幣🔒💵的過程。OpenZeppelin
是一套協助撰寫安全的加密合約的函式庫,裡面也提供了相容 ERC20 標準的智能合約。可以透過 npm 工具安裝到專案目錄node_modules/zeppelin-solodity/
中:
$ npm install zeppelin-solidity
準備完成,我們可以開始建立新的加密代幣智能合約了。
建立一個標準代幣合約
在contracts/
目錄下建立一個HelloToken.sol
檔案。也可以使用以下命令來產生檔案:
$ truffle create contract HelloToken
HelloToken.sol
檔案內容如下:
pragma solidity ^0.4.11;
import "zeppelin-solidity/contracts/token/StandardToken.sol";
contract HelloToken is StandardToken {
string public name = "HelloCoin";
string public symbol = "H@";
uint8 public decimals = 2;
uint256 public INITIAL_SUPPLY = 88888;
function HelloToken() public {
totalSupply = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}
}
講解
pragma solidity ^0.4.11;
第一行指名目前使用的 solidity 版本,不同版本的 solidity 可能會編譯出不同的 bytecode。
import "zeppelin-solidity/contracts/token/StandardToken.sol";
接著我們使用import
語句,來讀入zeppelin-solidity
提供的StandardToken
合約。
contract HelloToken is StandardToken {
}
建立HelloToken
合約時,使用is
語句繼承了StandardToken合約。因此HelloToken
合約繼承了StandardToken
合約所包含的資料與呼叫方法。
當我們繼承了StandardToken
合約,也就支援了以下 ERC-20 標準中2規定的函式
函式 | 描述 |
---|---|
totalSupply() | 代幣發行的總量 |
balanceOf(A) | 查詢 A 帳戶下的代幣數目 |
transfer(A,x) | 傳送 x 個代幣到 A 帳戶 |
transferFrom(A,x) | 從 A 帳戶提取 x 個代幣 |
approve(A,x) | 同意 A 帳戶從我的帳戶中提取代幣 |
allowance(A,B) | 查詢 B 帳戶可以從 A 帳戶提取多少代幣 |
和前一篇一樣,後面驗證時會用到balanceOf
和transfer
兩個函式。因為StandardToken
合約中已經幫我們實做了這些函式,因此我們不需要自己從頭再寫一次。
string public name = "HelloCoin";
string public symbol = "H@";
uint8 public decimals = 2;
uint256 public INITIAL_SUPPLY = 100000;
這邊設定參數的目的是指定這個代幣的一些特性。以美元為例,美元的名稱 (name
) 是dollar
,美元的代號為$
,拿一美元去找零最小可以拿到零錢是一美分 (cent),也就是 0.01 元。因為一美元最小可分割到小數點後 2 位 (0.01),因此最小交易單位 (decimals) 為 2。
這邊將這個加密代幣取名 (name) 為HelloCoin
(哈囉幣),代幣的代號 (symbol) 為H@
,最小分割單位是 2 (最小可以找 0.01 個哈囉幣)。
以下為美金,比特幣,以太幣,哈囉幣的對照表供參考:
Name | Symbol | decimals |
---|---|---|
Dollar | $ | 2 |
Bitcoin | BTC | 8 |
Ethereum | ETH | 18 |
HelloCoin | H@ | 2 |
最後也定義了初始代幣數目INITIAL_SUPPLY
為100000
。當我們把全域變數設為public
(公開),編譯時就會自動新增一個讀取公開變數的 ABI 接口,我們在truffle console
中也可以讀取這些變數。
function HelloToken() public {
totalSupply = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}
和合約同名的HelloToken
方法,就是HelloToken
合約的建構函式 (constructor)。
在建構式裡指定了totalSupply
數目,並將所有的初始代幣INITIAL_SUPPLY
都指定給msg.sender
帳號,也就是用來部署這個合約的帳號。‵totalSupply
定義於ERC20Basic.sol中,balances
定義於BasicToken.sol中。
import '../math/SafeMath.sol';
...
using SafeMath for uint256;
...
balances[msg.sender] = balances[msg.sender].sub(_value);
進一步追去看BasicToken.sol合約的內容,可以發現BasicToken.sol
合約中匯入了SafeMath.sol
合約7。SafeMath
對各種數值運算加入了必要的驗證,讓合約中的數字計算更安全。
如此一來,我們已寫好一個可透過以太幣錢包交易的新加密代幣🔒💵合約。如果將這個合約部署到乙太坊公開區塊鍊上,這個代幣合約就會一直存在,世界上從此也就多了一種新的加密代幣。只要你能找到人想擁有這種代幣,這種代幣就有交易的價值。
編譯與部署
在migrations/
目錄下建立一個4_deploy_hellotoken.js
檔案,內容如下:
var HelloToken = artifacts.require("HelloToken");
module.exports = function(deployer) {
deployer.deploy(HelloToken);
};
現在執行 compile 與 migrate 命令
$ truffle compile
...
$ truffle migrate --reset
Using network 'development'.
Running migration: 4_deploy_hellotoken.js
Deploying HelloToken...
... 0x2c4659528c68b4e43d1edff6c989fba05e8e7e56cc4085d408426d339b4e9ba4
HelloToken: 0x352fa9aa18106f269d944935503afe57a00a9a0d
Saving successful migration to network...
... 0x1434c1b61e9719f410fc6090ce37c0ec141a1738aba278dd320738e4a5d229fa
Saving artifacts...
如此一來我們已將 HelloCoin 代幣合約部署到 testrpc 上。
驗證
我們一樣可以透過truffle console
來驗證HelloToken
是否部署成功。
$ truffle console
> let contract
> HelloToken.deployed().then(instance => contract = instance)
> contract.address
'0x352fa9aa18106f269d944935503afe57a00a9a0d'
> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 100000 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
> contract.transfer(web3.eth.accounts[1], 123)
...
> contract.balanceOf(web3.eth.accounts[0])
BigNumber { s: 1, e: 4, c: [ 99877 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 123 ] }
>
講解
> let contract
> SimpleToken.deployed().then(instance => contract = instance)
這邊使用HelloToken.deployed().then
語句來取得 HelloToken 合約的 Instance (實例),並存到contract
變數中,以方便後續的呼叫。
> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 100000 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
web3.eth.coinbase
代表操作者預設的帳號,即 testrpc 所提供的 10 個帳號中的第一個帳號,也可以透過web3.eth.accounts[0]
取得。
這兩句的目的是在進行轉帳操作前,先查詢前兩個帳號所擁有的代幣餘額。透過呼叫balanceOf
函式,可以看到第一個帳號 (部署合約的帳號) 裡存著所有的代幣。
> contract.transfer(web3.eth.accounts[1], 123)
...
接著使用transfer
函式來傳送123
個代幣到第二個帳號web3.eth.accounts[1]
。如果轉帳成功,傳送者預設帳號中會減少123
個代幣,接收者帳號中會增加123
個代幣。
> contract.balanceOf(web3.eth.accounts[0])
BigNumber { s: 1, e: 4, c: [ 99877 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 123 ] }
>
我們再次透過呼叫balanceOf
函式,查詢傳送者帳號和接收者帳號各自剩下的 HelloToken 數目。發現轉帳真的成功了。
結語
我們用到 OpenZeppelin
提供的函式庫來簡化撰寫加密代幣合約的工作。要實際投入使用前,還是建議將呼叫到的程式碼都再審查幾遍。可以從 OpenZeppelin 自己提供的 Audit 開始看4,可以學到一些觀念。
參考資料
- [1] https://github.com/ethereumjs/testrpc
- [2] ERC20 https://theethereum.wiki/w/index.php/ERC20_Token_Standard 或 https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
- [3] OpenZeppelin https://github.com/OpenZeppelin/zeppelin-solidity
- [4] OpenZeppelin Audit https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/audit/ZeppelinAudit.md
- [5] An Ethereum Hello World Smart Contract for Beginners part 1 http://www.talkcrypto.org/blog/2017/04/17/an-ethereum-hello-world-smart-contract-for-beginners-part-1/
- [6] http://www.talkcrypto.org/blog/2017/04/22/an-ethereum-hello-world-smart-contract-for-beginners-part-2/
- [7] OpenZeppelin SafeMath 合約