/* Copyright 2020 DODO ZOO. SPDX-License-Identifier: Apache-2.0 */ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; import {DSPVault} from "./DSPVault.sol"; import {SafeMath} from "../../lib/SafeMath.sol"; import {DecimalMath} from "../../lib/DecimalMath.sol"; import {PMMPricing} from "../../lib/PMMPricing.sol"; import {IDODOCallee} from "../../intf/IDODOCallee.sol"; contract DSPTrader is DSPVault { 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); event RChange(PMMPricing.RState newRState); // ============ 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; uint256 newBaseTarget; PMMPricing.RState newRState; (receiveQuoteAmount, mtFee, newRState, newBaseTarget) = querySellBase(tx.origin, baseInput); _transferQuoteOut(to, receiveQuoteAmount); _transferQuoteOut(_MAINTAINER_, mtFee); // update TARGET if (_RState_ != uint32(newRState)) { require(newBaseTarget <= uint112(-1), "OVERFLOW"); _BASE_TARGET_ = uint112(newBaseTarget); _RState_ = uint32(newRState); emit RChange(newRState); } _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; uint256 newQuoteTarget; PMMPricing.RState newRState; (receiveBaseAmount, mtFee, newRState, newQuoteTarget) = querySellQuote( tx.origin, quoteInput ); _transferBaseOut(to, receiveBaseAmount); _transferBaseOut(_MAINTAINER_, mtFee); // update TARGET if (_RState_ != uint32(newRState)) { require(newQuoteTarget <= uint112(-1), "OVERFLOW"); _QUOTE_TARGET_ = uint112(newQuoteTarget); _RState_ = uint32(newRState); emit RChange(newRState); } _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).DSPFlashLoanCall(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 case // quote input + base output if (baseBalance < _BASE_RESERVE_) { uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_)); ( uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget ) = querySellQuote(tx.origin, quoteInput); // revert if quoteBalance