/* Copyright 2020 DODO ZOO. SPDX-License-Identifier: Apache-2.0 */ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; import {SafeMath} from "./SafeMath.sol"; import {DecimalMath} from "./DecimalMath.sol"; /** * @title DODOMath * @author DODO Breeder * * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions */ library DODOMath { using SafeMath for uint256; /* Integrate dodo curve from V1 to V2 require V0>=V1>=V2>0 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 V-res trading pair support k=1 & k=0 case [round down] */ function _GeneralIntegrate( uint256 V0, uint256 V1, uint256 V2, uint256 i, uint256 k ) internal pure returns (uint256) { require(V0 > 0, "TARGET_IS_ZERO"); uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta if (k == 0) { return fairAmount.div(DecimalMath.ONE); } 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); } /* 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 give out target of V support k=1 & k=0 case [round down] */ function _SolveQuadraticFunctionForTarget( uint256 V1, uint256 delta, uint256 i, uint256 k ) internal pure returns (uint256) { if (k == 0) { return V1.add(DecimalMath.mulFloor(i, delta)); } // 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(); if (V1 == 0) { return 0; } uint256 sqrt; uint256 ki = (4 * k).mul(i); if (ki == 0) { sqrt = DecimalMath.ONE; } else if ((ki * delta) / ki == delta) { sqrt = (ki * delta).div(V1).add(DecimalMath.ONE2).sqrt(); } else { sqrt = ki.div(V1).mul(delta).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.mulFloor(V1, 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 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"); if (delta == 0) { return 0; } if (k == 0) { return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta); } if (k == DecimalMath.ONE) { // if k==1 // Q2=Q1/(1+ideltaBQ1/Q0/Q0) // temp = ideltaBQ1/Q0/Q0 // Q2 = Q1/(1+temp) // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp)) // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0)); uint256 temp; uint256 idelta = i.mul(delta); if (idelta == 0) { temp = 0; } else if ((idelta * V1) / idelta == V1) { temp = (idelta * V1).div(V0.mul(V0)); } else { temp = delta.mul(V1).div(V0).mul(i).div(V0); } return V1.mul(temp).div(temp.add(DecimalMath.ONE)); } // calculate -b value and sig // b = kQ0^2/Q1-i*deltaB-(1-k)Q1 // part1 = (1-k)Q1 >=0 // part2 = kQ0^2/Q1-i*deltaB >=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 - part2; bSig = false; } else { bAbs = part2 - bAbs; bSig = true; } bAbs = bAbs.div(DecimalMath.ONE); // calculate sqrt uint256 squareRoot = DecimalMath.mulFloor( DecimalMath.ONE.sub(k).mul(4), DecimalMath.mulFloor(k, V0).mul(V0) ); // 4(1-k)kQ0^2 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 (bSig) { numerator = squareRoot.sub(bAbs); } else { numerator = bAbs.add(squareRoot); } uint256 V2 = DecimalMath.divCeil(numerator, denominator); if (V2 > V1) { return 0; } else { return V1 - V2; } } }