diff --git a/.gitignore b/.gitignore index e9a6cc6..06def01 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,6 @@ build/ dist/ -docs/img -docs/src node_modules/ coverage/ lint/ diff --git a/docs/DODO Smart Contract Instructions.html b/docs/DODO Smart Contract Instructions.html deleted file mode 100644 index 7aac059..0000000 --- a/docs/DODO Smart Contract Instructions.html +++ /dev/null @@ -1,968 +0,0 @@ - - - -
- - - -This is an instructions for users who want to interact with dodo smart contract directly.
- -Each trading pair is an independent DODO smart contract, and all DODO contracts are registered in DODO ZOO.
- -Users interact with each DODO smart contract directly. All external functions are divided into three sub-contracts according to the role of their users: Admin, Trader and LiquidityProvider.
- -To simplify users' operations, we developed DODO ETH Proxy, a helper contract that helps user handle with WETH-ETH converting.
- -
sellBaseToken(uint256 amount, uint256 minReceiveQuote) returns (uint256 receiveQuote)
| sellBaseToken | -Desc | -
|---|---|
| @dev | -Sell base token and receive quote token | -
| @param amount | -The amount of base token to sell | -
| @param minReceiveQuote | -The minimum amount of quote token required. Transaction will be reverted if the trader receive quote token less than this value. | -
| @returns receiveQuote | -The amount of quote token received | -
buyBaseToken(uint256 amount, uint256 maxPayQuote) returns (uint256 payQuote)
| buyBaseToken | -Desc | -
|---|---|
| @dev | -Buy base token and pay quote token | -
| @param amount | -The amount of base token to buy | -
| @param maxPayQuote | -The maximum amount of quote token needs to pay. Transaction will be reverted if the trader pay quote token greater than this value. | -
| @returns payQuote | -The amount of quote token paid | -
depositBase(uint256 amount)
| depositBase | -Desc | -
|---|---|
| @dev | -Deposit base token to liquidity pool | -
| @param amount | -The amount of base token to deposit | -
depositQuote(uint256 amount)
| depositQuote | -Desc | -
|---|---|
| @dev | -Deposit quote token to liquidity pool | -
| @param amount | -The amount of quote token to deposit | -
withdrawBase(uint256 amount) returns (uint256 receiveAmount)
| withdrawBase | -Desc | -
|---|---|
| @dev | -Withdraw base token from liquidity pool | -
| @param amount | -The amount of base token to withdraw | -
| @returns receiveAmount | -The amount of base token sender received | -
| @notice | -DODO charges penalty fee from liquidity providers who withdraw assets and share the fee with all remaining liquidity providers. Normally the penalty would be 0 or nearly 0. | -
withdrawQuote(uint256 amount) returns (uint256 receiveAmount)
| withdrawQuote | -Desc | -
|---|---|
| @dev | -Withdraw quote token from liquidity pool | -
| @param amount | -The amount of quote token to withdraw | -
| @returns receiveAmount | -The amount of quote token sender received | -
| @notice | -DODO charges penalty fee from liquidity providers who withdraw assets and share the fee with all remaining liquidity providers. Normally the penalty would be 0 or nearly 0. | -
withdrawAllBase() returns (uint256 receiveAmount)
| withdrawAllBase | -Desc | -
|---|---|
| @dev | -Withdraw message sender’s all base token deposited in the pool. | -
withdrawAllQuote() returns (uint256 receiveAmount)
| withdrawAllQuote | -Desc | -
|---|---|
| @dev | -Withdraw message sender’s all quote token deposited in the pool. | -
_GAS_PRICE_LIMIT_
| _GASPRICELIMIT_ | -Desc | -
|---|---|
| @dev | -A public variable. All transactions involving trading should be sent with gas price no greater than this value. Otherwise, DODO will revert the transactions. | -
getLpBaseBalance(address lp) returns (uint256 lpBalance)
| getLpBaseBalance | -Desc | -
|---|---|
| @dev | -Get liquidity provider’s base token balance in the pool | -
getLpQuoteBalance(address lp) returns (uint256 lpBalance)
| getLpQuoteBalance | -Desc | -
|---|---|
| @dev | -Get liquidity provider’s quote token balance in the pool | -
getWithdrawQuotePenalty(uint256 amount) returns (uint256 penalty)
| getWithdrawQuotePenalty | -Desc | -
|---|---|
| @dev | -Get the penalty fee will be charged if some liquidity provider withdraw a certain amount of quote token from pool. | -
getWithdrawBasePenalty(uint256 amount) returns (uint256 penalty)
| getWithdrawBasePenalty | -Desc | -
|---|---|
| @dev | -Get the penalty fee will be charged if some liquidity provider withdraw a certain amount of base token from pool. | -
querySellBaseToken(uint256 amount) returns (uint256 receiveQuote)
| querySellBaseToken | -Desc | -
|---|---|
| @dev | -A “view” version of sellBaseToken. This function can be really useful to query price because it doesn’t cost gas. |
-
queryBuyBaseToken(uint256 amount) returns (uint256 payQuote)
| queryBuyBaseToken | -Desc | -
|---|---|
| @dev | -A “view” version of buyBaseToken. This function can be really useful to query price because it doesn’t cost gas. |
-
As DODO only works fine with ERC20 tokens, if you want to use Ether directly on DODO, we provide a proxy smart contract to help you access DODO. Basicly, the proxy contract convert Ether to Wrapped ETH, a kind of ERC20 token, and use it on DODO. The operation is totally transparent to user.
- -sellEthTo(address quoteTokenAddress,uint256 ethAmount,uint256 minReceiveTokenAmount) returns (uint256 receiveTokenAmount)
| sellEthTo | -Desc | -
|---|---|
| @dev | -Sell ETH on WETH-quoteTokenAddress market | -
buyEthWith(address quoteTokenAddress,uint256 ethAmount,uint256 maxPayTokenAmount) returns (uint256 payTokenAmount)
| buyEthTo | -Desc | -
|---|---|
| @dev | -Buy ETH on WETH-quoteTokenAddress market | -
depositEth(uint256 ethAmount, address quoteTokenAddress)
| depositEth | -Desc | -
|---|---|
| @dev | -Deposit ETH to WETH-quoteTokenAddress market | -
withdrawBaseTo(address to, uint256 amount) returns (uint256)
depositBaseTo(address to, uint256 amount)
withdrawQuoteTo(address to, uint256 amount) returns (uint256)
depositQuoteTo(address to, uint256 amount)
withdrawAllBaseTo(address to) returns (uint256)
withdrawAllQuoteTo(address to) returns (uint256)
DODO 是新一代由算法驱动的纯链上流动性提供方案。很多人误以为这是对 AMM(Auto Market Maker)算法的改进,事实上 DODO 提出了全新的算法,其运作机制与 AMM 截然不同,我们将其命名为 PMM(Proactive Market Maker)算法。
- -本文将全面对算法进行解析,尤其是其链上实现版本。
- -PMM 的资金池由四个参数描述
- --- -以ETH-USDT交易对为例,BaseToken指ETH,QuoteToken指USDT
-
价格曲线由下面的公式给出
- -\[P_{margin}=iR\] -\[if \ B<B_0, \ R=1-k+(\frac{B_0}{B})^2k\] -\[if \ Q<Q_0, \ R=1/(1-k+(\frac{Q_0}{Q})^2k)\] -\[else \ R=1\]
- -可以发现PMM有三种工作场景:均衡、BaseToken匮乏、QuoteToken匮乏。
- -
参数\(R\)在此过程中起到了促进回归的作用,资金池偏离均衡状态越多,\(R\)越偏离1,PMM算法给出的价格便越偏离市场价,吸引套利者帮助资金池回归均衡状态。
- -至此我们介绍完毕了PMM算法的运作机制,下一节将进入合约实现环节。
- -对交易者来说,最重要的就是平均成交价。平均成交价是边际价格\(P_{margin}\)的积分
- -
\[\Delta Q =\int^{B_2}_{B_1}P_{margin}dB= \int^{B_2}_{B_1}(1-k)i+i(\frac{B_0}{B})^2kdB \] -\[= i(B_2-B_1)*(1-k+k\frac{B_0^2}{B_1B_2})\]
- -平均成交价为:
- -\[P=\frac{\Delta Q}{B_2-B_1}=i*(1-k+k\frac{B_0^2}{B_1B_2})\]
- -我们发现,平均成交价只与成交前后系统的状态有关,所以买卖两种操作的价格计算方式是一样的——都是对\(P_{margin}\)进行积分。
- -为了简化用户理解,我们只提供sellBaseToken和buyBaseToken两个接口,下面来推导quoteToken匮乏场景下,给出想要买卖的baseToken数量,如何计算价格。
- -\[\Delta B = \frac{1}{i}(Q_2-Q_1)*(1-k+k\frac{Q_0^2}{Q_1Q_2})\]
- -已知\(\Delta B, Q_0, Q_1\),求解\(Q_2\),这是一个二次方程,转化为标准形式后:
- -\[(1-k)Q_2^2+(\frac{kQ_0^2}{Q_1}-Q_1+kQ_1-i\Delta B)Q_2-kQ_0^2=0\]
- -\[let \ a=1-k, \ b=\frac{kQ_0^2}{Q_1}-Q_1+kQ_1-i\Delta B, \ c=-kQ_0^2\]
- -因为\(Q_2>=0\),所以舍掉一个负数根,得到\(Q_2\)的公式为
- -\[Q_2=\frac{-b+\sqrt{b^2-4ac}}{2a}\]
- -在\(\Delta B>0\)时,\(Q_2>Q_1\); 在\(\Delta B<0\)时,\(Q_2<Q_1\); 在\(\Delta B=0\)时,\(Q_2=Q_1\)
- -当系统不处于均衡态时,Oracle变化将带来盈利或亏损。举例来讲,假设现在系统处于BaseToken匮乏场景,Oracle价格提高,将导致多余的QuoteToken无法买入足够的BaseToken,提供BaseToken的lp出现亏损。同理如果Oracle价格下降,多余的QuoteToken可以买入更多的BaseToken,使BaseToken余额超过回归目标,提供BaseToken的lp有盈余。
- -为了计算新Oracle价格下的回归目标,我们进行如下推导:
- -\[\Delta Q = i(B_2-B_1)*(1-k+k\frac{B_0^2}{B_1B_2})\]
- -回归时,\(B_2=B_0\),整理出关于\(B_0\)的二次方程
- -\[\frac{k}{B_1}B_0^2+(1-2k)B_0-[(1-k)B_1+\frac{\Delta Q}{i}] = 0\]
- -舍弃掉负数解,得到\(B_0\)的解为
- -\[B_0=B_1+B_1*\frac{\sqrt{1+\frac{4k\Delta Q}{B_1 i}}-1}{2k}\]
- -这里\(\Delta Q=Q-Q_0\). 可以严格证明,在\(\Delta Q \ge 0\)时,\(B_0\ge B_1\). 这一性质非常重要,因为它保证了BaseToken余额和QuoteToken余额不会同时大于回归目标,或同时小于回归目标。这样一来,PMM只会在文章开头提到的三种状态中转换。
- -同理我们直接给出\(Q_0\)的计算公式
- -\[Q_0=Q_1+Q_1*\frac{\sqrt{1+\frac{4k\Delta B i}{Q_1}}-1}{2k}\]
- -由于PMM给出的价格曲线是分段的,如果一笔交易跨越了工作场景(比如在BaseToken匮乏时用户卖出大量BaseToken,直接进入QuoteToken匮乏场景),就需要分段计算价格,并累计。
- -这一累计要求精度很高,但计算机有精度损失,所以处理起来要非常小心。合约里提供了三种工作场景下的买卖函数,总计6个。其组合方式是关键。
- -流动性提供商又称为lp(liquidity provider)。lp有两种操作,充值&提现。
- -在BaseToken匮乏场景下充提BaseToken,或在QuoteToken匮乏场景下充提QuoteToken,都会使得改变价格曲线。这要求我们合理地处理充提操作,以使资金池保持健康和公平。
- -我们以BaseToken匮乏场景为例,研究充值如何影响lp收益。
- -根据上文推导出的\(B_0\)计算公式
- -\[B_0=B_1+B_1*\frac{\sqrt{1+\frac{4k\Delta Q}{B_1 i}}-1}{2k}\]
- -充值\(b\)后,\(B_1\)增加\(b\),而\(B_0\)的增量大于\(b\).就说明充值使得所有提供BaseToken的lp都获得了收益。这一收益的来源是,充值使价格曲线变得平缓,同样数量的\(\Delta Q\)可以购买更多BaseToken。
- -在这种情况下,lp一旦充值就会获得收益,我们称之为“充值奖励”,因为充值使得系统更接近均衡状态了。这笔奖励的本质来源是交易者付出的滑点。
- -但“充值奖励”并不是无风险套利机会,请继续阅读下一节。
- -与上一节同理,提现\(b\)后,\(B_1\)减少\(b\),而\(B_0\)减少的量多于\(b\)。说明提现使所有BaseToken的lp都蒙受损失。这一损失的来源是,提现使价格曲线变得陡峭,同样数量的\(\Delta Q\)只能购买更少的BaseToken。
- -PMM算法规定,这种情况下提现需要缴纳一笔“流动性罚金”。罚金数额等于提币后造成的lp损失,这笔罚金将直接充入资金池,分给所有未撤资的lp。
- -回到上一节中的情况,如果lp充值后立刻提现,罚金将大于收益,所以不会有无风险套利的机会。这一设计倾向于未撤资lp,可以最大限度留存资金。
- --- - - - - - - - - - - - - - -值得注意的是,不论是充值奖励,还是流动性罚金,都只有在系统偏离均衡状态较远,且充提金额很大的时候才显著。 -普通用户往往感知不到这一收益(损失)的存在。当然也欢迎羊毛党,在系统偏离均衡时充值赚取充值奖励,在系统回归平衡时提现来避免流动性罚金
-