1.Truffle 簡介
1.1.什麼是 Truffle ?
Truffle 是一個世界級的開發環境,測試框架,以太坊的資源管理通道,致力於讓以太坊上的開發變得簡單。
Truffle 有以下特性:
內置的智能合約編譯,鏈接,部署和二進制文件的管理;
快速開發下的自動合約測試;
腳本化的,可擴展的部署與發佈框架;
部署到不管多少的公網或私網的網絡環境管理功能;
使用 EthPM&NPM 提供的包管理,使用 ERC190 標準;
與合約直接通信的直接交互控制檯;
可配的構建流程,支持緊密集成;
在 Truffle 環境裏支持執行外部的腳本。
1.2.環境要求和安裝
環境要求:
NodeJS 5.0+;
Windows,Linux,或 Mac OS X。
安裝方式:
$ npm install -g truffle
Truffle 需要以太坊客戶端,需要支持標準的 JSON RPC API。
如果你是 Windows 用戶,推薦使用 Powershell 或 Git BASH 來安裝和使用 Truffle 框架。
1.3.Truffle 開發工具
當開發基於 Truffle 的應用時,推薦使用 EthereumJS TestRPC,它是一個完整的在內存中的區塊鏈僅僅存在於開發的設備上。它在執行交易時是實時返回,而不等待默認的出塊時間,這樣可以快速驗證新寫的代碼,當出現錯誤時,也能即時反饋給你。它同時還是一個支持自動化測試的功能強大的客戶端,Truffle 充分利用它的特性,能將測試運行時間提速近 90%。
使用 TestRPC 客戶端充分測試後,可是嘗試使用正式發佈的客戶端:Geth (go-ethereum)、WebThree(cpp-ethereum)、More,這些是完整的客戶端實現,包括挖礦,網絡,塊及交易的處理,Truffle 可以在不需要額外配置的情況下發布到這些客戶端。
2.Truffle 工程創建
創建工程目錄,可以使用文件瀏覽器或使用下面的命令在命令行創建一個目錄:
$ mkdir myproject
接下來,通過下面的命令初始化一個 Truffle 工程:
$ truffle
Truffle v3.4.11 - a development framework for Ethereum
$ cd myproject
$ truffle init
完成後,將擁有如下目錄:
- app/ - 應用文件運行的默認目錄,這裏麪包括推薦的 javascript 文件和 css 樣式文件目錄,但可以完全決定如何使用這些目錄;
- contract/ - Truffle 默認的合約文件存放地址;
- migrations/ - 存放發佈腳本文件 ;
- test/ - 用來測試應用和合約的測試文件;
- truffle.js - Truffle 的配置文件。
- truffle init 會默認創建一個構建在以太坊內的代幣 demo 應用:METACOIN,可以使用這個工程來進行快速的學習,或者也可以刪除這些文件來創建一個自己的工程。
3.編譯合約
3.1.合約位置
所有合約應該位於 ./contracts 目錄,默認提供一個合約文件,一個庫文件,均以 .sol 結尾作爲示例。
儘管庫文件有一定的特殊性,但爲簡單起見,當前均稱之爲合約。
3.2.命令
要編譯合約,使用:
truffle compile
Truffle 僅默認編譯自上次編譯後被修改過的文件,來減少不必要的編譯。如果想編譯全部文件,可以使用 --compile-all 選項:
truffle compile —compile-all
3.3. 約定
Truffle 需要定義的合約名稱和文件名準確匹配。
舉例來說,如果文件名爲 MyContract.sol,那麼合約文件須爲如下兩者之一:
contract MyContract {
...
}
// or
library MyContract {
...
}
這種匹配是區分大小寫的,也就是說大小寫也要一致,推薦大寫每一個開頭字母。
3.4.依賴
可以通過使用 import 來聲明依賴,Truffle 將會按正確順序依次編譯合約,並在需要的時候自動關聯庫。
3.5.編譯目錄
編譯的輸出位於 ./build/contracts 目錄,如果目錄不存在會自動創建。
編譯文件對於 Truffle 框架能否正常工作至關重要,不應該在正常的編譯或發佈以外手動修改這些文件。
4..移植
4.1.移植目的
移植是由一些 Javascript 文件組成來協助發佈到以太坊網絡,主要目的是用來緩存你的發佈任務,它的存在基於你的發佈需求會改變的前提。
當你的工程發生了重要的改變,你將創建新的移植腳本來將這些變化帶到區塊鏈上,之前運行移植的歷史記錄通過一個特殊的 Migrations 合約來記錄到鏈上。
4.2.命令
執行移植,使用下述命令:
truffle migrate
如果你之前的移植是成功執行的,這個命令會執行所有的位於 migrations 目錄內的移植腳本,truffle migrate 僅會執行新創建的移植。
如果沒有新的移植腳本,這個命令不同執行任何操作,可以使用選項 --reset 來從頭執行移植腳本。
4.3.移植腳本文件
一個樣例文件如下:文件名:4_example_migration.js
module.exports = function(deployer) {
// deployment steps
deployer.deploy(MyContract);
};
需要注意的是文件名以數字開頭,一個描述性的後綴結尾,數字前綴是必須的,用於記錄移植是否成功,後綴僅是爲了提高可讀性,以方便理解。
移植 js 裏的 exports 的函數接受一個 deployer 對象作爲第一個參數,這個對象用於發佈過程,提供了一個清晰的語法支持,同時提供一些通過的合約部署職責,比如保存發佈的文件以備稍後使用。deployer 對象是用來緩存(stage)發佈任務的主要操作接口。
像所有其它在 Truffle 中的代碼一樣,Truffle 提供了自己代碼的合約抽象層(contract abstractions),並且進行了初始化,以方便可以便利的與以太坊的網絡交互,這些抽象接口是發佈流程的一部分。
4.4.部署器(deployer)
移植文件會使用部署器來緩存部署任務,因此可以按一定順序排列發佈任務,它們會按正確順序執行:
// Stage deploying A before B
deployer.deploy(A);
deployer.deploy(B);
另一選中可選的部署方式是使用 Promise,將部署任務做成一個隊列,是否部署依賴於前一個合約的執行情況:
// Deploy A, then deploy B, passing in A's newly deployed address
deployer.deploy(A).then(function() {
return deployer.deploy(B, A.address);
});
如果想更清晰,也可以選擇實現一個 Promise 鏈。
可以根據發佈到的網絡的具體情況進行不同的部署流程,要實現不同條件的不同部署步驟,移植代碼中需要第二個參數 network。示例如下:
module.exports = function(deployer, network) {
// Add demo data if we're not deploying to the live network.
if (network != "live") {
deployer.exec("add_demo_data.js");
}
}
指定一個網絡:大多數 Truffle 提供的命令根據指定的網絡不同而表現不同,會使用對應網絡下的合約和配置信息,可以通過 --network 選項在參數上進行控制:
$ truffle migrate --network live
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*" // match any network
},
live: {
host: "178.25.19.88", // Random IP for example purposes (do not use)
port: 80,
network_id: 1, // Ethereum public network
// optional config values
// gas
// gasPrice
// from - default address to use for any transaction Truffle makes during migrations
}
}
在上面這個例子中,Truffle 會在 live 網絡中進行移植,如果配置如上述配置示例的 Example 所指定的內容的話,是最終在以太坊網絡上進行部署。
4.5.部署 API
4.5.1.DEPLOYER.DEPLOY
發佈一個指定的合約,第一參數是合約對象,後面是一些可選的構造器參數。這個函數適用於單例合約,它只會在 dapp 中只創建一個這個合約的實例(單例),函數會在部署後設置合約的地址(如:C ontract.address 將等於新的部署地址),它將會覆蓋之前存儲的地址。也可以傳入一個合約數組,或數組的數組來加速多合約的部署。
需要注意的是如果庫的地址可用,deploy 會自動爲這個部署的合約聯接任何需要的庫,因此如果合約依賴某個庫,應該先部署這個庫:
// Deploy a single contract without constructor arguments
deployer.deploy(A);
// Deploy a single contract with constructor arguments
deployer.deploy(A, arg1, arg2, ...);
// Deploy multiple contracts, some with arguments and some without.
// This is quicker than writing three `deployer.deploy()` statements as the deployer
// can perform the deployment as a batched request.
deployer.deploy([
[A, arg1, arg2, ...],
B,
[C, arg1]
]);
4.5.2.DEPLOYER.LINK
聯接一個已經發布的庫到一個或多個合約,destinations 可以是一個合約或多個合約組成的一個數組,如果目標合約並不依賴這個庫,部署器會忽略掉這個合約。
這對於在 dapp 中不打算部署的合約(如:非單例)但卻需要在使用前先聯接的情況下非常有用。
// Deploy library LibA, then link LibA to contract B
deployer.deploy(LibA);
deployer.link(LibA, B);
// Link LibA to many contracts
deployer.link(LibA, [B, C, D]);
4.5.3.DEPLOYER.AUTOLINK(CONTRACT)
關聯合約依賴的所有庫,這需要所依賴的庫已經部署,或在其前一步部署:
// Assume A depends on a LibB and LibC
deployer.deploy([LibB, LibC]);
deployer.autolink(A);
另外可以省略參數來調用函數 autolink(),這會自動關聯合約依賴的所有庫,需要保證在調用這個函數前,所有被需要的庫已經部署:
// Link *all* libraries to all available contracts
deployer.autolink();
4.5.4.DEPLOYER.THEN
Promise 語法糖,執行做生意的部署流程:
deployer.then(function() {
// Create a new version of A
return A.new();
}).then(function(instance) {
// Set the new instance of A's address on B.
var b = B.deployed();
return b.setA(instance.address);
});
4.5.5.DEPLOYER.EXEC
執行 truffle exec 做爲部署的一部分:
// Run the script, relative to the migrations file.
deployer.exec("../path/to/file/demo_data.js");
5.實例
5.1.環境安裝
npm install nodejs
npm install -g solc
npm install -g truffle
npm intall -g truffle-flattener
5.2.配置truffle
require('dotenv').config();
//const { MNEMONIC, PROJECT_ID } = process.env;
const private_keys = [
process.env.PK1
]
const HDWalletProvider = require('@truffle/hdwallet-provider');
module.exports = {
networks: {
dev: {
host: "127.0.0.1", // Localhost (default: none)
port: 7545, // Standard Ethereum port (default: none)
network_id: "5777", // Any network (default: none)
},
bsctest: {
provider: () => new HDWalletProvider({
privateKeys:['0x8b9d4e96fb91e23axxxxxxxxxxx65e0843d'],
providerOrUrl:`https://bsc-testnet.nodereal.io/v1/77055bfxxxxxxxd5d5b9`,
numberOfAddress:1
}),
network_id: 97,
confirmations: 2,
timeoutBlocks: 50,
gas: 5000000,
gasPrice: 50000000000,
skipDryRun: false // Skip dry run before migrations? (default: false for public nets )
},
// Set default mocha options here, use special reporters, etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: "0.8.1", // Fetch exact version from solc-bin
}
},
};
配置部署的信息,以bsctest爲例。
5.3.編譯合約
test@MacBook-Pro TripleC % sudo truffle compile --network bsctest
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
5.4.部署合約
test@MacBook-Pro TripleC % sudo truffle migrate --network bsctest
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'bsc'
> Network id: 56
> Block gas limit: 140000000 (0x8583b00)
1685499904_deploy__triplec_medal.js
===================================
[ '0xe245fa636F67E491b8052D7C77256Fc192edAd86' ]
Deploying 'TriplecMedal'
------------------------
> transaction hash: 0x5aeb0933e5eaf680a2df06b41dc59ab1131481e5e5730400cf8e37b9559a6ee4
> Blocks: 3 Seconds: 9
> contract address: 0xB8e8037410D23db82E111111b44920f1E92113A1
> block number: 34105401
> block timestamp: 1701830356
> account: 0xe245fa636F671111111d86
> balance: 0.401088294
> gas used: 4581180 (0x45e73c)
> gas price: 50 gwei
> value sent: 0 ETH
> total cost: 0.229059 ETH
Pausing for 2 confirmations...
-------------------------------
> confirmation number: 2 (block: 34105405)
> Saving artifacts
-------------------------------------
> Total cost: 0.229059 ETH
Summary
=======
> Total deployments: 1
> Final cost: 0.229059 ETH
這樣就可以得到contract address了