ETH Price: $2,192.85 (-5.45%)

Transaction Decoder

Block:
13309408 at Sep-27-2021 06:21:08 PM +UTC
Transaction Fee:
0.014653047502962756 ETH $32.13
Gas Used:
166,953 Gas / 87.767500452 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x15dd416f...55EfBb398
0.094056253931150648 Eth
Nonce: 353
0.079403206428187892 Eth
Nonce: 354
0.014653047502962756
(Ethermine)
2,441.19432310144962986 Eth2,441.19455850517962986 Eth0.00023540373

Execution Trace

DODOV2Proxy02.dodoSwapV1( fromToken=0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd, toToken=0xdAC17F958D2ee523a2206206994597C13D831ec7, fromTokenAmount=1162929896313260511552, minReturnAmount=1387794059, dodoPairs=[0x8876819535b48b551C9e97EBc07332C7482b4b2d], directions=0, isIncentive=False, deadLine=1632767818 ) => ( returnAmount=3963877391197344453575983046348115674221700746820753546331534351508065746944 )
  • DODOApproveProxy.claimTokens( token=0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd, who=0x15dd416f5E624979981667c50b1518755EfBb398, dest=0xa356867fDCEa8e71AEaF87805808803806231FdC, amount=1162929896313260511552 )
    • DODOApprove.claimTokens( token=0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd, who=0x15dd416f5E624979981667c50b1518755EfBb398, dest=0xa356867fDCEa8e71AEaF87805808803806231FdC, amount=1162929896313260511552 )
      • DODOToken.transferFrom( from=0x15dd416f5E624979981667c50b1518755EfBb398, to=0xa356867fDCEa8e71AEaF87805808803806231FdC, amount=1162929896313260511552 ) => ( True )
      • DODO.STATICCALL( )
        • DODO.DELEGATECALL( )
        • DODOToken.balanceOf( owner=0xa356867fDCEa8e71AEaF87805808803806231FdC ) => ( balance=1162929896313260511552 )
        • DODOToken.allowance( owner=0xa356867fDCEa8e71AEaF87805808803806231FdC, spender=0x8876819535b48b551C9e97EBc07332C7482b4b2d ) => ( 115792089237316195423570985008687907853269984665640529331784581374961593500608 )
        • DODO.sellBaseToken( amount=1162929896313260511552, minReceiveQuote=0, data=0x ) => ( 3963877391197344453575983046348115674221700746820753546331534351508065746944 )
          • DODO.sellBaseToken( amount=1162929896313260511552, minReceiveQuote=0, data=0x ) => ( 3963877391197344453575983046348115674221700746820753546331534351508065746944 )
            • ConstOracle.STATICCALL( )
            • ConstOracle.STATICCALL( )
            • TetherToken.transfer( _to=0xa356867fDCEa8e71AEaF87805808803806231FdC, _value=1394767899 )
              File 1 of 8: DODOV2Proxy02
              // File: contracts/SmartRoute/intf/IDODOV2Proxy01.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
                  SPDX-License-Identifier: Apache-2.0
              
              */
              
              pragma solidity 0.6.9;
              
              
              interface IDODOV2Proxy01 {
                  function dodoSwapV2ETHToToken(
                      address toToken,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  ) external payable returns (uint256 returnAmount);
              
                  function dodoSwapV2TokenToETH(
                      address fromToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  ) external returns (uint256 returnAmount);
              
                  function dodoSwapV2TokenToToken(
                      address fromToken,
                      address toToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  ) external returns (uint256 returnAmount);
              
                  function createDODOVendingMachine(
                      address baseToken,
                      address quoteToken,
                      uint256 baseInAmount,
                      uint256 quoteInAmount,
                      uint256 lpFeeRate,
                      uint256 i,
                      uint256 k,
                      bool isOpenTWAP,
                      uint256 deadLine
                  ) external payable returns (address newVendingMachine, uint256 shares);
              
                  function addDVMLiquidity(
                      address dvmAddress,
                      uint256 baseInAmount,
                      uint256 quoteInAmount,
                      uint256 baseMinAmount,
                      uint256 quoteMinAmount,
                      uint8 flag, //  0 - ERC20, 1 - baseInETH, 2 - quoteInETH
                      uint256 deadLine
                  )
                      external
                      payable
                      returns (
                          uint256 shares,
                          uint256 baseAdjustedInAmount,
                          uint256 quoteAdjustedInAmount
                      );
              
                  function createDODOPrivatePool(
                      address baseToken,
                      address quoteToken,
                      uint256 baseInAmount,
                      uint256 quoteInAmount,
                      uint256 lpFeeRate,
                      uint256 i,
                      uint256 k,
                      bool isOpenTwap,
                      uint256 deadLine
                  ) external payable returns (address newPrivatePool);
              
                  function resetDODOPrivatePool(
                      address dppAddress,
                      uint256[] memory paramList,  //0 - newLpFeeRate, 1 - newI, 2 - newK
                      uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3 - quoteOutAmount
                      uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH
                      uint256 minBaseReserve,
                      uint256 minQuoteReserve,
                      uint256 deadLine
                  ) external payable;
              
                  function createCrowdPooling(
                      address baseToken,
                      address quoteToken,
                      uint256 baseInAmount,
                      uint256[] memory timeLine,
                      uint256[] memory valueList,
                      bool isOpenTWAP,
                      uint256 deadLine
                  ) external payable returns (address payable newCrowdPooling);
              
                  function bid(
                      address cpAddress,
                      uint256 quoteAmount,
                      uint8 flag, // 0 - ERC20, 1 - quoteInETH
                      uint256 deadLine
                  ) external payable;
              
                  function addLiquidityToV1(
                      address pair,
                      uint256 baseAmount,
                      uint256 quoteAmount,
                      uint256 baseMinShares,
                      uint256 quoteMinShares,
                      uint8 flag, // 0 erc20 Out  1 baseInETH  2 quoteInETH 
                      uint256 deadLine
                  ) external payable returns(uint256, uint256);
              
                  function dodoSwapV1(
                      address fromToken,
                      address toToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  ) external payable returns (uint256 returnAmount);
              
                  function externalSwap(
                      address fromToken,
                      address toToken,
                      address approveTarget,
                      address to,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      bytes memory callDataConcat,
                      bool isIncentive,
                      uint256 deadLine
                  ) external payable returns (uint256 returnAmount);
              
                  function mixSwap(
                      address fromToken,
                      address toToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory mixAdapters,
                      address[] memory mixPairs,
                      address[] memory assetTo,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  ) external payable returns (uint256 returnAmount);
              
              }
              
              // File: contracts/SmartRoute/intf/IDODOV2.sol
              
              
              interface IDODOV2 {
              
                  //========== Common ==================
              
                  function sellBase(address to) external returns (uint256 receiveQuoteAmount);
              
                  function sellQuote(address to) external returns (uint256 receiveBaseAmount);
              
                  function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve);
              
                  function _BASE_TOKEN_() external view returns (address);
              
                  function _QUOTE_TOKEN_() external view returns (address);
              
                  function getPMMStateForCall() external view returns (
                          uint256 i,
                          uint256 K,
                          uint256 B,
                          uint256 Q,
                          uint256 B0,
                          uint256 Q0,
                          uint256 R
                  );
              
                  function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate);
              
                  
                  function getDODOPoolBidirection(address token0, address token1) external view returns (address[] memory, address[] memory);
              
                  //========== DODOVendingMachine ========
                  
                  function createDODOVendingMachine(
                      address baseToken,
                      address quoteToken,
                      uint256 lpFeeRate,
                      uint256 i,
                      uint256 k,
                      bool isOpenTWAP
                  ) external returns (address newVendingMachine);
                  
                  function buyShares(address to) external returns (uint256,uint256,uint256);
              
              
                  //========== DODOPrivatePool ===========
              
                  function createDODOPrivatePool() external returns (address newPrivatePool);
              
                  function initDODOPrivatePool(
                      address dppAddress,
                      address creator,
                      address baseToken,
                      address quoteToken,
                      uint256 lpFeeRate,
                      uint256 k,
                      uint256 i,
                      bool isOpenTwap
                  ) external;
              
                  function reset(
                      address operator,
                      uint256 newLpFeeRate,
                      uint256 newI,
                      uint256 newK,
                      uint256 baseOutAmount,
                      uint256 quoteOutAmount,
                      uint256 minBaseReserve,
                      uint256 minQuoteReserve
                  ) external returns (bool); 
              
              
                  function _OWNER_() external returns (address);
                  
                  //========== CrowdPooling ===========
              
                  function createCrowdPooling() external returns (address payable newCrowdPooling);
              
                  function initCrowdPooling(
                      address cpAddress,
                      address creator,
                      address baseToken,
                      address quoteToken,
                      uint256[] memory timeLine,
                      uint256[] memory valueList,
                      bool isOpenTWAP
                  ) external;
              
                  function bid(address to) external;
              }
              
              // File: contracts/SmartRoute/intf/IDODOV1.sol
              
              
              interface IDODOV1 {
                  function init(
                      address owner,
                      address supervisor,
                      address maintainer,
                      address baseToken,
                      address quoteToken,
                      address oracle,
                      uint256 lpFeeRate,
                      uint256 mtFeeRate,
                      uint256 k,
                      uint256 gasPriceLimit
                  ) external;
              
                  function transferOwnership(address newOwner) external;
              
                  function claimOwnership() external;
              
                  function sellBaseToken(
                      uint256 amount,
                      uint256 minReceiveQuote,
                      bytes calldata data
                  ) external returns (uint256);
              
                  function buyBaseToken(
                      uint256 amount,
                      uint256 maxPayQuote,
                      bytes calldata data
                  ) external returns (uint256);
              
                  function querySellBaseToken(uint256 amount) external view returns (uint256 receiveQuote);
              
                  function queryBuyBaseToken(uint256 amount) external view returns (uint256 payQuote);
              
                  function depositBaseTo(address to, uint256 amount) external returns (uint256);
              
                  function withdrawBase(uint256 amount) external returns (uint256);
              
                  function withdrawAllBase() external returns (uint256);
              
                  function depositQuoteTo(address to, uint256 amount) external returns (uint256);
              
                  function withdrawQuote(uint256 amount) external returns (uint256);
              
                  function withdrawAllQuote() external returns (uint256);
              
                  function _BASE_CAPITAL_TOKEN_() external returns (address);
              
                  function _QUOTE_CAPITAL_TOKEN_() external returns (address);
              
                  function _BASE_TOKEN_() external view returns (address);
              
                  function _QUOTE_TOKEN_() external view returns (address);
              
                  function _R_STATUS_() external view returns (uint8);
              
                  function _QUOTE_BALANCE_() external view returns (uint256);
              
                  function _BASE_BALANCE_() external view returns (uint256);
              
                  function _K_() external view returns (uint256);
              
                  function _MT_FEE_RATE_() external view returns (uint256);
              
                  function _LP_FEE_RATE_() external view returns (uint256);
              
                  function getExpectedTarget() external view returns (uint256 baseTarget, uint256 quoteTarget);
              
                  function getOraclePrice() external view returns (uint256);
              
                  function getMidPrice() external view returns (uint256 midPrice); 
              }
              
              // File: contracts/intf/IDODOApprove.sol
              
              
              interface IDODOApprove {
                  function claimTokens(address token,address who,address dest,uint256 amount) external;
                  function getDODOProxy() external view returns (address);
              }
              
              // File: contracts/lib/InitializableOwnable.sol
              
              
              /**
               * @title Ownable
               * @author DODO Breeder
               *
               * @notice Ownership related functions
               */
              contract InitializableOwnable {
                  address public _OWNER_;
                  address public _NEW_OWNER_;
                  bool internal _INITIALIZED_;
              
                  // ============ Events ============
              
                  event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  // ============ Modifiers ============
              
                  modifier notInitialized() {
                      require(!_INITIALIZED_, "DODO_INITIALIZED");
                      _;
                  }
              
                  modifier onlyOwner() {
                      require(msg.sender == _OWNER_, "NOT_OWNER");
                      _;
                  }
              
                  // ============ Functions ============
              
                  function initOwner(address newOwner) public notInitialized {
                      _INITIALIZED_ = true;
                      _OWNER_ = newOwner;
                  }
              
                  function transferOwnership(address newOwner) public onlyOwner {
                      emit OwnershipTransferPrepared(_OWNER_, newOwner);
                      _NEW_OWNER_ = newOwner;
                  }
              
                  function claimOwnership() public {
                      require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                      emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                      _OWNER_ = _NEW_OWNER_;
                      _NEW_OWNER_ = address(0);
                  }
              }
              
              // File: contracts/SmartRoute/DODOApproveProxy.sol
              
              
              
              
              interface IDODOApproveProxy {
                  function isAllowedProxy(address _proxy) external view returns (bool);
                  function claimTokens(address token,address who,address dest,uint256 amount) external;
              }
              
              /**
               * @title DODOApproveProxy
               * @author DODO Breeder
               *
               * @notice Allow different version dodoproxy to claim from DODOApprove
               */
              contract DODOApproveProxy is InitializableOwnable {
                  
                  // ============ Storage ============
                  uint256 private constant _TIMELOCK_DURATION_ = 3 days;
                  mapping (address => bool) public _IS_ALLOWED_PROXY_;
                  uint256 public _TIMELOCK_;
                  address public _PENDING_ADD_DODO_PROXY_;
                  address public immutable _DODO_APPROVE_;
              
                  // ============ Modifiers ============
                  modifier notLocked() {
                      require(
                          _TIMELOCK_ <= block.timestamp,
                          "SetProxy is timelocked"
                      );
                      _;
                  }
              
                  constructor(address dodoApporve) public {
                      _DODO_APPROVE_ = dodoApporve;
                  }
              
                  function init(address owner, address[] memory proxies) external {
                      initOwner(owner);
                      for(uint i = 0; i < proxies.length; i++) 
                          _IS_ALLOWED_PROXY_[proxies[i]] = true;
                  }
              
                  function unlockAddProxy(address newDodoProxy) public onlyOwner {
                      _TIMELOCK_ = block.timestamp + _TIMELOCK_DURATION_;
                      _PENDING_ADD_DODO_PROXY_ = newDodoProxy;
                  }
              
                  function lockAddProxy() public onlyOwner {
                     _PENDING_ADD_DODO_PROXY_ = address(0);
                     _TIMELOCK_ = 0;
                  }
              
              
                  function addDODOProxy() external onlyOwner notLocked() {
                      _IS_ALLOWED_PROXY_[_PENDING_ADD_DODO_PROXY_] = true;
                      lockAddProxy();
                  }
              
                  function removeDODOProxy (address oldDodoProxy) public onlyOwner {
                      _IS_ALLOWED_PROXY_[oldDodoProxy] = false;
                  }
                  
                  function claimTokens(
                      address token,
                      address who,
                      address dest,
                      uint256 amount
                  ) external {
                      require(_IS_ALLOWED_PROXY_[msg.sender], "DODOApproveProxy:Access restricted");
                      IDODOApprove(_DODO_APPROVE_).claimTokens(
                          token,
                          who,
                          dest,
                          amount
                      );
                  }
              
                  function isAllowedProxy(address _proxy) external view returns (bool) {
                      return _IS_ALLOWED_PROXY_[_proxy];
                  }
              }
              
              // File: contracts/lib/SafeMath.sol
              
              
              
              /**
               * @title SafeMath
               * @author DODO Breeder
               *
               * @notice Math operations with safety checks that revert on error
               */
              library SafeMath {
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
              
                      uint256 c = a * b;
                      require(c / a == b, "MUL_ERROR");
              
                      return c;
                  }
              
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "DIVIDING_ERROR");
                      return a / b;
                  }
              
                  function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 quotient = div(a, b);
                      uint256 remainder = a - quotient * b;
                      if (remainder > 0) {
                          return quotient + 1;
                      } else {
                          return quotient;
                      }
                  }
              
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a, "SUB_ERROR");
                      return a - b;
                  }
              
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a, "ADD_ERROR");
                      return c;
                  }
              
                  function sqrt(uint256 x) internal pure returns (uint256 y) {
                      uint256 z = x / 2 + 1;
                      y = x;
                      while (z < y) {
                          y = z;
                          z = (x / z + z) / 2;
                      }
                  }
              }
              
              // File: contracts/lib/DecimalMath.sol
              
              
              
              /**
               * @title DecimalMath
               * @author DODO Breeder
               *
               * @notice Functions for fixed point number with 18 decimals
               */
              library DecimalMath {
                  using SafeMath for uint256;
              
                  uint256 internal constant ONE = 10**18;
                  uint256 internal constant ONE2 = 10**36;
              
                  function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(d) / (10**18);
                  }
              
                  function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(d).divCeil(10**18);
                  }
              
                  function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(10**18).div(d);
                  }
              
                  function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(10**18).divCeil(d);
                  }
              
                  function reciprocalFloor(uint256 target) internal pure returns (uint256) {
                      return uint256(10**36).div(target);
                  }
              
                  function reciprocalCeil(uint256 target) internal pure returns (uint256) {
                      return uint256(10**36).divCeil(target);
                  }
              }
              
              // File: contracts/SmartRoute/helper/DODOSellHelper.sol
              
              
              
              
              // import {DODOMath} from "../lib/DODOMath.sol";
              
              interface IDODOSellHelper {
                  function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256);
                  
                  function querySellBaseToken(address dodo, uint256 amount) external view returns (uint256);
              }
              
              library DODOMath {
                  using SafeMath for uint256;
              
                  /*
                      Integrate dodo curve fron V1 to V2
                      require V0>=V1>=V2>0
                      res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1)
                      let V1-V2=delta
                      res = i*delta*(1-k+k(V0^2/V1/V2))
                  */
                  function _GeneralIntegrate(
                      uint256 V0,
                      uint256 V1,
                      uint256 V2,
                      uint256 i,
                      uint256 k
                  ) internal pure returns (uint256) {
                      uint256 fairAmount = DecimalMath.mulFloor(i, V1.sub(V2)); // i*delta
                      uint256 V0V0V1V2 = DecimalMath.divCeil(V0.mul(V0).div(V1), V2);
                      uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
                      return DecimalMath.mulFloor(fairAmount, DecimalMath.ONE.sub(k).add(penalty));
                  }
              
                  /*
                      The same with integration expression above, we have:
                      i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
                      Given Q1 and deltaB, solve Q2
                      This is a quadratic function and the standard version is
                      aQ2^2 + bQ2 + c = 0, where
                      a=1-k
                      -b=(1-k)Q1-kQ0^2/Q1+i*deltaB
                      c=-kQ0^2
                      and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k)
                      note: another root is negative, abondan
                      if deltaBSig=true, then Q2>Q1
                      if deltaBSig=false, then Q2<Q1
                  */
                  function _SolveQuadraticFunctionForTrade(
                      uint256 Q0,
                      uint256 Q1,
                      uint256 ideltaB,
                      bool deltaBSig,
                      uint256 k
                  ) internal pure returns (uint256) {
                      // calculate -b value and sig
                      // -b = (1-k)Q1-kQ0^2/Q1+i*deltaB
                      uint256 kQ02Q1 = DecimalMath.mulFloor(k, Q0).mul(Q0).div(Q1); // kQ0^2/Q1
                      uint256 b = DecimalMath.mulFloor(DecimalMath.ONE.sub(k), Q1); // (1-k)Q1
                      bool minusbSig = true;
                      if (deltaBSig) {
                          b = b.add(ideltaB); // (1-k)Q1+i*deltaB
                      } else {
                          kQ02Q1 = kQ02Q1.add(ideltaB); // i*deltaB+kQ0^2/Q1
                      }
                      if (b >= kQ02Q1) {
                          b = b.sub(kQ02Q1);
                          minusbSig = true;
                      } else {
                          b = kQ02Q1.sub(b);
                          minusbSig = false;
                      }
              
                      // calculate sqrt
                      uint256 squareRoot = DecimalMath.mulFloor(
                          DecimalMath.ONE.sub(k).mul(4),
                          DecimalMath.mulFloor(k, Q0).mul(Q0)
                      ); // 4(1-k)kQ0^2
                      squareRoot = b.mul(b).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0)
              
                      // final res
                      uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k)
                      uint256 numerator;
                      if (minusbSig) {
                          numerator = b.add(squareRoot);
                      } else {
                          numerator = squareRoot.sub(b);
                      }
              
                      if (deltaBSig) {
                          return DecimalMath.divFloor(numerator, denominator);
                      } else {
                          return DecimalMath.divCeil(numerator, denominator);
                      }
                  }
              
                  /*
                      Start from the integration function
                      i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
                      Assume Q2=Q0, Given Q1 and deltaB, solve Q0
                      let fairAmount = i*deltaB
                  */
                  function _SolveQuadraticFunctionForTarget(
                      uint256 V1,
                      uint256 k,
                      uint256 fairAmount
                  ) internal pure returns (uint256 V0) {
                      // V0 = V1+V1*(sqrt-1)/2k
                      uint256 sqrt = DecimalMath.divCeil(DecimalMath.mulFloor(k, fairAmount).mul(4), V1);
                      sqrt = sqrt.add(DecimalMath.ONE).mul(DecimalMath.ONE).sqrt();
                      uint256 premium = DecimalMath.divCeil(sqrt.sub(DecimalMath.ONE), k.mul(2));
                      // V0 is greater than or equal to V1 according to the solution
                      return DecimalMath.mulFloor(V1, DecimalMath.ONE.add(premium));
                  }
              }
              
              contract DODOSellHelper {
                  using SafeMath for uint256;
              
                  enum RStatus {ONE, ABOVE_ONE, BELOW_ONE}
              
                  uint256 constant ONE = 10**18;
              
                  struct DODOState {
                      uint256 oraclePrice;
                      uint256 K;
                      uint256 B;
                      uint256 Q;
                      uint256 baseTarget;
                      uint256 quoteTarget;
                      RStatus rStatus;
                  }
              
                  function querySellBaseToken(address dodo, uint256 amount) public view returns (uint256) {
                      return IDODOV1(dodo).querySellBaseToken(amount);
                  }
              
                  function querySellQuoteToken(address dodo, uint256 amount) public view returns (uint256) {
                      DODOState memory state;
                      (state.baseTarget, state.quoteTarget) = IDODOV1(dodo).getExpectedTarget();
                      state.rStatus = RStatus(IDODOV1(dodo)._R_STATUS_());
                      state.oraclePrice = IDODOV1(dodo).getOraclePrice();
                      state.Q = IDODOV1(dodo)._QUOTE_BALANCE_();
                      state.B = IDODOV1(dodo)._BASE_BALANCE_();
                      state.K = IDODOV1(dodo)._K_();
              
                      uint256 boughtAmount;
                      // Determine the status (RStatus) and calculate the amount
                      // based on the state
                      if (state.rStatus == RStatus.ONE) {
                          boughtAmount = _ROneSellQuoteToken(amount, state);
                      } else if (state.rStatus == RStatus.ABOVE_ONE) {
                          boughtAmount = _RAboveSellQuoteToken(amount, state);
                      } else {
                          uint256 backOneBase = state.B.sub(state.baseTarget);
                          uint256 backOneQuote = state.quoteTarget.sub(state.Q);
                          if (amount <= backOneQuote) {
                              boughtAmount = _RBelowSellQuoteToken(amount, state);
                          } else {
                              boughtAmount = backOneBase.add(
                                  _ROneSellQuoteToken(amount.sub(backOneQuote), state)
                              );
                          }
                      }
                      // Calculate fees
                      return
                          DecimalMath.divFloor(
                              boughtAmount,
                              DecimalMath.ONE.add(IDODOV1(dodo)._MT_FEE_RATE_()).add(
                                  IDODOV1(dodo)._LP_FEE_RATE_()
                              )
                          );
                  }
              
                  function _ROneSellQuoteToken(uint256 amount, DODOState memory state)
                      internal
                      pure
                      returns (uint256 receiveBaseToken)
                  {
                      uint256 i = DecimalMath.divFloor(ONE, state.oraclePrice);
                      uint256 B2 = DODOMath._SolveQuadraticFunctionForTrade(
                          state.baseTarget,
                          state.baseTarget,
                          DecimalMath.mulFloor(i, amount),
                          false,
                          state.K
                      );
                      return state.baseTarget.sub(B2);
                  }
              
                  function _RAboveSellQuoteToken(uint256 amount, DODOState memory state)
                      internal
                      pure
                      returns (uint256 receieBaseToken)
                  {
                      uint256 i = DecimalMath.divFloor(ONE, state.oraclePrice);
                      uint256 B2 = DODOMath._SolveQuadraticFunctionForTrade(
                          state.baseTarget,
                          state.B,
                          DecimalMath.mulFloor(i, amount),
                          false,
                          state.K
                      );
                      return state.B.sub(B2);
                  }
              
                  function _RBelowSellQuoteToken(uint256 amount, DODOState memory state)
                      internal
                      pure
                      returns (uint256 receiveBaseToken)
                  {
                      uint256 Q1 = state.Q.add(amount);
                      uint256 i = DecimalMath.divFloor(ONE, state.oraclePrice);
                      return DODOMath._GeneralIntegrate(state.quoteTarget, Q1, state.Q, i, state.K);
                  }
              }
              
              // File: contracts/intf/IERC20.sol
              
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
              
                  function decimals() external view returns (uint8);
              
                  function name() external view returns (string memory);
              
                  function symbol() external view returns (string memory);
              
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
              
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `recipient`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address recipient, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Moves `amount` tokens from `sender` to `recipient` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                  ) external returns (bool);
              }
              
              // File: contracts/intf/IWETH.sol
              
              
              
              interface IWETH {
                  function totalSupply() external view returns (uint256);
              
                  function balanceOf(address account) external view returns (uint256);
              
                  function transfer(address recipient, uint256 amount) external returns (bool);
              
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  function approve(address spender, uint256 amount) external returns (bool);
              
                  function transferFrom(
                      address src,
                      address dst,
                      uint256 wad
                  ) external returns (bool);
              
                  function deposit() external payable;
              
                  function withdraw(uint256 wad) external;
              }
              
              // File: contracts/SmartRoute/intf/IUni.sol
              
              interface IUni {
                  function swapExactTokensForTokens(
                      uint amountIn,
                      uint amountOutMin,
                      address[] calldata path,
                      address to,
                      uint deadline
                  ) external returns (uint[] memory amounts);
              
                  function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
              
                  function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
              
                  function token0() external view returns (address);
                  
                  function token1() external view returns (address);
              }
              
              // File: contracts/SmartRoute/intf/IChi.sol
              
              
              interface IChi {
                  function freeUpTo(uint256 value) external returns (uint256);
              }
              
              // File: contracts/lib/SafeERC20.sol
              
              
              
              
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using SafeMath for uint256;
              
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
              
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(
                          token,
                          abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
                      );
                  }
              
                  function safeApprove(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      // safeApprove should only be called when setting an initial allowance,
                      // or when resetting it to zero. To increase and decrease it, use
                      // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                      // solhint-disable-next-line max-line-length
                      require(
                          (value == 0) || (token.allowance(address(this), spender) == 0),
                          "SafeERC20: approve from non-zero to non-zero allowance"
                      );
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                  }
              
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves.
              
                      // A Solidity high level call has three parts:
                      //  1. The target address is checked to verify it contains contract code
                      //  2. The call itself is made, and success asserted
                      //  3. The return value is decoded, which in turn checks the size of the returned data.
                      // solhint-disable-next-line max-line-length
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = address(token).call(data);
                      require(success, "SafeERC20: low-level call failed");
              
                      if (returndata.length > 0) {
                          // Return data is optional
                          // solhint-disable-next-line max-line-length
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              
              // File: contracts/SmartRoute/lib/UniversalERC20.sol
              
              
              
              
              
              library UniversalERC20 {
                  using SafeMath for uint256;
                  using SafeERC20 for IERC20;
              
                  IERC20 private constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
              
                  function universalTransfer(
                      IERC20 token,
                      address payable to,
                      uint256 amount
                  ) internal {
                      if (amount > 0) {
                          if (isETH(token)) {
                              to.transfer(amount);
                          } else {
                              token.safeTransfer(to, amount);
                          }
                      }
                  }
              
                  function universalApproveMax(
                      IERC20 token,
                      address to,
                      uint256 amount
                  ) internal {
                      uint256 allowance = token.allowance(address(this), to);
                      if (allowance < amount) {
                          if (allowance > 0) {
                              token.safeApprove(to, 0);
                          }
                          token.safeApprove(to, uint256(-1));
                      }
                  }
              
                  function universalBalanceOf(IERC20 token, address who) internal view returns (uint256) {
                      if (isETH(token)) {
                          return who.balance;
                      } else {
                          return token.balanceOf(who);
                      }
                  }
              
                  function tokenBalanceOf(IERC20 token, address who) internal view returns (uint256) {
                      return token.balanceOf(who);
                  }
              
                  function isETH(IERC20 token) internal pure returns (bool) {
                      return token == ETH_ADDRESS;
                  }
              }
              
              // File: contracts/lib/ReentrancyGuard.sol
              
              
              /**
               * @title ReentrancyGuard
               * @author DODO Breeder
               *
               * @notice Protect functions from Reentrancy Attack
               */
              contract ReentrancyGuard {
                  // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations
                  // zero-state of _ENTERED_ is false
                  bool private _ENTERED_;
              
                  modifier preventReentrant() {
                      require(!_ENTERED_, "REENTRANT");
                      _ENTERED_ = true;
                      _;
                      _ENTERED_ = false;
                  }
              }
              
              // File: contracts/DODOToken/DODOIncentive.sol
              
              
              
              
              interface IDODOIncentive {
                  function triggerIncentive(
                      address fromToken,
                      address toToken,
                      address assetTo
                  ) external;
              }
              
              /**
               * @title DODOIncentive
               * @author DODO Breeder
               *
               * @notice Trade Incentive in DODO platform
               */
              contract DODOIncentive is InitializableOwnable {
                  using SafeMath for uint256;
                  using SafeERC20 for IERC20;
              
                  // ============ Storage ============
                  address public immutable _DODO_TOKEN_;
                  address public _DODO_PROXY_;
                  uint256 public dodoPerBlock;
                  uint256 public defaultRate = 10;
                  mapping(address => uint256) public boosts;
              
                  uint32 public lastRewardBlock;
                  uint112 public totalReward;
                  uint112 public totalDistribution;
              
                  // ============ Events ============
              
                  event SetBoost(address token, uint256 boostRate);
                  event SetNewProxy(address dodoProxy);
                  event SetPerReward(uint256 dodoPerBlock);
                  event SetDefaultRate(uint256 defaultRate);
                  event Incentive(address user, uint256 reward);
              
                  constructor(address _dodoToken) public {
                      _DODO_TOKEN_ = _dodoToken;
                  }
              
                  // ============ Ownable ============
              
                  function changeBoost(address _token, uint256 _boostRate) public onlyOwner {
                      require(_token != address(0));
                      require(_boostRate + defaultRate <= 1000);
                      boosts[_token] = _boostRate;
                      emit SetBoost(_token, _boostRate);
                  }
              
                  function changePerReward(uint256 _dodoPerBlock) public onlyOwner {
                      _updateTotalReward();
                      dodoPerBlock = _dodoPerBlock;
                      emit SetPerReward(dodoPerBlock);
                  }
              
                  function changeDefaultRate(uint256 _defaultRate) public onlyOwner {
                      defaultRate = _defaultRate;
                      emit SetDefaultRate(defaultRate);
                  }
              
                  function changeDODOProxy(address _dodoProxy) public onlyOwner {
                      _DODO_PROXY_ = _dodoProxy;
                      emit SetNewProxy(_DODO_PROXY_);
                  }
              
                  function emptyReward(address assetTo) public onlyOwner {
                      uint256 balance = IERC20(_DODO_TOKEN_).balanceOf(address(this));
                      IERC20(_DODO_TOKEN_).transfer(assetTo, balance);
                  }
              
                  // ============ Incentive  function ============
              
                  function triggerIncentive(
                      address fromToken,
                      address toToken,
                      address assetTo
                  ) external {
                      require(msg.sender == _DODO_PROXY_, "DODOIncentive:Access restricted");
              
                      uint256 curTotalDistribution = totalDistribution;
                      uint256 fromRate = boosts[fromToken];
                      uint256 toRate = boosts[toToken];
                      uint256 rate = (fromRate >= toRate ? fromRate : toRate) + defaultRate;
                      require(rate <= 1000, "RATE_INVALID");
                      
                      uint256 _totalReward = _getTotalReward();
                      uint256 reward = ((_totalReward - curTotalDistribution) * rate) / 1000;
                      uint256 _totalDistribution = curTotalDistribution + reward;
              
                      _update(_totalReward, _totalDistribution);
                      if (reward != 0) {
                          IERC20(_DODO_TOKEN_).transfer(assetTo, reward);
                          emit Incentive(assetTo, reward);
                      }
                  }
              
                  function _updateTotalReward() internal {
                      uint256 _totalReward = _getTotalReward();
                      require(_totalReward < uint112(-1), "OVERFLOW");
                      totalReward = uint112(_totalReward);
                      lastRewardBlock = uint32(block.number);
                  }
              
                  function _update(uint256 _totalReward, uint256 _totalDistribution) internal {
                      require(
                          _totalReward < uint112(-1) && _totalDistribution < uint112(-1) && block.number < uint32(-1),
                          "OVERFLOW"
                      );
                      lastRewardBlock = uint32(block.number);
                      totalReward = uint112(_totalReward);
                      totalDistribution = uint112(_totalDistribution);
                  }
              
                  function _getTotalReward() internal view returns (uint256) {
                      if (lastRewardBlock == 0) {
                          return totalReward;
                      } else {
                          return totalReward + (block.number - lastRewardBlock) * dodoPerBlock;
                      }
                  }
              
                  // ============= Helper function ===============
              
                  function incentiveStatus(address fromToken, address toToken)
                      external
                      view
                      returns (
                          uint256 reward,
                          uint256 baseRate,
                          uint256 totalRate,
                          uint256 curTotalReward,
                          uint256 perBlockReward
                      )
                  {
                      baseRate = defaultRate;
                      uint256 fromRate = boosts[fromToken];
                      uint256 toRate = boosts[toToken];
                      totalRate = (fromRate >= toRate ? fromRate : toRate) + defaultRate;
                      uint256 _totalReward = _getTotalReward();
                      reward = ((_totalReward - totalDistribution) * totalRate) / 1000;
                      curTotalReward = _totalReward - totalDistribution;
                      perBlockReward = dodoPerBlock;
                  }
              }
              
              // File: contracts/SmartRoute/intf/IDODOAdapter.sol
              
              
              interface IDODOAdapter {
                  
                  function sellBase(address to, address pool) external;
              
                  function sellQuote(address to, address pool) external;
              }
              
              // File: contracts/SmartRoute/DODOV2Proxy02.sol
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              /**
               * @title DODOV2Proxy02
               * @author DODO Breeder
               *
               * @notice Entrance of trading in DODO platform
               */
              contract DODOV2Proxy02 is IDODOV2Proxy01, ReentrancyGuard, InitializableOwnable {
                  using SafeMath for uint256;
                  using UniversalERC20 for IERC20;
              
                  // ============ Storage ============
              
                  address constant _ETH_ADDRESS_ = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                  address public immutable _WETH_;
                  address public immutable _DODO_APPROVE_PROXY_;
                  address public immutable _DODO_SELL_HELPER_;
                  address public immutable _DVM_FACTORY_;
                  address public immutable _DPP_FACTORY_;
                  address public immutable _CP_FACTORY_;
                  address public immutable _DODO_INCENTIVE_;
                  address public immutable _CHI_TOKEN_;
                  uint256 public _GAS_DODO_MAX_RETURN_ = 0;
                  uint256 public _GAS_EXTERNAL_RETURN_ = 0;
                  mapping (address => bool) public isWhiteListed;
              
                  // ============ Events ============
              
                  event OrderHistory(
                      address fromToken,
                      address toToken,
                      address sender,
                      uint256 fromAmount,
                      uint256 returnAmount
                  );
              
                  // ============ Modifiers ============
              
                  modifier judgeExpired(uint256 deadLine) {
                      require(deadLine >= block.timestamp, "DODOV2Proxy02: EXPIRED");
                      _;
                  }
              
                  fallback() external payable {}
              
                  receive() external payable {}
              
                  constructor(
                      address dvmFactory,
                      address dppFactory,
                      address cpFactory,
                      address payable weth,
                      address dodoApproveProxy,
                      address dodoSellHelper,
                      address chiToken,
                      address dodoIncentive
                  ) public {
                      _DVM_FACTORY_ = dvmFactory;
                      _DPP_FACTORY_ = dppFactory;
                      _CP_FACTORY_ = cpFactory;
                      _WETH_ = weth;
                      _DODO_APPROVE_PROXY_ = dodoApproveProxy;
                      _DODO_SELL_HELPER_ = dodoSellHelper;
                      _CHI_TOKEN_ = chiToken;
                      _DODO_INCENTIVE_ = dodoIncentive;
                  }
              
                  function addWhiteList (address contractAddr) public onlyOwner {
                      isWhiteListed[contractAddr] = true;
                  }
              
                  function removeWhiteList (address contractAddr) public onlyOwner {
                      isWhiteListed[contractAddr] = false;
                  }
              
                  function updateGasReturn(uint256 newDodoGasReturn, uint256 newExternalGasReturn) public onlyOwner {
                      _GAS_DODO_MAX_RETURN_ = newDodoGasReturn;
                      _GAS_EXTERNAL_RETURN_ = newExternalGasReturn;
                  }
              
                  // ============ DVM Functions (create & add liquidity) ============
              
                  function createDODOVendingMachine(
                      address baseToken,
                      address quoteToken,
                      uint256 baseInAmount,
                      uint256 quoteInAmount,
                      uint256 lpFeeRate,
                      uint256 i,
                      uint256 k,
                      bool isOpenTWAP,
                      uint256 deadLine
                  )
                      external
                      override
                      payable
                      preventReentrant
                      judgeExpired(deadLine)
                      returns (address newVendingMachine, uint256 shares)
                  {
                      {
                          address _baseToken = baseToken == _ETH_ADDRESS_ ? _WETH_ : baseToken;
                          address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken;
                          newVendingMachine = IDODOV2(_DVM_FACTORY_).createDODOVendingMachine(
                              _baseToken,
                              _quoteToken,
                              lpFeeRate,
                              i,
                              k,
                              isOpenTWAP
                          );
                      }
              
                      {
                          address _baseToken = baseToken;
                          address _quoteToken = quoteToken;
                          _deposit(
                              msg.sender,
                              newVendingMachine,
                              _baseToken,
                              baseInAmount,
                              _baseToken == _ETH_ADDRESS_
                          );
                          _deposit(
                              msg.sender,
                              newVendingMachine,
                              _quoteToken,
                              quoteInAmount,
                              _quoteToken == _ETH_ADDRESS_
                          );
                      }
              
                      (shares, , ) = IDODOV2(newVendingMachine).buyShares(msg.sender);
                  }
              
                  function addDVMLiquidity(
                      address dvmAddress,
                      uint256 baseInAmount,
                      uint256 quoteInAmount,
                      uint256 baseMinAmount,
                      uint256 quoteMinAmount,
                      uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH
                      uint256 deadLine
                  )
                      external
                      override
                      payable
                      preventReentrant
                      judgeExpired(deadLine)
                      returns (
                          uint256 shares,
                          uint256 baseAdjustedInAmount,
                          uint256 quoteAdjustedInAmount
                      )
                  {
                      address _dvm = dvmAddress;
                      (baseAdjustedInAmount, quoteAdjustedInAmount) = _addDVMLiquidity(
                          _dvm,
                          baseInAmount,
                          quoteInAmount
                      );
                      require(
                          baseAdjustedInAmount >= baseMinAmount && quoteAdjustedInAmount >= quoteMinAmount,
                          "DODOV2Proxy02: deposit amount is not enough"
                      );
              
                      _deposit(msg.sender, _dvm, IDODOV2(_dvm)._BASE_TOKEN_(), baseAdjustedInAmount, flag == 1);
                      _deposit(msg.sender, _dvm, IDODOV2(_dvm)._QUOTE_TOKEN_(), quoteAdjustedInAmount, flag == 2);
                      
                      (shares, , ) = IDODOV2(_dvm).buyShares(msg.sender);
                      // refund dust eth
                      if (flag == 1 && msg.value > baseAdjustedInAmount) msg.sender.transfer(msg.value - baseAdjustedInAmount);
                      if (flag == 2 && msg.value > quoteAdjustedInAmount) msg.sender.transfer(msg.value - quoteAdjustedInAmount);
                  }
              
                  function _addDVMLiquidity(
                      address dvmAddress,
                      uint256 baseInAmount,
                      uint256 quoteInAmount
                  ) internal view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount) {
                      (uint256 baseReserve, uint256 quoteReserve) = IDODOV2(dvmAddress).getVaultReserve();
                      if (quoteReserve == 0 && baseReserve == 0) {
                          baseAdjustedInAmount = baseInAmount;
                          quoteAdjustedInAmount = quoteInAmount;
                      }
                      if (quoteReserve == 0 && baseReserve > 0) {
                          baseAdjustedInAmount = baseInAmount;
                          quoteAdjustedInAmount = 0;
                      }
                      if (quoteReserve > 0 && baseReserve > 0) {
                          uint256 baseIncreaseRatio = DecimalMath.divFloor(baseInAmount, baseReserve);
                          uint256 quoteIncreaseRatio = DecimalMath.divFloor(quoteInAmount, quoteReserve);
                          if (baseIncreaseRatio <= quoteIncreaseRatio) {
                              baseAdjustedInAmount = baseInAmount;
                              quoteAdjustedInAmount = DecimalMath.mulFloor(quoteReserve, baseIncreaseRatio);
                          } else {
                              quoteAdjustedInAmount = quoteInAmount;
                              baseAdjustedInAmount = DecimalMath.mulFloor(baseReserve, quoteIncreaseRatio);
                          }
                      }
                  }
              
                  // ============ DPP Functions (create & reset) ============
              
                  function createDODOPrivatePool(
                      address baseToken,
                      address quoteToken,
                      uint256 baseInAmount,
                      uint256 quoteInAmount,
                      uint256 lpFeeRate,
                      uint256 i,
                      uint256 k,
                      bool isOpenTwap,
                      uint256 deadLine
                  )
                      external
                      override
                      payable
                      preventReentrant
                      judgeExpired(deadLine)
                      returns (address newPrivatePool)
                  {
                      newPrivatePool = IDODOV2(_DPP_FACTORY_).createDODOPrivatePool();
              
                      address _baseToken = baseToken;
                      address _quoteToken = quoteToken;
                      _deposit(msg.sender, newPrivatePool, _baseToken, baseInAmount, _baseToken == _ETH_ADDRESS_);
                      _deposit(
                          msg.sender,
                          newPrivatePool,
                          _quoteToken,
                          quoteInAmount,
                          _quoteToken == _ETH_ADDRESS_
                      );
              
                      if (_baseToken == _ETH_ADDRESS_) _baseToken = _WETH_;
                      if (_quoteToken == _ETH_ADDRESS_) _quoteToken = _WETH_;
              
                      IDODOV2(_DPP_FACTORY_).initDODOPrivatePool(
                          newPrivatePool,
                          msg.sender,
                          _baseToken,
                          _quoteToken,
                          lpFeeRate,
                          k,
                          i,
                          isOpenTwap
                      );
                  }
              
                  function resetDODOPrivatePool(
                      address dppAddress,
                      uint256[] memory paramList,  //0 - newLpFeeRate, 1 - newI, 2 - newK
                      uint256[] memory amountList, //0 - baseInAmount, 1 - quoteInAmount, 2 - baseOutAmount, 3- quoteOutAmount
                      uint8 flag, // 0 - ERC20, 1 - baseInETH, 2 - quoteInETH, 3 - baseOutETH, 4 - quoteOutETH
                      uint256 minBaseReserve,
                      uint256 minQuoteReserve,
                      uint256 deadLine
                  ) external override payable preventReentrant judgeExpired(deadLine) {
                      _deposit(
                          msg.sender,
                          dppAddress,
                          IDODOV2(dppAddress)._BASE_TOKEN_(),
                          amountList[0],
                          flag == 1
                      );
                      _deposit(
                          msg.sender,
                          dppAddress,
                          IDODOV2(dppAddress)._QUOTE_TOKEN_(),
                          amountList[1],
                          flag == 2
                      );
              
                      require(IDODOV2(IDODOV2(dppAddress)._OWNER_()).reset(
                          msg.sender,
                          paramList[0],
                          paramList[1],
                          paramList[2],
                          amountList[2],
                          amountList[3],
                          minBaseReserve,
                          minQuoteReserve
                      ), "Reset Failed");
              
                      _withdraw(msg.sender, IDODOV2(dppAddress)._BASE_TOKEN_(), amountList[2], flag == 3);
                      _withdraw(msg.sender, IDODOV2(dppAddress)._QUOTE_TOKEN_(), amountList[3], flag == 4);
                  }
              
                  // ============ Swap ============
              
                  function dodoSwapV2ETHToToken(
                      address toToken,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  )
                      external
                      override
                      payable
                      judgeExpired(deadLine)
                      returns (uint256 returnAmount)
                  {
                      require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY");
                      require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO");
                      uint256 originGas = gasleft();
                      
                      uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender);
                      IWETH(_WETH_).deposit{value: msg.value}();
                      IWETH(_WETH_).transfer(dodoPairs[0], msg.value);
              
                      for (uint256 i = 0; i < dodoPairs.length; i++) {
                          if (i == dodoPairs.length - 1) {
                              if (directions & 1 == 0) {
                                  IDODOV2(dodoPairs[i]).sellBase(msg.sender);
                              } else {
                                  IDODOV2(dodoPairs[i]).sellQuote(msg.sender);
                              }
                          } else {
                              if (directions & 1 == 0) {
                                  IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]);
                              } else {
                                  IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]);
                              }
                          }
                          directions = directions >> 1;
                      }
              
                      returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance);
                      require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough");
              
                      _dodoGasReturn(originGas);
              
                      _execIncentive(isIncentive, _ETH_ADDRESS_, toToken);
              
                      emit OrderHistory(
                          _ETH_ADDRESS_,
                          toToken,
                          msg.sender,
                          msg.value,
                          returnAmount
                      );
                  }
              
                  function dodoSwapV2TokenToETH(
                      address fromToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  )
                      external
                      override
                      judgeExpired(deadLine)
                      returns (uint256 returnAmount)
                  {
                      require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY");
                      require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO");
                      uint256 originGas = gasleft();
                      
                      IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount);
              
                      for (uint256 i = 0; i < dodoPairs.length; i++) {
                          if (i == dodoPairs.length - 1) {
                              if (directions & 1 == 0) {
                                  IDODOV2(dodoPairs[i]).sellBase(address(this));
                              } else {
                                  IDODOV2(dodoPairs[i]).sellQuote(address(this));
                              }
                          } else {
                              if (directions & 1 == 0) {
                                  IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]);
                              } else {
                                  IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]);
                              }
                          }
                          directions = directions >> 1;
                      }
                      returnAmount = IWETH(_WETH_).balanceOf(address(this));
                      require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough");
                      IWETH(_WETH_).withdraw(returnAmount);
                      msg.sender.transfer(returnAmount);
              
                      _dodoGasReturn(originGas);
              
                      _execIncentive(isIncentive, fromToken, _ETH_ADDRESS_);
              
                      emit OrderHistory(
                          fromToken,
                          _ETH_ADDRESS_,
                          msg.sender,
                          fromTokenAmount,
                          returnAmount
                      );
                  }
              
                  function dodoSwapV2TokenToToken(
                      address fromToken,
                      address toToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  )
                      external
                      override
                      judgeExpired(deadLine)
                      returns (uint256 returnAmount)
                  {
                      require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY");
                      require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO");
                      uint256 originGas = gasleft();
              
                      uint256 originToTokenBalance = IERC20(toToken).balanceOf(msg.sender);
                      IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(fromToken, msg.sender, dodoPairs[0], fromTokenAmount);
              
                      for (uint256 i = 0; i < dodoPairs.length; i++) {
                          if (i == dodoPairs.length - 1) {
                              if (directions & 1 == 0) {
                                  IDODOV2(dodoPairs[i]).sellBase(msg.sender);
                              } else {
                                  IDODOV2(dodoPairs[i]).sellQuote(msg.sender);
                              }
                          } else {
                              if (directions& 1 == 0) {
                                  IDODOV2(dodoPairs[i]).sellBase(dodoPairs[i + 1]);
                              } else {
                                  IDODOV2(dodoPairs[i]).sellQuote(dodoPairs[i + 1]);
                              }
                          }
                          directions = directions >> 1;
                      }
                      returnAmount = IERC20(toToken).balanceOf(msg.sender).sub(originToTokenBalance);
                      require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough");
                      
                      _dodoGasReturn(originGas);
              
                      _execIncentive(isIncentive, fromToken, toToken);
              
                      emit OrderHistory(
                          fromToken,
                          toToken,
                          msg.sender,
                          fromTokenAmount,
                          returnAmount
                      );
                  }
              
                  function externalSwap(
                      address fromToken,
                      address toToken,
                      address approveTarget,
                      address swapTarget,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      bytes memory callDataConcat,
                      bool isIncentive,
                      uint256 deadLine
                  )
                      external
                      override
                      payable
                      judgeExpired(deadLine)
                      returns (uint256 returnAmount)
                  {
                      require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO");
                      require(fromToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_SELL_CHI");
                      require(toToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_BUY_CHI");
                      
                      uint256 toTokenOriginBalance = IERC20(toToken).universalBalanceOf(msg.sender);
                      if (fromToken != _ETH_ADDRESS_) {
                          IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(
                              fromToken,
                              msg.sender,
                              address(this),
                              fromTokenAmount
                          );
                          IERC20(fromToken).universalApproveMax(approveTarget, fromTokenAmount);
                      }
              
                      require(isWhiteListed[swapTarget], "DODOV2Proxy02: Not Whitelist Contract");
                      (bool success, ) = swapTarget.call{value: fromToken == _ETH_ADDRESS_ ? msg.value : 0}(callDataConcat);
              
                      require(success, "DODOV2Proxy02: External Swap execution Failed");
              
                      IERC20(toToken).universalTransfer(
                          msg.sender,
                          IERC20(toToken).universalBalanceOf(address(this))
                      );
              
                      returnAmount = IERC20(toToken).universalBalanceOf(msg.sender).sub(toTokenOriginBalance);
                      require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough");
              
                      _externalGasReturn();
              
                      _execIncentive(isIncentive, fromToken, toToken);
              
                      emit OrderHistory(
                          fromToken,
                          toToken,
                          msg.sender,
                          fromTokenAmount,
                          returnAmount
                      );
                  }
              
                  function dodoSwapV1(
                      address fromToken,
                      address toToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory dodoPairs,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  )
                      external
                      override
                      payable
                      judgeExpired(deadLine)
                      returns (uint256 returnAmount)
                  {
                      require(dodoPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY");
                      require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO");
                      require(fromToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_SELL_CHI");
                      require(toToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_BUY_CHI");
                      
                      uint256 originGas = gasleft();
              
                      address _fromToken = fromToken;
                      address _toToken = toToken;
                      
                      _deposit(msg.sender, address(this), _fromToken, fromTokenAmount, _fromToken == _ETH_ADDRESS_);
              
                      for (uint256 i = 0; i < dodoPairs.length; i++) {
                          address curDodoPair = dodoPairs[i];
                          if (directions & 1 == 0) {
                              address curDodoBase = IDODOV1(curDodoPair)._BASE_TOKEN_();
                              require(curDodoBase != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_CHI");
                              uint256 curAmountIn = IERC20(curDodoBase).balanceOf(address(this));
                              IERC20(curDodoBase).universalApproveMax(curDodoPair, curAmountIn);
                              IDODOV1(curDodoPair).sellBaseToken(curAmountIn, 0, "");
                          } else {
                              address curDodoQuote = IDODOV1(curDodoPair)._QUOTE_TOKEN_();
                              require(curDodoQuote != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_CHI");
                              uint256 curAmountIn = IERC20(curDodoQuote).balanceOf(address(this));
                              IERC20(curDodoQuote).universalApproveMax(curDodoPair, curAmountIn);
                              uint256 canBuyBaseAmount = IDODOSellHelper(_DODO_SELL_HELPER_).querySellQuoteToken(
                                  curDodoPair,
                                  curAmountIn
                              );
                              IDODOV1(curDodoPair).buyBaseToken(canBuyBaseAmount, curAmountIn, "");
                          }
                          directions = directions >> 1;
                      }
              
                      
                      if (_toToken == _ETH_ADDRESS_) {
                          returnAmount = IWETH(_WETH_).balanceOf(address(this));
                          IWETH(_WETH_).withdraw(returnAmount);
                      } else {
                          returnAmount = IERC20(_toToken).tokenBalanceOf(address(this));
                      }
                      
                      require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough");
                      IERC20(_toToken).universalTransfer(msg.sender, returnAmount);
              
                      _dodoGasReturn(originGas);
              
                      _execIncentive(isIncentive, _fromToken, _toToken);
              
                      emit OrderHistory(_fromToken, _toToken, msg.sender, fromTokenAmount, returnAmount);
                  }
              
              
                  function mixSwap(
                      address fromToken,
                      address toToken,
                      uint256 fromTokenAmount,
                      uint256 minReturnAmount,
                      address[] memory mixAdapters,
                      address[] memory mixPairs,
                      address[] memory assetTo,
                      uint256 directions,
                      bool isIncentive,
                      uint256 deadLine
                  ) external override payable judgeExpired(deadLine) returns (uint256 returnAmount) {
                      require(mixPairs.length > 0, "DODOV2Proxy02: PAIRS_EMPTY");
                      require(mixPairs.length == mixAdapters.length, "DODOV2Proxy02: PAIR_ADAPTER_NOT_MATCH");
                      require(mixPairs.length == assetTo.length - 1, "DODOV2Proxy02: PAIR_ASSETTO_NOT_MATCH");
                      require(minReturnAmount > 0, "DODOV2Proxy02: RETURN_AMOUNT_ZERO");
              
                      address _fromToken = fromToken;
                      address _toToken = toToken;
                      uint256 _fromTokenAmount = fromTokenAmount;
              
                      require(_fromToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_SELL_CHI");
                      require(_toToken != _CHI_TOKEN_, "DODOV2Proxy02: NOT_SUPPORT_BUY_CHI");
                      
                      uint256 originGas = gasleft();
                      uint256 toTokenOriginBalance = IERC20(_toToken).universalBalanceOf(msg.sender);
                      
                      _deposit(msg.sender, assetTo[0], _fromToken, _fromTokenAmount, _fromToken == _ETH_ADDRESS_);
              
                      for (uint256 i = 0; i < mixPairs.length; i++) {
                          if (directions & 1 == 0) {
                              IDODOAdapter(mixAdapters[i]).sellBase(assetTo[i + 1],mixPairs[i]);
                          } else {
                              IDODOAdapter(mixAdapters[i]).sellQuote(assetTo[i + 1],mixPairs[i]);
                          }
                          directions = directions >> 1;
                      }
              
                      if(_toToken == _ETH_ADDRESS_) {
                          returnAmount = IWETH(_WETH_).balanceOf(address(this));
                          IWETH(_WETH_).withdraw(returnAmount);
                          msg.sender.transfer(returnAmount);
                      }else {
                          returnAmount = IERC20(_toToken).tokenBalanceOf(msg.sender).sub(toTokenOriginBalance);
                      }
              
                      require(returnAmount >= minReturnAmount, "DODOV2Proxy02: Return amount is not enough");
                      
                      _dodoGasReturn(originGas);
              
                      _execIncentive(isIncentive, _fromToken, _toToken);
              
                      emit OrderHistory(
                          _fromToken,
                          _toToken,
                          msg.sender,
                          _fromTokenAmount,
                          returnAmount
                      );
                  }
              
                  //============ CrowdPooling Functions (create & bid) ============
              
                  function createCrowdPooling(
                      address baseToken,
                      address quoteToken,
                      uint256 baseInAmount,
                      uint256[] memory timeLine,
                      uint256[] memory valueList,
                      bool isOpenTWAP,
                      uint256 deadLine
                  ) external override payable preventReentrant judgeExpired(deadLine) returns (address payable newCrowdPooling) {
                      address _baseToken = baseToken;
                      address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken;
                      
                      newCrowdPooling = IDODOV2(_CP_FACTORY_).createCrowdPooling();
              
                      _deposit(
                          msg.sender,
                          newCrowdPooling,
                          _baseToken,
                          baseInAmount,
                          false
                      );
              
                      newCrowdPooling.transfer(msg.value);
              
                      IDODOV2(_CP_FACTORY_).initCrowdPooling(
                          newCrowdPooling,
                          msg.sender,
                          _baseToken,
                          _quoteToken,
                          timeLine,
                          valueList,
                          isOpenTWAP
                      );
                  }
              
                  function bid(
                      address cpAddress,
                      uint256 quoteAmount,
                      uint8 flag, // 0 - ERC20, 1 - quoteInETH
                      uint256 deadLine
                  ) external override payable preventReentrant judgeExpired(deadLine) {
                      _deposit(msg.sender, cpAddress, IDODOV2(cpAddress)._QUOTE_TOKEN_(), quoteAmount, flag == 1);
                      IDODOV2(cpAddress).bid(msg.sender);
                  }
              
              
                  function addLiquidityToV1(
                      address pair,
                      uint256 baseAmount,
                      uint256 quoteAmount,
                      uint256 baseMinShares,
                      uint256 quoteMinShares,
                      uint8 flag, // 0 erc20 In  1 baseInETH  2 quoteIn ETH 
                      uint256 deadLine
                  ) external override payable preventReentrant judgeExpired(deadLine) returns(uint256 baseShares, uint256 quoteShares) {
                      address _baseToken = IDODOV1(pair)._BASE_TOKEN_();
                      address _quoteToken = IDODOV1(pair)._QUOTE_TOKEN_();
                      
                      _deposit(msg.sender, address(this), _baseToken, baseAmount, flag == 1);
                      _deposit(msg.sender, address(this), _quoteToken, quoteAmount, flag == 2);
              
                      
                      if(baseAmount > 0) {
                          IERC20(_baseToken).universalApproveMax(pair, baseAmount);
                          baseShares = IDODOV1(pair).depositBaseTo(msg.sender, baseAmount);
                      }
                      if(quoteAmount > 0) {
                          IERC20(_quoteToken).universalApproveMax(pair, quoteAmount);
                          quoteShares = IDODOV1(pair).depositQuoteTo(msg.sender, quoteAmount);
                      }
              
                      require(baseShares >= baseMinShares && quoteShares >= quoteMinShares,"DODOV2Proxy02: Return DLP is not enough");
                  }
                  
              
                  function _deposit(
                      address from,
                      address to,
                      address token,
                      uint256 amount,
                      bool isETH
                  ) internal {
                      if (isETH) {
                          if (amount > 0) {
                              IWETH(_WETH_).deposit{value: amount}();
                              if (to != address(this)) SafeERC20.safeTransfer(IERC20(_WETH_), to, amount);
                          }
                      } else {
                          IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(token, from, to, amount);
                      }
                  }
              
                  function _withdraw(
                      address payable to,
                      address token,
                      uint256 amount,
                      bool isETH
                  ) internal {
                      if (isETH) {
                          if (amount > 0) {
                              IWETH(_WETH_).withdraw(amount);
                              to.transfer(amount);
                          }
                      } else {
                          if (amount > 0) {
                              SafeERC20.safeTransfer(IERC20(token), to, amount);
                          }
                      }
                  }
              
                  function _dodoGasReturn(uint256 originGas) internal {
                      uint256 _gasDodoMaxReturn = _GAS_DODO_MAX_RETURN_;
                      if(_gasDodoMaxReturn > 0) {
                          uint256 calcGasTokenBurn = originGas.sub(gasleft()) / 65000;
                          uint256 gasTokenBurn = calcGasTokenBurn > _gasDodoMaxReturn ? _gasDodoMaxReturn : calcGasTokenBurn;
                          if(gasTokenBurn >= 3 && gasleft() > 27710 + gasTokenBurn * 6080)
                              IChi(_CHI_TOKEN_).freeUpTo(gasTokenBurn);
                      }
                  }
              
                  function _externalGasReturn() internal {
                      uint256 _gasExternalReturn = _GAS_EXTERNAL_RETURN_;
                      if(_gasExternalReturn > 0) {
                          if(gasleft() > 27710 + _gasExternalReturn * 6080)
                              IChi(_CHI_TOKEN_).freeUpTo(_gasExternalReturn);
                      }
                  }
              
                  function _execIncentive(bool isIncentive, address fromToken,address toToken) internal {
                      if(isIncentive && gasleft() > 30000) {
                          IDODOIncentive(_DODO_INCENTIVE_).triggerIncentive(fromToken, toToken, msg.sender);
                      }
                  }
              
              }

              File 2 of 8: DODOApproveProxy
              // File: contracts/intf/IDODOApprove.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
                  SPDX-License-Identifier: Apache-2.0
              
              */
              
              pragma solidity 0.6.9;
              pragma experimental ABIEncoderV2;
              
              interface IDODOApprove {
                  function claimTokens(address token,address who,address dest,uint256 amount) external;
                  function getDODOProxy() external view returns (address);
              }
              
              // File: contracts/lib/InitializableOwnable.sol
              
              
              /**
               * @title Ownable
               * @author DODO Breeder
               *
               * @notice Ownership related functions
               */
              contract InitializableOwnable {
                  address public _OWNER_;
                  address public _NEW_OWNER_;
                  bool internal _INITIALIZED_;
              
                  // ============ Events ============
              
                  event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  // ============ Modifiers ============
              
                  modifier notInitialized() {
                      require(!_INITIALIZED_, "DODO_INITIALIZED");
                      _;
                  }
              
                  modifier onlyOwner() {
                      require(msg.sender == _OWNER_, "NOT_OWNER");
                      _;
                  }
              
                  // ============ Functions ============
              
                  function initOwner(address newOwner) public notInitialized {
                      _INITIALIZED_ = true;
                      _OWNER_ = newOwner;
                  }
              
                  function transferOwnership(address newOwner) public onlyOwner {
                      emit OwnershipTransferPrepared(_OWNER_, newOwner);
                      _NEW_OWNER_ = newOwner;
                  }
              
                  function claimOwnership() public {
                      require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                      emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                      _OWNER_ = _NEW_OWNER_;
                      _NEW_OWNER_ = address(0);
                  }
              }
              
              // File: contracts/SmartRoute/DODOApproveProxy.sol
              
              
              
              
              interface IDODOApproveProxy {
                  function isAllowedProxy(address _proxy) external view returns (bool);
                  function claimTokens(address token,address who,address dest,uint256 amount) external;
              }
              
              /**
               * @title DODOApproveProxy
               * @author DODO Breeder
               *
               * @notice Allow different version dodoproxy to claim from DODOApprove
               */
              contract DODOApproveProxy is InitializableOwnable {
                  
                  // ============ Storage ============
                  uint256 private constant _TIMELOCK_DURATION_ = 3 days;
                  mapping (address => bool) public _IS_ALLOWED_PROXY_;
                  uint256 public _TIMELOCK_;
                  address public _PENDING_ADD_DODO_PROXY_;
                  address public immutable _DODO_APPROVE_;
              
                  // ============ Modifiers ============
                  modifier notLocked() {
                      require(
                          _TIMELOCK_ <= block.timestamp,
                          "SetProxy is timelocked"
                      );
                      _;
                  }
              
                  constructor(address dodoApporve) public {
                      _DODO_APPROVE_ = dodoApporve;
                  }
              
                  function init(address owner, address[] memory proxies) external {
                      initOwner(owner);
                      for(uint i = 0; i < proxies.length; i++) 
                          _IS_ALLOWED_PROXY_[proxies[i]] = true;
                  }
              
                  function unlockAddProxy(address newDodoProxy) public onlyOwner {
                      _TIMELOCK_ = block.timestamp + _TIMELOCK_DURATION_;
                      _PENDING_ADD_DODO_PROXY_ = newDodoProxy;
                  }
              
                  function lockAddProxy() public onlyOwner {
                     _PENDING_ADD_DODO_PROXY_ = address(0);
                     _TIMELOCK_ = 0;
                  }
              
              
                  function addDODOProxy() external onlyOwner notLocked() {
                      _IS_ALLOWED_PROXY_[_PENDING_ADD_DODO_PROXY_] = true;
                      lockAddProxy();
                  }
              
                  function removeDODOProxy (address oldDodoProxy) public onlyOwner {
                      _IS_ALLOWED_PROXY_[oldDodoProxy] = false;
                  }
                  
                  function claimTokens(
                      address token,
                      address who,
                      address dest,
                      uint256 amount
                  ) external {
                      require(_IS_ALLOWED_PROXY_[msg.sender], "DODOApproveProxy:Access restricted");
                      IDODOApprove(_DODO_APPROVE_).claimTokens(
                          token,
                          who,
                          dest,
                          amount
                      );
                  }
              
                  function isAllowedProxy(address _proxy) external view returns (bool) {
                      return _IS_ALLOWED_PROXY_[_proxy];
                  }
              }

              File 3 of 8: DODOApprove
              // File: contracts/intf/IERC20.sol
              
              // This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
              
              pragma solidity 0.6.9;
              
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
              
                  function decimals() external view returns (uint8);
              
                  function name() external view returns (string memory);
              
                  function symbol() external view returns (string memory);
              
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
              
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `recipient`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address recipient, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Moves `amount` tokens from `sender` to `recipient` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                  ) external returns (bool);
              }
              
              // File: contracts/lib/SafeMath.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              
              
              /**
               * @title SafeMath
               * @author DODO Breeder
               *
               * @notice Math operations with safety checks that revert on error
               */
              library SafeMath {
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
              
                      uint256 c = a * b;
                      require(c / a == b, "MUL_ERROR");
              
                      return c;
                  }
              
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "DIVIDING_ERROR");
                      return a / b;
                  }
              
                  function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 quotient = div(a, b);
                      uint256 remainder = a - quotient * b;
                      if (remainder > 0) {
                          return quotient + 1;
                      } else {
                          return quotient;
                      }
                  }
              
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a, "SUB_ERROR");
                      return a - b;
                  }
              
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a, "ADD_ERROR");
                      return c;
                  }
              
                  function sqrt(uint256 x) internal pure returns (uint256 y) {
                      uint256 z = x / 2 + 1;
                      y = x;
                      while (z < y) {
                          y = z;
                          z = (x / z + z) / 2;
                      }
                  }
              }
              
              // File: contracts/lib/SafeERC20.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
                  This is a simplified version of OpenZepplin's SafeERC20 library
              
              */
              
              
              
              
              
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using SafeMath for uint256;
              
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
              
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(
                          token,
                          abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
                      );
                  }
              
                  function safeApprove(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      // safeApprove should only be called when setting an initial allowance,
                      // or when resetting it to zero. To increase and decrease it, use
                      // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                      // solhint-disable-next-line max-line-length
                      require(
                          (value == 0) || (token.allowance(address(this), spender) == 0),
                          "SafeERC20: approve from non-zero to non-zero allowance"
                      );
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                  }
              
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves.
              
                      // A Solidity high level call has three parts:
                      //  1. The target address is checked to verify it contains contract code
                      //  2. The call itself is made, and success asserted
                      //  3. The return value is decoded, which in turn checks the size of the returned data.
                      // solhint-disable-next-line max-line-length
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = address(token).call(data);
                      require(success, "SafeERC20: low-level call failed");
              
                      if (returndata.length > 0) {
                          // Return data is optional
                          // solhint-disable-next-line max-line-length
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              
              // File: contracts/lib/InitializableOwnable.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              
              /**
               * @title Ownable
               * @author DODO Breeder
               *
               * @notice Ownership related functions
               */
              contract InitializableOwnable {
                  address public _OWNER_;
                  address public _NEW_OWNER_;
                  bool internal _INITIALIZED_;
              
                  // ============ Events ============
              
                  event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  // ============ Modifiers ============
              
                  modifier notInitialized() {
                      require(!_INITIALIZED_, "DODO_INITIALIZED");
                      _;
                  }
              
                  modifier onlyOwner() {
                      require(msg.sender == _OWNER_, "NOT_OWNER");
                      _;
                  }
              
                  // ============ Functions ============
              
                  function initOwner(address newOwner) public notInitialized {
                      _INITIALIZED_ = true;
                      _OWNER_ = newOwner;
                  }
              
                  function transferOwnership(address newOwner) public onlyOwner {
                      emit OwnershipTransferPrepared(_OWNER_, newOwner);
                      _NEW_OWNER_ = newOwner;
                  }
              
                  function claimOwnership() public {
                      require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                      emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                      _OWNER_ = _NEW_OWNER_;
                      _NEW_OWNER_ = address(0);
                  }
              }
              
              // File: contracts/SmartRoute/DODOApprove.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              
              
              
              
              
              /**
               * @title DODOApprove
               * @author DODO Breeder
               *
               * @notice Handle authorizations in DODO platform
               */
              contract DODOApprove is InitializableOwnable {
                  using SafeERC20 for IERC20;
                  
                  // ============ Storage ============
                  uint256 private constant _TIMELOCK_DURATION_ = 3 days;
                  uint256 private constant _TIMELOCK_EMERGENCY_DURATION_ = 24 hours;
                  uint256 public _TIMELOCK_;
                  address public _PENDING_DODO_PROXY_;
                  address public _DODO_PROXY_;
              
                  // ============ Events ============
              
                  event SetDODOProxy(address indexed oldProxy, address indexed newProxy);
              
                  
                  // ============ Modifiers ============
                  modifier notLocked() {
                      require(
                          _TIMELOCK_ <= block.timestamp,
                          "SetProxy is timelocked"
                      );
                      _;
                  }
              
                  function init(address owner, address initProxyAddress) external {
                      initOwner(owner);
                      _DODO_PROXY_ = initProxyAddress;
                  }
              
                  function unlockSetProxy(address newDodoProxy) public onlyOwner {
                      if(_DODO_PROXY_ == address(0))
                          _TIMELOCK_ = block.timestamp + _TIMELOCK_EMERGENCY_DURATION_;
                      else
                          _TIMELOCK_ = block.timestamp + _TIMELOCK_DURATION_;
                      _PENDING_DODO_PROXY_ = newDodoProxy;
                  }
              
              
                  function lockSetProxy() public onlyOwner {
                     _PENDING_DODO_PROXY_ = address(0);
                     _TIMELOCK_ = 0;
                  }
              
              
                  function setDODOProxy() external onlyOwner notLocked() {
                      emit SetDODOProxy(_DODO_PROXY_, _PENDING_DODO_PROXY_);
                      _DODO_PROXY_ = _PENDING_DODO_PROXY_;
                      lockSetProxy();
                  }
              
              
                  function claimTokens(
                      address token,
                      address who,
                      address dest,
                      uint256 amount
                  ) external {
                      require(msg.sender == _DODO_PROXY_, "DODOApprove:Access restricted");
                      if (amount > 0) {
                          IERC20(token).safeTransferFrom(who, dest, amount);
                      }
                  }
              
                  function getDODOProxy() public view returns (address) {
                      return _DODO_PROXY_;
                  }
              }

              File 4 of 8: DODOToken
              /*
              
                  Copyright 2020 DODO ZOO.
                  SPDX-License-Identifier: Apache-2.0
              
              */
              
              pragma solidity 0.6.9;
              pragma experimental ABIEncoderV2;
              
              
              /**
               * @title SafeMath
               * @author DODO Breeder
               *
               * @notice Math operations with safety checks that revert on error
               */
              library SafeMath {
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
              
                      uint256 c = a * b;
                      require(c / a == b, "MUL_ERROR");
              
                      return c;
                  }
              
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "DIVIDING_ERROR");
                      return a / b;
                  }
              
                  function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 quotient = div(a, b);
                      uint256 remainder = a - quotient * b;
                      if (remainder > 0) {
                          return quotient + 1;
                      } else {
                          return quotient;
                      }
                  }
              
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a, "SUB_ERROR");
                      return a - b;
                  }
              
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a, "ADD_ERROR");
                      return c;
                  }
              
                  function sqrt(uint256 x) internal pure returns (uint256 y) {
                      uint256 z = x / 2 + 1;
                      y = x;
                      while (z < y) {
                          y = z;
                          z = (x / z + z) / 2;
                      }
                  }
              }
              
              
              // File: contracts/token/DODOToken.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DODO Token
               * @author DODO Breeder
               */
              contract DODOToken {
                  using SafeMath for uint256;
              
                  string public symbol = "DODO";
                  string public name = "DODO bird";
              
                  uint256 public decimals = 18;
                  uint256 public totalSupply = 1000000000 * 10**18; // 1 Billion
              
                  mapping(address => uint256) internal balances;
                  mapping(address => mapping(address => uint256)) internal allowed;
              
                  // ============ Events ============
              
                  event Transfer(address indexed from, address indexed to, uint256 amount);
              
                  event Approval(address indexed owner, address indexed spender, uint256 amount);
              
                  // ============ Functions ============
              
                  constructor() public {
                      balances[msg.sender] = totalSupply;
                  }
              
                  /**
                   * @dev transfer token for a specified address
                   * @param to The address to transfer to.
                   * @param amount The amount to be transferred.
                   */
                  function transfer(address to, uint256 amount) public returns (bool) {
                      require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH");
              
                      balances[msg.sender] = balances[msg.sender].sub(amount);
                      balances[to] = balances[to].add(amount);
                      emit Transfer(msg.sender, to, amount);
                      return true;
                  }
              
                  /**
                   * @dev Gets the balance of the specified address.
                   * @param owner The address to query the the balance of.
                   * @return balance An uint256 representing the amount owned by the passed address.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance) {
                      return balances[owner];
                  }
              
                  /**
                   * @dev Transfer tokens from one address to another
                   * @param from address The address which you want to send tokens from
                   * @param to address The address which you want to transfer to
                   * @param amount uint256 the amount of tokens to be transferred
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) public returns (bool) {
                      require(amount <= balances[from], "BALANCE_NOT_ENOUGH");
                      require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
              
                      balances[from] = balances[from].sub(amount);
                      balances[to] = balances[to].add(amount);
                      allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount);
                      emit Transfer(from, to, amount);
                      return true;
                  }
              
                  /**
                   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                   * @param spender The address which will spend the funds.
                   * @param amount The amount of tokens to be spent.
                   */
                  function approve(address spender, uint256 amount) public returns (bool) {
                      allowed[msg.sender][spender] = amount;
                      emit Approval(msg.sender, spender, amount);
                      return true;
                  }
              
                  /**
                   * @dev Function to check the amount of tokens that an owner allowed to a spender.
                   * @param owner address The address which owns the funds.
                   * @param spender address The address which will spend the funds.
                   * @return A uint256 specifying the amount of tokens still available for the spender.
                   */
                  function allowance(address owner, address spender) public view returns (uint256) {
                      return allowed[owner][spender];
                  }
              }

              File 5 of 8: DODO
              /*
              
                  Copyright 2020 DODO ZOO.
                  SPDX-License-Identifier: Apache-2.0
              
              */
              
              pragma solidity 0.6.9;
              pragma experimental ABIEncoderV2;
              
              library Types {
                  enum RStatus {ONE, ABOVE_ONE, BELOW_ONE}
              }
              
              // File: contracts/intf/IERC20.sol
              
              // This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
              
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
              
                  function decimals() external view returns (uint8);
              
                  function name() external view returns (string memory);
              
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
              
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `recipient`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address recipient, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Moves `amount` tokens from `sender` to `recipient` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                  ) external returns (bool);
              }
              
              // File: contracts/lib/InitializableOwnable.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Ownable
               * @author DODO Breeder
               *
               * @notice Ownership related functions
               */
              contract InitializableOwnable {
                  address public _OWNER_;
                  address public _NEW_OWNER_;
              
                  // ============ Events ============
              
                  event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  // ============ Modifiers ============
              
                  modifier onlyOwner() {
                      require(msg.sender == _OWNER_, "NOT_OWNER");
                      _;
                  }
              
                  // ============ Functions ============
              
                  function transferOwnership(address newOwner) external onlyOwner {
                      require(newOwner != address(0), "INVALID_OWNER");
                      emit OwnershipTransferPrepared(_OWNER_, newOwner);
                      _NEW_OWNER_ = newOwner;
                  }
              
                  function claimOwnership() external {
                      require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                      emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                      _OWNER_ = _NEW_OWNER_;
                      _NEW_OWNER_ = address(0);
                  }
              }
              
              // File: contracts/lib/SafeMath.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title SafeMath
               * @author DODO Breeder
               *
               * @notice Math operations with safety checks that revert on error
               */
              library SafeMath {
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
              
                      uint256 c = a * b;
                      require(c / a == b, "MUL_ERROR");
              
                      return c;
                  }
              
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "DIVIDING_ERROR");
                      return a / b;
                  }
              
                  function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 quotient = div(a, b);
                      uint256 remainder = a - quotient * b;
                      if (remainder > 0) {
                          return quotient + 1;
                      } else {
                          return quotient;
                      }
                  }
              
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a, "SUB_ERROR");
                      return a - b;
                  }
              
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a, "ADD_ERROR");
                      return c;
                  }
              
                  function sqrt(uint256 x) internal pure returns (uint256 y) {
                      uint256 z = x / 2 + 1;
                      y = x;
                      while (z < y) {
                          y = z;
                          z = (x / z + z) / 2;
                      }
                  }
              }
              
              // File: contracts/lib/DecimalMath.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DecimalMath
               * @author DODO Breeder
               *
               * @notice Functions for fixed point number with 18 decimals
               */
              library DecimalMath {
                  using SafeMath for uint256;
              
                  uint256 constant ONE = 10**18;
              
                  function mul(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(d) / ONE;
                  }
              
                  function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(ONE).div(d);
                  }
              
                  function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(ONE).divCeil(d);
                  }
              }
              
              // File: contracts/lib/ReentrancyGuard.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title ReentrancyGuard
               * @author DODO Breeder
               *
               * @notice Protect functions from Reentrancy Attack
               */
              contract ReentrancyGuard {
                  // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations
                  // zero-state of _ENTERED_ is false
                  bool private _ENTERED_;
              
                  modifier preventReentrant() {
                      require(!_ENTERED_, "REENTRANT");
                      _ENTERED_ = true;
                      _;
                      _ENTERED_ = false;
                  }
              }
              
              // File: contracts/intf/IOracle.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              interface IOracle {
                  function getPrice() external view returns (uint256);
              }
              
              // File: contracts/intf/IDODOLpToken.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              interface IDODOLpToken {
                  function mint(address user, uint256 value) external;
              
                  function burn(address user, uint256 value) external;
              
                  function balanceOf(address owner) external view returns (uint256);
              
                  function totalSupply() external view returns (uint256);
              }
              
              // File: contracts/impl/Storage.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Storage
               * @author DODO Breeder
               *
               * @notice Local Variables
               */
              contract Storage is InitializableOwnable, ReentrancyGuard {
                  using SafeMath for uint256;
              
                  // ============ Variables for Control ============
              
                  bool internal _INITIALIZED_;
                  bool public _CLOSED_;
                  bool public _DEPOSIT_QUOTE_ALLOWED_;
                  bool public _DEPOSIT_BASE_ALLOWED_;
                  bool public _TRADE_ALLOWED_;
                  uint256 public _GAS_PRICE_LIMIT_;
              
                  // ============ Core Address ============
              
                  address public _SUPERVISOR_; // could freeze system in emergency
                  address public _MAINTAINER_; // collect maintainer fee to buy food for DODO
              
                  address public _BASE_TOKEN_;
                  address public _QUOTE_TOKEN_;
                  address public _ORACLE_;
              
                  // ============ Variables for PMM Algorithm ============
              
                  uint256 public _LP_FEE_RATE_;
                  uint256 public _MT_FEE_RATE_;
                  uint256 public _K_;
              
                  Types.RStatus public _R_STATUS_;
                  uint256 public _TARGET_BASE_TOKEN_AMOUNT_;
                  uint256 public _TARGET_QUOTE_TOKEN_AMOUNT_;
                  uint256 public _BASE_BALANCE_;
                  uint256 public _QUOTE_BALANCE_;
              
                  address public _BASE_CAPITAL_TOKEN_;
                  address public _QUOTE_CAPITAL_TOKEN_;
              
                  // ============ Variables for Final Settlement ============
              
                  uint256 public _BASE_CAPITAL_RECEIVE_QUOTE_;
                  uint256 public _QUOTE_CAPITAL_RECEIVE_BASE_;
                  mapping(address => bool) public _CLAIMED_;
              
                  // ============ Modifiers ============
              
                  modifier onlySupervisorOrOwner() {
                      require(msg.sender == _SUPERVISOR_ || msg.sender == _OWNER_, "NOT_SUPERVISOR_OR_OWNER");
                      _;
                  }
              
                  modifier notClosed() {
                      require(!_CLOSED_, "DODO_CLOSED");
                      _;
                  }
              
                  // ============ Helper Functions ============
              
                  function _checkDODOParameters() internal view returns (uint256) {
                      require(_K_ < DecimalMath.ONE, "K>=1");
                      require(_K_ > 0, "K=0");
                      require(_LP_FEE_RATE_.add(_MT_FEE_RATE_) < DecimalMath.ONE, "FEE_RATE>=1");
                  }
              
                  function getOraclePrice() public view returns (uint256) {
                      return IOracle(_ORACLE_).getPrice();
                  }
              
                  function getBaseCapitalBalanceOf(address lp) public view returns (uint256) {
                      return IDODOLpToken(_BASE_CAPITAL_TOKEN_).balanceOf(lp);
                  }
              
                  function getTotalBaseCapital() public view returns (uint256) {
                      return IDODOLpToken(_BASE_CAPITAL_TOKEN_).totalSupply();
                  }
              
                  function getQuoteCapitalBalanceOf(address lp) public view returns (uint256) {
                      return IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).balanceOf(lp);
                  }
              
                  function getTotalQuoteCapital() public view returns (uint256) {
                      return IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).totalSupply();
                  }
              
                  // ============ Version Control ============
                  function version() external pure returns (uint256) {
                      return 100; // 1.0.0
                  }
              }
              
              // File: contracts/intf/IDODOCallee.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              interface IDODOCallee {
                  function dodoCall(
                      bool isBuyBaseToken,
                      uint256 baseAmount,
                      uint256 quoteAmount,
                      bytes calldata data
                  ) external;
              }
              
              // File: contracts/lib/DODOMath.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DODOMath
               * @author DODO Breeder
               *
               * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions
               */
              library DODOMath {
                  using SafeMath for uint256;
              
                  /*
                      Integrate dodo curve fron V1 to V2
                      require V0>=V1>=V2>0
                      res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1)
                      let V1-V2=delta
                      res = i*delta*(1-k+k(V0^2/V1/V2))
                  */
                  function _GeneralIntegrate(
                      uint256 V0,
                      uint256 V1,
                      uint256 V2,
                      uint256 i,
                      uint256 k
                  ) internal pure returns (uint256) {
                      uint256 fairAmount = DecimalMath.mul(i, V1.sub(V2)); // i*delta
                      uint256 V0V0V1V2 = DecimalMath.divCeil(V0.mul(V0).div(V1), V2);
                      uint256 penalty = DecimalMath.mul(k, V0V0V1V2); // k(V0^2/V1/V2)
                      return DecimalMath.mul(fairAmount, DecimalMath.ONE.sub(k).add(penalty));
                  }
              
                  /*
                      The same with integration expression above, we have:
                      i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
                      Given Q1 and deltaB, solve Q2
                      This is a quadratic function and the standard version is
                      aQ2^2 + bQ2 + c = 0, where
                      a=1-k
                      -b=(1-k)Q1-kQ0^2/Q1+i*deltaB
                      c=-kQ0^2
                      and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k)
                      note: another root is negative, abondan
                      if deltaBSig=true, then Q2>Q1
                      if deltaBSig=false, then Q2<Q1
                  */
                  function _SolveQuadraticFunctionForTrade(
                      uint256 Q0,
                      uint256 Q1,
                      uint256 ideltaB,
                      bool deltaBSig,
                      uint256 k
                  ) internal pure returns (uint256) {
                      // calculate -b value and sig
                      // -b = (1-k)Q1-kQ0^2/Q1+i*deltaB
                      uint256 kQ02Q1 = DecimalMath.mul(k, Q0).mul(Q0).div(Q1); // kQ0^2/Q1
                      uint256 b = DecimalMath.mul(DecimalMath.ONE.sub(k), Q1); // (1-k)Q1
                      bool minusbSig = true;
                      if (deltaBSig) {
                          b = b.add(ideltaB); // (1-k)Q1+i*deltaB
                      } else {
                          kQ02Q1 = kQ02Q1.add(ideltaB); // i*deltaB+kQ0^2/Q1
                      }
                      if (b >= kQ02Q1) {
                          b = b.sub(kQ02Q1);
                          minusbSig = true;
                      } else {
                          b = kQ02Q1.sub(b);
                          minusbSig = false;
                      }
              
                      // calculate sqrt
                      uint256 squareRoot = DecimalMath.mul(
                          DecimalMath.ONE.sub(k).mul(4),
                          DecimalMath.mul(k, Q0).mul(Q0)
                      ); // 4(1-k)kQ0^2
                      squareRoot = b.mul(b).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0)
              
                      // final res
                      uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k)
                      uint256 numerator;
                      if (minusbSig) {
                          numerator = b.add(squareRoot);
                      } else {
                          numerator = squareRoot.sub(b);
                      }
              
                      if (deltaBSig) {
                          return DecimalMath.divFloor(numerator, denominator);
                      } else {
                          return DecimalMath.divCeil(numerator, denominator);
                      }
                  }
              
                  /*
                      Start from the integration function
                      i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
                      Assume Q2=Q0, Given Q1 and deltaB, solve Q0
                      let fairAmount = i*deltaB
                  */
                  function _SolveQuadraticFunctionForTarget(
                      uint256 V1,
                      uint256 k,
                      uint256 fairAmount
                  ) internal pure returns (uint256 V0) {
                      // V0 = V1+V1*(sqrt-1)/2k
                      uint256 sqrt = DecimalMath.divCeil(DecimalMath.mul(k, fairAmount).mul(4), V1);
                      sqrt = sqrt.add(DecimalMath.ONE).mul(DecimalMath.ONE).sqrt();
                      uint256 premium = DecimalMath.divCeil(sqrt.sub(DecimalMath.ONE), k.mul(2));
                      // V0 is greater than or equal to V1 according to the solution
                      return DecimalMath.mul(V1, DecimalMath.ONE.add(premium));
                  }
              }
              
              // File: contracts/impl/Pricing.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @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_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) {
                      // 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_);
                  }
              
                  // ============ R > 1 cases ============
              
                  function _RAboveBuyBaseToken(
                      uint256 amount,
                      uint256 baseBalance,
                      uint256 targetBaseAmount
                  ) internal view returns (uint256 payQuoteToken) {
                      require(amount < baseBalance, "DODO_BASE_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) {
                      // 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_);
                  }
              
                  // ============ 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 = _RBelowBackToOne();
                          return (_TARGET_BASE_TOKEN_AMOUNT_, Q.add(payQuoteToken));
                      } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          uint256 payBaseToken = _RAboveBackToOne();
                          return (B.add(payBaseToken), _TARGET_QUOTE_TOKEN_AMOUNT_);
                      }
                  }
              
                  function getMidPrice() public view returns (uint256 midPrice) {
                      (uint256 baseTarget, uint256 quoteTarget) = getExpectedTarget();
                      if (_R_STATUS_ == Types.RStatus.BELOW_ONE) {
                          uint256 R = DecimalMath.divFloor(
                              quoteTarget.mul(quoteTarget).div(_QUOTE_BALANCE_),
                              _QUOTE_BALANCE_
                          );
                          R = DecimalMath.ONE.sub(_K_).add(DecimalMath.mul(_K_, R));
                          return DecimalMath.divFloor(getOraclePrice(), R);
                      } else {
                          uint256 R = DecimalMath.divFloor(
                              baseTarget.mul(baseTarget).div(_BASE_BALANCE_),
                              _BASE_BALANCE_
                          );
                          R = DecimalMath.ONE.sub(_K_).add(DecimalMath.mul(_K_, R));
                          return DecimalMath.mul(getOraclePrice(), R);
                      }
                  }
              
                  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_);
                  // }
              }
              
              // File: contracts/lib/SafeERC20.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
                  This is a simplified version of OpenZepplin's SafeERC20 library
              
              */
              
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using SafeMath for uint256;
              
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
              
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(
                          token,
                          abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
                      );
                  }
              
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves.
              
                      // A Solidity high level call has three parts:
                      //  1. The target address is checked to verify it contains contract code
                      //  2. The call itself is made, and success asserted
                      //  3. The return value is decoded, which in turn checks the size of the returned data.
                      // solhint-disable-next-line max-line-length
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = address(token).call(data);
                      require(success, "SafeERC20: low-level call failed");
              
                      if (returndata.length > 0) {
                          // Return data is optional
                          // solhint-disable-next-line max-line-length
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              
              // File: contracts/impl/Settlement.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Settlement
               * @author DODO Breeder
               *
               * @notice Functions for assets settlement
               */
              contract Settlement is Storage {
                  using SafeMath for uint256;
                  using SafeERC20 for IERC20;
              
                  // ============ Events ============
              
                  event Donate(uint256 amount, bool isBaseToken);
              
                  event ClaimAssets(address indexed user, uint256 baseTokenAmount, uint256 quoteTokenAmount);
              
                  // ============ Assets IN/OUT Functions ============
              
                  function _baseTokenTransferIn(address from, uint256 amount) internal {
                      IERC20(_BASE_TOKEN_).safeTransferFrom(from, address(this), amount);
                      _BASE_BALANCE_ = _BASE_BALANCE_.add(amount);
                  }
              
                  function _quoteTokenTransferIn(address from, uint256 amount) internal {
                      IERC20(_QUOTE_TOKEN_).safeTransferFrom(from, address(this), amount);
                      _QUOTE_BALANCE_ = _QUOTE_BALANCE_.add(amount);
                  }
              
                  function _baseTokenTransferOut(address to, uint256 amount) internal {
                      IERC20(_BASE_TOKEN_).safeTransfer(to, amount);
                      _BASE_BALANCE_ = _BASE_BALANCE_.sub(amount);
                  }
              
                  function _quoteTokenTransferOut(address to, uint256 amount) internal {
                      IERC20(_QUOTE_TOKEN_).safeTransfer(to, amount);
                      _QUOTE_BALANCE_ = _QUOTE_BALANCE_.sub(amount);
                  }
              
                  // ============ Donate to Liquidity Pool Functions ============
              
                  function _donateBaseToken(uint256 amount) internal {
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.add(amount);
                      emit Donate(amount, true);
                  }
              
                  function _donateQuoteToken(uint256 amount) internal {
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.add(amount);
                      emit Donate(amount, false);
                  }
              
                  function donateBaseToken(uint256 amount) external preventReentrant {
                      _baseTokenTransferIn(msg.sender, amount);
                      _donateBaseToken(amount);
                  }
              
                  function donateQuoteToken(uint256 amount) external preventReentrant {
                      _quoteTokenTransferIn(msg.sender, amount);
                      _donateQuoteToken(amount);
                  }
              
                  // ============ Final Settlement Functions ============
              
                  // last step to shut down dodo
                  function finalSettlement() external onlyOwner notClosed {
                      _CLOSED_ = true;
                      _DEPOSIT_QUOTE_ALLOWED_ = false;
                      _DEPOSIT_BASE_ALLOWED_ = false;
                      _TRADE_ALLOWED_ = false;
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
              
                      if (_QUOTE_BALANCE_ > _TARGET_QUOTE_TOKEN_AMOUNT_) {
                          uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_);
                          _BASE_CAPITAL_RECEIVE_QUOTE_ = DecimalMath.divFloor(spareQuote, totalBaseCapital);
                      } else {
                          _TARGET_QUOTE_TOKEN_AMOUNT_ = _QUOTE_BALANCE_;
                      }
              
                      if (_BASE_BALANCE_ > _TARGET_BASE_TOKEN_AMOUNT_) {
                          uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_);
                          _QUOTE_CAPITAL_RECEIVE_BASE_ = DecimalMath.divFloor(spareBase, totalQuoteCapital);
                      } else {
                          _TARGET_BASE_TOKEN_AMOUNT_ = _BASE_BALANCE_;
                      }
              
                      _R_STATUS_ = Types.RStatus.ONE;
                  }
              
                  // claim remaining assets after final settlement
                  function claimAssets() external preventReentrant {
                      require(_CLOSED_, "DODO_NOT_CLOSED");
                      require(!_CLAIMED_[msg.sender], "ALREADY_CLAIMED");
                      _CLAIMED_[msg.sender] = true;
                      uint256 quoteAmount = DecimalMath.mul(
                          getBaseCapitalBalanceOf(msg.sender),
                          _BASE_CAPITAL_RECEIVE_QUOTE_
                      );
                      uint256 baseAmount = DecimalMath.mul(
                          getQuoteCapitalBalanceOf(msg.sender),
                          _QUOTE_CAPITAL_RECEIVE_BASE_
                      );
                      _baseTokenTransferOut(msg.sender, baseAmount);
                      _quoteTokenTransferOut(msg.sender, quoteAmount);
                      emit ClaimAssets(msg.sender, baseAmount, quoteAmount);
                      return;
                  }
              
                  // in case someone transfer to contract directly
                  function retrieve(address token, uint256 amount) external onlyOwner {
                      if (token == _BASE_TOKEN_) {
                          require(
                              IERC20(_BASE_TOKEN_).balanceOf(address(this)) >= _BASE_BALANCE_.add(amount),
                              "DODO_BASE_BALANCE_NOT_ENOUGH"
                          );
                      }
                      if (token == _QUOTE_TOKEN_) {
                          require(
                              IERC20(_QUOTE_TOKEN_).balanceOf(address(this)) >= _QUOTE_BALANCE_.add(amount),
                              "DODO_QUOTE_BALANCE_NOT_ENOUGH"
                          );
                      }
                      IERC20(token).safeTransfer(msg.sender, amount);
                  }
              }
              
              // File: contracts/impl/Trader.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Trader
               * @author DODO Breeder
               *
               * @notice Functions for trader operations
               */
              contract Trader is Storage, Pricing, Settlement {
                  using SafeMath for uint256;
              
                  // ============ Events ============
              
                  event SellBaseToken(address indexed seller, uint256 payBase, uint256 receiveQuote);
              
                  event BuyBaseToken(address indexed buyer, uint256 receiveBase, uint256 payQuote);
              
                  event ChargeMaintainerFee(address indexed maintainer, bool isBaseToken, uint256 amount);
              
                  // ============ Modifiers ============
              
                  modifier tradeAllowed() {
                      require(_TRADE_ALLOWED_, "TRADE_NOT_ALLOWED");
                      _;
                  }
              
                  modifier gasPriceLimit() {
                      require(tx.gasprice <= _GAS_PRICE_LIMIT_, "GAS_PRICE_EXCEED");
                      _;
                  }
              
                  // ============ Trade Functions ============
              
                  function sellBaseToken(
                      uint256 amount,
                      uint256 minReceiveQuote,
                      bytes calldata data
                  ) external tradeAllowed gasPriceLimit preventReentrant returns (uint256) {
                      // query price
                      (
                          uint256 receiveQuote,
                          uint256 lpFeeQuote,
                          uint256 mtFeeQuote,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      ) = _querySellBaseToken(amount);
                      require(receiveQuote >= minReceiveQuote, "SELL_BASE_RECEIVE_NOT_ENOUGH");
              
                      // settle assets
                      _quoteTokenTransferOut(msg.sender, receiveQuote);
                      if (data.length > 0) {
                          IDODOCallee(msg.sender).dodoCall(false, amount, receiveQuote, data);
                      }
                      _baseTokenTransferIn(msg.sender, amount);
                      if (mtFeeQuote != 0) {
                          _quoteTokenTransferOut(_MAINTAINER_, mtFeeQuote);
                          emit ChargeMaintainerFee(_MAINTAINER_, false, mtFeeQuote);
                      }
              
                      // update TARGET
                      if (_TARGET_QUOTE_TOKEN_AMOUNT_ != newQuoteTarget) {
                          _TARGET_QUOTE_TOKEN_AMOUNT_ = newQuoteTarget;
                      }
                      if (_TARGET_BASE_TOKEN_AMOUNT_ != newBaseTarget) {
                          _TARGET_BASE_TOKEN_AMOUNT_ = newBaseTarget;
                      }
                      if (_R_STATUS_ != newRStatus) {
                          _R_STATUS_ = newRStatus;
                      }
              
                      _donateQuoteToken(lpFeeQuote);
                      emit SellBaseToken(msg.sender, amount, receiveQuote);
              
                      return receiveQuote;
                  }
              
                  function buyBaseToken(
                      uint256 amount,
                      uint256 maxPayQuote,
                      bytes calldata data
                  ) external tradeAllowed gasPriceLimit preventReentrant returns (uint256) {
                      // query price
                      (
                          uint256 payQuote,
                          uint256 lpFeeBase,
                          uint256 mtFeeBase,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      ) = _queryBuyBaseToken(amount);
                      require(payQuote <= maxPayQuote, "BUY_BASE_COST_TOO_MUCH");
              
                      // settle assets
                      _baseTokenTransferOut(msg.sender, amount);
                      if (data.length > 0) {
                          IDODOCallee(msg.sender).dodoCall(true, amount, payQuote, data);
                      }
                      _quoteTokenTransferIn(msg.sender, payQuote);
                      if (mtFeeBase != 0) {
                          _baseTokenTransferOut(_MAINTAINER_, mtFeeBase);
                          emit ChargeMaintainerFee(_MAINTAINER_, true, mtFeeBase);
                      }
              
                      // update TARGET
                      if (_TARGET_QUOTE_TOKEN_AMOUNT_ != newQuoteTarget) {
                          _TARGET_QUOTE_TOKEN_AMOUNT_ = newQuoteTarget;
                      }
                      if (_TARGET_BASE_TOKEN_AMOUNT_ != newBaseTarget) {
                          _TARGET_BASE_TOKEN_AMOUNT_ = newBaseTarget;
                      }
                      if (_R_STATUS_ != newRStatus) {
                          _R_STATUS_ = newRStatus;
                      }
              
                      _donateBaseToken(lpFeeBase);
                      emit BuyBaseToken(msg.sender, amount, payQuote);
              
                      return payQuote;
                  }
              
                  // ============ Query Functions ============
              
                  function querySellBaseToken(uint256 amount) external view returns (uint256 receiveQuote) {
                      (receiveQuote, , , , , ) = _querySellBaseToken(amount);
                      return receiveQuote;
                  }
              
                  function queryBuyBaseToken(uint256 amount) external view returns (uint256 payQuote) {
                      (payQuote, , , , , ) = _queryBuyBaseToken(amount);
                      return payQuote;
                  }
              
                  function _querySellBaseToken(uint256 amount)
                      internal
                      view
                      returns (
                          uint256 receiveQuote,
                          uint256 lpFeeQuote,
                          uint256 mtFeeQuote,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      )
                  {
                      (newBaseTarget, newQuoteTarget) = getExpectedTarget();
              
                      uint256 sellBaseAmount = amount;
              
                      if (_R_STATUS_ == Types.RStatus.ONE) {
                          // case 1: R=1
                          // R falls below one
                          receiveQuote = _ROneSellBaseToken(sellBaseAmount, newQuoteTarget);
                          newRStatus = Types.RStatus.BELOW_ONE;
                      } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          uint256 backToOnePayBase = newBaseTarget.sub(_BASE_BALANCE_);
                          uint256 backToOneReceiveQuote = _QUOTE_BALANCE_.sub(newQuoteTarget);
                          // case 2: R>1
                          // complex case, R status depends on trading amount
                          if (sellBaseAmount < backToOnePayBase) {
                              // case 2.1: R status do not change
                              receiveQuote = _RAboveSellBaseToken(sellBaseAmount, _BASE_BALANCE_, newBaseTarget);
                              newRStatus = Types.RStatus.ABOVE_ONE;
                              if (receiveQuote > 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
                                  receiveQuote = backToOneReceiveQuote;
                              }
                          } else if (sellBaseAmount == backToOnePayBase) {
                              // case 2.2: R status changes to ONE
                              receiveQuote = backToOneReceiveQuote;
                              newRStatus = Types.RStatus.ONE;
                          } else {
                              // case 2.3: R status changes to BELOW_ONE
                              receiveQuote = backToOneReceiveQuote.add(
                                  _ROneSellBaseToken(sellBaseAmount.sub(backToOnePayBase), newQuoteTarget)
                              );
                              newRStatus = Types.RStatus.BELOW_ONE;
                          }
                      } else {
                          // _R_STATUS_ == Types.RStatus.BELOW_ONE
                          // case 3: R<1
                          receiveQuote = _RBelowSellBaseToken(sellBaseAmount, _QUOTE_BALANCE_, newQuoteTarget);
                          newRStatus = Types.RStatus.BELOW_ONE;
                      }
              
                      // count fees
                      lpFeeQuote = DecimalMath.mul(receiveQuote, _LP_FEE_RATE_);
                      mtFeeQuote = DecimalMath.mul(receiveQuote, _MT_FEE_RATE_);
                      receiveQuote = receiveQuote.sub(lpFeeQuote).sub(mtFeeQuote);
              
                      return (receiveQuote, lpFeeQuote, mtFeeQuote, newRStatus, newQuoteTarget, newBaseTarget);
                  }
              
                  function _queryBuyBaseToken(uint256 amount)
                      internal
                      view
                      returns (
                          uint256 payQuote,
                          uint256 lpFeeBase,
                          uint256 mtFeeBase,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      )
                  {
                      (newBaseTarget, newQuoteTarget) = getExpectedTarget();
              
                      // charge fee from user receive amount
                      lpFeeBase = DecimalMath.mul(amount, _LP_FEE_RATE_);
                      mtFeeBase = DecimalMath.mul(amount, _MT_FEE_RATE_);
                      uint256 buyBaseAmount = amount.add(lpFeeBase).add(mtFeeBase);
              
                      if (_R_STATUS_ == Types.RStatus.ONE) {
                          // case 1: R=1
                          payQuote = _ROneBuyBaseToken(buyBaseAmount, newBaseTarget);
                          newRStatus = Types.RStatus.ABOVE_ONE;
                      } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          // case 2: R>1
                          payQuote = _RAboveBuyBaseToken(buyBaseAmount, _BASE_BALANCE_, newBaseTarget);
                          newRStatus = Types.RStatus.ABOVE_ONE;
                      } else if (_R_STATUS_ == Types.RStatus.BELOW_ONE) {
                          uint256 backToOnePayQuote = newQuoteTarget.sub(_QUOTE_BALANCE_);
                          uint256 backToOneReceiveBase = _BASE_BALANCE_.sub(newBaseTarget);
                          // case 3: R<1
                          // complex case, R status may change
                          if (buyBaseAmount < backToOneReceiveBase) {
                              // case 3.1: R status do not change
                              // no need to check payQuote because spare base token must be greater than zero
                              payQuote = _RBelowBuyBaseToken(buyBaseAmount, _QUOTE_BALANCE_, newQuoteTarget);
                              newRStatus = Types.RStatus.BELOW_ONE;
                          } else if (buyBaseAmount == backToOneReceiveBase) {
                              // case 3.2: R status changes to ONE
                              payQuote = backToOnePayQuote;
                              newRStatus = Types.RStatus.ONE;
                          } else {
                              // case 3.3: R status changes to ABOVE_ONE
                              payQuote = backToOnePayQuote.add(
                                  _ROneBuyBaseToken(buyBaseAmount.sub(backToOneReceiveBase), newBaseTarget)
                              );
                              newRStatus = Types.RStatus.ABOVE_ONE;
                          }
                      }
              
                      return (payQuote, lpFeeBase, mtFeeBase, newRStatus, newQuoteTarget, newBaseTarget);
                  }
              }
              
              // File: contracts/impl/LiquidityProvider.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title LiquidityProvider
               * @author DODO Breeder
               *
               * @notice Functions for liquidity provider operations
               */
              contract LiquidityProvider is Storage, Pricing, Settlement {
                  using SafeMath for uint256;
              
                  // ============ Events ============
              
                  event Deposit(
                      address indexed payer,
                      address indexed receiver,
                      bool isBaseToken,
                      uint256 amount,
                      uint256 lpTokenAmount
                  );
              
                  event Withdraw(
                      address indexed payer,
                      address indexed receiver,
                      bool isBaseToken,
                      uint256 amount,
                      uint256 lpTokenAmount
                  );
              
                  event ChargePenalty(address indexed payer, bool isBaseToken, uint256 amount);
              
                  // ============ Modifiers ============
              
                  modifier depositQuoteAllowed() {
                      require(_DEPOSIT_QUOTE_ALLOWED_, "DEPOSIT_QUOTE_NOT_ALLOWED");
                      _;
                  }
              
                  modifier depositBaseAllowed() {
                      require(_DEPOSIT_BASE_ALLOWED_, "DEPOSIT_BASE_NOT_ALLOWED");
                      _;
                  }
              
                  // ============ Routine Functions ============
              
                  function withdrawBase(uint256 amount) external returns (uint256) {
                      return withdrawBaseTo(msg.sender, amount);
                  }
              
                  function depositBase(uint256 amount) external returns (uint256) {
                      return depositBaseTo(msg.sender, amount);
                  }
              
                  function withdrawQuote(uint256 amount) external returns (uint256) {
                      return withdrawQuoteTo(msg.sender, amount);
                  }
              
                  function depositQuote(uint256 amount) external returns (uint256) {
                      return depositQuoteTo(msg.sender, amount);
                  }
              
                  function withdrawAllBase() external returns (uint256) {
                      return withdrawAllBaseTo(msg.sender);
                  }
              
                  function withdrawAllQuote() external returns (uint256) {
                      return withdrawAllQuoteTo(msg.sender);
                  }
              
                  // ============ Deposit Functions ============
              
                  function depositQuoteTo(address to, uint256 amount)
                      public
                      preventReentrant
                      depositQuoteAllowed
                      returns (uint256)
                  {
                      (, uint256 quoteTarget) = getExpectedTarget();
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
                      uint256 capital = amount;
                      if (totalQuoteCapital == 0) {
                          // give remaining quote token to lp as a gift
                          capital = amount.add(quoteTarget);
                      } else if (quoteTarget > 0) {
                          capital = amount.mul(totalQuoteCapital).div(quoteTarget);
                      }
              
                      // settlement
                      _quoteTokenTransferIn(msg.sender, amount);
                      _mintQuoteCapital(to, capital);
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.add(amount);
              
                      emit Deposit(msg.sender, to, false, amount, capital);
                      return capital;
                  }
              
                  function depositBaseTo(address to, uint256 amount)
                      public
                      preventReentrant
                      depositBaseAllowed
                      returns (uint256)
                  {
                      (uint256 baseTarget, ) = getExpectedTarget();
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      uint256 capital = amount;
                      if (totalBaseCapital == 0) {
                          // give remaining base token to lp as a gift
                          capital = amount.add(baseTarget);
                      } else if (baseTarget > 0) {
                          capital = amount.mul(totalBaseCapital).div(baseTarget);
                      }
              
                      // settlement
                      _baseTokenTransferIn(msg.sender, amount);
                      _mintBaseCapital(to, capital);
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.add(amount);
              
                      emit Deposit(msg.sender, to, true, amount, capital);
                      return capital;
                  }
              
                  // ============ Withdraw Functions ============
              
                  function withdrawQuoteTo(address to, uint256 amount) public preventReentrant returns (uint256) {
                      // calculate capital
                      (, uint256 quoteTarget) = getExpectedTarget();
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
                      require(totalQuoteCapital > 0, "NO_QUOTE_LP");
              
                      uint256 requireQuoteCapital = amount.mul(totalQuoteCapital).divCeil(quoteTarget);
                      require(
                          requireQuoteCapital <= getQuoteCapitalBalanceOf(msg.sender),
                          "LP_QUOTE_CAPITAL_BALANCE_NOT_ENOUGH"
                      );
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawQuotePenalty(amount);
                      require(penalty < amount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.sub(amount);
                      _burnQuoteCapital(msg.sender, requireQuoteCapital);
                      _quoteTokenTransferOut(to, amount.sub(penalty));
                      _donateQuoteToken(penalty);
              
                      emit Withdraw(msg.sender, to, false, amount.sub(penalty), requireQuoteCapital);
                      emit ChargePenalty(msg.sender, false, penalty);
              
                      return amount.sub(penalty);
                  }
              
                  function withdrawBaseTo(address to, uint256 amount) public preventReentrant returns (uint256) {
                      // calculate capital
                      (uint256 baseTarget, ) = getExpectedTarget();
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      require(totalBaseCapital > 0, "NO_BASE_LP");
              
                      uint256 requireBaseCapital = amount.mul(totalBaseCapital).divCeil(baseTarget);
                      require(
                          requireBaseCapital <= getBaseCapitalBalanceOf(msg.sender),
                          "LP_BASE_CAPITAL_BALANCE_NOT_ENOUGH"
                      );
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawBasePenalty(amount);
                      require(penalty <= amount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.sub(amount);
                      _burnBaseCapital(msg.sender, requireBaseCapital);
                      _baseTokenTransferOut(to, amount.sub(penalty));
                      _donateBaseToken(penalty);
              
                      emit Withdraw(msg.sender, to, true, amount.sub(penalty), requireBaseCapital);
                      emit ChargePenalty(msg.sender, true, penalty);
              
                      return amount.sub(penalty);
                  }
              
                  // ============ Withdraw all Functions ============
              
                  function withdrawAllQuoteTo(address to) public preventReentrant returns (uint256) {
                      uint256 withdrawAmount = getLpQuoteBalance(msg.sender);
                      uint256 capital = getQuoteCapitalBalanceOf(msg.sender);
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawQuotePenalty(withdrawAmount);
                      require(penalty <= withdrawAmount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.sub(withdrawAmount);
                      _burnQuoteCapital(msg.sender, capital);
                      _quoteTokenTransferOut(to, withdrawAmount.sub(penalty));
                      _donateQuoteToken(penalty);
              
                      emit Withdraw(msg.sender, to, false, withdrawAmount, capital);
                      emit ChargePenalty(msg.sender, false, penalty);
              
                      return withdrawAmount.sub(penalty);
                  }
              
                  function withdrawAllBaseTo(address to) public preventReentrant returns (uint256) {
                      uint256 withdrawAmount = getLpBaseBalance(msg.sender);
                      uint256 capital = getBaseCapitalBalanceOf(msg.sender);
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawBasePenalty(withdrawAmount);
                      require(penalty <= withdrawAmount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.sub(withdrawAmount);
                      _burnBaseCapital(msg.sender, capital);
                      _baseTokenTransferOut(to, withdrawAmount.sub(penalty));
                      _donateBaseToken(penalty);
              
                      emit Withdraw(msg.sender, to, true, withdrawAmount, capital);
                      emit ChargePenalty(msg.sender, true, penalty);
              
                      return withdrawAmount.sub(penalty);
                  }
              
                  // ============ Helper Functions ============
              
                  function _mintBaseCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_BASE_CAPITAL_TOKEN_).mint(user, amount);
                  }
              
                  function _mintQuoteCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).mint(user, amount);
                  }
              
                  function _burnBaseCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_BASE_CAPITAL_TOKEN_).burn(user, amount);
                  }
              
                  function _burnQuoteCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).burn(user, amount);
                  }
              
                  // ============ Getter Functions ============
              
                  function getLpBaseBalance(address lp) public view returns (uint256 lpBalance) {
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      (uint256 baseTarget, ) = getExpectedTarget();
                      if (totalBaseCapital == 0) {
                          return 0;
                      }
                      lpBalance = getBaseCapitalBalanceOf(lp).mul(baseTarget).div(totalBaseCapital);
                      return lpBalance;
                  }
              
                  function getLpQuoteBalance(address lp) public view returns (uint256 lpBalance) {
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
                      (, uint256 quoteTarget) = getExpectedTarget();
                      if (totalQuoteCapital == 0) {
                          return 0;
                      }
                      lpBalance = getQuoteCapitalBalanceOf(lp).mul(quoteTarget).div(totalQuoteCapital);
                      return lpBalance;
                  }
              
                  function getWithdrawQuotePenalty(uint256 amount) public view returns (uint256 penalty) {
                      require(amount <= _QUOTE_BALANCE_, "DODO_QUOTE_BALANCE_NOT_ENOUGH");
                      if (_R_STATUS_ == Types.RStatus.BELOW_ONE) {
                          uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_);
                          uint256 price = getOraclePrice();
                          uint256 fairAmount = DecimalMath.mul(spareBase, price);
                          uint256 targetQuote = DODOMath._SolveQuadraticFunctionForTarget(
                              _QUOTE_BALANCE_,
                              _K_,
                              fairAmount
                          );
                          // if amount = _QUOTE_BALANCE_, div error
                          uint256 targetQuoteWithWithdraw = DODOMath._SolveQuadraticFunctionForTarget(
                              _QUOTE_BALANCE_.sub(amount),
                              _K_,
                              fairAmount
                          );
                          return targetQuote.sub(targetQuoteWithWithdraw.add(amount));
                      } else {
                          return 0;
                      }
                  }
              
                  function getWithdrawBasePenalty(uint256 amount) public view returns (uint256 penalty) {
                      require(amount <= _BASE_BALANCE_, "DODO_BASE_BALANCE_NOT_ENOUGH");
                      if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_);
                          uint256 price = getOraclePrice();
                          uint256 fairAmount = DecimalMath.divFloor(spareQuote, price);
                          uint256 targetBase = DODOMath._SolveQuadraticFunctionForTarget(
                              _BASE_BALANCE_,
                              _K_,
                              fairAmount
                          );
                          // if amount = _BASE_BALANCE_, div error
                          uint256 targetBaseWithWithdraw = DODOMath._SolveQuadraticFunctionForTarget(
                              _BASE_BALANCE_.sub(amount),
                              _K_,
                              fairAmount
                          );
                          return targetBase.sub(targetBaseWithWithdraw.add(amount));
                      } else {
                          return 0;
                      }
                  }
              }
              
              // File: contracts/impl/Admin.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Admin
               * @author DODO Breeder
               *
               * @notice Functions for admin operations
               */
              contract Admin is Storage {
                  // ============ Events ============
              
                  event UpdateGasPriceLimit(uint256 oldGasPriceLimit, uint256 newGasPriceLimit);
              
                  event UpdateLiquidityProviderFeeRate(
                      uint256 oldLiquidityProviderFeeRate,
                      uint256 newLiquidityProviderFeeRate
                  );
              
                  event UpdateMaintainerFeeRate(uint256 oldMaintainerFeeRate, uint256 newMaintainerFeeRate);
              
                  event UpdateK(uint256 oldK, uint256 newK);
              
                  // ============ Params Setting Functions ============
              
                  function setOracle(address newOracle) external onlyOwner {
                      _ORACLE_ = newOracle;
                  }
              
                  function setSupervisor(address newSupervisor) external onlyOwner {
                      _SUPERVISOR_ = newSupervisor;
                  }
              
                  function setMaintainer(address newMaintainer) external onlyOwner {
                      _MAINTAINER_ = newMaintainer;
                  }
              
                  function setLiquidityProviderFeeRate(uint256 newLiquidityPorviderFeeRate) external onlyOwner {
                      emit UpdateLiquidityProviderFeeRate(_LP_FEE_RATE_, newLiquidityPorviderFeeRate);
                      _LP_FEE_RATE_ = newLiquidityPorviderFeeRate;
                      _checkDODOParameters();
                  }
              
                  function setMaintainerFeeRate(uint256 newMaintainerFeeRate) external onlyOwner {
                      emit UpdateMaintainerFeeRate(_MT_FEE_RATE_, newMaintainerFeeRate);
                      _MT_FEE_RATE_ = newMaintainerFeeRate;
                      _checkDODOParameters();
                  }
              
                  function setK(uint256 newK) external onlyOwner {
                      emit UpdateK(_K_, newK);
                      _K_ = newK;
                      _checkDODOParameters();
                  }
              
                  function setGasPriceLimit(uint256 newGasPriceLimit) external onlySupervisorOrOwner {
                      emit UpdateGasPriceLimit(_GAS_PRICE_LIMIT_, newGasPriceLimit);
                      _GAS_PRICE_LIMIT_ = newGasPriceLimit;
                  }
              
                  // ============ System Control Functions ============
              
                  function disableTrading() external onlySupervisorOrOwner {
                      _TRADE_ALLOWED_ = false;
                  }
              
                  function enableTrading() external onlyOwner notClosed {
                      _TRADE_ALLOWED_ = true;
                  }
              
                  function disableQuoteDeposit() external onlySupervisorOrOwner {
                      _DEPOSIT_QUOTE_ALLOWED_ = false;
                  }
              
                  function enableQuoteDeposit() external onlyOwner notClosed {
                      _DEPOSIT_QUOTE_ALLOWED_ = true;
                  }
              
                  function disableBaseDeposit() external onlySupervisorOrOwner {
                      _DEPOSIT_BASE_ALLOWED_ = false;
                  }
              
                  function enableBaseDeposit() external onlyOwner notClosed {
                      _DEPOSIT_BASE_ALLOWED_ = true;
                  }
              }
              
              // File: contracts/lib/Ownable.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Ownable
               * @author DODO Breeder
               *
               * @notice Ownership related functions
               */
              contract Ownable {
                  address public _OWNER_;
                  address public _NEW_OWNER_;
              
                  // ============ Events ============
              
                  event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  // ============ Modifiers ============
              
                  modifier onlyOwner() {
                      require(msg.sender == _OWNER_, "NOT_OWNER");
                      _;
                  }
              
                  // ============ Functions ============
              
                  constructor() internal {
                      _OWNER_ = msg.sender;
                      emit OwnershipTransferred(address(0), _OWNER_);
                  }
              
                  function transferOwnership(address newOwner) external onlyOwner {
                      require(newOwner != address(0), "INVALID_OWNER");
                      emit OwnershipTransferPrepared(_OWNER_, newOwner);
                      _NEW_OWNER_ = newOwner;
                  }
              
                  function claimOwnership() external {
                      require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                      emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                      _OWNER_ = _NEW_OWNER_;
                      _NEW_OWNER_ = address(0);
                  }
              }
              
              // File: contracts/impl/DODOLpToken.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DODOLpToken
               * @author DODO Breeder
               *
               * @notice Tokenize liquidity pool assets. An ordinary ERC20 contract with mint and burn functions
               */
              contract DODOLpToken is Ownable {
                  using SafeMath for uint256;
              
                  string public symbol = "DLP";
                  address public originToken;
              
                  uint256 public totalSupply;
                  mapping(address => uint256) internal balances;
                  mapping(address => mapping(address => uint256)) internal allowed;
              
                  // ============ Events ============
              
                  event Transfer(address indexed from, address indexed to, uint256 amount);
              
                  event Approval(address indexed owner, address indexed spender, uint256 amount);
              
                  event Mint(address indexed user, uint256 value);
              
                  event Burn(address indexed user, uint256 value);
              
                  // ============ Functions ============
              
                  constructor(address _originToken) public {
                      originToken = _originToken;
                  }
              
                  function name() public view returns (string memory) {
                      string memory lpTokenSuffix = "_DODO_LP_TOKEN_";
                      return string(abi.encodePacked(IERC20(originToken).name(), lpTokenSuffix));
                  }
              
                  function decimals() public view returns (uint8) {
                      return IERC20(originToken).decimals();
                  }
              
                  /**
                   * @dev transfer token for a specified address
                   * @param to The address to transfer to.
                   * @param amount The amount to be transferred.
                   */
                  function transfer(address to, uint256 amount) public returns (bool) {
                      require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH");
              
                      balances[msg.sender] = balances[msg.sender].sub(amount);
                      balances[to] = balances[to].add(amount);
                      emit Transfer(msg.sender, to, amount);
                      return true;
                  }
              
                  /**
                   * @dev Gets the balance of the specified address.
                   * @param owner The address to query the the balance of.
                   * @return balance An uint256 representing the amount owned by the passed address.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance) {
                      return balances[owner];
                  }
              
                  /**
                   * @dev Transfer tokens from one address to another
                   * @param from address The address which you want to send tokens from
                   * @param to address The address which you want to transfer to
                   * @param amount uint256 the amount of tokens to be transferred
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) public returns (bool) {
                      require(amount <= balances[from], "BALANCE_NOT_ENOUGH");
                      require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
              
                      balances[from] = balances[from].sub(amount);
                      balances[to] = balances[to].add(amount);
                      allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount);
                      emit Transfer(from, to, amount);
                      return true;
                  }
              
                  /**
                   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                   * @param spender The address which will spend the funds.
                   * @param amount The amount of tokens to be spent.
                   */
                  function approve(address spender, uint256 amount) public returns (bool) {
                      allowed[msg.sender][spender] = amount;
                      emit Approval(msg.sender, spender, amount);
                      return true;
                  }
              
                  /**
                   * @dev Function to check the amount of tokens that an owner allowed to a spender.
                   * @param owner address The address which owns the funds.
                   * @param spender address The address which will spend the funds.
                   * @return A uint256 specifying the amount of tokens still available for the spender.
                   */
                  function allowance(address owner, address spender) public view returns (uint256) {
                      return allowed[owner][spender];
                  }
              
                  function mint(address user, uint256 value) external onlyOwner {
                      balances[user] = balances[user].add(value);
                      totalSupply = totalSupply.add(value);
                      emit Mint(user, value);
                      emit Transfer(address(0), user, value);
                  }
              
                  function burn(address user, uint256 value) external onlyOwner {
                      balances[user] = balances[user].sub(value);
                      totalSupply = totalSupply.sub(value);
                      emit Burn(user, value);
                      emit Transfer(user, address(0), value);
                  }
              }
              
              // File: contracts/dodo.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DODO
               * @author DODO Breeder
               *
               * @notice Entrance for users
               */
              contract DODO is Admin, Trader, LiquidityProvider {
                  function init(
                      address owner,
                      address supervisor,
                      address maintainer,
                      address baseToken,
                      address quoteToken,
                      address oracle,
                      uint256 lpFeeRate,
                      uint256 mtFeeRate,
                      uint256 k,
                      uint256 gasPriceLimit
                  ) external {
                      require(!_INITIALIZED_, "DODO_INITIALIZED");
                      _INITIALIZED_ = true;
              
                      // constructor
                      _OWNER_ = owner;
                      emit OwnershipTransferred(address(0), _OWNER_);
              
                      _SUPERVISOR_ = supervisor;
                      _MAINTAINER_ = maintainer;
                      _BASE_TOKEN_ = baseToken;
                      _QUOTE_TOKEN_ = quoteToken;
                      _ORACLE_ = oracle;
              
                      _DEPOSIT_BASE_ALLOWED_ = true;
                      _DEPOSIT_QUOTE_ALLOWED_ = true;
                      _TRADE_ALLOWED_ = true;
                      _GAS_PRICE_LIMIT_ = gasPriceLimit;
              
                      _LP_FEE_RATE_ = lpFeeRate;
                      _MT_FEE_RATE_ = mtFeeRate;
                      _K_ = k;
                      _R_STATUS_ = Types.RStatus.ONE;
              
                      _BASE_CAPITAL_TOKEN_ = address(new DODOLpToken(_BASE_TOKEN_));
                      _QUOTE_CAPITAL_TOKEN_ = address(new DODOLpToken(_QUOTE_TOKEN_));
              
                      _checkDODOParameters();
                  }
              }

              File 6 of 8: DODO
              /*
              
                  Copyright 2020 DODO ZOO.
                  SPDX-License-Identifier: Apache-2.0
              
              */
              
              pragma solidity 0.6.9;
              pragma experimental ABIEncoderV2;
              
              library Types {
                  enum RStatus {ONE, ABOVE_ONE, BELOW_ONE}
              }
              
              // File: contracts/intf/IERC20.sol
              
              // This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
              
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
              
                  function decimals() external view returns (uint8);
              
                  function name() external view returns (string memory);
              
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
              
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `recipient`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address recipient, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Moves `amount` tokens from `sender` to `recipient` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                  ) external returns (bool);
              }
              
              // File: contracts/lib/InitializableOwnable.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Ownable
               * @author DODO Breeder
               *
               * @notice Ownership related functions
               */
              contract InitializableOwnable {
                  address public _OWNER_;
                  address public _NEW_OWNER_;
              
                  // ============ Events ============
              
                  event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  // ============ Modifiers ============
              
                  modifier onlyOwner() {
                      require(msg.sender == _OWNER_, "NOT_OWNER");
                      _;
                  }
              
                  // ============ Functions ============
              
                  function transferOwnership(address newOwner) external onlyOwner {
                      require(newOwner != address(0), "INVALID_OWNER");
                      emit OwnershipTransferPrepared(_OWNER_, newOwner);
                      _NEW_OWNER_ = newOwner;
                  }
              
                  function claimOwnership() external {
                      require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                      emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                      _OWNER_ = _NEW_OWNER_;
                      _NEW_OWNER_ = address(0);
                  }
              }
              
              // File: contracts/lib/SafeMath.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title SafeMath
               * @author DODO Breeder
               *
               * @notice Math operations with safety checks that revert on error
               */
              library SafeMath {
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
              
                      uint256 c = a * b;
                      require(c / a == b, "MUL_ERROR");
              
                      return c;
                  }
              
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "DIVIDING_ERROR");
                      return a / b;
                  }
              
                  function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 quotient = div(a, b);
                      uint256 remainder = a - quotient * b;
                      if (remainder > 0) {
                          return quotient + 1;
                      } else {
                          return quotient;
                      }
                  }
              
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a, "SUB_ERROR");
                      return a - b;
                  }
              
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a, "ADD_ERROR");
                      return c;
                  }
              
                  function sqrt(uint256 x) internal pure returns (uint256 y) {
                      uint256 z = x / 2 + 1;
                      y = x;
                      while (z < y) {
                          y = z;
                          z = (x / z + z) / 2;
                      }
                  }
              }
              
              // File: contracts/lib/DecimalMath.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DecimalMath
               * @author DODO Breeder
               *
               * @notice Functions for fixed point number with 18 decimals
               */
              library DecimalMath {
                  using SafeMath for uint256;
              
                  uint256 constant ONE = 10**18;
              
                  function mul(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(d) / ONE;
                  }
              
                  function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(ONE).div(d);
                  }
              
                  function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
                      return target.mul(ONE).divCeil(d);
                  }
              }
              
              // File: contracts/lib/ReentrancyGuard.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title ReentrancyGuard
               * @author DODO Breeder
               *
               * @notice Protect functions from Reentrancy Attack
               */
              contract ReentrancyGuard {
                  // https://solidity.readthedocs.io/en/latest/control-structures.html?highlight=zero-state#scoping-and-declarations
                  // zero-state of _ENTERED_ is false
                  bool private _ENTERED_;
              
                  modifier preventReentrant() {
                      require(!_ENTERED_, "REENTRANT");
                      _ENTERED_ = true;
                      _;
                      _ENTERED_ = false;
                  }
              }
              
              // File: contracts/intf/IOracle.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              interface IOracle {
                  function getPrice() external view returns (uint256);
              }
              
              // File: contracts/intf/IDODOLpToken.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              interface IDODOLpToken {
                  function mint(address user, uint256 value) external;
              
                  function burn(address user, uint256 value) external;
              
                  function balanceOf(address owner) external view returns (uint256);
              
                  function totalSupply() external view returns (uint256);
              }
              
              // File: contracts/impl/Storage.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Storage
               * @author DODO Breeder
               *
               * @notice Local Variables
               */
              contract Storage is InitializableOwnable, ReentrancyGuard {
                  using SafeMath for uint256;
              
                  // ============ Variables for Control ============
              
                  bool internal _INITIALIZED_;
                  bool public _CLOSED_;
                  bool public _DEPOSIT_QUOTE_ALLOWED_;
                  bool public _DEPOSIT_BASE_ALLOWED_;
                  bool public _TRADE_ALLOWED_;
                  uint256 public _GAS_PRICE_LIMIT_;
              
                  // ============ Core Address ============
              
                  address public _SUPERVISOR_; // could freeze system in emergency
                  address public _MAINTAINER_; // collect maintainer fee to buy food for DODO
              
                  address public _BASE_TOKEN_;
                  address public _QUOTE_TOKEN_;
                  address public _ORACLE_;
              
                  // ============ Variables for PMM Algorithm ============
              
                  uint256 public _LP_FEE_RATE_;
                  uint256 public _MT_FEE_RATE_;
                  uint256 public _K_;
              
                  Types.RStatus public _R_STATUS_;
                  uint256 public _TARGET_BASE_TOKEN_AMOUNT_;
                  uint256 public _TARGET_QUOTE_TOKEN_AMOUNT_;
                  uint256 public _BASE_BALANCE_;
                  uint256 public _QUOTE_BALANCE_;
              
                  address public _BASE_CAPITAL_TOKEN_;
                  address public _QUOTE_CAPITAL_TOKEN_;
              
                  // ============ Variables for Final Settlement ============
              
                  uint256 public _BASE_CAPITAL_RECEIVE_QUOTE_;
                  uint256 public _QUOTE_CAPITAL_RECEIVE_BASE_;
                  mapping(address => bool) public _CLAIMED_;
              
                  // ============ Modifiers ============
              
                  modifier onlySupervisorOrOwner() {
                      require(msg.sender == _SUPERVISOR_ || msg.sender == _OWNER_, "NOT_SUPERVISOR_OR_OWNER");
                      _;
                  }
              
                  modifier notClosed() {
                      require(!_CLOSED_, "DODO_CLOSED");
                      _;
                  }
              
                  // ============ Helper Functions ============
              
                  function _checkDODOParameters() internal view returns (uint256) {
                      require(_K_ < DecimalMath.ONE, "K>=1");
                      require(_K_ > 0, "K=0");
                      require(_LP_FEE_RATE_.add(_MT_FEE_RATE_) < DecimalMath.ONE, "FEE_RATE>=1");
                  }
              
                  function getOraclePrice() public view returns (uint256) {
                      return IOracle(_ORACLE_).getPrice();
                  }
              
                  function getBaseCapitalBalanceOf(address lp) public view returns (uint256) {
                      return IDODOLpToken(_BASE_CAPITAL_TOKEN_).balanceOf(lp);
                  }
              
                  function getTotalBaseCapital() public view returns (uint256) {
                      return IDODOLpToken(_BASE_CAPITAL_TOKEN_).totalSupply();
                  }
              
                  function getQuoteCapitalBalanceOf(address lp) public view returns (uint256) {
                      return IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).balanceOf(lp);
                  }
              
                  function getTotalQuoteCapital() public view returns (uint256) {
                      return IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).totalSupply();
                  }
              
                  // ============ Version Control ============
                  function version() external pure returns (uint256) {
                      return 100; // 1.0.0
                  }
              }
              
              // File: contracts/intf/IDODOCallee.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              interface IDODOCallee {
                  function dodoCall(
                      bool isBuyBaseToken,
                      uint256 baseAmount,
                      uint256 quoteAmount,
                      bytes calldata data
                  ) external;
              }
              
              // File: contracts/lib/DODOMath.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DODOMath
               * @author DODO Breeder
               *
               * @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions
               */
              library DODOMath {
                  using SafeMath for uint256;
              
                  /*
                      Integrate dodo curve fron V1 to V2
                      require V0>=V1>=V2>0
                      res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1)
                      let V1-V2=delta
                      res = i*delta*(1-k+k(V0^2/V1/V2))
                  */
                  function _GeneralIntegrate(
                      uint256 V0,
                      uint256 V1,
                      uint256 V2,
                      uint256 i,
                      uint256 k
                  ) internal pure returns (uint256) {
                      uint256 fairAmount = DecimalMath.mul(i, V1.sub(V2)); // i*delta
                      uint256 V0V0V1V2 = DecimalMath.divCeil(V0.mul(V0).div(V1), V2);
                      uint256 penalty = DecimalMath.mul(k, V0V0V1V2); // k(V0^2/V1/V2)
                      return DecimalMath.mul(fairAmount, DecimalMath.ONE.sub(k).add(penalty));
                  }
              
                  /*
                      The same with integration expression above, we have:
                      i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
                      Given Q1 and deltaB, solve Q2
                      This is a quadratic function and the standard version is
                      aQ2^2 + bQ2 + c = 0, where
                      a=1-k
                      -b=(1-k)Q1-kQ0^2/Q1+i*deltaB
                      c=-kQ0^2
                      and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k)
                      note: another root is negative, abondan
                      if deltaBSig=true, then Q2>Q1
                      if deltaBSig=false, then Q2<Q1
                  */
                  function _SolveQuadraticFunctionForTrade(
                      uint256 Q0,
                      uint256 Q1,
                      uint256 ideltaB,
                      bool deltaBSig,
                      uint256 k
                  ) internal pure returns (uint256) {
                      // calculate -b value and sig
                      // -b = (1-k)Q1-kQ0^2/Q1+i*deltaB
                      uint256 kQ02Q1 = DecimalMath.mul(k, Q0).mul(Q0).div(Q1); // kQ0^2/Q1
                      uint256 b = DecimalMath.mul(DecimalMath.ONE.sub(k), Q1); // (1-k)Q1
                      bool minusbSig = true;
                      if (deltaBSig) {
                          b = b.add(ideltaB); // (1-k)Q1+i*deltaB
                      } else {
                          kQ02Q1 = kQ02Q1.add(ideltaB); // i*deltaB+kQ0^2/Q1
                      }
                      if (b >= kQ02Q1) {
                          b = b.sub(kQ02Q1);
                          minusbSig = true;
                      } else {
                          b = kQ02Q1.sub(b);
                          minusbSig = false;
                      }
              
                      // calculate sqrt
                      uint256 squareRoot = DecimalMath.mul(
                          DecimalMath.ONE.sub(k).mul(4),
                          DecimalMath.mul(k, Q0).mul(Q0)
                      ); // 4(1-k)kQ0^2
                      squareRoot = b.mul(b).add(squareRoot).sqrt(); // sqrt(b*b+4(1-k)kQ0*Q0)
              
                      // final res
                      uint256 denominator = DecimalMath.ONE.sub(k).mul(2); // 2(1-k)
                      uint256 numerator;
                      if (minusbSig) {
                          numerator = b.add(squareRoot);
                      } else {
                          numerator = squareRoot.sub(b);
                      }
              
                      if (deltaBSig) {
                          return DecimalMath.divFloor(numerator, denominator);
                      } else {
                          return DecimalMath.divCeil(numerator, denominator);
                      }
                  }
              
                  /*
                      Start from the integration function
                      i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
                      Assume Q2=Q0, Given Q1 and deltaB, solve Q0
                      let fairAmount = i*deltaB
                  */
                  function _SolveQuadraticFunctionForTarget(
                      uint256 V1,
                      uint256 k,
                      uint256 fairAmount
                  ) internal pure returns (uint256 V0) {
                      // V0 = V1+V1*(sqrt-1)/2k
                      uint256 sqrt = DecimalMath.divCeil(DecimalMath.mul(k, fairAmount).mul(4), V1);
                      sqrt = sqrt.add(DecimalMath.ONE).mul(DecimalMath.ONE).sqrt();
                      uint256 premium = DecimalMath.divCeil(sqrt.sub(DecimalMath.ONE), k.mul(2));
                      // V0 is greater than or equal to V1 according to the solution
                      return DecimalMath.mul(V1, DecimalMath.ONE.add(premium));
                  }
              }
              
              // File: contracts/impl/Pricing.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @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_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) {
                      // 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_);
                  }
              
                  // ============ R > 1 cases ============
              
                  function _RAboveBuyBaseToken(
                      uint256 amount,
                      uint256 baseBalance,
                      uint256 targetBaseAmount
                  ) internal view returns (uint256 payQuoteToken) {
                      require(amount < baseBalance, "DODO_BASE_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) {
                      // 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_);
                  }
              
                  // ============ 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 = _RBelowBackToOne();
                          return (_TARGET_BASE_TOKEN_AMOUNT_, Q.add(payQuoteToken));
                      } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          uint256 payBaseToken = _RAboveBackToOne();
                          return (B.add(payBaseToken), _TARGET_QUOTE_TOKEN_AMOUNT_);
                      }
                  }
              
                  function getMidPrice() public view returns (uint256 midPrice) {
                      (uint256 baseTarget, uint256 quoteTarget) = getExpectedTarget();
                      if (_R_STATUS_ == Types.RStatus.BELOW_ONE) {
                          uint256 R = DecimalMath.divFloor(
                              quoteTarget.mul(quoteTarget).div(_QUOTE_BALANCE_),
                              _QUOTE_BALANCE_
                          );
                          R = DecimalMath.ONE.sub(_K_).add(DecimalMath.mul(_K_, R));
                          return DecimalMath.divFloor(getOraclePrice(), R);
                      } else {
                          uint256 R = DecimalMath.divFloor(
                              baseTarget.mul(baseTarget).div(_BASE_BALANCE_),
                              _BASE_BALANCE_
                          );
                          R = DecimalMath.ONE.sub(_K_).add(DecimalMath.mul(_K_, R));
                          return DecimalMath.mul(getOraclePrice(), R);
                      }
                  }
              
                  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_);
                  // }
              }
              
              // File: contracts/lib/SafeERC20.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
                  This is a simplified version of OpenZepplin's SafeERC20 library
              
              */
              
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using SafeMath for uint256;
              
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
              
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(
                          token,
                          abi.encodeWithSelector(token.transferFrom.selector, from, to, value)
                      );
                  }
              
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves.
              
                      // A Solidity high level call has three parts:
                      //  1. The target address is checked to verify it contains contract code
                      //  2. The call itself is made, and success asserted
                      //  3. The return value is decoded, which in turn checks the size of the returned data.
                      // solhint-disable-next-line max-line-length
              
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = address(token).call(data);
                      require(success, "SafeERC20: low-level call failed");
              
                      if (returndata.length > 0) {
                          // Return data is optional
                          // solhint-disable-next-line max-line-length
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              
              // File: contracts/impl/Settlement.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Settlement
               * @author DODO Breeder
               *
               * @notice Functions for assets settlement
               */
              contract Settlement is Storage {
                  using SafeMath for uint256;
                  using SafeERC20 for IERC20;
              
                  // ============ Events ============
              
                  event Donate(uint256 amount, bool isBaseToken);
              
                  event ClaimAssets(address indexed user, uint256 baseTokenAmount, uint256 quoteTokenAmount);
              
                  // ============ Assets IN/OUT Functions ============
              
                  function _baseTokenTransferIn(address from, uint256 amount) internal {
                      IERC20(_BASE_TOKEN_).safeTransferFrom(from, address(this), amount);
                      _BASE_BALANCE_ = _BASE_BALANCE_.add(amount);
                  }
              
                  function _quoteTokenTransferIn(address from, uint256 amount) internal {
                      IERC20(_QUOTE_TOKEN_).safeTransferFrom(from, address(this), amount);
                      _QUOTE_BALANCE_ = _QUOTE_BALANCE_.add(amount);
                  }
              
                  function _baseTokenTransferOut(address to, uint256 amount) internal {
                      IERC20(_BASE_TOKEN_).safeTransfer(to, amount);
                      _BASE_BALANCE_ = _BASE_BALANCE_.sub(amount);
                  }
              
                  function _quoteTokenTransferOut(address to, uint256 amount) internal {
                      IERC20(_QUOTE_TOKEN_).safeTransfer(to, amount);
                      _QUOTE_BALANCE_ = _QUOTE_BALANCE_.sub(amount);
                  }
              
                  // ============ Donate to Liquidity Pool Functions ============
              
                  function _donateBaseToken(uint256 amount) internal {
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.add(amount);
                      emit Donate(amount, true);
                  }
              
                  function _donateQuoteToken(uint256 amount) internal {
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.add(amount);
                      emit Donate(amount, false);
                  }
              
                  function donateBaseToken(uint256 amount) external preventReentrant {
                      _baseTokenTransferIn(msg.sender, amount);
                      _donateBaseToken(amount);
                  }
              
                  function donateQuoteToken(uint256 amount) external preventReentrant {
                      _quoteTokenTransferIn(msg.sender, amount);
                      _donateQuoteToken(amount);
                  }
              
                  // ============ Final Settlement Functions ============
              
                  // last step to shut down dodo
                  function finalSettlement() external onlyOwner notClosed {
                      _CLOSED_ = true;
                      _DEPOSIT_QUOTE_ALLOWED_ = false;
                      _DEPOSIT_BASE_ALLOWED_ = false;
                      _TRADE_ALLOWED_ = false;
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
              
                      if (_QUOTE_BALANCE_ > _TARGET_QUOTE_TOKEN_AMOUNT_) {
                          uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_);
                          _BASE_CAPITAL_RECEIVE_QUOTE_ = DecimalMath.divFloor(spareQuote, totalBaseCapital);
                      } else {
                          _TARGET_QUOTE_TOKEN_AMOUNT_ = _QUOTE_BALANCE_;
                      }
              
                      if (_BASE_BALANCE_ > _TARGET_BASE_TOKEN_AMOUNT_) {
                          uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_);
                          _QUOTE_CAPITAL_RECEIVE_BASE_ = DecimalMath.divFloor(spareBase, totalQuoteCapital);
                      } else {
                          _TARGET_BASE_TOKEN_AMOUNT_ = _BASE_BALANCE_;
                      }
              
                      _R_STATUS_ = Types.RStatus.ONE;
                  }
              
                  // claim remaining assets after final settlement
                  function claimAssets() external preventReentrant {
                      require(_CLOSED_, "DODO_NOT_CLOSED");
                      require(!_CLAIMED_[msg.sender], "ALREADY_CLAIMED");
                      _CLAIMED_[msg.sender] = true;
                      uint256 quoteAmount = DecimalMath.mul(
                          getBaseCapitalBalanceOf(msg.sender),
                          _BASE_CAPITAL_RECEIVE_QUOTE_
                      );
                      uint256 baseAmount = DecimalMath.mul(
                          getQuoteCapitalBalanceOf(msg.sender),
                          _QUOTE_CAPITAL_RECEIVE_BASE_
                      );
                      _baseTokenTransferOut(msg.sender, baseAmount);
                      _quoteTokenTransferOut(msg.sender, quoteAmount);
                      emit ClaimAssets(msg.sender, baseAmount, quoteAmount);
                      return;
                  }
              
                  // in case someone transfer to contract directly
                  function retrieve(address token, uint256 amount) external onlyOwner {
                      if (token == _BASE_TOKEN_) {
                          require(
                              IERC20(_BASE_TOKEN_).balanceOf(address(this)) >= _BASE_BALANCE_.add(amount),
                              "DODO_BASE_BALANCE_NOT_ENOUGH"
                          );
                      }
                      if (token == _QUOTE_TOKEN_) {
                          require(
                              IERC20(_QUOTE_TOKEN_).balanceOf(address(this)) >= _QUOTE_BALANCE_.add(amount),
                              "DODO_QUOTE_BALANCE_NOT_ENOUGH"
                          );
                      }
                      IERC20(token).safeTransfer(msg.sender, amount);
                  }
              }
              
              // File: contracts/impl/Trader.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Trader
               * @author DODO Breeder
               *
               * @notice Functions for trader operations
               */
              contract Trader is Storage, Pricing, Settlement {
                  using SafeMath for uint256;
              
                  // ============ Events ============
              
                  event SellBaseToken(address indexed seller, uint256 payBase, uint256 receiveQuote);
              
                  event BuyBaseToken(address indexed buyer, uint256 receiveBase, uint256 payQuote);
              
                  event ChargeMaintainerFee(address indexed maintainer, bool isBaseToken, uint256 amount);
              
                  // ============ Modifiers ============
              
                  modifier tradeAllowed() {
                      require(_TRADE_ALLOWED_, "TRADE_NOT_ALLOWED");
                      _;
                  }
              
                  modifier gasPriceLimit() {
                      require(tx.gasprice <= _GAS_PRICE_LIMIT_, "GAS_PRICE_EXCEED");
                      _;
                  }
              
                  // ============ Trade Functions ============
              
                  function sellBaseToken(
                      uint256 amount,
                      uint256 minReceiveQuote,
                      bytes calldata data
                  ) external tradeAllowed gasPriceLimit preventReentrant returns (uint256) {
                      // query price
                      (
                          uint256 receiveQuote,
                          uint256 lpFeeQuote,
                          uint256 mtFeeQuote,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      ) = _querySellBaseToken(amount);
                      require(receiveQuote >= minReceiveQuote, "SELL_BASE_RECEIVE_NOT_ENOUGH");
              
                      // settle assets
                      _quoteTokenTransferOut(msg.sender, receiveQuote);
                      if (data.length > 0) {
                          IDODOCallee(msg.sender).dodoCall(false, amount, receiveQuote, data);
                      }
                      _baseTokenTransferIn(msg.sender, amount);
                      if (mtFeeQuote != 0) {
                          _quoteTokenTransferOut(_MAINTAINER_, mtFeeQuote);
                          emit ChargeMaintainerFee(_MAINTAINER_, false, mtFeeQuote);
                      }
              
                      // update TARGET
                      if (_TARGET_QUOTE_TOKEN_AMOUNT_ != newQuoteTarget) {
                          _TARGET_QUOTE_TOKEN_AMOUNT_ = newQuoteTarget;
                      }
                      if (_TARGET_BASE_TOKEN_AMOUNT_ != newBaseTarget) {
                          _TARGET_BASE_TOKEN_AMOUNT_ = newBaseTarget;
                      }
                      if (_R_STATUS_ != newRStatus) {
                          _R_STATUS_ = newRStatus;
                      }
              
                      _donateQuoteToken(lpFeeQuote);
                      emit SellBaseToken(msg.sender, amount, receiveQuote);
              
                      return receiveQuote;
                  }
              
                  function buyBaseToken(
                      uint256 amount,
                      uint256 maxPayQuote,
                      bytes calldata data
                  ) external tradeAllowed gasPriceLimit preventReentrant returns (uint256) {
                      // query price
                      (
                          uint256 payQuote,
                          uint256 lpFeeBase,
                          uint256 mtFeeBase,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      ) = _queryBuyBaseToken(amount);
                      require(payQuote <= maxPayQuote, "BUY_BASE_COST_TOO_MUCH");
              
                      // settle assets
                      _baseTokenTransferOut(msg.sender, amount);
                      if (data.length > 0) {
                          IDODOCallee(msg.sender).dodoCall(true, amount, payQuote, data);
                      }
                      _quoteTokenTransferIn(msg.sender, payQuote);
                      if (mtFeeBase != 0) {
                          _baseTokenTransferOut(_MAINTAINER_, mtFeeBase);
                          emit ChargeMaintainerFee(_MAINTAINER_, true, mtFeeBase);
                      }
              
                      // update TARGET
                      if (_TARGET_QUOTE_TOKEN_AMOUNT_ != newQuoteTarget) {
                          _TARGET_QUOTE_TOKEN_AMOUNT_ = newQuoteTarget;
                      }
                      if (_TARGET_BASE_TOKEN_AMOUNT_ != newBaseTarget) {
                          _TARGET_BASE_TOKEN_AMOUNT_ = newBaseTarget;
                      }
                      if (_R_STATUS_ != newRStatus) {
                          _R_STATUS_ = newRStatus;
                      }
              
                      _donateBaseToken(lpFeeBase);
                      emit BuyBaseToken(msg.sender, amount, payQuote);
              
                      return payQuote;
                  }
              
                  // ============ Query Functions ============
              
                  function querySellBaseToken(uint256 amount) external view returns (uint256 receiveQuote) {
                      (receiveQuote, , , , , ) = _querySellBaseToken(amount);
                      return receiveQuote;
                  }
              
                  function queryBuyBaseToken(uint256 amount) external view returns (uint256 payQuote) {
                      (payQuote, , , , , ) = _queryBuyBaseToken(amount);
                      return payQuote;
                  }
              
                  function _querySellBaseToken(uint256 amount)
                      internal
                      view
                      returns (
                          uint256 receiveQuote,
                          uint256 lpFeeQuote,
                          uint256 mtFeeQuote,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      )
                  {
                      (newBaseTarget, newQuoteTarget) = getExpectedTarget();
              
                      uint256 sellBaseAmount = amount;
              
                      if (_R_STATUS_ == Types.RStatus.ONE) {
                          // case 1: R=1
                          // R falls below one
                          receiveQuote = _ROneSellBaseToken(sellBaseAmount, newQuoteTarget);
                          newRStatus = Types.RStatus.BELOW_ONE;
                      } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          uint256 backToOnePayBase = newBaseTarget.sub(_BASE_BALANCE_);
                          uint256 backToOneReceiveQuote = _QUOTE_BALANCE_.sub(newQuoteTarget);
                          // case 2: R>1
                          // complex case, R status depends on trading amount
                          if (sellBaseAmount < backToOnePayBase) {
                              // case 2.1: R status do not change
                              receiveQuote = _RAboveSellBaseToken(sellBaseAmount, _BASE_BALANCE_, newBaseTarget);
                              newRStatus = Types.RStatus.ABOVE_ONE;
                              if (receiveQuote > 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
                                  receiveQuote = backToOneReceiveQuote;
                              }
                          } else if (sellBaseAmount == backToOnePayBase) {
                              // case 2.2: R status changes to ONE
                              receiveQuote = backToOneReceiveQuote;
                              newRStatus = Types.RStatus.ONE;
                          } else {
                              // case 2.3: R status changes to BELOW_ONE
                              receiveQuote = backToOneReceiveQuote.add(
                                  _ROneSellBaseToken(sellBaseAmount.sub(backToOnePayBase), newQuoteTarget)
                              );
                              newRStatus = Types.RStatus.BELOW_ONE;
                          }
                      } else {
                          // _R_STATUS_ == Types.RStatus.BELOW_ONE
                          // case 3: R<1
                          receiveQuote = _RBelowSellBaseToken(sellBaseAmount, _QUOTE_BALANCE_, newQuoteTarget);
                          newRStatus = Types.RStatus.BELOW_ONE;
                      }
              
                      // count fees
                      lpFeeQuote = DecimalMath.mul(receiveQuote, _LP_FEE_RATE_);
                      mtFeeQuote = DecimalMath.mul(receiveQuote, _MT_FEE_RATE_);
                      receiveQuote = receiveQuote.sub(lpFeeQuote).sub(mtFeeQuote);
              
                      return (receiveQuote, lpFeeQuote, mtFeeQuote, newRStatus, newQuoteTarget, newBaseTarget);
                  }
              
                  function _queryBuyBaseToken(uint256 amount)
                      internal
                      view
                      returns (
                          uint256 payQuote,
                          uint256 lpFeeBase,
                          uint256 mtFeeBase,
                          Types.RStatus newRStatus,
                          uint256 newQuoteTarget,
                          uint256 newBaseTarget
                      )
                  {
                      (newBaseTarget, newQuoteTarget) = getExpectedTarget();
              
                      // charge fee from user receive amount
                      lpFeeBase = DecimalMath.mul(amount, _LP_FEE_RATE_);
                      mtFeeBase = DecimalMath.mul(amount, _MT_FEE_RATE_);
                      uint256 buyBaseAmount = amount.add(lpFeeBase).add(mtFeeBase);
              
                      if (_R_STATUS_ == Types.RStatus.ONE) {
                          // case 1: R=1
                          payQuote = _ROneBuyBaseToken(buyBaseAmount, newBaseTarget);
                          newRStatus = Types.RStatus.ABOVE_ONE;
                      } else if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          // case 2: R>1
                          payQuote = _RAboveBuyBaseToken(buyBaseAmount, _BASE_BALANCE_, newBaseTarget);
                          newRStatus = Types.RStatus.ABOVE_ONE;
                      } else if (_R_STATUS_ == Types.RStatus.BELOW_ONE) {
                          uint256 backToOnePayQuote = newQuoteTarget.sub(_QUOTE_BALANCE_);
                          uint256 backToOneReceiveBase = _BASE_BALANCE_.sub(newBaseTarget);
                          // case 3: R<1
                          // complex case, R status may change
                          if (buyBaseAmount < backToOneReceiveBase) {
                              // case 3.1: R status do not change
                              // no need to check payQuote because spare base token must be greater than zero
                              payQuote = _RBelowBuyBaseToken(buyBaseAmount, _QUOTE_BALANCE_, newQuoteTarget);
                              newRStatus = Types.RStatus.BELOW_ONE;
                          } else if (buyBaseAmount == backToOneReceiveBase) {
                              // case 3.2: R status changes to ONE
                              payQuote = backToOnePayQuote;
                              newRStatus = Types.RStatus.ONE;
                          } else {
                              // case 3.3: R status changes to ABOVE_ONE
                              payQuote = backToOnePayQuote.add(
                                  _ROneBuyBaseToken(buyBaseAmount.sub(backToOneReceiveBase), newBaseTarget)
                              );
                              newRStatus = Types.RStatus.ABOVE_ONE;
                          }
                      }
              
                      return (payQuote, lpFeeBase, mtFeeBase, newRStatus, newQuoteTarget, newBaseTarget);
                  }
              }
              
              // File: contracts/impl/LiquidityProvider.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title LiquidityProvider
               * @author DODO Breeder
               *
               * @notice Functions for liquidity provider operations
               */
              contract LiquidityProvider is Storage, Pricing, Settlement {
                  using SafeMath for uint256;
              
                  // ============ Events ============
              
                  event Deposit(
                      address indexed payer,
                      address indexed receiver,
                      bool isBaseToken,
                      uint256 amount,
                      uint256 lpTokenAmount
                  );
              
                  event Withdraw(
                      address indexed payer,
                      address indexed receiver,
                      bool isBaseToken,
                      uint256 amount,
                      uint256 lpTokenAmount
                  );
              
                  event ChargePenalty(address indexed payer, bool isBaseToken, uint256 amount);
              
                  // ============ Modifiers ============
              
                  modifier depositQuoteAllowed() {
                      require(_DEPOSIT_QUOTE_ALLOWED_, "DEPOSIT_QUOTE_NOT_ALLOWED");
                      _;
                  }
              
                  modifier depositBaseAllowed() {
                      require(_DEPOSIT_BASE_ALLOWED_, "DEPOSIT_BASE_NOT_ALLOWED");
                      _;
                  }
              
                  // ============ Routine Functions ============
              
                  function withdrawBase(uint256 amount) external returns (uint256) {
                      return withdrawBaseTo(msg.sender, amount);
                  }
              
                  function depositBase(uint256 amount) external returns (uint256) {
                      return depositBaseTo(msg.sender, amount);
                  }
              
                  function withdrawQuote(uint256 amount) external returns (uint256) {
                      return withdrawQuoteTo(msg.sender, amount);
                  }
              
                  function depositQuote(uint256 amount) external returns (uint256) {
                      return depositQuoteTo(msg.sender, amount);
                  }
              
                  function withdrawAllBase() external returns (uint256) {
                      return withdrawAllBaseTo(msg.sender);
                  }
              
                  function withdrawAllQuote() external returns (uint256) {
                      return withdrawAllQuoteTo(msg.sender);
                  }
              
                  // ============ Deposit Functions ============
              
                  function depositQuoteTo(address to, uint256 amount)
                      public
                      preventReentrant
                      depositQuoteAllowed
                      returns (uint256)
                  {
                      (, uint256 quoteTarget) = getExpectedTarget();
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
                      uint256 capital = amount;
                      if (totalQuoteCapital == 0) {
                          // give remaining quote token to lp as a gift
                          capital = amount.add(quoteTarget);
                      } else if (quoteTarget > 0) {
                          capital = amount.mul(totalQuoteCapital).div(quoteTarget);
                      }
              
                      // settlement
                      _quoteTokenTransferIn(msg.sender, amount);
                      _mintQuoteCapital(to, capital);
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.add(amount);
              
                      emit Deposit(msg.sender, to, false, amount, capital);
                      return capital;
                  }
              
                  function depositBaseTo(address to, uint256 amount)
                      public
                      preventReentrant
                      depositBaseAllowed
                      returns (uint256)
                  {
                      (uint256 baseTarget, ) = getExpectedTarget();
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      uint256 capital = amount;
                      if (totalBaseCapital == 0) {
                          // give remaining base token to lp as a gift
                          capital = amount.add(baseTarget);
                      } else if (baseTarget > 0) {
                          capital = amount.mul(totalBaseCapital).div(baseTarget);
                      }
              
                      // settlement
                      _baseTokenTransferIn(msg.sender, amount);
                      _mintBaseCapital(to, capital);
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.add(amount);
              
                      emit Deposit(msg.sender, to, true, amount, capital);
                      return capital;
                  }
              
                  // ============ Withdraw Functions ============
              
                  function withdrawQuoteTo(address to, uint256 amount) public preventReentrant returns (uint256) {
                      // calculate capital
                      (, uint256 quoteTarget) = getExpectedTarget();
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
                      require(totalQuoteCapital > 0, "NO_QUOTE_LP");
              
                      uint256 requireQuoteCapital = amount.mul(totalQuoteCapital).divCeil(quoteTarget);
                      require(
                          requireQuoteCapital <= getQuoteCapitalBalanceOf(msg.sender),
                          "LP_QUOTE_CAPITAL_BALANCE_NOT_ENOUGH"
                      );
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawQuotePenalty(amount);
                      require(penalty < amount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.sub(amount);
                      _burnQuoteCapital(msg.sender, requireQuoteCapital);
                      _quoteTokenTransferOut(to, amount.sub(penalty));
                      _donateQuoteToken(penalty);
              
                      emit Withdraw(msg.sender, to, false, amount.sub(penalty), requireQuoteCapital);
                      emit ChargePenalty(msg.sender, false, penalty);
              
                      return amount.sub(penalty);
                  }
              
                  function withdrawBaseTo(address to, uint256 amount) public preventReentrant returns (uint256) {
                      // calculate capital
                      (uint256 baseTarget, ) = getExpectedTarget();
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      require(totalBaseCapital > 0, "NO_BASE_LP");
              
                      uint256 requireBaseCapital = amount.mul(totalBaseCapital).divCeil(baseTarget);
                      require(
                          requireBaseCapital <= getBaseCapitalBalanceOf(msg.sender),
                          "LP_BASE_CAPITAL_BALANCE_NOT_ENOUGH"
                      );
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawBasePenalty(amount);
                      require(penalty <= amount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.sub(amount);
                      _burnBaseCapital(msg.sender, requireBaseCapital);
                      _baseTokenTransferOut(to, amount.sub(penalty));
                      _donateBaseToken(penalty);
              
                      emit Withdraw(msg.sender, to, true, amount.sub(penalty), requireBaseCapital);
                      emit ChargePenalty(msg.sender, true, penalty);
              
                      return amount.sub(penalty);
                  }
              
                  // ============ Withdraw all Functions ============
              
                  function withdrawAllQuoteTo(address to) public preventReentrant returns (uint256) {
                      uint256 withdrawAmount = getLpQuoteBalance(msg.sender);
                      uint256 capital = getQuoteCapitalBalanceOf(msg.sender);
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawQuotePenalty(withdrawAmount);
                      require(penalty <= withdrawAmount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_QUOTE_TOKEN_AMOUNT_ = _TARGET_QUOTE_TOKEN_AMOUNT_.sub(withdrawAmount);
                      _burnQuoteCapital(msg.sender, capital);
                      _quoteTokenTransferOut(to, withdrawAmount.sub(penalty));
                      _donateQuoteToken(penalty);
              
                      emit Withdraw(msg.sender, to, false, withdrawAmount, capital);
                      emit ChargePenalty(msg.sender, false, penalty);
              
                      return withdrawAmount.sub(penalty);
                  }
              
                  function withdrawAllBaseTo(address to) public preventReentrant returns (uint256) {
                      uint256 withdrawAmount = getLpBaseBalance(msg.sender);
                      uint256 capital = getBaseCapitalBalanceOf(msg.sender);
              
                      // handle penalty, penalty may exceed amount
                      uint256 penalty = getWithdrawBasePenalty(withdrawAmount);
                      require(penalty <= withdrawAmount, "PENALTY_EXCEED");
              
                      // settlement
                      _TARGET_BASE_TOKEN_AMOUNT_ = _TARGET_BASE_TOKEN_AMOUNT_.sub(withdrawAmount);
                      _burnBaseCapital(msg.sender, capital);
                      _baseTokenTransferOut(to, withdrawAmount.sub(penalty));
                      _donateBaseToken(penalty);
              
                      emit Withdraw(msg.sender, to, true, withdrawAmount, capital);
                      emit ChargePenalty(msg.sender, true, penalty);
              
                      return withdrawAmount.sub(penalty);
                  }
              
                  // ============ Helper Functions ============
              
                  function _mintBaseCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_BASE_CAPITAL_TOKEN_).mint(user, amount);
                  }
              
                  function _mintQuoteCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).mint(user, amount);
                  }
              
                  function _burnBaseCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_BASE_CAPITAL_TOKEN_).burn(user, amount);
                  }
              
                  function _burnQuoteCapital(address user, uint256 amount) internal {
                      IDODOLpToken(_QUOTE_CAPITAL_TOKEN_).burn(user, amount);
                  }
              
                  // ============ Getter Functions ============
              
                  function getLpBaseBalance(address lp) public view returns (uint256 lpBalance) {
                      uint256 totalBaseCapital = getTotalBaseCapital();
                      (uint256 baseTarget, ) = getExpectedTarget();
                      if (totalBaseCapital == 0) {
                          return 0;
                      }
                      lpBalance = getBaseCapitalBalanceOf(lp).mul(baseTarget).div(totalBaseCapital);
                      return lpBalance;
                  }
              
                  function getLpQuoteBalance(address lp) public view returns (uint256 lpBalance) {
                      uint256 totalQuoteCapital = getTotalQuoteCapital();
                      (, uint256 quoteTarget) = getExpectedTarget();
                      if (totalQuoteCapital == 0) {
                          return 0;
                      }
                      lpBalance = getQuoteCapitalBalanceOf(lp).mul(quoteTarget).div(totalQuoteCapital);
                      return lpBalance;
                  }
              
                  function getWithdrawQuotePenalty(uint256 amount) public view returns (uint256 penalty) {
                      require(amount <= _QUOTE_BALANCE_, "DODO_QUOTE_BALANCE_NOT_ENOUGH");
                      if (_R_STATUS_ == Types.RStatus.BELOW_ONE) {
                          uint256 spareBase = _BASE_BALANCE_.sub(_TARGET_BASE_TOKEN_AMOUNT_);
                          uint256 price = getOraclePrice();
                          uint256 fairAmount = DecimalMath.mul(spareBase, price);
                          uint256 targetQuote = DODOMath._SolveQuadraticFunctionForTarget(
                              _QUOTE_BALANCE_,
                              _K_,
                              fairAmount
                          );
                          // if amount = _QUOTE_BALANCE_, div error
                          uint256 targetQuoteWithWithdraw = DODOMath._SolveQuadraticFunctionForTarget(
                              _QUOTE_BALANCE_.sub(amount),
                              _K_,
                              fairAmount
                          );
                          return targetQuote.sub(targetQuoteWithWithdraw.add(amount));
                      } else {
                          return 0;
                      }
                  }
              
                  function getWithdrawBasePenalty(uint256 amount) public view returns (uint256 penalty) {
                      require(amount <= _BASE_BALANCE_, "DODO_BASE_BALANCE_NOT_ENOUGH");
                      if (_R_STATUS_ == Types.RStatus.ABOVE_ONE) {
                          uint256 spareQuote = _QUOTE_BALANCE_.sub(_TARGET_QUOTE_TOKEN_AMOUNT_);
                          uint256 price = getOraclePrice();
                          uint256 fairAmount = DecimalMath.divFloor(spareQuote, price);
                          uint256 targetBase = DODOMath._SolveQuadraticFunctionForTarget(
                              _BASE_BALANCE_,
                              _K_,
                              fairAmount
                          );
                          // if amount = _BASE_BALANCE_, div error
                          uint256 targetBaseWithWithdraw = DODOMath._SolveQuadraticFunctionForTarget(
                              _BASE_BALANCE_.sub(amount),
                              _K_,
                              fairAmount
                          );
                          return targetBase.sub(targetBaseWithWithdraw.add(amount));
                      } else {
                          return 0;
                      }
                  }
              }
              
              // File: contracts/impl/Admin.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Admin
               * @author DODO Breeder
               *
               * @notice Functions for admin operations
               */
              contract Admin is Storage {
                  // ============ Events ============
              
                  event UpdateGasPriceLimit(uint256 oldGasPriceLimit, uint256 newGasPriceLimit);
              
                  event UpdateLiquidityProviderFeeRate(
                      uint256 oldLiquidityProviderFeeRate,
                      uint256 newLiquidityProviderFeeRate
                  );
              
                  event UpdateMaintainerFeeRate(uint256 oldMaintainerFeeRate, uint256 newMaintainerFeeRate);
              
                  event UpdateK(uint256 oldK, uint256 newK);
              
                  // ============ Params Setting Functions ============
              
                  function setOracle(address newOracle) external onlyOwner {
                      _ORACLE_ = newOracle;
                  }
              
                  function setSupervisor(address newSupervisor) external onlyOwner {
                      _SUPERVISOR_ = newSupervisor;
                  }
              
                  function setMaintainer(address newMaintainer) external onlyOwner {
                      _MAINTAINER_ = newMaintainer;
                  }
              
                  function setLiquidityProviderFeeRate(uint256 newLiquidityPorviderFeeRate) external onlyOwner {
                      emit UpdateLiquidityProviderFeeRate(_LP_FEE_RATE_, newLiquidityPorviderFeeRate);
                      _LP_FEE_RATE_ = newLiquidityPorviderFeeRate;
                      _checkDODOParameters();
                  }
              
                  function setMaintainerFeeRate(uint256 newMaintainerFeeRate) external onlyOwner {
                      emit UpdateMaintainerFeeRate(_MT_FEE_RATE_, newMaintainerFeeRate);
                      _MT_FEE_RATE_ = newMaintainerFeeRate;
                      _checkDODOParameters();
                  }
              
                  function setK(uint256 newK) external onlyOwner {
                      emit UpdateK(_K_, newK);
                      _K_ = newK;
                      _checkDODOParameters();
                  }
              
                  function setGasPriceLimit(uint256 newGasPriceLimit) external onlySupervisorOrOwner {
                      emit UpdateGasPriceLimit(_GAS_PRICE_LIMIT_, newGasPriceLimit);
                      _GAS_PRICE_LIMIT_ = newGasPriceLimit;
                  }
              
                  // ============ System Control Functions ============
              
                  function disableTrading() external onlySupervisorOrOwner {
                      _TRADE_ALLOWED_ = false;
                  }
              
                  function enableTrading() external onlyOwner notClosed {
                      _TRADE_ALLOWED_ = true;
                  }
              
                  function disableQuoteDeposit() external onlySupervisorOrOwner {
                      _DEPOSIT_QUOTE_ALLOWED_ = false;
                  }
              
                  function enableQuoteDeposit() external onlyOwner notClosed {
                      _DEPOSIT_QUOTE_ALLOWED_ = true;
                  }
              
                  function disableBaseDeposit() external onlySupervisorOrOwner {
                      _DEPOSIT_BASE_ALLOWED_ = false;
                  }
              
                  function enableBaseDeposit() external onlyOwner notClosed {
                      _DEPOSIT_BASE_ALLOWED_ = true;
                  }
              }
              
              // File: contracts/lib/Ownable.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title Ownable
               * @author DODO Breeder
               *
               * @notice Ownership related functions
               */
              contract Ownable {
                  address public _OWNER_;
                  address public _NEW_OWNER_;
              
                  // ============ Events ============
              
                  event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  // ============ Modifiers ============
              
                  modifier onlyOwner() {
                      require(msg.sender == _OWNER_, "NOT_OWNER");
                      _;
                  }
              
                  // ============ Functions ============
              
                  constructor() internal {
                      _OWNER_ = msg.sender;
                      emit OwnershipTransferred(address(0), _OWNER_);
                  }
              
                  function transferOwnership(address newOwner) external onlyOwner {
                      require(newOwner != address(0), "INVALID_OWNER");
                      emit OwnershipTransferPrepared(_OWNER_, newOwner);
                      _NEW_OWNER_ = newOwner;
                  }
              
                  function claimOwnership() external {
                      require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                      emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                      _OWNER_ = _NEW_OWNER_;
                      _NEW_OWNER_ = address(0);
                  }
              }
              
              // File: contracts/impl/DODOLpToken.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DODOLpToken
               * @author DODO Breeder
               *
               * @notice Tokenize liquidity pool assets. An ordinary ERC20 contract with mint and burn functions
               */
              contract DODOLpToken is Ownable {
                  using SafeMath for uint256;
              
                  string public symbol = "DLP";
                  address public originToken;
              
                  uint256 public totalSupply;
                  mapping(address => uint256) internal balances;
                  mapping(address => mapping(address => uint256)) internal allowed;
              
                  // ============ Events ============
              
                  event Transfer(address indexed from, address indexed to, uint256 amount);
              
                  event Approval(address indexed owner, address indexed spender, uint256 amount);
              
                  event Mint(address indexed user, uint256 value);
              
                  event Burn(address indexed user, uint256 value);
              
                  // ============ Functions ============
              
                  constructor(address _originToken) public {
                      originToken = _originToken;
                  }
              
                  function name() public view returns (string memory) {
                      string memory lpTokenSuffix = "_DODO_LP_TOKEN_";
                      return string(abi.encodePacked(IERC20(originToken).name(), lpTokenSuffix));
                  }
              
                  function decimals() public view returns (uint8) {
                      return IERC20(originToken).decimals();
                  }
              
                  /**
                   * @dev transfer token for a specified address
                   * @param to The address to transfer to.
                   * @param amount The amount to be transferred.
                   */
                  function transfer(address to, uint256 amount) public returns (bool) {
                      require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH");
              
                      balances[msg.sender] = balances[msg.sender].sub(amount);
                      balances[to] = balances[to].add(amount);
                      emit Transfer(msg.sender, to, amount);
                      return true;
                  }
              
                  /**
                   * @dev Gets the balance of the specified address.
                   * @param owner The address to query the the balance of.
                   * @return balance An uint256 representing the amount owned by the passed address.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance) {
                      return balances[owner];
                  }
              
                  /**
                   * @dev Transfer tokens from one address to another
                   * @param from address The address which you want to send tokens from
                   * @param to address The address which you want to transfer to
                   * @param amount uint256 the amount of tokens to be transferred
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) public returns (bool) {
                      require(amount <= balances[from], "BALANCE_NOT_ENOUGH");
                      require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
              
                      balances[from] = balances[from].sub(amount);
                      balances[to] = balances[to].add(amount);
                      allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount);
                      emit Transfer(from, to, amount);
                      return true;
                  }
              
                  /**
                   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                   * @param spender The address which will spend the funds.
                   * @param amount The amount of tokens to be spent.
                   */
                  function approve(address spender, uint256 amount) public returns (bool) {
                      allowed[msg.sender][spender] = amount;
                      emit Approval(msg.sender, spender, amount);
                      return true;
                  }
              
                  /**
                   * @dev Function to check the amount of tokens that an owner allowed to a spender.
                   * @param owner address The address which owns the funds.
                   * @param spender address The address which will spend the funds.
                   * @return A uint256 specifying the amount of tokens still available for the spender.
                   */
                  function allowance(address owner, address spender) public view returns (uint256) {
                      return allowed[owner][spender];
                  }
              
                  function mint(address user, uint256 value) external onlyOwner {
                      balances[user] = balances[user].add(value);
                      totalSupply = totalSupply.add(value);
                      emit Mint(user, value);
                      emit Transfer(address(0), user, value);
                  }
              
                  function burn(address user, uint256 value) external onlyOwner {
                      balances[user] = balances[user].sub(value);
                      totalSupply = totalSupply.sub(value);
                      emit Burn(user, value);
                      emit Transfer(user, address(0), value);
                  }
              }
              
              // File: contracts/dodo.sol
              
              /*
              
                  Copyright 2020 DODO ZOO.
              
              */
              
              /**
               * @title DODO
               * @author DODO Breeder
               *
               * @notice Entrance for users
               */
              contract DODO is Admin, Trader, LiquidityProvider {
                  function init(
                      address owner,
                      address supervisor,
                      address maintainer,
                      address baseToken,
                      address quoteToken,
                      address oracle,
                      uint256 lpFeeRate,
                      uint256 mtFeeRate,
                      uint256 k,
                      uint256 gasPriceLimit
                  ) external {
                      require(!_INITIALIZED_, "DODO_INITIALIZED");
                      _INITIALIZED_ = true;
              
                      // constructor
                      _OWNER_ = owner;
                      emit OwnershipTransferred(address(0), _OWNER_);
              
                      _SUPERVISOR_ = supervisor;
                      _MAINTAINER_ = maintainer;
                      _BASE_TOKEN_ = baseToken;
                      _QUOTE_TOKEN_ = quoteToken;
                      _ORACLE_ = oracle;
              
                      _DEPOSIT_BASE_ALLOWED_ = true;
                      _DEPOSIT_QUOTE_ALLOWED_ = true;
                      _TRADE_ALLOWED_ = true;
                      _GAS_PRICE_LIMIT_ = gasPriceLimit;
              
                      _LP_FEE_RATE_ = lpFeeRate;
                      _MT_FEE_RATE_ = mtFeeRate;
                      _K_ = k;
                      _R_STATUS_ = Types.RStatus.ONE;
              
                      _BASE_CAPITAL_TOKEN_ = address(new DODOLpToken(_BASE_TOKEN_));
                      _QUOTE_CAPITAL_TOKEN_ = address(new DODOLpToken(_QUOTE_TOKEN_));
              
                      _checkDODOParameters();
                  }
              }

              File 7 of 8: ConstOracle
              /*
              
                  Copyright 2020 DODO ZOO.
                  SPDX-License-Identifier: Apache-2.0
              
              */
              
              pragma solidity 0.6.9;
              pragma experimental ABIEncoderV2;
              
              
              contract ConstOracle {
                  uint256 public tokenPrice;
              
                  constructor(uint256 _price) public {
                      tokenPrice = _price;
                  }
              
                  function getPrice() external view returns (uint256) {
                      return tokenPrice;
                  }
              }

              File 8 of 8: TetherToken
              pragma solidity ^0.4.17;
              
              /**
               * @title SafeMath
               * @dev Math operations with safety checks that throw on error
               */
              library SafeMath {
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      uint256 c = a * b;
                      assert(c / a == b);
                      return c;
                  }
              
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      // assert(b > 0); // Solidity automatically throws when dividing by 0
                      uint256 c = a / b;
                      // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                      return c;
                  }
              
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      assert(b <= a);
                      return a - b;
                  }
              
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      assert(c >= a);
                      return c;
                  }
              }
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                  address public owner;
              
                  /**
                    * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                    * account.
                    */
                  function Ownable() public {
                      owner = msg.sender;
                  }
              
                  /**
                    * @dev Throws if called by any account other than the owner.
                    */
                  modifier onlyOwner() {
                      require(msg.sender == owner);
                      _;
                  }
              
                  /**
                  * @dev Allows the current owner to transfer control of the contract to a newOwner.
                  * @param newOwner The address to transfer ownership to.
                  */
                  function transferOwnership(address newOwner) public onlyOwner {
                      if (newOwner != address(0)) {
                          owner = newOwner;
                      }
                  }
              
              }
              
              /**
               * @title ERC20Basic
               * @dev Simpler version of ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              contract ERC20Basic {
                  uint public _totalSupply;
                  function totalSupply() public constant returns (uint);
                  function balanceOf(address who) public constant returns (uint);
                  function transfer(address to, uint value) public;
                  event Transfer(address indexed from, address indexed to, uint value);
              }
              
              /**
               * @title ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              contract ERC20 is ERC20Basic {
                  function allowance(address owner, address spender) public constant returns (uint);
                  function transferFrom(address from, address to, uint value) public;
                  function approve(address spender, uint value) public;
                  event Approval(address indexed owner, address indexed spender, uint value);
              }
              
              /**
               * @title Basic token
               * @dev Basic version of StandardToken, with no allowances.
               */
              contract BasicToken is Ownable, ERC20Basic {
                  using SafeMath for uint;
              
                  mapping(address => uint) public balances;
              
                  // additional variables for use if transaction fees ever became necessary
                  uint public basisPointsRate = 0;
                  uint public maximumFee = 0;
              
                  /**
                  * @dev Fix for the ERC20 short address attack.
                  */
                  modifier onlyPayloadSize(uint size) {
                      require(!(msg.data.length < size + 4));
                      _;
                  }
              
                  /**
                  * @dev transfer token for a specified address
                  * @param _to The address to transfer to.
                  * @param _value The amount to be transferred.
                  */
                  function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
                      uint fee = (_value.mul(basisPointsRate)).div(10000);
                      if (fee > maximumFee) {
                          fee = maximumFee;
                      }
                      uint sendAmount = _value.sub(fee);
                      balances[msg.sender] = balances[msg.sender].sub(_value);
                      balances[_to] = balances[_to].add(sendAmount);
                      if (fee > 0) {
                          balances[owner] = balances[owner].add(fee);
                          Transfer(msg.sender, owner, fee);
                      }
                      Transfer(msg.sender, _to, sendAmount);
                  }
              
                  /**
                  * @dev Gets the balance of the specified address.
                  * @param _owner The address to query the the balance of.
                  * @return An uint representing the amount owned by the passed address.
                  */
                  function balanceOf(address _owner) public constant returns (uint balance) {
                      return balances[_owner];
                  }
              
              }
              
              /**
               * @title Standard ERC20 token
               *
               * @dev Implementation of the basic standard token.
               * @dev https://github.com/ethereum/EIPs/issues/20
               * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
               */
              contract StandardToken is BasicToken, ERC20 {
              
                  mapping (address => mapping (address => uint)) public allowed;
              
                  uint public constant MAX_UINT = 2**256 - 1;
              
                  /**
                  * @dev Transfer tokens from one address to another
                  * @param _from address The address which you want to send tokens from
                  * @param _to address The address which you want to transfer to
                  * @param _value uint the amount of tokens to be transferred
                  */
                  function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
                      var _allowance = allowed[_from][msg.sender];
              
                      // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                      // if (_value > _allowance) throw;
              
                      uint fee = (_value.mul(basisPointsRate)).div(10000);
                      if (fee > maximumFee) {
                          fee = maximumFee;
                      }
                      if (_allowance < MAX_UINT) {
                          allowed[_from][msg.sender] = _allowance.sub(_value);
                      }
                      uint sendAmount = _value.sub(fee);
                      balances[_from] = balances[_from].sub(_value);
                      balances[_to] = balances[_to].add(sendAmount);
                      if (fee > 0) {
                          balances[owner] = balances[owner].add(fee);
                          Transfer(_from, owner, fee);
                      }
                      Transfer(_from, _to, sendAmount);
                  }
              
                  /**
                  * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                  * @param _spender The address which will spend the funds.
                  * @param _value The amount of tokens to be spent.
                  */
                  function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
              
                      // To change the approve amount you first have to reduce the addresses`
                      //  allowance to zero by calling `approve(_spender, 0)` if it is not
                      //  already 0 to mitigate the race condition described here:
                      //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                      require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
              
                      allowed[msg.sender][_spender] = _value;
                      Approval(msg.sender, _spender, _value);
                  }
              
                  /**
                  * @dev Function to check the amount of tokens than an owner allowed to a spender.
                  * @param _owner address The address which owns the funds.
                  * @param _spender address The address which will spend the funds.
                  * @return A uint specifying the amount of tokens still available for the spender.
                  */
                  function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                      return allowed[_owner][_spender];
                  }
              
              }
              
              
              /**
               * @title Pausable
               * @dev Base contract which allows children to implement an emergency stop mechanism.
               */
              contract Pausable is Ownable {
                event Pause();
                event Unpause();
              
                bool public paused = false;
              
              
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 */
                modifier whenNotPaused() {
                  require(!paused);
                  _;
                }
              
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 */
                modifier whenPaused() {
                  require(paused);
                  _;
                }
              
                /**
                 * @dev called by the owner to pause, triggers stopped state
                 */
                function pause() onlyOwner whenNotPaused public {
                  paused = true;
                  Pause();
                }
              
                /**
                 * @dev called by the owner to unpause, returns to normal state
                 */
                function unpause() onlyOwner whenPaused public {
                  paused = false;
                  Unpause();
                }
              }
              
              contract BlackList is Ownable, BasicToken {
              
                  /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
                  function getBlackListStatus(address _maker) external constant returns (bool) {
                      return isBlackListed[_maker];
                  }
              
                  function getOwner() external constant returns (address) {
                      return owner;
                  }
              
                  mapping (address => bool) public isBlackListed;
                  
                  function addBlackList (address _evilUser) public onlyOwner {
                      isBlackListed[_evilUser] = true;
                      AddedBlackList(_evilUser);
                  }
              
                  function removeBlackList (address _clearedUser) public onlyOwner {
                      isBlackListed[_clearedUser] = false;
                      RemovedBlackList(_clearedUser);
                  }
              
                  function destroyBlackFunds (address _blackListedUser) public onlyOwner {
                      require(isBlackListed[_blackListedUser]);
                      uint dirtyFunds = balanceOf(_blackListedUser);
                      balances[_blackListedUser] = 0;
                      _totalSupply -= dirtyFunds;
                      DestroyedBlackFunds(_blackListedUser, dirtyFunds);
                  }
              
                  event DestroyedBlackFunds(address _blackListedUser, uint _balance);
              
                  event AddedBlackList(address _user);
              
                  event RemovedBlackList(address _user);
              
              }
              
              contract UpgradedStandardToken is StandardToken{
                  // those methods are called by the legacy contract
                  // and they must ensure msg.sender to be the contract address
                  function transferByLegacy(address from, address to, uint value) public;
                  function transferFromByLegacy(address sender, address from, address spender, uint value) public;
                  function approveByLegacy(address from, address spender, uint value) public;
              }
              
              contract TetherToken is Pausable, StandardToken, BlackList {
              
                  string public name;
                  string public symbol;
                  uint public decimals;
                  address public upgradedAddress;
                  bool public deprecated;
              
                  //  The contract can be initialized with a number of tokens
                  //  All the tokens are deposited to the owner address
                  //
                  // @param _balance Initial supply of the contract
                  // @param _name Token Name
                  // @param _symbol Token symbol
                  // @param _decimals Token decimals
                  function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
                      _totalSupply = _initialSupply;
                      name = _name;
                      symbol = _symbol;
                      decimals = _decimals;
                      balances[owner] = _initialSupply;
                      deprecated = false;
                  }
              
                  // Forward ERC20 methods to upgraded contract if this one is deprecated
                  function transfer(address _to, uint _value) public whenNotPaused {
                      require(!isBlackListed[msg.sender]);
                      if (deprecated) {
                          return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
                      } else {
                          return super.transfer(_to, _value);
                      }
                  }
              
                  // Forward ERC20 methods to upgraded contract if this one is deprecated
                  function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
                      require(!isBlackListed[_from]);
                      if (deprecated) {
                          return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
                      } else {
                          return super.transferFrom(_from, _to, _value);
                      }
                  }
              
                  // Forward ERC20 methods to upgraded contract if this one is deprecated
                  function balanceOf(address who) public constant returns (uint) {
                      if (deprecated) {
                          return UpgradedStandardToken(upgradedAddress).balanceOf(who);
                      } else {
                          return super.balanceOf(who);
                      }
                  }
              
                  // Forward ERC20 methods to upgraded contract if this one is deprecated
                  function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                      if (deprecated) {
                          return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
                      } else {
                          return super.approve(_spender, _value);
                      }
                  }
              
                  // Forward ERC20 methods to upgraded contract if this one is deprecated
                  function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                      if (deprecated) {
                          return StandardToken(upgradedAddress).allowance(_owner, _spender);
                      } else {
                          return super.allowance(_owner, _spender);
                      }
                  }
              
                  // deprecate current contract in favour of a new one
                  function deprecate(address _upgradedAddress) public onlyOwner {
                      deprecated = true;
                      upgradedAddress = _upgradedAddress;
                      Deprecate(_upgradedAddress);
                  }
              
                  // deprecate current contract if favour of a new one
                  function totalSupply() public constant returns (uint) {
                      if (deprecated) {
                          return StandardToken(upgradedAddress).totalSupply();
                      } else {
                          return _totalSupply;
                      }
                  }
              
                  // Issue a new amount of tokens
                  // these tokens are deposited into the owner address
                  //
                  // @param _amount Number of tokens to be issued
                  function issue(uint amount) public onlyOwner {
                      require(_totalSupply + amount > _totalSupply);
                      require(balances[owner] + amount > balances[owner]);
              
                      balances[owner] += amount;
                      _totalSupply += amount;
                      Issue(amount);
                  }
              
                  // Redeem tokens.
                  // These tokens are withdrawn from the owner address
                  // if the balance must be enough to cover the redeem
                  // or the call will fail.
                  // @param _amount Number of tokens to be issued
                  function redeem(uint amount) public onlyOwner {
                      require(_totalSupply >= amount);
                      require(balances[owner] >= amount);
              
                      _totalSupply -= amount;
                      balances[owner] -= amount;
                      Redeem(amount);
                  }
              
                  function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
                      // Ensure transparency by hardcoding limit beyond which fees can never be added
                      require(newBasisPoints < 20);
                      require(newMaxFee < 50);
              
                      basisPointsRate = newBasisPoints;
                      maximumFee = newMaxFee.mul(10**decimals);
              
                      Params(basisPointsRate, maximumFee);
                  }
              
                  // Called when new token are issued
                  event Issue(uint amount);
              
                  // Called when tokens are redeemed
                  event Redeem(uint amount);
              
                  // Called when contract is deprecated
                  event Deprecate(address newAddress);
              
                  // Called if contract ever adds fees
                  event Params(uint feeBasisPoints, uint maxFee);
              }