DApp教程:用Truffle 開發一個鏈上記事本
以編寫一個鏈上記事本為例,介紹如何開發DApp,一年多前寫的開發、部署第一個DApp因為Truffle 、MetaMask、Solidity都有升級,也隨手更新了。
通過兩個教程大家可以更好理解前端如何與合約進行互動, 本文也將介紹如何使用Truffle 把合約部署到以太坊正式網路上(貌似很多人遇到問題)。
專案背景及效果
鏈上記事本讓事件永久上鍊,讓事件成為無法修改的歷史,從此再無刪帖,之前有一個帖子,介紹如何MetaMask上鍊記事,現在我們通過這個DApp來完成。
鏈上記事本有兩個功能:
- 新增一個新記事
- 檢視之前(自己的)記事本
實現效果:

本合約也部署到以太坊官方測試網路Ropsten, 如Englist first Note 的交易記錄可以在 EtherScan查詢 。
專案準備
建立專案資料夾:noteOnChain,然後在目錄下,執行:
truffle unbox pet-shop
使用Truffle 對專案初始化。
如果沒有使用過truffle 可以閱讀開發、部署第一個DApp。
Truffle 的Box,是一套套的開發模板, 它會幫助我們安裝好相應的依賴,快速的啟動應用開發。
如果我們專案需要是使用到 JQuery, Bootstrap庫,使用 pet-shop
這個Box 是不錯的選擇,官方還提供了React 、 Vue 專案相應的模板,所有的Box 可以在 這裡 查詢。
合約實現
專案初始化會在 noteOnChain
目錄下生成 contracts
目錄來存放合約檔案,在 contracts
目錄下新增一個合約檔案 NoteContract.sol
:
pragma solidity ^0.5.0; contract NoteContract { mapping(address => string [] ) public notes; constructor() public { } event NewNote(address, string note); // 新增記事 function addNote( string memory note) public { notes[msg.sender].push(note); emit NewNote(msg.sender, note); } function getNotesLen(address own) public view returns (uint) { return notes[own].length; } }
合約關鍵是狀態變數 notes
的定義,這是一個mapping, 儲存著所有地址下所有的記事本。
修改記事本邏輯
如果需要修改筆記功能,可以在合約中加入以下程式碼:
event ModifyNote(address, uint index); function modifyNote(address own, uint index, string memory note) public { notes[own][index] = note; emit ModifyNote(own, index); }
如果需要只有自己能修改筆記可以modifyNote的第一行加上:
require(own == msg.sender);
合約部署
先為合約新增一個部署指令碼:
var Note = artifacts.require("./NoteContract.sol"); module.exports = function(deployer) { deployer.deploy(Note); };
truffle部署的命令是
truffle migrate
預設情況下,會部署到本地的Ganache提供的測試網路,本文介紹下如何通過Truffle部署到太坊官方網路,這裡以
Ropsten為例介紹。
Ganache 的安裝使用可閱讀開發、部署第一個DApp
Infura 節點服務註冊 與 HDWalletProvider 安裝
大多數人應該都沒有部署自己的節點,我們可以使用 Infura 提供的節點服務。
有部分人可能不解 Infura 服務,其實 MetaMask 後面的節點服務就是Infura。
然後通過 HDWalletProvider
連線到Infura節點,併為我們簽署交易,通過下面命令安裝HDWalletProvider:
npm install truffle-hdwallet-provider
在使用Infura之前,我們需要註冊一個訪問Infura服務的Token, 註冊地址為: https://infura.io/register , 註冊後建立一個 Project, 複製節點url:
為 truffle 配置一個新網路
修改 truffle.js
加入一個新網路.
- 首先引入 HDWalletProvider:
var HDWalletProvider = require("truffle-hdwallet-provider");
- 配置簽名的錢包助記詞:
var mnemonic = "orange apple banana ... ";
助記詞其實不應該明文配置儲存,最好配置在一個隱私檔案裡,並被程式碼管理工具所忽略。
- 加入新網路,以Ropsten為例:
networks: { ropsten: { provider: function() { return new HDWalletProvider(mnemonic, "https://ropsten.infura.io/xxx") }, network_id: 3, gas: 7003605, gasPrice: 100000000000, } }
HDWalletProvider 的第一個引數是助記詞(確保賬號有足夠的餘額),第二個引數是 上面複製的 Infura 節點服務地址,gas 和 gasPrice 分別配置部署時的Gas Limit 和 Gas Price。
Truffle 網路的配置可查閱 連結 。
部署
通過以下命令來選擇網路部署:
truffle migrate --network ropsten
此過程大約需要等待半分鐘,正常的話會輸出像下面的提示:
Running migration: 1_initial_migration.js Deploying Migrations... ... 0xd79bc3c5a7d338a7f85db9f86febbee738ebdec9494f49bda8f9f4c90b649db7 Migrations: 0x0c6c4fc8831755595eda4b5724a61ff989e2f8b9 Saving successful migration to network... ... 0xc37320561d0004dc149ea42d839375c3fc53752bae5776e4e7543ad16c1b06f0 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying NoteContract... ... 0x7efbb3e4f028aa8834d0078293e0db7ff8aff88e72f33960fc806a618a6ce4d3 NoteContract: 0xda05d7bfa5b6af7feab7bd156e812b4e564ef2b1 Saving successful migration to network... ... 0x6257dd237eb8b120c8038b066e257baee03b9c447c3ba43f843d1856de1fe132 Saving artifacts...
我們可以用輸出的交易Hash到 https://ropsten.etherscan.io/ 查詢。
前端介面
Truffle Boxs為專案生成了html前端檔案 src/index.html
,刪除原來Boxs提供的寵物相關程式碼,加入一下html:
<div class="form-group"> <div class="col-sm-8 col-sm-push-1 "> <textarea class="form-control" id="new_note" ></textarea> </div> <button for="new_note" class="" id="add_new">新增筆記</button> </div> <div id="notes" > </div>
以上html 定義了一個文字框 textarea
用來輸入筆記,定義了一個 button
用來提交筆記上鍊。
定義了一個id為 notes 的div, 用來載入已有筆記。初始內容為空,後通過web3讀取到合約裡筆記後,通過JQuery插入。
合約互動
刪除原來Boxs提供的載入寵物邏輯,邏輯分三個部分:
- 初始化 web3 及合約
- 獲取筆記填充到前端頁面
- 釋出筆記上鍊
初始化
在 initWeb3
函式中,完成web3的初始化:
// 最新dapp 瀏覽器或MetaMask if (window.ethereum) { App.web3Provider = window.ethereum; try { // 請求賬號授權 await window.ethereum.enable(); } catch (error) { // User denied account access... console.error("User denied account access") } } // Legacy dapp browsers... else if (window.web3) { App.web3Provider = window.web3.currentProvider; } else { App.web3Provider = new Web3.providers.HttpProvider('http://localhost:9545'); } web3 = new Web3(App.web3Provider);
完成 initContract
初始化合約:
initContract: function() { $.getJSON('NoteContract.json', function(data) { App.contracts.noteContract = TruffleContract(data); App.contracts.noteContract.setProvider(App.web3Provider); App.contracts.noteContract.deployed().then(function(instance) { App.noteIntance = instance; return App.getNotes(); }); }); return App.bindEvents(); }
獲取筆記填充到前端頁面
initContract
函式裡, noteIntance
儲存了部署後的合約例項,getNotes用來獲取當前賬號的所有筆記:
getNotes: function() { App.noteIntance.getNotesLen(App.account).then(function(len) { App.noteLength = len; if (len > 0) { App.loadNote( len - 1); } }).catch(function(err) { }); }
目前solidity 還無法支援返回動態型別的陣列,沒有辦法直接獲取到如string 陣列的內容,所有這裡採用一個變通的方法,先獲取到筆記的長度,然後通過loadNote來逐條獲取筆記:
loadNote: function(index) { App.noteIntance.notes(App.account, index).then(function(note) { $("#notes").append( '<div > <textarea >' + note + '</textarea></div>' ; if (index -1 >= 0) { App.loadNote(index - 1); } } ).catch(function(err) { }); }
釋出筆記上鍊
使用JQuery監聽使用者點選 add_new
按鈕,然後呼叫合約的 addNote
函式把使用者輸入的筆記儲存到智慧合約。
bindEvents: function() { $("#add_new").on('click', function() { $("#loader").show(); App.noteIntance.addNote($("#new_note").val()).then(function(result) { return App.watchChange(); }).catch(function (err) { console.log(err.message); }); }); }
執行DApp
使用以下命令,啟動DApp 服務:
npm run dev
在瀏覽器開啟 http://localhost:3000
瀏覽器的MetaMask 也需要連線Ropsten網路,確保網路一致。
不知道如何設定MetaMask 可閱讀 開發、部署第一個去中心化應用( 。
本文為保持主幹清晰,程式碼有刪減, 網站程式碼請訂閱 小專欄 檢視。
參考文件
加我微信:xlbxiong 備註:DApp, 加入以太坊DApp開發微信群。
加入知識星球 成長比別人快一點。
深入淺出區塊鏈 - 系統學習區塊鏈,學區塊鏈的都在這裡,打造最好的區塊鏈技術部落格。