# Brownie 功能簡介與範例 此篇文章將會介紹一些 Brownie 的基礎操作。 Brownie 是一個讓開發者可以在本地測試自己的 Solidity 專案的工具。在測試時使用者可以在自己的專案資料夾下開啟 brownie console,並於此 console 中測試各種不同的 transaction。其操作的方式跟 python console 極為類似,非常適合熟悉 python 的人使用。 ## 1. brownie init 我們可以用以下指令將目前的空資料夾變成一個 brownie 框架的專案資料夾: ``` $ brownie init ``` ![](https://i.imgur.com/qcL1DWa.png) 原本的空資料夾將會有這些東西,我們待到第五、六節再去進行介紹。 <img src="https://i.imgur.com/VTEYDeG.png" width="200"/> ## 2. fork ### 2.1 進入 brownie console 在多數做測試的時候,我們會希望測試環境盡可能與真實區塊鏈相同,這個時候我們會希望以「fork」的方式去開啟我們的 console。他會依照目前鏈上的資料去創造一個擬真的環境。 (在使用 fork 之前我們會需要做一些一次性的設定,關於這些設定詳見 [Brownie: network setting for BSC](https://hackmd.io/@vM5aHIBcRcGU6GOAOk33Ig/SJQSZuMkt)) 以 fork 開啟 brownie 的指令為:`brownie console --network <name of network>` Ex: ``` $ brownie console --network bsc-main-fork ``` 上面的「bsc-main-fork」是我事先設定的 network,這個 network 的設定是從 BSC 上 fork 資料到自己的測試環境。 成功的話可以看到最後顯示「Brownie environment is ready」,terminal 的前面也會變成 console 的「>>>」。 <img src="https://i.imgur.com/g2grYsX.png"> ### 2.2 離開 brownie console 離開 brownie console 的方法是呼叫 quit() 函數 ```python= >>> quit() ``` ## 3. from_explorer() 有時候我們會想要測試自己的 solidity 與其他鏈上的合約互動是否正常。這個時候就可以利用 from_explorer() 這個函數,將合約地址傳入函數,就可以把鏈上的特定合約和其現有的資料複製下來。舉例來說,我們可以輸入: ```python= >>> WBNB = from_explorer("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c") ``` 將鏈上的 WBNB 合約複製到 console 的 WBNB 變數內。 <img src="https://i.imgur.com/X15askK.png"> ## 4. accounts and transaction ### 4.1 呼叫函數 在這個測試環境中,系統有幫我們預設幾個 account 可以讓我們操作。(預設是10個 account ,每個 account 裡面有 100 顆 BNB。這個設定可以改,詳見 technical note。我自己是設定成 5 個 account,個 1000 顆 BNB)這些帳號被存在 accounts 這個 list 中。 ```python= >>> accounts ``` <img src="https://i.imgur.com/sGD434F.png"> 我們可以藉由 indexing 去存取各個帳號,例如查詢餘額。 ```python= >>> accounts[1].balance() / 1e18 ``` <img src="https://i.imgur.com/TXMVXgI.png"> 或進行交易,例如 transfer: ```python= >>> accounts[1].transfer(accounts[0], "1000 ether") ``` 第一個參數,accounts[0],是我們的轉帳對象。第二個參數則是我們要轉帳的數量,是一個字串,格式是“x顆 ether” <img src = "https://i.imgur.com/xYUhfdZ.png"> 我們也可以與剛剛複製下來的合約做互動(這不會影響鏈上的資料)。我們知道智能合約的 transaction 需要傳遞兩類的資料,一類是智能合約本身函數的參數,另一類是夾在簽名裡面的資料。當我們在用 Brownie 互動時,智能合約本身的參數和一般的 python 參數完全一樣,而夾在簽名裡的資料則是用 python dictionary 的方式傳送。(可以參考[以 python 與 smart contract 互動](https://medium.com/n2-research-lab/以-python-或-node-js-呼叫-smart-contract-5f9642f4b756)) 以 WBNB deposit 這個函數來說,他本身沒有參數需要指定,而夾在簽名裡的資料包含:交易的發起人以及要傳入的 BNB 的數量。所以整個指令會是: ```python= >>> WBNB.deposit({'from': accounts[0], 'value': 1000 * 1e18}) ``` 至於 view function 的話就直接傳入函數所需的參數就好,不需要額外傳送 dictionary。例如 balanceOf() 就是這樣的函數,我們只需要傳入特定帳號的地址就可以了。 ```python= >>> WBNB.balanceOf(accounts[0]) >>> WBNB.balanceOf(accounts[0]) / 1e18 ``` ![](https://i.imgur.com/crG4d1J.png) ### 4.2 失敗的交易 有時候交易會因為某些原因失效(reverted),我們可以利用info()、call_trace()等方法來幫助我們查看交易在進行到哪個合約時失效。以下的例子我們故意使用 WBNB 的 withdraw 函數來提取超過我們存款的數目。 ```python= >>> WBNB.withdraw(2000 * 1e18, {'from':accounts[0]}) ``` ![](https://i.imgur.com/lEPQQX3.png) 大家可以看到,失敗的交易 transaction hash 會是紅色字體。傳回的訊息也有紅色的「reverted」字樣。 所有發送出去的交易都被儲存在 history 這個 list 裏面,我們可以藉由 indexing 存取這個測試環境中的所有交易。最新的交易是 list 的最後一個元素。我們可以 index 他,並調用他的 info()函數。 ```python= >>> history[-1].info() ``` ![](https://i.imgur.com/uCTMWCc.png) 或是 call_trace() 函數。 ```python= >>> history[-1].call_trace() ``` ![](https://i.imgur.com/cA93QuZ.png) call_trace() 函數的回傳結果顯示這筆在 WBNB.withdraw() 這個地方失效了。因為 withdraw() 這個交易步驟非常單純,這個資訊在這邊顯得很沒有用處。但當我們的合約會發起步驟龐雜的交易時,知道在哪一步出錯是很有用的資訊。 ### 4.3 小結 截至目前為止,我們只介紹到如何將鏈上現有的合約複製到 brownie console 這個測試環境中,並示範如何進行操作。以上的功能其實對 trace code 非常有幫助。有時候我們並沒有辦法直接看出這段程式碼的用意。這時候我們就可以利用以上介紹的功能,自己發起交易測試看看,幫助我們了解抽象程式碼實際的用途。 接下來在第五節,我們會去介紹如何將我們自行撰寫的合約部署到 brownie console 上進行測試。 ## 5. compile and deploy 在第二節中,我們使用了 ``$ brownie init`` 這個指令後,brownie 幫我們建立了幾個資料夾,其中 contract 這個資料夾就是我們 solidity code 擺放的位置。在本文的範例,我們使用到的 solidity code 可以到[這個 github gist ](https://gist.github.com/jian5753/ec37adba591feba27db09c947779c0f6)去查看,檔名是 demo.sol。 完成撰寫後我們可以使用: ``` $ brownie compile ``` 這個指令為我們的 solidity code 進行編譯。(要先使用`` >>> quit() ``離開 brownie console) ![](https://i.imgur.com/kH0Duwa.png) compile 成功後 build 資料夾的就會多一些東西,包含 abi 等等 json 檔。一般來說我們不會手工動到 build 資料夾內的東西。 <img src="https://i.imgur.com/TYL2eGc.png" width = 200px;> 完成 compile 之後就可以利用 `` brownie console --network bsc-main-fork `` 回到 brownie console。 進入到 console 後,我們發現 console 認得出我們 compile 的合約。在這個範例中,合約的名稱叫做 TwoWayArbi。而且這個 TwoWayArbi 是一個類似 list 的東西。 我們可以利用 TwoWayArbi.deploy() 進行部署,這個操作形同於是發起一個呼叫 constructor 的 transaction。 ```python= >>> TwoWayArbi [] >>> TwoWayArbi.deploy({'from': accounts[0]}) ``` ![](https://i.imgur.com/nejVIhd.png) 部署完成之後可以看到 TwoWayArbi 這個 list 多了一個合約地址。我們可以用 index 的方式存取他,並調用他的函數或發起交易。 ## 6 Scripts 有時候我們要進行的測試會牽涉到許多步驟,例如說重複的轉帳,重複的發起某些交易。我們可以預先將這些步驟寫在一個 python scripts 內,並把它放在專案中的「scripts」資料夾內,之後就可以在 console 內執行它。在本文的範例中,demo.py 就是我們的scripts,完整的程式碼可以從這個 [github gist](https://gist.github.com/jian5753/ec37adba591feba27db09c947779c0f6#file-demoscript-py) 取得。 <img src="https://i.imgur.com/67pSOFr.png" width = 200px;> 這個 script 中定義了一個名為 main() 的函數,main()函數裡的內容就是我們希望他運行的操作。我們在 brownie console 內執行: ```python= >>> run("demo.py", method_name='main', args=[accounts[0], accounts[1], accounts[2], accounts[3]]) ``` 指定我們要運行的 script、函數並利用 args=[...] 傳入呼叫 main() 時需要傳入四個參數:``owner、nonOwner、guyA、guyB``。 接著 brownie console 就會一步一步執行 main() 函數內的所有操作。 ## 7. 結語 Brownie 提供給我們一個非常靈活可控的測試環境。不管是想測試智能合約的邏輯,或者是瞭解鏈上既有的智能合約之運作,Brownie都會是很強大的工具。希望這篇文章可以幫助大家快速掌握 brownie 的基本功能,並利用 Brownie 有效率地達成自己的目標。 ## 8. Read more 1. [本文用到的範例程式碼(gist)](https://gist.github.com/jian5753/ec37adba591feba27db09c947779c0f6) 2. [Brownie: network setting for BSC](https://hackmd.io/@vM5aHIBcRcGU6GOAOk33Ig/SJQSZuMkt) 4. [以 python 與 smart contract 互動](https://reurl.cc/kZbWkL) 5. [brownie 安裝方法 (官方文件)](https://eth-brownie.readthedocs.io/en/stable/install.html) ## 9. References 1. [brownie documentation](https://eth-brownie.readthedocs.io/en/stable/toctree.html)