使用OpenZeppelin在RSK上進行ERC20代幣開發
在本文中,我們將討論通過RSK
網路部署和互動Smart-Contracts
智慧合約。我們的合約將是一個基於OpenZeppelin庫的ERC20代幣,我們將把它直接部署到Mainnet中。
建立合約
我們需要做的第一件事就是知道如何使用Truffle
。
當我們這樣做
$ truffle init
在一個空資料夾中,除了建立配置檔案外,我們還為專案和遷移合約建立了資料夾,以記錄對同一合約的更改。
合約的.sol
程式碼檔案位於
~/Truffle/contracts
遷移指令碼在
~/Truffle/migrations
已編譯的合約在
~/Truffle/build
測試合約在
~/Truffle/test
我們現在只處理前兩個資料夾。
在Truffle資料夾中,我們從OpenZeppelin匯入庫
$ npm install -E openzeppelin-solidity
這些庫不僅會安裝我們代幣token的主要類庫,還會安裝所有權相關,安全數學運算和許多其他設施的庫。值得一提的是,這些庫已經過稽核以實現高標準的安全性,因此依賴於它們的合約在正確使用時不易受到黑客攻擊。
我們的庫將安裝在
~/Truffle/node_modules/openzeppelin-solidity/contracts
之後,我們可以將庫ABCD.sol
import 'zeppelin-solidity/contracts/token/ERC20/ABCD.sol';
要建立我們的ERC20代幣,我們將從該儲存庫匯入2個庫:StandardToken.sol
,它具有代幣的主要功能,並且已經更多地匯入了一堆庫,例如SafeMath.sol
;Ownable.sol
,這些允許我們設定所有者對合約中的功能控制。
要繼承庫屬性和函式,我們只需使用“is”關鍵字以這種方式將合約定義為StandardToken
和Ownable
:
contract CoinFabrikToken is StandardToken, Ownable { }
之後,我們擁有這些庫和匯入庫中的所有功能。
接下來,我們將代幣的名稱定義為CoinFabrik
,這是它的符號,18個小數位,用於代幣的精度(以太坊類網路中的標準,使我們有可能使用web3的以太轉換功能)並將代幣的初始供應量設定為1000,像這樣:
string public name = 'CoinFabrik';
string public symbol = 'CF';
uint8 public decimals = 18;
uint public INITIAL_SUPPLY = 1000;
我們還將建立另一個字串,一個與代幣功能無關的非公共變數,以顯示Ownable
庫屬性的用法,該屬性僅允許建立者與某些指定的函式進行互動。我們稍後會看到。
已經定義了我們的引數,現在是時候通過建構函式將它們分配給Token變量了。到目前為止,建構函式被定義為一個與智慧合約同名的函式,但是從現在開始,將會有一個名為constructor()
的函式,它將替換舊方法。如果你像以前一樣呼叫建構函式,Solidity編譯器將發出警告。
INITIAL_SUPPLY
乘以小數精度的次方將分配給BasicToken
合約的totalSupply_
:
totalSupply_ = INITIAL_SUPPLY * (10**uint(decimals));
並將它們存入創作者的帳戶:
balancesb [msg.sender] = totalSupply_;
有了這個,我們就可以使用一個簡單而標準的代幣,但正如我們所說,我們將使用Ownable
合約新增一些功能。首先,我們將定義一些函式:一個修改我們的非公共變數的狀態,但只有你擁有許可權,而另一個函式返回字串的訊息。定義如下:
function setON(string _n) public onlyOwner returns (bool) {
Owner = _n;
return true;
}
function getON() public view returns (string) {
return Owner;
}
兩者都是公開的,所以任何人都可以嘗試呼叫他們,但對於第一個,只有所有者的地址不會導致恢復。如果你是所有者並且呼叫了函式,則字串將儲存在我們的變數Owner
(帶有大寫字母)中,並且它還將返回一個我們可以在交易中檢查的true值。
由於Owner
變數不是公共的並且沒有Getter,我們需要一個函式來返回變數的值而不改變區塊鏈的狀態。這是第二個功能。
我們還將建立一個回撥函式,如果有人錯誤地呼叫我們的合約,則會發出事件:
function () public payable {
if (msg.value > 0) {
emit Yes('Thanks for donating SBTC! :)');
} else {
emit No('Error 404: Function not found :P');
}
}
最後,我們在合約中添加了一個可銷燬的功能,其中所有者是唯一可以執行它的人。
我們的簡單代幣已經完成。所有程式碼應該是一樣的:
pragma solidity ^0.4.17;
import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol';
import "zeppelin-solidity/contracts/ownership/Ownable.sol";
contract CoinFabrikToken is StandardToken, Ownable {
string public name = 'CoinFabrik';
string public symbol = 'CF';
uint8 public decimals = 18;
uint public INITIAL_SUPPLY = 1000;
string Owner;
event Yes(string);
event No(string);
constructor() public {
totalSupply_ = INITIAL_SUPPLY * (10**uint(decimals));
balances[msg.sender] = totalSupply_;
}
function setON(string _n) public onlyOwner returns (bool) {
Owner = _n;
return true;
}
function getON() public view returns (string) {
return Owner;
}
function () public payable {
if (msg.value > 0) {
emit Yes('Thanks for donating SBTC! :)');
} else {
emit No('Error 404: Function not found :P');
}
}
function destroy() public onlyOwner {
selfdestruct(owner);
}
}
建立遷移
對於每個合約,我們需要告訴Truffle哪個合約是我們想要部署的合約以及我們可以在哪裡找到合約。這是通過/Truffle/migrations
資料夾中的遷移檔案完成的。
遷移指令碼02_deploy_token.js
應如下所示
var CoinFabrikToken = artifacts.require("./CoinFabrikToken.sol");
module.exports = function(deployer) {
deployer.deploy(CoinFabrikToken);
};
我們已配置Truffle,我們的節點已同步,我們的合約已經編寫並且我們的遷移已配置,完成部署就是個時間問題。
部署
如果我們之前停止了我們的節點,我們將恢復線上狀態,然後我們將與Truffle連線:
$ sudo service rsk start
$ cd ~/Truffle/ && truffle console --network rsk
之後編譯合約:
truffle(rsk)> compile --all
不應該對我們的合約有任何錯誤或警告。然後我們轉移合約:
truffle(rsk)> migrate --reset
為了節約時間,我們可以在一行中執行兩個命令
truffle(rsk)> migrate --all --reset
將首先部署遷移合約。Truffle為我們提供了每個操作的交易雜湊,因此我們可以稍後檢查詳細資訊或日誌。這是我收到的完整輸出
truffle(rsk)> migrate --all --reset
Compiling ./contracts/CoinFabrikToken.sol...
Compiling ./contracts/Migrations.sol...
Compiling zeppelin-solidity/contracts/math/SafeMath.sol...
Compiling zeppelin-solidity/contracts/ownership/Ownable.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/BasicToken.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/ERC20.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol...
Compiling zeppelin-solidity/contracts/token/ERC20/StandardToken.sol...
Writing artifacts to ./build/contracts
Using network 'rsk'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xf00d4ecf2b5752022384f7609fe991aa72dda00a0167a974e8c69864844ae270
Migrations: 0x1dc2550023bc8858a7e5521292356a3d42cdcbe9
Saving successful migration to network...
... 0x3e759e8ff8a7b8e47a441481fa5573ccf502b83f3d591ad3047e622af0f9169e
Saving artifacts...
Running migration: 2_deploy_token.js
Deploying CoinFabrikToken...
... 0x300c8bb1e434e2aa4b13dcc76087d42fcbe0cb953989ca53a336c59298716433
CoinFabrikToken: 0xc341678c01bcffa4f7362b2fceb23fbfd33373ea
Saving successful migration to network...
... 0x71771f7ee5d4e251e386979122bdda8728fa519d95a054572751bb10d40eb8c5
Saving artifacts...
如果我們檢查交易,我們可以計算所有部署過程的gas成本。在我這裡的情況,它是2340788gas(277462+42008+1994310+27008)。
因此將其更改為真實的SBTC,我們得到2340788*183000000/10^18=0,000428364 SBTC。在撰寫本文時,這大約是4美元左右。
我們的合約現在部署在0xc341678c01bcffa4f7362b2fceb23fbfd33373ea。
恭喜!
與合約互動
通過Truffle遷移給出的地址,以及合約的ABI,我們建立了一個例項,因此簡化語法更容易處理函式。為此,在我們部署之後,我們寫了
truffle(rsk)> var cfToken = web3.eth.contract(CoinFabrikToken.abi).at(CoinFabrikToken.address)
如果合約已經部署,並且知道它的地址和ABI,我們就可以做到
truffle(rsk)> var cfToken = web3.eth.contract(‘Contract_ABI’).at(‘Contract_ADDRESS’)
其中Contract_ABI
是簡化為一行ABI,Contract_ADDRESS
不需要解釋。
我之前建立了2個帳戶,現在為方便起見,我們將它們重新命名:
truffle(rsk)> var acc0 = web3.eth.accounts[0]
truffle(rsk)> var acc1 = web3.eth.accounts[1]
acc0
是部署合約的人。Acc0
被新增到truffle.js
和node.conf
配置檔案中。
所有權控制
我們將首先使用我們討論過的庫來測試合約的所有權功能。
如果我們從任何帳戶呼叫getON
函式,只要它是公開的並且沒有任何所有權問題,我們就會得到:
truffle(rsk)> cfToken.getON()
''
現在,setON函式具有所有權屬性。任何來自其他帳戶的交易都將被駁回。例如,我們看到,試圖用我的名字從acc1
簽訂合約不會改變它的價值。
truffle(rsk)> cfToken.setON('Andres Bachfischer', {from: acc1})
0x5f115190b60238240bedf36d1c5bb69a443a0f8ee971b0fc40fe5ca9c727d47c
使用交易的雜湊,我們看到返回的值為false,並且函式未正確執行。再次呼叫getON函式,我們看到變數沒有改變它的值。
現在簽署相同的交易但是從所有者的帳戶acc0,我們得到狀態'0x01'並且該功能正確執行。
truffle(rsk)> cfToken.setON('Andres Bachfischer', {from: acc0})
0x0c894fa7e5369573fb14addeaed4cd9d5b6cd1425cb4eeeae16cb4e1fa8e0364
再次呼叫函式getON
,我們看到所有權庫按照我們希望的那樣工作。
truffle(rsk)> cfToken.getON()
Ownable.sol
還具有允許我們將合約所有者更改為其他地址的功能。我們不會用它。然而,它的用法如下:
truffle(rsk)> cfToken.transferOwnership(acc1, {from: acc0})
有了這個,acc1將成為合約的新所有者。
讓我們轉到代幣。
代幣操作
我們要做的第一件事是檢查在建立合約時是否正確分配了代幣的餘額。
我們檢查每個帳戶的餘額如下:
web3.fromWei(cfToken.balanceOf(acc0).toString(10)) // = ‘1000’
web3.fromWei(cfToken.balanceOf(acc1).toString(10)) // = ‘0’
因此,我們可以看到所有代幣都已正確分配到我們的初始帳戶。
我們要做的第一筆交易是將一些代幣轉移到第二個帳戶acc1
,進行三次。
為第一筆交易這樣做:
truffle(rsk)> cfToken.transfer(acc1, web3.toWei(88.8), {from: acc0})
0xd45437b777f1430e7cec57bd80b261ce8f87bf8a3f9a113fecd20563403c4d9c
truffle(rsk)> web3.fromWei(cfToken.balanceOf(acc0).toString(10)) // = '733.6'
truffle(rsk)> web3.fromWei(cfToken.balanceOf(acc1).toString(10)) // = '266.4'
我們看到從我們的部署帳戶中獲取的代幣與在acc1中收到的代幣數量相同。
使用StandardToken
合約,我們還獲得了代表某個帳戶(在本例中為acc1)支出代幣的許可權。如果我們想在獲得批准之前執行此操作,則交易將失敗(狀態為“0x00”)
truffle(rsk)> cfToken.transferFrom(acc1, acc0, web3.toWei(5), {from: acc0})
0x5cee7cf60849283a0088d71483a606ba2101b500e13f972abada4f75781596bf
檢查後,acc0
不允許從acc1
傳送:
truffle(rsk)> web3.fromWei(cfToken.allowance(acc1, acc0, {from: acc0}).toString(10)) // = '0'
我們授權acc0
從acc1
的交易中以acc1
的名義花費10個代幣:
truffle(rsk)> cfToken.approve(acc0, web3.toWei(10), {from: acc1})
0x6e1a202f4ca7f43dfb28034952d54a572993b986a55857790aa51854afbc1fb4
在輸出日誌中,我們看到函式已成功完成,並且日誌顯示允許acc0
用於支出的金額。檢查allowance:
truffle(rsk)> web3.fromWei(cfToken.allowance(acc1, acc0, {from: acc0}).toString(10)) // = '10'
現在,如果我們再次執行支出交易:
truffle(rsk)> cfToken.transferFrom(acc1, acc0, web3.toWei(5), {from: acc0})
0x41f750eabb6e0d3ab576aac0333b0d337ca61808aae1eeafa9d8e2a0b81b979b
我們得到狀態為“0x01”的成功交易。
再檢查一下餘額:
truffle(rsk)> web3.fromWei(cfToken.balanceOf(acc0).toString(10)) // = '738.6'
truffle(rsk)> web3.fromWei(cfToken.balanceOf(acc1).toString(10)) // = '261.4'
最後,如果我們簽署一個呼叫不可用函式的事務,我們將呼叫我們的回退函式。 簽署一個像這樣的交易:
truffle(rsk)> web3.eth.sendTransaction({from: acc0, to: cfToken.address})
0x4106a287fc60669bf9682a73ec4c457b094c086ec7408a5dea95d200688c4ee9
將返回一個日誌,其資料表示字串Error 404:Function not found:P
(十六進位制:'0x00 ... 00204572726f72203430343a2046756e6374696f6e206e6f7420666f756e64203a50'
)。
我們的最後一個功能,即我們不會因為顯而易見的原因而執行,就是銷燬功能。我們需要合約不被銷燬才能顯示交易。要呼叫,所有者應該這樣做:
truffle(rsk)> cfToken.destroy({from: acc0})
結論
在演練的第二部分中,我展示了在RSK網路中開發簡單智慧合約的示例。 我們已經看過:
- 從OpenZeppelin套件匯入庫和合約,
- 使用這些庫建立一個簡單的代幣,
- 配置Truffle的遷移過程,
- 將我們的合約部署到RSK主網路,
- 通過不同的賬戶與合約互動,
- 檢查塊的日誌以獲取有關事務的反饋。
正如我們所看到的,RSK網路用於Solidity Smart Contracts部署和互動的用法幾乎與以太坊節點中的相同。當然,這仍然是一個測試網路,預計會出現問題和錯誤,主要是在節點中,但RSK Labs團隊在他們出現時儘可能快地解決它們。隨著時間的推移,將實現穩健性。
======================================================================
分享一些以太坊、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語言工程師快速入門區塊鏈開發的最佳選擇。
匯智網原創翻譯,轉載請標明出處。這裡是原文使用OpenZeppelin在RSK上進行ERC20代幣開發