187 lines
6.1 KiB
Solidity
187 lines
6.1 KiB
Solidity
/*
|
|
|
|
Copyright 2020 DODO ZOO.
|
|
SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
pragma solidity 0.6.9;
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
import {SafeMath} from "../lib/SafeMath.sol";
|
|
import {DecimalMath} from "../lib/DecimalMath.sol";
|
|
import {DODOMath} from "../lib/DODOMath.sol";
|
|
import {Types} from "../lib/Types.sol";
|
|
import {Storage} from "./Storage.sol";
|
|
|
|
/**
|
|
* @title Pricing
|
|
* @author DODO Breeder
|
|
*
|
|
* @notice DODO Pricing model
|
|
*/
|
|
contract Pricing is Storage {
|
|
using SafeMath for uint256;
|
|
|
|
// ============ R = 1 cases ============
|
|
|
|
function _ROneSellBaseToken(uint256 amount, uint256 targetQuoteTokenAmount)
|
|
internal
|
|
view
|
|
returns (uint256 receiveQuoteToken)
|
|
{
|
|
uint256 i = getOraclePrice();
|
|
uint256 Q2 = DODOMath._SolveQuadraticFunctionForTrade(
|
|
targetQuoteTokenAmount,
|
|
targetQuoteTokenAmount,
|
|
DecimalMath.mul(i, amount),
|
|
false,
|
|
_K_
|
|
);
|
|
// in theory Q2 <= targetQuoteTokenAmount
|
|
// however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount
|
|
return targetQuoteTokenAmount.sub(Q2);
|
|
}
|
|
|
|
function _ROneBuyBaseToken(uint256 amount, uint256 targetBaseTokenAmount)
|
|
internal
|
|
view
|
|
returns (uint256 payQuoteToken)
|
|
{
|
|
require(amount < targetBaseTokenAmount, "DODO_BASE_TOKEN_BALANCE_NOT_ENOUGH");
|
|
uint256 B2 = targetBaseTokenAmount.sub(amount);
|
|
payQuoteToken = _RAboveIntegrate(targetBaseTokenAmount, targetBaseTokenAmount, B2);
|
|
return payQuoteToken;
|
|
}
|
|
|
|
// ============ R < 1 cases ============
|
|
|
|
function _RBelowSellBaseToken(
|
|
uint256 amount,
|
|
uint256 quoteBalance,
|
|
uint256 targetQuoteAmount
|
|
) internal view returns (uint256 receieQuoteToken) {
|
|
uint256 i = getOraclePrice();
|
|
uint256 Q2 = DODOMath._SolveQuadraticFunctionForTrade(
|
|
targetQuoteAmount,
|
|
quoteBalance,
|
|
DecimalMath.mul(i, amount),
|
|
false,
|
|
_K_
|
|
);
|
|
return quoteBalance.sub(Q2);
|
|
}
|
|
|
|
function _RBelowBuyBaseToken(
|
|
uint256 amount,
|
|
uint256 quoteBalance,
|
|
uint256 targetQuoteAmount
|
|
) internal view returns (uint256 payQuoteToken) {
|
|
// Here we don't require amount less than some value
|
|
// Because it is limited at upper function
|
|
// See Trader.queryBuyBaseToken
|
|
uint256 i = getOraclePrice();
|
|
uint256 Q2 = DODOMath._SolveQuadraticFunctionForTrade(
|
|
targetQuoteAmount,
|
|
quoteBalance,
|
|
DecimalMath.mul(i, amount),
|
|
true,
|
|
_K_
|
|
);
|
|
return Q2.sub(quoteBalance);
|
|
}
|
|
|
|
function _RBelowBackToOne()
|
|
internal
|
|
view
|
|
returns (uint256 payQuoteToken, uint256 receiveBaseToken)
|
|
{
|
|
// important: carefully design the system to make sure spareBase always greater than or equal to 0
|
|
uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_);
|
|
uint256 price = getOraclePrice();
|
|
uint256 fairAmount = DecimalMath.mul(spareBase, price);
|
|
uint256 newTargetQuote = DODOMath._SolveQuadraticFunctionForTarget(
|
|
_QUOTE_BALANCE_,
|
|
_K_,
|
|
fairAmount
|
|
);
|
|
return (newTargetQuote.sub(_QUOTE_BALANCE_), spareBase);
|
|
}
|
|
|
|
// ============ R > 1 cases ============
|
|
|
|
function _RAboveBuyBaseToken(
|
|
uint256 amount,
|
|
uint256 baseBalance,
|
|
uint256 targetBaseAmount
|
|
) internal view returns (uint256 payQuoteToken) {
|
|
require(amount < baseBalance, "DODO_BASE_TOKEN_BALANCE_NOT_ENOUGH");
|
|
uint256 B2 = baseBalance.sub(amount);
|
|
return _RAboveIntegrate(targetBaseAmount, baseBalance, B2);
|
|
}
|
|
|
|
function _RAboveSellBaseToken(
|
|
uint256 amount,
|
|
uint256 baseBalance,
|
|
uint256 targetBaseAmount
|
|
) internal view returns (uint256 receiveQuoteToken) {
|
|
// here we don't require B1 <= targetBaseAmount
|
|
// Because it is limited at upper function
|
|
// See Trader.querySellBaseToken
|
|
uint256 B1 = baseBalance.add(amount);
|
|
return _RAboveIntegrate(targetBaseAmount, B1, baseBalance);
|
|
}
|
|
|
|
function _RAboveBackToOne()
|
|
internal
|
|
view
|
|
returns (uint256 payBaseToken, uint256 receiveQuoteToken)
|
|
{
|
|
// important: carefully design the system to make sure spareBase always greater than or equal to 0
|
|
uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_);
|
|
uint256 price = getOraclePrice();
|
|
uint256 fairAmount = DecimalMath.divFloor(spareQuote, price);
|
|
uint256 newTargetBase = DODOMath._SolveQuadraticFunctionForTarget(
|
|
_BASE_BALANCE_,
|
|
_K_,
|
|
fairAmount
|
|
);
|
|
return (newTargetBase.sub(_BASE_BALANCE_), spareQuote);
|
|
}
|
|
|
|
// ============ Helper functions ============
|
|
|
|
function getExpectedTarget() public view returns (uint256 baseTarget, uint256 quoteTarget) {
|
|
uint256 Q = _QUOTE_BALANCE_;
|
|
uint256 B = _BASE_BALANCE_;
|
|
if (_R_STATUS_ == Types.RStatus.ONE) {
|
|
return (_TARGET_BASE_TOKEN_AMOUNT_, _TARGET_QUOTE_TOKEN_AMOUNT_);
|
|
} else if (_R_STATUS_ == Types.RStatus.BELOW_ONE) {
|
|
(uint256 payQuoteToken, uint256 receiveBaseToken) = _RBelowBackToOne();
|
|
return (B.sub(receiveBaseToken), Q.add(payQuoteToken));
|
|
} else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
|
|
(uint256 payBaseToken, uint256 receiveQuoteToken) = _RAboveBackToOne();
|
|
return (B.add(payBaseToken), Q.sub(receiveQuoteToken));
|
|
}
|
|
}
|
|
|
|
function _RAboveIntegrate(
|
|
uint256 B0,
|
|
uint256 B1,
|
|
uint256 B2
|
|
) internal view returns (uint256) {
|
|
uint256 i = getOraclePrice();
|
|
return DODOMath._GeneralIntegrate(B0, B1, B2, i, _K_);
|
|
}
|
|
|
|
// function _RBelowIntegrate(
|
|
// uint256 Q0,
|
|
// uint256 Q1,
|
|
// uint256 Q2
|
|
// ) internal view returns (uint256) {
|
|
// uint256 i = getOraclePrice();
|
|
// i = DecimalMath.divFloor(DecimalMath.ONE, i); // 1/i
|
|
// return DODOMath._GeneralIntegrate(Q0, Q1, Q2, i, _K_);
|
|
// }
|
|
}
|