Files
dodo-contractV2/contracts/DODOVendorMachine/impl/DVMTrader.sol
2020-11-20 15:33:51 +08:00

235 lines
8.4 KiB
Solidity
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity 0.6.9;
pragma experimental ABIEncoderV2;
import {DVMVault} from "./DVMVault.sol";
import {SafeMath} from "../../lib/SafeMath.sol";
import {DecimalMath} from "../../lib/DecimalMath.sol";
import {DODOMath} from "../../lib/DODOMath.sol";
import {IDODOCallee} from "../../intf/IDODOCallee.sol";
import {RState, PMMState, PMMPricing} from "../../lib/PMMPricing.sol";
contract DVMTrader is DVMVault {
using SafeMath for uint256;
// ============ Modifiers ============
modifier isBuyAllow(address trader) {
require(!_BUYING_CLOSE_ && _TRADE_PERMISSION_.isAllowed(trader), "TRADER_BUY_NOT_ALLOWED");
_;
}
modifier isSellAllow(address trader) {
require(
!_SELLING_CLOSE_ && _TRADE_PERMISSION_.isAllowed(trader),
"TRADER_SELL_NOT_ALLOWED"
);
_;
}
modifier limitGasPrice() {
require(tx.gasprice <= _GAS_PRICE_LIMIT_.get(), "GAS_PRICE_EXCEED");
_;
}
// ============ Execute ============
function sellBase(address to)
external
preventReentrant
limitGasPrice
isSellAllow(to)
returns (uint256 receiveQuoteAmount)
{
uint256 baseInput = getBaseInput();
uint256 mtFee;
(receiveQuoteAmount, mtFee) = querySellBase(tx.origin, baseInput);
_transferQuoteOut(to, receiveQuoteAmount);
_transferQuoteOut(_MAINTAINER_, mtFee);
_sync();
return receiveQuoteAmount;
}
function sellQuote(address to)
external
preventReentrant
limitGasPrice
isBuyAllow(to)
returns (uint256 receiveBaseAmount)
{
uint256 quoteInput = getQuoteInput();
uint256 mtFee;
(receiveBaseAmount, mtFee) = querySellQuote(tx.origin, quoteInput);
_transferBaseOut(to, receiveBaseAmount);
_transferBaseOut(_MAINTAINER_, mtFee);
_sync();
return receiveBaseAmount;
}
// 这是一个试验性质的函数
// 没有走标准库,需要仔细考虑下
function flashLoan(
uint256 baseAmount,
uint256 quoteAmount,
address assetTo,
bytes calldata data
) external preventReentrant {
_transferBaseOut(assetTo, baseAmount);
_transferQuoteOut(assetTo, quoteAmount);
if (data.length > 0)
IDODOCallee(assetTo).DVMFlashLoanCall(msg.sender, baseAmount, quoteAmount, data);
(uint256 baseReserve, uint256 quoteReserve) = getVaultReserve();
(uint256 baseBalance, uint256 quoteBalance) = getVaultBalance();
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(tx.origin);
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(tx.origin);
if (baseBalance < baseReserve) {
uint256 validBaseOut = DecimalMath.divCeil(
baseReserve - baseBalance,
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
);
baseBalance = baseReserve.sub(validBaseOut);
_transferBaseOut(_MAINTAINER_, DecimalMath.mulCeil(validBaseOut, mtFeeRate));
}
if (quoteBalance < quoteReserve) {
uint256 validQuoteOut = DecimalMath.divCeil(
quoteReserve - quoteBalance,
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
);
quoteBalance = quoteReserve.sub(validQuoteOut);
_transferQuoteOut(_MAINTAINER_, DecimalMath.mulCeil(validQuoteOut, mtFeeRate));
}
require(
calculateBase0(baseBalance, quoteBalance) >= calculateBase0(baseReserve, quoteReserve),
"FLASH_LOAN_FAILED"
);
_sync();
}
function querySellBase(address trader, uint256 payBaseAmount)
public
view
returns (uint256 receiveQuoteAmount, uint256 mtFee)
{
(receiveQuoteAmount, ) = PMMPricing.sellBaseToken(getPMMState(), payBaseAmount);
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader);
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
mtFee = DecimalMath.mulCeil(receiveQuoteAmount, mtFeeRate);
receiveQuoteAmount = DecimalMath.mulFloor(
receiveQuoteAmount,
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
);
return (receiveQuoteAmount, mtFee);
}
function querySellQuote(address trader, uint256 payQuoteAmount)
public
view
returns (uint256 receiveBaseAmount, uint256 mtFee)
{
(receiveBaseAmount, ) = PMMPricing.sellQuoteToken(getPMMState(), payQuoteAmount);
uint256 lpFeeRate = _LP_FEE_RATE_MODEL_.getFeeRate(trader);
uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
mtFee = DecimalMath.mulCeil(receiveBaseAmount, mtFeeRate);
receiveBaseAmount = DecimalMath.mulFloor(
receiveBaseAmount,
DecimalMath.ONE.sub(mtFeeRate).sub(lpFeeRate)
);
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) {
(uint256 baseReserve, uint256 quoteReserve) = getVaultReserve();
uint256 B0 = calculateBase0(baseReserve, quoteReserve);
uint256 offsetRatio = DecimalMath.ONE.mul(B0).div(baseReserve).mul(B0).div(baseReserve);
uint256 offset = DecimalMath.ONE.sub(_K_).add(DecimalMath.mulFloor(offsetRatio, _K_));
return DecimalMath.mulFloor(_I_, offset);
}
// ============ Helper Functions ============
function getPMMState() public view returns (PMMState memory state) {
state.i = _I_;
state.K = _K_;
state.B = _BASE_RESERVE_;
state.Q = _QUOTE_RESERVE_;
state.B0 = calculateBase0(state.B, state.Q);
state.Q0 = 0;
state.R = RState.ABOVE_ONE;
return state;
}
function calculateBase0(uint256 baseAmount, uint256 quoteAmount) public view returns (uint256) {
uint256 fairAmount = DecimalMath.divFloor(quoteAmount, _I_);
return DODOMath._SolveQuadraticFunctionForTarget(baseAmount, _K_, fairAmount);
}
function getBase0() public view returns (uint256) {
(uint256 baseAmount, uint256 quoteAmount) = getVaultReserve();
uint256 fairAmount = DecimalMath.divFloor(quoteAmount, _I_);
return DODOMath._SolveQuadraticFunctionForTarget(baseAmount, _K_, fairAmount);
}
}