合约目前仅支持兼容 UniswapV2 接口的 AMM 之间套利。
假设我们要在 代币 TokenX 和代币 WETH 的交易对中套利,那么有交易对 TokenX/WETH:
- 这里的 WETH 称作 Base Token,它可以是任意代币,但必须是「有价值」的,例如 USDT/USDC/DAI/BUSD/WBNB... 等等
- 这里的 TokenX 称为 Quote Token,它可以是任意代币,即使毫无价值也没问题,因为套利结束后不会保留 Quote Token
- 套利结束后,只会保留 Base Token,即赚取的利润是以 Base Token 计价的
- 如果一个交易对中两个代币都可以作为 Base Token,那么会保留任意一个(随机)
套利使用 Uniswap v2 的 flashswap 功能,套利的流程为:
- 假设有交易对 Pair0 和 Pair1,只要他们之间有差价,就可以进行套利条件(抛开 gas 费的考虑)
- 调用合约开始套利
- 合约计算 Quote Token 的价格,假设 Pair0 中 Quote Token 价格较低,那么套利过程为:
- 通过 flash swap ,从 Pair0 中借取数量为 x 的 Quote Token,此时我们产生了一笔负债,在交易结束前,我们需要向 Pair0 偿还数量为 y1 的 Base Token(借出 Token 和还入 Token 不同,这个是 Uni flash swap 的功能,只要保证交易对 k 值不变即可)
- 将借来的 Quote Token 在 Pair1 中全部卖出,得到数量为 y2 的 Base Token
- 向 Pair0 偿还 Base Token,数量为 y1
- 交易结束,净利润为 y2 - y1
这里的关键点是计算需要借出的 Quote Token 数量,使得套利的收益最大化。
我们假设 Pair0 和 Pair1 的初始状态如下:
Pair0 | Pair1 | |
---|---|---|
Base Token 余额 | a1 | a2 |
Quote Token 余额 | b1 | b2 |
那么有:
因为借出的 Quote Token 数量相同,即 Delta b1
= Delta b2
,我们令 x = \Delta b
,那么利润与 x 关系的函数为:
我们需要求出当利润最大时 x 的值,此时 x 即为我们需要借出的 Quote Token 数量。先对上面的函数求导:
导函数为 0 时,函数有极限值,我们可以通过一些条件设定,忽略极小值时的解。可以解出:
我们可以令:
那么前面的方程式化为一般的一元二次方程:
解得:
最后求出满足条件的 x 值,即为我们需要借贷的 Quote Token 数量。
-
编辑
hardhat.config.ts
中的网络配置。(目前都是 BSC 的地址)。 -
拷贝私钥配置文件:
$ cp .secret.ts.sample .secret.ts
- 填入部署账户的私钥和地址信息。运行脚本部署合约:
$ hardhart --network XXX run scripts/deploy.ts
合约提供了 getProfit(address pool1, address pool2)
接口,可以计算出两个交易对之间套利的最大利润(以 Base Tokne计价)。
Bot 需要在多个 AMM DEX 的多个代币对之间,调用 getProfit()
查询利润,一但利润满足设定的阈值,即可调用 flashArbitrage(pool1, pool2)
进行套利,获得的收益将保存在合约中。
项目实现了 typescript 版本的 bot,运行方式:
$ yarn run bot
- PancakeSwap
- MDEX
- BakerySwap
- JulSwap
ValueDeFi(暂不支持)
$ hardhat test