/* 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 {PMMPricing} from "../../lib/PMMPricing.sol"; contract DVMTrader is DVMVault { using SafeMath for uint256; // ============ Events ============ event DODOSwap( address fromToken, address toToken, uint256 fromAmount, uint256 toAmount, address trader, address receiver ); event DODOFlashLoan( address borrower, address assetTo, uint256 baseAmount, uint256 quoteAmount ); // ============ Trade Functions ============ function sellBase(address to) external preventReentrant returns (uint256 receiveQuoteAmount) { uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_)); uint256 mtFee; (receiveQuoteAmount, mtFee) = querySellBase(tx.origin, baseInput); _transferQuoteOut(to, receiveQuoteAmount); _transferQuoteOut(_MAINTAINER_, mtFee); _setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this))); emit DODOSwap( address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, to ); } function sellQuote(address to) external preventReentrant returns (uint256 receiveBaseAmount) { uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_)); uint256 mtFee; (receiveBaseAmount, mtFee) = querySellQuote(tx.origin, quoteInput); _transferBaseOut(to, receiveBaseAmount); _transferBaseOut(_MAINTAINER_, mtFee); _setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance); emit DODOSwap( address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, to ); } 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 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); // no input -> pure loss require( baseBalance >= _BASE_RESERVE_ || quoteBalance >= _QUOTE_RESERVE_, "FLASH_LOAN_FAILED" ); // sell quote if (baseBalance < _BASE_RESERVE_) { uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_)); (uint256 receiveBaseAmount, uint256 mtFee) = querySellQuote(tx.origin, quoteInput); require(uint256(_BASE_RESERVE_).sub(baseBalance) <= receiveBaseAmount, "FLASH_LOAN_FAILED"); _transferBaseOut(_MAINTAINER_, mtFee); emit DODOSwap( address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo ); } // sell base if (quoteBalance < _QUOTE_RESERVE_) { uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_)); (uint256 receiveQuoteAmount, uint256 mtFee) = querySellBase(tx.origin, baseInput); require(uint256(_QUOTE_RESERVE_).sub(quoteBalance) <= receiveQuoteAmount, "FLASH_LOAN_FAILED"); _transferQuoteOut(_MAINTAINER_, mtFee); emit DODOSwap( address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo ); } _sync(); emit DODOFlashLoan(msg.sender, assetTo, baseAmount, quoteAmount); } // ============ Query Functions ============ function querySellBase(address trader, uint256 payBaseAmount) public view returns (uint256 receiveQuoteAmount, uint256 mtFee) { (receiveQuoteAmount, ) = PMMPricing.sellBaseToken(getPMMState(), payBaseAmount); uint256 lpFeeRate = _LP_FEE_RATE_; uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader); mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate); receiveQuoteAmount = receiveQuoteAmount .sub(DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate)) .sub(mtFee); } function querySellQuote(address trader, uint256 payQuoteAmount) public view returns (uint256 receiveBaseAmount, uint256 mtFee) { (receiveBaseAmount, ) = PMMPricing.sellQuoteToken(getPMMState(), payQuoteAmount); uint256 lpFeeRate = _LP_FEE_RATE_; uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader); mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate); receiveBaseAmount = receiveBaseAmount .sub(DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate)) .sub(mtFee); } }