- 一個自動化套利機器人,在不同 DEX 之間利用 FlashLoan 進行套利活動
- 支援 UniswapV2 之間與 Balancer 和 UniswapV2 之間的 AMM 套利
- 使用 BalancerV2 FlashLoan 旨在利用目前 0% 手續費的特點將利潤最大化
- 因 Balancer 池 (0.001% - 10%) 比起 UniswapV2 池 (0.3%) 容易有更低的手續費,故合約支援 Balancer AMM,可選擇的池子請參考此連結
- (WIP) Bot 負責監聽 AMM Swap Event 並觸發合約執行套利
![image](https://private-user-images.githubusercontent.com/57789692/294745123-3a8dbbbd-4a0f-4185-aeed-3010c3da989e.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQyNDIsIm5iZiI6MTczOTQ1Mzk0MiwicGF0aCI6Ii81Nzc4OTY5Mi8yOTQ3NDUxMjMtM2E4ZGJiYmQtNGEwZi00MTg1LWFlZWQtMzAxMGMzZGE5ODllLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzMzkwMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTBiOGY1ODNmYmMyYjI5MmU4MTA3ODE2YzQ1NjgzZGIwYjc0N2FjNjY0NDQ5YjBlNDgzODM4ZGI1ODNjYjlkNTEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.CaLALL8S0IylENSRt4OHgkLCTYdt5j5xW1B_DJFEEuQ)
套利使用 Balancer 的 Flashloan 功能,套利流程為:
- 檢查同一交易對 (e.g. BAL/WETH) 在不同 AMM (Pair0 和 Pair1) 中是否存在價差
- 假設 Pair0 和 Pair1 之間存在差價,合約計算兩個 pair 中的 Quote Token 價格
- 令 Pair0 為 lowerPrice Pool,Pair1 為 higherPrice Pool
- 計算 Pair0 和 Pair1 中最佳的 borrowAmount (Quote Token Amount) 使套利收益最大化。
- 藉由 borrowAmount 推算出 Pair0 的 amountIn (Base Token) 與 Pair1 的 amountOut (Base Token)
- 執行 makeFlashLoan,透過 Balancer Flashloan 借出數量為 amountIn 的 Base Token,
- 在 receiveFlashLoan 中向 lowerPrice Pool 買入 amountIn 數量的 Base Token, 接著向 Pair1 中賣出 amountOut 數量的 Base Token,最後 repayFlashloan amountIn 數量的 Base Token
- (amountOut - amountIn) 的 Base Token 即為本次交易淨利
![image](https://private-user-images.githubusercontent.com/57789692/294743041-5ba262c8-d374-46c6-af96-1ac618a974a7.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQyNDIsIm5iZiI6MTczOTQ1Mzk0MiwicGF0aCI6Ii81Nzc4OTY5Mi8yOTQ3NDMwNDEtNWJhMjYyYzgtZDM3NC00NmM2LWFmOTYtMWFjNjE4YTk3NGE3LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzMzkwMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWU0MmIzODFkNzQwZWQ2YjY1MTEwMmI2YmVhZDJhMmY0YzQ5YzUwZjRhNmIxN2NhMzMyMDRmYmZkZGYyMDg4ZmUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.hBwgbiKs66u_opNAWPNEiy9C0MDKxy8eNeALEirdRa4)
$ git clone https://github.com/tannerang/FinalProject.git
$ cd FinalProject
$ forge install
$ forge test
// in .env file
MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/<YOUR_RPC_URL>
INFURA_WEBSOCKET_URL=wss://mainnet.infura.io/ws/v3/<YOUR_API_KEY>
File | % Lines | % Statements | % Branches | % Funcs |
---|---|---|---|---|
src/FlashBot.sol | 88.16% (216/245) | 90.45% (284/314) | 48.94% (46/94) | 100.00% (28/28) |
Total | 88.16% (216/245) | 90.45% (284/314) | 48.94% (46/94) | 100.00% (28/28) |
- 目前僅包含 UniSwapV2、Balancer 兩種 AMM
- 目前僅適用
Decimal = 1e18
的代幣 - Balancer AMM 適用的池子僅限於代幣權重皆為 50%
- Base Token 預設為 WETH
已知 Pair0 = Lower Price Pool
、Pair1 = Higher Price Pool
,假設 Pair0 與 Pair1 的初始狀態如下:
Pair0 | Pair1 | |
---|---|---|
Base Token Reserve | a1 | a2 |
Quote Token Reserve | b1 | b2 |
我們預計在較便宜的 Pair0 中買入 Delta b1
數量的 Quote Token,基於 x * y = k
的公式,可得:
接著在較高價的 Pair1 中賣出 Delta b2
數量的 Quote Token,可得:
整理上方兩式得:
因我們在低價池買入的 Quote Token 數量和在高價池賣出的 Quote Token 數量相同,故 Delta b1 = Delta b2 (= Delta b)
我們令 x = Delta b
,那麼 x 與利潤 (Delta a2 - Delta a1
) 的函數為:
欲求出利潤最大時的 x 值,可以對上面的函數求導函數:
當導函數為 0 時,存在極大/極小值,再透過一些條件設定忽略極小值時的解。令導函數等於 0 得:
我們假設:
將前述的方程式轉換成一般的一元二次方程式:
得解:
最後求出滿足條件的 x 值,即為能使利潤最大化的 Quote Token 數量
然而,上述推論僅適用於符合 x * y = k
公式的 AMM,也就是交易對僅包含兩種代幣且權重各為 50% 的 AMM
無法適用像 Balancer 這種可以自定義交易對代幣數量和代幣權重的 AMM,這種 AMM 在利潤最大化之下的 Quote Token 最佳數量計算也隨之複雜
根據 Balancer 白皮書,已知以下公式:
Balancer: Spot Price Calc
![image](https://private-user-images.githubusercontent.com/57789692/292639118-80447c0b-cad2-4851-846d-612f9e23aa1e.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQyNDIsIm5iZiI6MTczOTQ1Mzk0MiwicGF0aCI6Ii81Nzc4OTY5Mi8yOTI2MzkxMTgtODA0NDdjMGItY2FkMi00ODUxLTg0NmQtNjEyZjllMjNhYTFlLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzMzkwMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWRhMjRjN2NlNWUwZWRlMTFkZTA0MDUxZWFjNjNiOTgwNzkzY2M1ZGM3MDg1OWU0MTM0ZjM4NDE2OTAxNTVlNWImWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.gwC9Zz0rs4Tna6r9qCVE31WPE-HMdsLx7PC_AbUskNI)
Balancer: Out-Given-In
![image](https://private-user-images.githubusercontent.com/57789692/292639145-7abf0153-c470-4662-92a7-b38889a8b54d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQyNDIsIm5iZiI6MTczOTQ1Mzk0MiwicGF0aCI6Ii81Nzc4OTY5Mi8yOTI2MzkxNDUtN2FiZjAxNTMtYzQ3MC00NjYyLTkyYTctYjM4ODg5YThiNTRkLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzMzkwMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ3OWFhYTg5MjVlZDAyNzU4YjMxM2FmZWJhYTExNmJlZWYyYzg0NGI5ZGJlZDkxMjcyYjQ4M2Y4YzI4MjdlNWMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.BzG8kD1n0YD9iyRWDLAgDefZQ_KCYQO8QkFicUyGTV4)
Balancer: In-Given-Out
![image](https://private-user-images.githubusercontent.com/57789692/292639179-7400c51f-14e1-409e-b722-9a9051cb3297.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQyNDIsIm5iZiI6MTczOTQ1Mzk0MiwicGF0aCI6Ii81Nzc4OTY5Mi8yOTI2MzkxNzktNzQwMGM1MWYtMTRlMS00MDllLWI3MjItOWE5MDUxY2IzMjk3LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzMzkwMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTllZmZlYjc4NGEzYWQ3OGNjYTJjNzE2MjBkYjI4OGVhZWZlZTEyNmVmOWUzZjMxNWUyMzA0N2ZjYWFiMjdmM2YmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.cerjgNQrNSKob2zGvtZWHMHQtnvLM9WdJaA1Ph6V3II)
若我們想在符合 x * y = k
公式的 AMM 和 Balancer AMM 之間進行套利,假設 PairB = Lower Price Pool
和 PairU = Higher Price Pool
,則初始狀態如下:(在此先不討論 PairB = Higher Price Pool
和 PairU = Lower Price Pool
的情況)
PairB | PairU | |
---|---|---|
Base Token Reserve | a1 | a2 |
Quote Token Reserve | b1 | b2 |
因我們在低價池買入的 Quote Token 數量和在高價池賣出的 Quote Token 數量相同,故令 Quote Token Amount = x
,則 x 與利潤的函數為:
其中:
為了避免 Token Weight 在指數讓導函數的表達式過於複雜,我們令:
再令:
同樣地,欲求出利潤最大時的 x 值,對上面的函數求導函數,得下:
由於當 k 為浮點數時會大幅增加計算難度,故排除考慮所有情況下的權重,僅以實務上常見的權重為主
一般來說,在任意 Balancer AMM 當中的任兩個 Token Weight 幾乎都會落在 20% ~ 80% 的比例之中,也就是 k 值高機率會落在 4 ~ 0.25 之間
此時假設 k 為整數,得:
當 k = 1
時:
![image](https://private-user-images.githubusercontent.com/57789692/294768602-60475e1f-5d02-4853-81fd-31fb6b3dab9c.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQyNDIsIm5iZiI6MTczOTQ1Mzk0MiwicGF0aCI6Ii81Nzc4OTY5Mi8yOTQ3Njg2MDItNjA0NzVlMWYtNWQwMi00ODUzLTgxZmQtMzFmYjZiM2RhYjljLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzMzkwMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTMxZTIyNTdjMzcwYWY0ZjVlZTI0YTJhM2NjYjY0NTM0MTY0NjllOWE4MGFhMjhkZGNkNmZlY2YwMGZmYTc5YmImWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.P9G7p-3ha5g-3adxlXSet1U_Tw08KpVK64mNzmT-Bjk)
分別將 a, b, c, d = a1, b1, a2, b2
代回公式,整理後可得一元二次方程式:
剩下步驟如數學推導後半段,此不贅述
最後可以發現,當 k = 1
的時候,代表兩代幣權重一致 Wb/Wa = 1
,也與 x * y = k
AMM 的計算過程一模一樣,故進行套利時適用代幣權重皆為 50% 的 Balancer AMM。惟 k = 2, 3, 4
時,公式化簡後會變成一元三次、一元四次、一元五次方程式,無法直接帶入公式解求根,故合約不支援其他 k 值的運算
以上說明了注意事項第三點 Balancer AMM 僅適用代幣權重皆為 50% 的池子原因