PMMPricing library
This commit is contained in:
237
contracts/lib/PMMPricing.sol
Normal file
237
contracts/lib/PMMPricing.sol
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
|
||||
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";
|
||||
|
||||
/**
|
||||
* @title Pricing
|
||||
* @author DODO Breeder
|
||||
*
|
||||
* @notice DODO Pricing model
|
||||
*/
|
||||
|
||||
enum RState {ONE, ABOVE_ONE, BELOW_ONE}
|
||||
|
||||
struct PMMState {
|
||||
uint256 i;
|
||||
uint256 K;
|
||||
uint256 B;
|
||||
uint256 Q;
|
||||
uint256 B0;
|
||||
uint256 Q0;
|
||||
RState R;
|
||||
}
|
||||
|
||||
library PMMPricing {
|
||||
using SafeMath for uint256;
|
||||
|
||||
function sellBaseToken(PMMState memory state, uint256 payBaseAmount)
|
||||
public
|
||||
pure
|
||||
returns (uint256 receiveQuoteAmount, RState newR)
|
||||
{
|
||||
if (state.R == RState.ONE) {
|
||||
// case 1: R=1
|
||||
// R falls below one
|
||||
receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount);
|
||||
newR = RState.BELOW_ONE;
|
||||
} else if (state.R == RState.ABOVE_ONE) {
|
||||
uint256 backToOnePayBase = state.B0.sub(state.B);
|
||||
uint256 backToOneReceiveQuote = state.Q.sub(state.Q0);
|
||||
// case 2: R>1
|
||||
// complex case, R status depends on trading amount
|
||||
if (payBaseAmount < backToOnePayBase) {
|
||||
// case 2.1: R status do not change
|
||||
receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount);
|
||||
newR = RState.ABOVE_ONE;
|
||||
if (receiveQuoteAmount > backToOneReceiveQuote) {
|
||||
// [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount
|
||||
// to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote
|
||||
receiveQuoteAmount = backToOneReceiveQuote;
|
||||
}
|
||||
} else if (payBaseAmount == backToOnePayBase) {
|
||||
// case 2.2: R status changes to ONE
|
||||
receiveQuoteAmount = backToOneReceiveQuote;
|
||||
newR = RState.ONE;
|
||||
} else {
|
||||
// case 2.3: R status changes to BELOW_ONE
|
||||
receiveQuoteAmount = backToOneReceiveQuote.add(
|
||||
_ROneSellBaseToken(state, payBaseAmount.sub(backToOnePayBase))
|
||||
);
|
||||
newR = RState.BELOW_ONE;
|
||||
}
|
||||
} else {
|
||||
// state.R == RState.BELOW_ONE
|
||||
// case 3: R<1
|
||||
receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount);
|
||||
newR = RState.BELOW_ONE;
|
||||
}
|
||||
|
||||
return (receiveQuoteAmount, newR);
|
||||
}
|
||||
|
||||
function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
|
||||
public
|
||||
pure
|
||||
returns (uint256 receiveBaseAmount, RState newR)
|
||||
{
|
||||
if (state.R == RState.ONE) {
|
||||
receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount);
|
||||
newR = RState.ABOVE_ONE;
|
||||
} else if (state.R == RState.ABOVE_ONE) {
|
||||
receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount);
|
||||
newR = RState.ABOVE_ONE;
|
||||
} else {
|
||||
uint256 backToOnePayQuote = state.Q0.sub(state.Q);
|
||||
uint256 backToOneReceiveBase = state.B.sub(state.B0);
|
||||
if (payQuoteAmount < backToOnePayQuote) {
|
||||
receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount);
|
||||
newR = RState.BELOW_ONE;
|
||||
if (receiveBaseAmount > backToOneReceiveBase) {
|
||||
receiveBaseAmount = backToOneReceiveBase;
|
||||
}
|
||||
} else if (payQuoteAmount == backToOnePayQuote) {
|
||||
receiveBaseAmount = backToOneReceiveBase;
|
||||
newR = RState.ONE;
|
||||
} else {
|
||||
receiveBaseAmount = backToOneReceiveBase.add(
|
||||
_ROneSellQuoteToken(state, payQuoteAmount.sub(backToOnePayQuote))
|
||||
);
|
||||
newR = RState.ABOVE_ONE;
|
||||
}
|
||||
}
|
||||
|
||||
return (receiveBaseAmount, newR);
|
||||
}
|
||||
|
||||
// ============ R = 1 cases ============
|
||||
|
||||
function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 receiveQuoteToken)
|
||||
{
|
||||
// in theory Q2 <= targetQuoteTokenAmount
|
||||
// however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount
|
||||
return
|
||||
DODOMath._SolveQuadraticFunctionForTrade(
|
||||
state.Q0,
|
||||
state.Q0,
|
||||
DecimalMath.mulFloor(state.i, payBaseAmount),
|
||||
false,
|
||||
state.K
|
||||
);
|
||||
}
|
||||
|
||||
function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 receiveBaseToken)
|
||||
{
|
||||
return
|
||||
DODOMath._SolveQuadraticFunctionForTrade(
|
||||
state.B0,
|
||||
state.B0,
|
||||
DecimalMath.divFloor(payQuoteAmount, state.i),
|
||||
false,
|
||||
state.K
|
||||
);
|
||||
}
|
||||
|
||||
// ============ R < 1 cases ============
|
||||
|
||||
function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 receiveBaseToken)
|
||||
{
|
||||
return
|
||||
DODOMath._GeneralIntegrate(
|
||||
state.Q0,
|
||||
state.Q.add(payQuoteAmount),
|
||||
state.Q,
|
||||
DecimalMath.divFloor(DecimalMath.ONE, state.i),
|
||||
state.K
|
||||
);
|
||||
}
|
||||
|
||||
function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 receiveQuoteToken)
|
||||
{
|
||||
return
|
||||
DODOMath._SolveQuadraticFunctionForTrade(
|
||||
state.Q0,
|
||||
state.Q,
|
||||
DecimalMath.mul(state.i, payBaseAmount),
|
||||
false,
|
||||
state.K
|
||||
);
|
||||
}
|
||||
|
||||
// ============ R > 1 cases ============
|
||||
|
||||
function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 receiveQuoteToken)
|
||||
{
|
||||
return
|
||||
DODOMath._GeneralIntegrate(
|
||||
state.B0,
|
||||
state.B.add(payBaseAmount),
|
||||
state.B,
|
||||
state.i,
|
||||
state.K
|
||||
);
|
||||
}
|
||||
|
||||
function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 receiveBaseToken)
|
||||
{
|
||||
return
|
||||
DODOMath._SolveQuadraticFunctionForTrade(
|
||||
state.B0,
|
||||
state.B,
|
||||
DecimalMath.divFloor(payQuoteAmount, state.i),
|
||||
false,
|
||||
state.i
|
||||
);
|
||||
}
|
||||
|
||||
// ============ Helper functions ============
|
||||
|
||||
function adjustedTarget(PMMState memory state) public 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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
function getMidPrice(PMMState memory state) public pure returns (uint256 midPrice) {
|
||||
if (state.R == RState.BELOW_ONE) {
|
||||
uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q);
|
||||
R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mul(state.K, R));
|
||||
return DecimalMath.divFloor(state.i, R);
|
||||
} else {
|
||||
uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B);
|
||||
R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mul(state.K, R));
|
||||
return DecimalMath.mul(state.i, R);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user