---
# System prepended metadata

title: Brownie 功能簡介與範例

---

# 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)