diff --git a/contracts/DODOVendingMachine/impl/DVMTrader.sol b/contracts/DODOVendingMachine/impl/DVMTrader.sol index 4a275b8..5c9f35b 100644 --- a/contracts/DODOVendingMachine/impl/DVMTrader.sol +++ b/contracts/DODOVendingMachine/impl/DVMTrader.sol @@ -151,54 +151,6 @@ contract DVMTrader is DVMVault { return (receiveBaseAmount, mtFee); } - // // 这是一个仅供查询的合约,所有交易都是基于先给input,再输出output的 - // // 所以想要买10ETH,这个函数可以给你一个大概的成本,你用这个成本输入,最后能否得到10ETH是要看情况的 - // function queryBuyBase(address trader, uint256 receiveBaseAmount) - // public - // view - // returns (uint256 payQuoteAmount) - // { - // uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader); - // uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader); - // uint256 validReceiveBaseAmount = DecimalMath.divCeil( - // receiveBaseAmount, - // DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate) - // ); - // (uint256 baseReserve, uint256 quoteReserve) = getVaultReserve(); - // require(baseReserve > validReceiveBaseAmount, "DODO_BASE_BALANCE_NOT_ENOUGH"); - - // uint256 B0 = calculateBase0(baseReserve, quoteReserve); - // uint256 B2 = baseReserve.sub(validReceiveBaseAmount); - // payQuoteAmount = DODOMath._GeneralIntegrate(B0, baseReserve, B2, _I_, _K_); - // return payQuoteAmount; - // } - - // function queryBuyQuote(address trader, uint256 receiveQuoteAmount) - // public - // view - // returns (uint256 payBaseAmount) - // { - // uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader); - // uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader); - // uint256 validReceiveQuoteAmount = DecimalMath.divCeil( - // receiveQuoteAmount, - // DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate) - // ); - // (uint256 baseReserve, uint256 quoteReserve) = getVaultReserve(); - // require(quoteReserve > validReceiveQuoteAmount, "DODO_QUOTE_BALANCE_NOT_ENOUGH"); - - // uint256 B0 = calculateBase0(baseReserve, quoteReserve); - // uint256 fairAmount = DecimalMath.divFloor(validReceiveQuoteAmount, _I_); - // payBaseAmount = DODOMath._SolveQuadraticFunctionForTrade( - // B0, - // baseReserve, - // fairAmount, - // true, - // _K_ - // ); - // return payBaseAmount; - // } - function getMidPrice() public view returns (uint256 midPrice) { return PMMPricing.getMidPrice(getPMMState()); } @@ -217,13 +169,23 @@ contract DVMTrader is DVMVault { } function calculateBase0(uint256 baseAmount, uint256 quoteAmount) public view returns (uint256) { - uint256 fairAmount = DecimalMath.divFloor(quoteAmount, _I_); - return DODOMath._SolveQuadraticFunctionForTarget(baseAmount, _K_, fairAmount); + return + DODOMath._SolveQuadraticFunctionForTarget( + baseAmount, + quoteAmount, + DecimalMath.reciprocalFloor(_I_), + _K_ + ); } function getBase0() public view returns (uint256) { (uint256 baseAmount, uint256 quoteAmount) = getVaultReserve(); - uint256 fairAmount = DecimalMath.divFloor(quoteAmount, _I_); - return DODOMath._SolveQuadraticFunctionForTarget(baseAmount, _K_, fairAmount); + return + DODOMath._SolveQuadraticFunctionForTarget( + baseAmount, + quoteAmount, + DecimalMath.reciprocalFloor(_I_), + _K_ + ); } } diff --git a/contracts/lib/DODOMath.sol b/contracts/lib/DODOMath.sol index 8bbacb6..e1844e2 100644 --- a/contracts/lib/DODOMath.sol +++ b/contracts/lib/DODOMath.sol @@ -26,6 +26,10 @@ library DODOMath { res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1) let V1-V2=delta res = i*delta*(1-k+k(V0^2/V1/V2)) + + i is the price of res-V trading pair + + [round down] */ function _GeneralIntegrate( uint256 V0, @@ -35,94 +39,114 @@ library DODOMath { uint256 k ) internal pure returns (uint256) { require(V0 > 0, "TARGET_IS_ZERO"); - uint256 fairAmount = DecimalMath.mul(i, V1.sub(V2)); // i*delta - uint256 V0V0V1V2 = DecimalMath.divCeil(V0.mul(V0).div(V1), V2); - uint256 penalty = DecimalMath.mul(k, V0V0V1V2); // k(V0^2/V1/V2) - return DecimalMath.mul(fairAmount, DecimalMath.ONE.sub(k).add(penalty)); + uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta + uint256 V0V0V1V2 = DecimalMath.divFloor(V0.mul(V0).div(V1), V2); + uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2) + return DecimalMath.ONE.sub(k).add(penalty).mul(fairAmount).div(DecimalMath.ONE2); } /* - The same with integration expression above, we have: + Follow the integration function above + i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) + Assume Q2=Q0, Given Q1 and deltaB, solve Q0 + + i is the price of delta-V trading pair + + [round down] + */ + function _SolveQuadraticFunctionForTarget( + uint256 V1, + uint256 delta, + uint256 i, + uint256 k + ) internal pure returns (uint256 V0) { + // V0 = V1*(1+(sqrt-1)/2k) + // sqrt = √(1+4kidelta/V1) + // premium = 1+(sqrt-1)/2k + uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt(); + uint256 premium = DecimalMath.divFloor(sqrt.sub(DecimalMath.ONE), k * 2).add( + DecimalMath.ONE + ); + // V0 is greater than or equal to V1 according to the solution + return DecimalMath.mul(V1, DecimalMath.ONE.add(premium)); + } + + /* + Follow the integration expression above, we have: i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) Given Q1 and deltaB, solve Q2 This is a quadratic function and the standard version is aQ2^2 + bQ2 + c = 0, where a=1-k -b=(1-k)Q1-kQ0^2/Q1+i*deltaB - c=-kQ0^2 + c=-kQ0^2 and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k) note: another root is negative, abondan + if deltaBSig=true, then Q2>Q1, user sell Q and receive B if deltaBSig=false, then Q2 0, "TARGET_IS_ZERO"); + require(V0 > 0, "TARGET_IS_ZERO"); if (k == DecimalMath.ONE) { - // Q2=Q1/(1-ideltaBQ1/Q0/Q0) - uint256 temp = ideltaB.mul(Q1).mul(DecimalMath.ONE).div(Q0.mul(Q0)); - return Q1.sub(DecimalMath.divFloor(DecimalMath.ONE, DecimalMath.ONE.add(temp))); + // if k==1 + // Q2=Q1/(1+ideltaBQ1/Q0/Q0) + // temp = (1+ideltaBQ1/Q0/Q0) + // Q1-Q2 = Q1*(temp/(1+temp)) + uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0)); + return V1.mul(temp).div(temp.add(DecimalMath.ONE)); } + // calculate -b value and sig // -b = (1-k)Q1-kQ0^2/Q1+i*deltaB - uint256 kQ02Q1 = DecimalMath.mul(k, Q0).mul(Q0).div(Q1); // kQ0^2/Q1 - uint256 b = DecimalMath.mul(DecimalMath.ONE.sub(k), Q1); // (1-k)Q1 - bool minusbSig = true; - kQ02Q1 = kQ02Q1.add(ideltaB); // i*deltaB+kQ0^2/Q1 - if (b >= kQ02Q1) { - b = b.sub(kQ02Q1); - minusbSig = true; + // part1 = (1-k)Q1 >=0 + // part2 = -i*deltaB+kQ0^2/Q1 >=0 + // bAbs = abs(part1-part2) + // if part1>part2 => b is negative => bSig is false + // if part2>part1 => b is positive => bSig is true + uint256 part2 = k.mul(V0).div(V1).mul(V0).add(i.mul(delta)); // kQ0^2/Q1-i*deltaB + uint256 bAbs = DecimalMath.ONE.sub(k).mul(V1); // (1-k)Q1 + + bool bSig; + if (bAbs >= part2) { + bAbs = bAbs.sub(part2); + bSig = false; } else { - b = kQ02Q1.sub(b); - minusbSig = false; + bAbs = part2.sub(bAbs); + bSig = true; } + bAbs = bAbs.div(DecimalMath.ONE); // calculate sqrt uint256 squareRoot = DecimalMath.mul( DecimalMath.ONE.sub(k).mul(4), - DecimalMath.mul(k, Q0).mul(Q0) + DecimalMath.mul(k, V0).mul(V0) ); // 4(1-k)kQ0^2 - squareRoot = b.mul(b).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0) + squareRoot = bAbs.mul(bAbs).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0) // final res uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k) uint256 numerator; - if (minusbSig) { - numerator = b.add(squareRoot); + if (bSig) { + numerator = squareRoot.sub(bAbs); } else { - numerator = squareRoot.sub(b); + numerator = bAbs.add(squareRoot); } - return Q1.sub(DecimalMath.divCeil(numerator, denominator)); - } - - /* - Start from the integration function - i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2) - Assume Q2=Q0, Given Q1 and deltaB, solve Q0 - let fairAmount = i*deltaB - */ - function _SolveQuadraticFunctionForTarget( - uint256 V1, - uint256 k, - uint256 fairAmount - ) internal pure returns (uint256 V0) { - // V0 = V1+V1*(sqrt-1)/2k - uint256 sqrt = DecimalMath.divCeil(DecimalMath.mul(k, fairAmount).mul(4), V1); - sqrt = sqrt.add(DecimalMath.ONE).mul(DecimalMath.ONE).sqrt(); - uint256 premium = DecimalMath.divCeil(sqrt.sub(DecimalMath.ONE), k.mul(2)); - // V0 is greater than or equal to V1 according to the solution - return DecimalMath.mul(V1, DecimalMath.ONE.add(premium)); + return V1.sub(DecimalMath.divCeil(numerator, denominator)); } } diff --git a/contracts/lib/DecimalMath.sol b/contracts/lib/DecimalMath.sol index 0a406a4..6218b17 100644 --- a/contracts/lib/DecimalMath.sol +++ b/contracts/lib/DecimalMath.sol @@ -10,7 +10,6 @@ pragma experimental ABIEncoderV2; import {SafeMath} from "./SafeMath.sol"; - /** * @title DecimalMath * @author DODO Breeder @@ -21,24 +20,33 @@ library DecimalMath { using SafeMath for uint256; uint256 constant ONE = 10**18; + uint256 constant ONE2 = 10**36; function mul(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(d) / ONE; + return target.mul(d) / (10**18); } function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(d) / ONE; + return target.mul(d) / (10**18); } function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(d).divCeil(ONE); + return target.mul(d).divCeil(10**18); } function divFloor(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(ONE).div(d); + return target.mul(10**18).div(d); } function divCeil(uint256 target, uint256 d) internal pure returns (uint256) { - return target.mul(ONE).divCeil(d); + return target.mul(10**18).divCeil(d); + } + + function reciprocalFloor(uint256 target) internal pure returns (uint256) { + return uint256(10**36).div(target); + } + + function reciprocalCeil(uint256 target) internal pure returns (uint256) { + return uint256(10**36).divCeil(target); } } diff --git a/contracts/lib/PMMPricing.sol b/contracts/lib/PMMPricing.sol index a6846ab..7e274b8 100644 --- a/contracts/lib/PMMPricing.sol +++ b/contracts/lib/PMMPricing.sol @@ -126,7 +126,8 @@ library PMMPricing { DODOMath._SolveQuadraticFunctionForTrade( state.Q0, state.Q0, - DecimalMath.mulFloor(state.i, payBaseAmount), + payBaseAmount, + state.i, state.K ); } @@ -140,7 +141,8 @@ library PMMPricing { DODOMath._SolveQuadraticFunctionForTrade( state.B0, state.B0, - DecimalMath.divFloor(payQuoteAmount, state.i), + payQuoteAmount, + DecimalMath.reciprocalFloor(state.i), state.K ); } @@ -157,7 +159,7 @@ library PMMPricing { state.Q0, state.Q.add(payQuoteAmount), state.Q, - DecimalMath.divFloor(DecimalMath.ONE, state.i), + DecimalMath.reciprocalFloor(state.i), state.K ); } @@ -171,7 +173,8 @@ library PMMPricing { DODOMath._SolveQuadraticFunctionForTrade( state.Q0, state.Q, - DecimalMath.mul(state.i, payBaseAmount), + payBaseAmount, + state.i, state.K ); } @@ -202,7 +205,8 @@ library PMMPricing { DODOMath._SolveQuadraticFunctionForTrade( state.B0, state.B, - DecimalMath.divFloor(payQuoteAmount, state.i), + payQuoteAmount, + DecimalMath.reciprocalFloor(state.i), state.K ); } @@ -212,11 +216,19 @@ library PMMPricing { // todo 我不确定这个函数是不是能改state的状态 function adjustedTarget(PMMState memory state) internal pure { if (state.R == RState.BELOW_ONE) { - uint256 fairAmount = DecimalMath.mulFloor(state.B.sub(state.B0), state.i); - state.Q0 = DODOMath._SolveQuadraticFunctionForTarget(state.B, state.K, fairAmount); + state.Q0 = DODOMath._SolveQuadraticFunctionForTarget( + state.B, + state.B.sub(state.B0), + state.i, + state.K + ); } else if (state.R == RState.ABOVE_ONE) { - uint256 fairAmount = DecimalMath.divFloor(state.Q.sub(state.Q0), state.i); - state.B0 = DODOMath._SolveQuadraticFunctionForTarget(state.Q, state.K, fairAmount); + state.B0 = DODOMath._SolveQuadraticFunctionForTarget( + state.Q, + state.Q.sub(state.Q0), + DecimalMath.reciprocalFloor(state.i), + state.K + ); } }