ETH Price: $2,101.90 (+0.43%)

Transaction Decoder

Block:
12288862 at Apr-22-2021 08:12:57 AM +UTC
Transaction Fee:
0.006505356 ETH $13.67
Gas Used:
49,283 Gas / 132 Gwei

Emitted Events:

109 DVM.Approval( owner=[Sender] 0x156788fb011da6e8e4df62d3b3846ff76cea0cd6, spender=0x2ff2CeE6...2045447E4, amount=115792089237316195423570985008687907853269984665640564039457584007913129639935 )

Account State Difference:

  Address   Before After State Difference Code
0x156788Fb...76cea0cD6
7.856786628190901113 Eth
Nonce: 84
7.850281272190901113 Eth
Nonce: 85
0.006505356
(Hiveon Pool)
3,502.598979106558481307 Eth3,502.605484462558481307 Eth0.006505356
0x39Eb9c3C...4dF3d9D7e

Execution Trace

DVM.approve( spender=0x2ff2CeE6E9359F9eA1cF2f51d18bF9F2045447E4, amount=115792089237316195423570985008687907853269984665640564039457584007913129639935 ) => ( True )
  • DVM.approve( spender=0x2ff2CeE6E9359F9eA1cF2f51d18bF9F2045447E4, amount=115792089237316195423570985008687907853269984665640564039457584007913129639935 ) => ( True )
    File 1 of 2: DVM
    // File: contracts/lib/InitializableOwnable.sol
    
    /*
    
        Copyright 2020 DODO ZOO.
        SPDX-License-Identifier: Apache-2.0
    
    */
    
    pragma solidity 0.6.9;
    pragma experimental ABIEncoderV2;
    
    /**
     * @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/lib/FeeRateModel.sol
    
    
    interface IFeeRateImpl {
        function getFeeRate(address pool, address trader) external view returns (uint256);
    }
    
    interface IFeeRateModel {
        function getFeeRate(address trader) external view returns (uint256);
    }
    
    contract FeeRateModel is InitializableOwnable {
        address public feeRateImpl;
    
        function setFeeProxy(address _feeRateImpl) public onlyOwner {
            feeRateImpl = _feeRateImpl;
        }
        
        function getFeeRate(address trader) external view returns (uint256) {
            if(feeRateImpl == address(0))
                return 0;
            return IFeeRateImpl(feeRateImpl).getFeeRate(msg.sender,trader);
        }
    }
    
    // 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/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/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/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/lib/DODOMath.sol
    
    
    /**
     * @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 from 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))
    
            i is the price of V-res trading pair
    
            support k=1 & k=0 case
    
            [round down]
        */
        function _GeneralIntegrate(
            uint256 V0,
            uint256 V1,
            uint256 V2,
            uint256 i,
            uint256 k
        ) internal pure returns (uint256) {
            require(V0 > 0, "TARGET_IS_ZERO");
            uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta
            if (k == 0) {
                return fairAmount.div(DecimalMath.ONE);
            }
            uint256 V0V0V1V2 = DecimalMath.divFloor(V0.mul(V0).div(V1), V2);
            uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
            return DecimalMath.ONE.sub(k).add(penalty).mul(fairAmount).div(DecimalMath.ONE2);
        }
    
        /*
            Follow the integration function above
            i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
            Assume Q2=Q0, Given Q1 and deltaB, solve Q0
    
            i is the price of delta-V trading pair
            give out target of V
    
            support k=1 & k=0 case
    
            [round down]
        */
        function _SolveQuadraticFunctionForTarget(
            uint256 V1,
            uint256 delta,
            uint256 i,
            uint256 k
        ) internal pure returns (uint256) {
            if (V1 == 0) {
                return 0;
            }
            if (k == 0) {
                return V1.add(DecimalMath.mulFloor(i, delta));
            }
            // V0 = V1*(1+(sqrt-1)/2k)
            // sqrt = √(1+4kidelta/V1)
            // premium = 1+(sqrt-1)/2k
            // uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt();
            uint256 sqrt;
            uint256 ki = (4 * k).mul(i);
            if (ki == 0) {
                sqrt = DecimalMath.ONE;
            } else if ((ki * delta) / ki == delta) {
                sqrt = (ki * delta).div(V1).add(DecimalMath.ONE2).sqrt();
            } else {
                sqrt = ki.div(V1).mul(delta).add(DecimalMath.ONE2).sqrt();
            }
            uint256 premium =
                DecimalMath.divFloor(sqrt.sub(DecimalMath.ONE), k * 2).add(DecimalMath.ONE);
            // V0 is greater than or equal to V1 according to the solution
            return DecimalMath.mulFloor(V1, premium);
        }
    
        /*
            Follow the 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, user sell Q and receive B
            if deltaBSig=false, then Q2<Q1, user sell B and receive Q
            return |Q1-Q2|
    
            as we only support sell amount as delta, the deltaB is always negative
            the input ideltaB is actually -ideltaB in the equation
    
            i is the price of delta-V trading pair
    
            support k=1 & k=0 case
    
            [round down]
        */
        function _SolveQuadraticFunctionForTrade(
            uint256 V0,
            uint256 V1,
            uint256 delta,
            uint256 i,
            uint256 k
        ) internal pure returns (uint256) {
            require(V0 > 0, "TARGET_IS_ZERO");
            if (delta == 0) {
                return 0;
            }
    
            if (k == 0) {
                return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta);
            }
    
            if (k == DecimalMath.ONE) {
                // if k==1
                // Q2=Q1/(1+ideltaBQ1/Q0/Q0)
                // temp = ideltaBQ1/Q0/Q0
                // Q2 = Q1/(1+temp)
                // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp))
                // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0));
                uint256 temp;
                uint256 idelta = i.mul(delta);
                if (idelta == 0) {
                    temp = 0;
                } else if ((idelta * V1) / idelta == V1) {
                    temp = (idelta * V1).div(V0.mul(V0));
                } else {
                    temp = delta.mul(V1).div(V0).mul(i).div(V0);
                }
                return V1.mul(temp).div(temp.add(DecimalMath.ONE));
            }
    
            // calculate -b value and sig
            // b = kQ0^2/Q1-i*deltaB-(1-k)Q1
            // part1 = (1-k)Q1 >=0
            // part2 = kQ0^2/Q1-i*deltaB >=0
            // bAbs = abs(part1-part2)
            // if part1>part2 => b is negative => bSig is false
            // if part2>part1 => b is positive => bSig is true
            uint256 part2 = k.mul(V0).div(V1).mul(V0).add(i.mul(delta)); // kQ0^2/Q1-i*deltaB
            uint256 bAbs = DecimalMath.ONE.sub(k).mul(V1); // (1-k)Q1
    
            bool bSig;
            if (bAbs >= part2) {
                bAbs = bAbs - part2;
                bSig = false;
            } else {
                bAbs = part2 - bAbs;
                bSig = true;
            }
            bAbs = bAbs.div(DecimalMath.ONE);
    
            // calculate sqrt
            uint256 squareRoot =
                DecimalMath.mulFloor(
                    DecimalMath.ONE.sub(k).mul(4),
                    DecimalMath.mulFloor(k, V0).mul(V0)
                ); // 4(1-k)kQ0^2
            squareRoot = bAbs.mul(bAbs).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 (bSig) {
                numerator = squareRoot.sub(bAbs);
            } else {
                numerator = bAbs.add(squareRoot);
            }
    
            uint256 V2 = DecimalMath.divCeil(numerator, denominator);
            if (V2 > V1) {
                return 0;
            } else {
                return V1 - V2;
            }
        }
    }
    
    // File: contracts/lib/PMMPricing.sol
    
    
    /**
     * @title Pricing
     * @author DODO Breeder
     *
     * @notice DODO Pricing model
     */
    
    library PMMPricing {
        using SafeMath for uint256;
    
        enum RState {ONE, ABOVE_ONE, BELOW_ONE}
    
        struct PMMState {
            uint256 i;
            uint256 K;
            uint256 B;
            uint256 Q;
            uint256 B0;
            uint256 Q0;
            RState R;
        }
    
        // ============ buy & sell ============
    
        function sellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (uint256 receiveQuoteAmount, RState newR)
        {
            if (state.R == RState.ONE) {
                // case 1: R=1
                // R falls below one
                receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount);
                newR = RState.BELOW_ONE;
            } else if (state.R == RState.ABOVE_ONE) {
                uint256 backToOnePayBase = state.B0.sub(state.B);
                uint256 backToOneReceiveQuote = state.Q.sub(state.Q0);
                // case 2: R>1
                // complex case, R status depends on trading amount
                if (payBaseAmount < backToOnePayBase) {
                    // case 2.1: R status do not change
                    receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount);
                    newR = RState.ABOVE_ONE;
                    if (receiveQuoteAmount > backToOneReceiveQuote) {
                        // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount
                        // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote
                        receiveQuoteAmount = backToOneReceiveQuote;
                    }
                } else if (payBaseAmount == backToOnePayBase) {
                    // case 2.2: R status changes to ONE
                    receiveQuoteAmount = backToOneReceiveQuote;
                    newR = RState.ONE;
                } else {
                    // case 2.3: R status changes to BELOW_ONE
                    receiveQuoteAmount = backToOneReceiveQuote.add(
                        _ROneSellBaseToken(state, payBaseAmount.sub(backToOnePayBase))
                    );
                    newR = RState.BELOW_ONE;
                }
            } else {
                // state.R == RState.BELOW_ONE
                // case 3: R<1
                receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount);
                newR = RState.BELOW_ONE;
            }
        }
    
        function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (uint256 receiveBaseAmount, RState newR)
        {
            if (state.R == RState.ONE) {
                receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount);
                newR = RState.ABOVE_ONE;
            } else if (state.R == RState.ABOVE_ONE) {
                receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount);
                newR = RState.ABOVE_ONE;
            } else {
                uint256 backToOnePayQuote = state.Q0.sub(state.Q);
                uint256 backToOneReceiveBase = state.B.sub(state.B0);
                if (payQuoteAmount < backToOnePayQuote) {
                    receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount);
                    newR = RState.BELOW_ONE;
                    if (receiveBaseAmount > backToOneReceiveBase) {
                        receiveBaseAmount = backToOneReceiveBase;
                    }
                } else if (payQuoteAmount == backToOnePayQuote) {
                    receiveBaseAmount = backToOneReceiveBase;
                    newR = RState.ONE;
                } else {
                    receiveBaseAmount = backToOneReceiveBase.add(
                        _ROneSellQuoteToken(state, payQuoteAmount.sub(backToOnePayQuote))
                    );
                    newR = RState.ABOVE_ONE;
                }
            }
        }
    
        // ============ R = 1 cases ============
    
        function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (
                uint256 // receiveQuoteToken
            )
        {
            // in theory Q2 <= targetQuoteTokenAmount
            // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.Q0,
                    state.Q0,
                    payBaseAmount,
                    state.i,
                    state.K
                );
        }
    
        function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (
                uint256 // receiveBaseToken
            )
        {
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.B0,
                    state.B0,
                    payQuoteAmount,
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
        }
    
        // ============ R < 1 cases ============
    
        function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (
                uint256 // receiveBaseToken
            )
        {
            return
                DODOMath._GeneralIntegrate(
                    state.Q0,
                    state.Q.add(payQuoteAmount),
                    state.Q,
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
        }
    
        function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (
                uint256 // receiveQuoteToken
            )
        {
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.Q0,
                    state.Q,
                    payBaseAmount,
                    state.i,
                    state.K
                );
        }
    
        // ============ R > 1 cases ============
    
        function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (
                uint256 // receiveQuoteToken
            )
        {
            return
                DODOMath._GeneralIntegrate(
                    state.B0,
                    state.B.add(payBaseAmount),
                    state.B,
                    state.i,
                    state.K
                );
        }
    
        function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (
                uint256 // receiveBaseToken
            )
        {
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.B0,
                    state.B,
                    payQuoteAmount,
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
        }
    
        // ============ Helper functions ============
    
        function adjustedTarget(PMMState memory state) internal pure {
            if (state.R == RState.BELOW_ONE) {
                state.Q0 = DODOMath._SolveQuadraticFunctionForTarget(
                    state.Q,
                    state.B.sub(state.B0),
                    state.i,
                    state.K
                );
            } else if (state.R == RState.ABOVE_ONE) {
                state.B0 = DODOMath._SolveQuadraticFunctionForTarget(
                    state.B,
                    state.Q.sub(state.Q0),
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
            }
        }
    
        function getMidPrice(PMMState memory state) internal pure returns (uint256) {
            if (state.R == RState.BELOW_ONE) {
                uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q);
                R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
                return DecimalMath.divFloor(state.i, R);
            } else {
                uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B);
                R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
                return DecimalMath.mulFloor(state.i, R);
            }
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMStorage.sol
    
    
    contract DVMStorage is ReentrancyGuard {
        using SafeMath for uint256;
    
        bool public _IS_OPEN_TWAP_ = false;
    
        bool internal _DVM_INITIALIZED_;
    
        // ============ Core Address ============
    
        address public _MAINTAINER_;
    
        IERC20 public _BASE_TOKEN_;
        IERC20 public _QUOTE_TOKEN_;
    
        uint112 public _BASE_RESERVE_;
        uint112 public _QUOTE_RESERVE_;
        uint32 public _BLOCK_TIMESTAMP_LAST_;
    
        uint256 public _BASE_PRICE_CUMULATIVE_LAST_;
    
        // ============ Shares (ERC20) ============
    
        string public symbol;
        uint8 public decimals;
        string public name;
    
        uint256 public totalSupply;
        mapping(address => uint256) internal _SHARES_;
        mapping(address => mapping(address => uint256)) internal _ALLOWED_;
    
        // ================= Permit ======================
    
        bytes32 public DOMAIN_SEPARATOR;
        // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
        mapping(address => uint256) public nonces;
    
        // ============ Variables for Pricing ============
    
        uint256 public _LP_FEE_RATE_;
        IFeeRateModel public _MT_FEE_RATE_MODEL_;
        uint256 public _K_;
        uint256 public _I_;
    
    
        // ============ Helper Functions ============
    
        function getPMMState() public view returns (PMMPricing.PMMState memory state) {
            state.i = _I_;
            state.K = _K_;
            state.B = _BASE_RESERVE_;
            state.Q = _QUOTE_RESERVE_;
            state.B0 = 0; // will be calculated in adjustedTarget
            state.Q0 = 0;
            state.R = PMMPricing.RState.ABOVE_ONE;
            PMMPricing.adjustedTarget(state);
        }
    
        function getPMMStateForCall() 
            external 
            view 
            returns (
                uint256 i,
                uint256 K,
                uint256 B,
                uint256 Q,
                uint256 B0,
                uint256 Q0,
                uint256 R
            )
        {
            PMMPricing.PMMState memory state = getPMMState();
            i = state.i;
            K = state.K;
            B = state.B;
            Q = state.Q;
            B0 = state.B0;
            Q0 = state.Q0;
            R = uint256(state.R);
        }
    
        function getMidPrice() public view returns (uint256 midPrice) {
            return PMMPricing.getMidPrice(getPMMState());
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMVault.sol
    
    
    contract DVMVault is DVMStorage {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
    
        // ============ 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);
    
        // ============ View Functions ============
    
        function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve) {
            baseReserve = _BASE_RESERVE_;
            quoteReserve = _QUOTE_RESERVE_;
        }
    
        function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate) {
            lpFeeRate = _LP_FEE_RATE_;
            mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(user);
        }
    
        // ============ Asset In ============
    
        function getBaseInput() public view returns (uint256 input) {
            return _BASE_TOKEN_.balanceOf(address(this)).sub(uint256(_BASE_RESERVE_));
        }
    
        function getQuoteInput() public view returns (uint256 input) {
            return _QUOTE_TOKEN_.balanceOf(address(this)).sub(uint256(_QUOTE_RESERVE_));
        }
    
        // ============ TWAP UPDATE ===========
        
        function _twapUpdate() internal {
            uint32 blockTimestamp = uint32(block.timestamp % 2**32);
            uint32 timeElapsed = blockTimestamp - _BLOCK_TIMESTAMP_LAST_;
            if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {
                _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;
            }
            _BLOCK_TIMESTAMP_LAST_ = blockTimestamp;
        }
    
        // ============ Set States ============
    
        function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
            require(baseReserve <= uint112(-1) && quoteReserve <= uint112(-1), "OVERFLOW");
            _BASE_RESERVE_ = uint112(baseReserve);
            _QUOTE_RESERVE_ = uint112(quoteReserve);
    
            if(_IS_OPEN_TWAP_) _twapUpdate();
        }
    
        function _sync() internal {
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            require(baseBalance <= uint112(-1) && quoteBalance <= uint112(-1), "OVERFLOW");
            if (baseBalance != _BASE_RESERVE_) {
                _BASE_RESERVE_ = uint112(baseBalance);
            }
            if (quoteBalance != _QUOTE_RESERVE_) {
                _QUOTE_RESERVE_ = uint112(quoteBalance);
            }
    
            if(_IS_OPEN_TWAP_) _twapUpdate();
        }
    
    
        function sync() external preventReentrant {
            _sync();
        }
    
        // ============ Asset Out ============
    
        function _transferBaseOut(address to, uint256 amount) internal {
            if (amount > 0) {
                _BASE_TOKEN_.safeTransfer(to, amount);
            }
        }
    
        function _transferQuoteOut(address to, uint256 amount) internal {
            if (amount > 0) {
                _QUOTE_TOKEN_.safeTransfer(to, amount);
            }
        }
    
        // ============ Shares (ERC20) ============
    
        /**
         * @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 <= _SHARES_[msg.sender], "BALANCE_NOT_ENOUGH");
    
            _SHARES_[msg.sender] = _SHARES_[msg.sender].sub(amount);
            _SHARES_[to] = _SHARES_[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 _SHARES_[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 <= _SHARES_[from], "BALANCE_NOT_ENOUGH");
            require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
    
            _SHARES_[from] = _SHARES_[from].sub(amount);
            _SHARES_[to] = _SHARES_[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) {
            _approve(msg.sender, spender, amount);
            return true;
        }
    
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) private {
            _ALLOWED_[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
    
        /**
         * @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) internal {
            require(value > 1000, "MINT_INVALID");
            _SHARES_[user] = _SHARES_[user].add(value);
            totalSupply = totalSupply.add(value);
            emit Mint(user, value);
            emit Transfer(address(0), user, value);
        }
    
        function _burn(address user, uint256 value) internal {
            _SHARES_[user] = _SHARES_[user].sub(value);
            totalSupply = totalSupply.sub(value);
            emit Burn(user, value);
            emit Transfer(user, address(0), value);
        }
    
        // ============================ Permit ======================================
        
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external {
            require(deadline >= block.timestamp, "DODO_DVM_LP: EXPIRED");
            bytes32 digest = keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR,
                    keccak256(
                        abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)
                    )
                )
            );
            address recoveredAddress = ecrecover(digest, v, r, s);
            require(
                recoveredAddress != address(0) && recoveredAddress == owner,
                "DODO_DVM_LP: INVALID_SIGNATURE"
            );
            _approve(owner, spender, value);
        }
    }
    
    // File: contracts/intf/IDODOCallee.sol
    
    
    interface IDODOCallee {
        function DVMSellShareCall(
            address sender,
            uint256 burnShareAmount,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    
        function DVMFlashLoanCall(
            address sender,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    
        function DPPFlashLoanCall(
            address sender,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    
        function CPCancelCall(
            address sender,
            uint256 amount,
            bytes calldata data
        ) external;
    
    	function CPClaimBidCall(
            address sender,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMTrader.sol
    
    
    
    contract DVMTrader is DVMVault {
        using SafeMath for uint256;
    
        // ============ Events ============
    
        event DODOSwap(
            address fromToken,
            address toToken,
            uint256 fromAmount,
            uint256 toAmount,
            address trader,
            address receiver
        );
    
        event DODOFlashLoan(
            address borrower,
            address assetTo,
            uint256 baseAmount,
            uint256 quoteAmount
        );
    
        // ============ Trade Functions ============
    
        function sellBase(address to)
            external
            preventReentrant
            returns (uint256 receiveQuoteAmount)
        {
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_));
            uint256 mtFee;
            (receiveQuoteAmount, mtFee) = querySellBase(tx.origin, baseInput);
    
            _transferQuoteOut(to, receiveQuoteAmount);
            _transferQuoteOut(_MAINTAINER_, mtFee);
            _setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this)));
    
            emit DODOSwap(
                address(_BASE_TOKEN_),
                address(_QUOTE_TOKEN_),
                baseInput,
                receiveQuoteAmount,
                msg.sender,
                to
            );
        }
    
        function sellQuote(address to)
            external
            preventReentrant
            returns (uint256 receiveBaseAmount)
        {
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_));
            uint256 mtFee;
            (receiveBaseAmount, mtFee) = querySellQuote(tx.origin, quoteInput);
    
            _transferBaseOut(to, receiveBaseAmount);
            _transferBaseOut(_MAINTAINER_, mtFee);
            _setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance);
    
            emit DODOSwap(
                address(_QUOTE_TOKEN_),
                address(_BASE_TOKEN_),
                quoteInput,
                receiveBaseAmount,
                msg.sender,
                to
            );
        }
    
        function flashLoan(
            uint256 baseAmount,
            uint256 quoteAmount,
            address assetTo,
            bytes calldata data
        ) external preventReentrant {
            _transferBaseOut(assetTo, baseAmount);
            _transferQuoteOut(assetTo, quoteAmount);
    
            if (data.length > 0)
                IDODOCallee(assetTo).DVMFlashLoanCall(msg.sender, baseAmount, quoteAmount, data);
    
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            
            // no input -> pure loss
            require(
                baseBalance >= _BASE_RESERVE_ || quoteBalance >= _QUOTE_RESERVE_,
                "FLASH_LOAN_FAILED"
            );
    
            // sell quote
            if (baseBalance < _BASE_RESERVE_) {
                uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_));
                (uint256 receiveBaseAmount, uint256 mtFee) = querySellQuote(tx.origin, quoteInput);
                require(uint256(_BASE_RESERVE_).sub(baseBalance) <= receiveBaseAmount, "FLASH_LOAN_FAILED");
    
                _transferBaseOut(_MAINTAINER_, mtFee);
                emit DODOSwap(
                    address(_QUOTE_TOKEN_),
                    address(_BASE_TOKEN_),
                    quoteInput,
                    receiveBaseAmount,
                    msg.sender,
                    assetTo
                );
            }
    
            // sell base
            if (quoteBalance < _QUOTE_RESERVE_) {
                uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_));
                (uint256 receiveQuoteAmount, uint256 mtFee) = querySellBase(tx.origin, baseInput);
                require(uint256(_QUOTE_RESERVE_).sub(quoteBalance) <= receiveQuoteAmount, "FLASH_LOAN_FAILED");
    
                _transferQuoteOut(_MAINTAINER_, mtFee);
                emit DODOSwap(
                    address(_BASE_TOKEN_),
                    address(_QUOTE_TOKEN_),
                    baseInput,
                    receiveQuoteAmount,
                    msg.sender,
                    assetTo
                );
            }
    
            _sync();
            
            emit DODOFlashLoan(msg.sender, assetTo, baseAmount, quoteAmount);
        }
    
        // ============ Query Functions ============
    
        function querySellBase(address trader, uint256 payBaseAmount)
            public
            view
            returns (uint256 receiveQuoteAmount, uint256 mtFee)
        {
            (receiveQuoteAmount, ) = PMMPricing.sellBaseToken(getPMMState(), payBaseAmount);
    
            uint256 lpFeeRate = _LP_FEE_RATE_;
            uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
            mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
            receiveQuoteAmount = receiveQuoteAmount
                .sub(DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate))
                .sub(mtFee);
        }
    
        function querySellQuote(address trader, uint256 payQuoteAmount)
            public
            view
            returns (uint256 receiveBaseAmount, uint256 mtFee)
        {
            (receiveBaseAmount, ) = PMMPricing.sellQuoteToken(getPMMState(), payQuoteAmount);
    
            uint256 lpFeeRate = _LP_FEE_RATE_;
            uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
            mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
            receiveBaseAmount = receiveBaseAmount
                .sub(DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate))
                .sub(mtFee);
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMFunding.sol
    
    
    contract DVMFunding is DVMVault {
        // ============ Events ============
    
        event BuyShares(address to, uint256 increaseShares, uint256 totalShares);
    
        event SellShares(address payer, address to, uint256 decreaseShares, uint256 totalShares);
    
        // ============ Buy & Sell Shares ============
    
        // buy shares [round down]
        function buyShares(address to)
            external
            preventReentrant
            returns (
                uint256 shares,
                uint256 baseInput,
                uint256 quoteInput
            )
        {
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            uint256 baseReserve = _BASE_RESERVE_;
            uint256 quoteReserve = _QUOTE_RESERVE_;
    
            baseInput = baseBalance.sub(baseReserve);
            quoteInput = quoteBalance.sub(quoteReserve);
            require(baseInput > 0, "NO_BASE_INPUT");
    
            // Round down when withdrawing. Therefore, never be a situation occuring balance is 0 but totalsupply is not 0
            // But May Happen,reserve >0 But totalSupply = 0
            if (totalSupply == 0) {
                // case 1. initial supply
                require(baseBalance >= 10**3, "INSUFFICIENT_LIQUIDITY_MINED");
                shares = baseBalance; // 以免出现balance很大但shares很小的情况
            } else if (baseReserve > 0 && quoteReserve == 0) {
                // case 2. supply when quote reserve is 0
                shares = baseInput.mul(totalSupply).div(baseReserve);
            } else if (baseReserve > 0 && quoteReserve > 0) {
                // case 3. normal case
                uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
                uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
                uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio;
                shares = DecimalMath.mulFloor(totalSupply, mintRatio);
            }
            _mint(to, shares);
            _setReserve(baseBalance, quoteBalance);
            emit BuyShares(to, shares, _SHARES_[to]);
        }
    
        // sell shares [round down]
        function sellShares(
            uint256 shareAmount,
            address to,
            uint256 baseMinAmount,
            uint256 quoteMinAmount,
            bytes calldata data,
            uint256 deadline
        ) external preventReentrant returns (uint256 baseAmount, uint256 quoteAmount) {
            require(deadline >= block.timestamp, "TIME_EXPIRED");
            require(shareAmount <= _SHARES_[msg.sender], "DLP_NOT_ENOUGH");
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            uint256 totalShares = totalSupply;
    
            baseAmount = baseBalance.mul(shareAmount).div(totalShares);
            quoteAmount = quoteBalance.mul(shareAmount).div(totalShares);
    
            require(
                baseAmount >= baseMinAmount && quoteAmount >= quoteMinAmount,
                "WITHDRAW_NOT_ENOUGH"
            );
    
            _burn(msg.sender, shareAmount);
            _transferBaseOut(to, baseAmount);
            _transferQuoteOut(to, quoteAmount);
            _sync();
    
            if (data.length > 0) {
                IDODOCallee(to).DVMSellShareCall(
                    msg.sender,
                    shareAmount,
                    baseAmount,
                    quoteAmount,
                    data
                );
            }
    
            emit SellShares(msg.sender, to, shareAmount, _SHARES_[msg.sender]);
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVM.sol
    
    
    
    /**
     * @title DODO VendingMachine
     * @author DODO Breeder
     *
     * @notice DODOVendingMachine initialization
     */
    contract DVM is DVMTrader, DVMFunding {
        function init(
            address maintainer,
            address baseTokenAddress,
            address quoteTokenAddress,
            uint256 lpFeeRate,
            address mtFeeRateModel,
            uint256 i,
            uint256 k,
            bool isOpenTWAP
        ) external {
            require(!_DVM_INITIALIZED_, "DVM_INITIALIZED");
            _DVM_INITIALIZED_ = true;
            
            require(baseTokenAddress != quoteTokenAddress, "BASE_QUOTE_CAN_NOT_BE_SAME");
            _BASE_TOKEN_ = IERC20(baseTokenAddress);
            _QUOTE_TOKEN_ = IERC20(quoteTokenAddress);
    
            require(i > 0 && i <= 10**36);
            _I_ = i;
    
            require(k <= 10**18);
            _K_ = k;
    
            _LP_FEE_RATE_ = lpFeeRate;
            _MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel);
            _MAINTAINER_ = maintainer;
    
            _IS_OPEN_TWAP_ = isOpenTWAP;
            if(isOpenTWAP) _BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2**32);
    
            string memory connect = "_";
            string memory suffix = "DLP";
    
            name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this))));
            symbol = "DLP";
            decimals = _BASE_TOKEN_.decimals();
    
            // ============================== Permit ====================================
            uint256 chainId;
            assembly {
                chainId := chainid()
            }
            DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                    0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                    keccak256(bytes(name)),
                    keccak256(bytes("1")),
                    chainId,
                    address(this)
                )
            );
            // ==========================================================================
        }
    
        function addressToShortString(address _addr) public pure returns (string memory) {
            bytes32 value = bytes32(uint256(_addr));
            bytes memory alphabet = "0123456789abcdef";
    
            bytes memory str = new bytes(8);
            for (uint256 i = 0; i < 4; i++) {
                str[i * 2] = alphabet[uint8(value[i + 12] >> 4)];
                str[1 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];
            }
            return string(str);
        }
    
        // ============ Version Control ============
        
        function version() external pure returns (string memory) {
            return "DVM 1.0.2";
        }
    }

    File 2 of 2: DVM
    // File: contracts/lib/InitializableOwnable.sol
    
    /*
    
        Copyright 2020 DODO ZOO.
        SPDX-License-Identifier: Apache-2.0
    
    */
    
    pragma solidity 0.6.9;
    pragma experimental ABIEncoderV2;
    
    /**
     * @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/lib/FeeRateModel.sol
    
    
    interface IFeeRateImpl {
        function getFeeRate(address pool, address trader) external view returns (uint256);
    }
    
    interface IFeeRateModel {
        function getFeeRate(address trader) external view returns (uint256);
    }
    
    contract FeeRateModel is InitializableOwnable {
        address public feeRateImpl;
    
        function setFeeProxy(address _feeRateImpl) public onlyOwner {
            feeRateImpl = _feeRateImpl;
        }
        
        function getFeeRate(address trader) external view returns (uint256) {
            if(feeRateImpl == address(0))
                return 0;
            return IFeeRateImpl(feeRateImpl).getFeeRate(msg.sender,trader);
        }
    }
    
    // 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/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/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/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/lib/DODOMath.sol
    
    
    /**
     * @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 from 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))
    
            i is the price of V-res trading pair
    
            support k=1 & k=0 case
    
            [round down]
        */
        function _GeneralIntegrate(
            uint256 V0,
            uint256 V1,
            uint256 V2,
            uint256 i,
            uint256 k
        ) internal pure returns (uint256) {
            require(V0 > 0, "TARGET_IS_ZERO");
            uint256 fairAmount = i.mul(V1.sub(V2)); // i*delta
            if (k == 0) {
                return fairAmount.div(DecimalMath.ONE);
            }
            uint256 V0V0V1V2 = DecimalMath.divFloor(V0.mul(V0).div(V1), V2);
            uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
            return DecimalMath.ONE.sub(k).add(penalty).mul(fairAmount).div(DecimalMath.ONE2);
        }
    
        /*
            Follow the integration function above
            i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
            Assume Q2=Q0, Given Q1 and deltaB, solve Q0
    
            i is the price of delta-V trading pair
            give out target of V
    
            support k=1 & k=0 case
    
            [round down]
        */
        function _SolveQuadraticFunctionForTarget(
            uint256 V1,
            uint256 delta,
            uint256 i,
            uint256 k
        ) internal pure returns (uint256) {
            if (V1 == 0) {
                return 0;
            }
            if (k == 0) {
                return V1.add(DecimalMath.mulFloor(i, delta));
            }
            // V0 = V1*(1+(sqrt-1)/2k)
            // sqrt = √(1+4kidelta/V1)
            // premium = 1+(sqrt-1)/2k
            // uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt();
            uint256 sqrt;
            uint256 ki = (4 * k).mul(i);
            if (ki == 0) {
                sqrt = DecimalMath.ONE;
            } else if ((ki * delta) / ki == delta) {
                sqrt = (ki * delta).div(V1).add(DecimalMath.ONE2).sqrt();
            } else {
                sqrt = ki.div(V1).mul(delta).add(DecimalMath.ONE2).sqrt();
            }
            uint256 premium =
                DecimalMath.divFloor(sqrt.sub(DecimalMath.ONE), k * 2).add(DecimalMath.ONE);
            // V0 is greater than or equal to V1 according to the solution
            return DecimalMath.mulFloor(V1, premium);
        }
    
        /*
            Follow the 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, user sell Q and receive B
            if deltaBSig=false, then Q2<Q1, user sell B and receive Q
            return |Q1-Q2|
    
            as we only support sell amount as delta, the deltaB is always negative
            the input ideltaB is actually -ideltaB in the equation
    
            i is the price of delta-V trading pair
    
            support k=1 & k=0 case
    
            [round down]
        */
        function _SolveQuadraticFunctionForTrade(
            uint256 V0,
            uint256 V1,
            uint256 delta,
            uint256 i,
            uint256 k
        ) internal pure returns (uint256) {
            require(V0 > 0, "TARGET_IS_ZERO");
            if (delta == 0) {
                return 0;
            }
    
            if (k == 0) {
                return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta);
            }
    
            if (k == DecimalMath.ONE) {
                // if k==1
                // Q2=Q1/(1+ideltaBQ1/Q0/Q0)
                // temp = ideltaBQ1/Q0/Q0
                // Q2 = Q1/(1+temp)
                // Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp))
                // uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0));
                uint256 temp;
                uint256 idelta = i.mul(delta);
                if (idelta == 0) {
                    temp = 0;
                } else if ((idelta * V1) / idelta == V1) {
                    temp = (idelta * V1).div(V0.mul(V0));
                } else {
                    temp = delta.mul(V1).div(V0).mul(i).div(V0);
                }
                return V1.mul(temp).div(temp.add(DecimalMath.ONE));
            }
    
            // calculate -b value and sig
            // b = kQ0^2/Q1-i*deltaB-(1-k)Q1
            // part1 = (1-k)Q1 >=0
            // part2 = kQ0^2/Q1-i*deltaB >=0
            // bAbs = abs(part1-part2)
            // if part1>part2 => b is negative => bSig is false
            // if part2>part1 => b is positive => bSig is true
            uint256 part2 = k.mul(V0).div(V1).mul(V0).add(i.mul(delta)); // kQ0^2/Q1-i*deltaB
            uint256 bAbs = DecimalMath.ONE.sub(k).mul(V1); // (1-k)Q1
    
            bool bSig;
            if (bAbs >= part2) {
                bAbs = bAbs - part2;
                bSig = false;
            } else {
                bAbs = part2 - bAbs;
                bSig = true;
            }
            bAbs = bAbs.div(DecimalMath.ONE);
    
            // calculate sqrt
            uint256 squareRoot =
                DecimalMath.mulFloor(
                    DecimalMath.ONE.sub(k).mul(4),
                    DecimalMath.mulFloor(k, V0).mul(V0)
                ); // 4(1-k)kQ0^2
            squareRoot = bAbs.mul(bAbs).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 (bSig) {
                numerator = squareRoot.sub(bAbs);
            } else {
                numerator = bAbs.add(squareRoot);
            }
    
            uint256 V2 = DecimalMath.divCeil(numerator, denominator);
            if (V2 > V1) {
                return 0;
            } else {
                return V1 - V2;
            }
        }
    }
    
    // File: contracts/lib/PMMPricing.sol
    
    
    /**
     * @title Pricing
     * @author DODO Breeder
     *
     * @notice DODO Pricing model
     */
    
    library PMMPricing {
        using SafeMath for uint256;
    
        enum RState {ONE, ABOVE_ONE, BELOW_ONE}
    
        struct PMMState {
            uint256 i;
            uint256 K;
            uint256 B;
            uint256 Q;
            uint256 B0;
            uint256 Q0;
            RState R;
        }
    
        // ============ buy & sell ============
    
        function sellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (uint256 receiveQuoteAmount, RState newR)
        {
            if (state.R == RState.ONE) {
                // case 1: R=1
                // R falls below one
                receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount);
                newR = RState.BELOW_ONE;
            } else if (state.R == RState.ABOVE_ONE) {
                uint256 backToOnePayBase = state.B0.sub(state.B);
                uint256 backToOneReceiveQuote = state.Q.sub(state.Q0);
                // case 2: R>1
                // complex case, R status depends on trading amount
                if (payBaseAmount < backToOnePayBase) {
                    // case 2.1: R status do not change
                    receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount);
                    newR = RState.ABOVE_ONE;
                    if (receiveQuoteAmount > backToOneReceiveQuote) {
                        // [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount
                        // to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote
                        receiveQuoteAmount = backToOneReceiveQuote;
                    }
                } else if (payBaseAmount == backToOnePayBase) {
                    // case 2.2: R status changes to ONE
                    receiveQuoteAmount = backToOneReceiveQuote;
                    newR = RState.ONE;
                } else {
                    // case 2.3: R status changes to BELOW_ONE
                    receiveQuoteAmount = backToOneReceiveQuote.add(
                        _ROneSellBaseToken(state, payBaseAmount.sub(backToOnePayBase))
                    );
                    newR = RState.BELOW_ONE;
                }
            } else {
                // state.R == RState.BELOW_ONE
                // case 3: R<1
                receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount);
                newR = RState.BELOW_ONE;
            }
        }
    
        function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (uint256 receiveBaseAmount, RState newR)
        {
            if (state.R == RState.ONE) {
                receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount);
                newR = RState.ABOVE_ONE;
            } else if (state.R == RState.ABOVE_ONE) {
                receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount);
                newR = RState.ABOVE_ONE;
            } else {
                uint256 backToOnePayQuote = state.Q0.sub(state.Q);
                uint256 backToOneReceiveBase = state.B.sub(state.B0);
                if (payQuoteAmount < backToOnePayQuote) {
                    receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount);
                    newR = RState.BELOW_ONE;
                    if (receiveBaseAmount > backToOneReceiveBase) {
                        receiveBaseAmount = backToOneReceiveBase;
                    }
                } else if (payQuoteAmount == backToOnePayQuote) {
                    receiveBaseAmount = backToOneReceiveBase;
                    newR = RState.ONE;
                } else {
                    receiveBaseAmount = backToOneReceiveBase.add(
                        _ROneSellQuoteToken(state, payQuoteAmount.sub(backToOnePayQuote))
                    );
                    newR = RState.ABOVE_ONE;
                }
            }
        }
    
        // ============ R = 1 cases ============
    
        function _ROneSellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (
                uint256 // receiveQuoteToken
            )
        {
            // in theory Q2 <= targetQuoteTokenAmount
            // however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.Q0,
                    state.Q0,
                    payBaseAmount,
                    state.i,
                    state.K
                );
        }
    
        function _ROneSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (
                uint256 // receiveBaseToken
            )
        {
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.B0,
                    state.B0,
                    payQuoteAmount,
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
        }
    
        // ============ R < 1 cases ============
    
        function _RBelowSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (
                uint256 // receiveBaseToken
            )
        {
            return
                DODOMath._GeneralIntegrate(
                    state.Q0,
                    state.Q.add(payQuoteAmount),
                    state.Q,
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
        }
    
        function _RBelowSellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (
                uint256 // receiveQuoteToken
            )
        {
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.Q0,
                    state.Q,
                    payBaseAmount,
                    state.i,
                    state.K
                );
        }
    
        // ============ R > 1 cases ============
    
        function _RAboveSellBaseToken(PMMState memory state, uint256 payBaseAmount)
            internal
            pure
            returns (
                uint256 // receiveQuoteToken
            )
        {
            return
                DODOMath._GeneralIntegrate(
                    state.B0,
                    state.B.add(payBaseAmount),
                    state.B,
                    state.i,
                    state.K
                );
        }
    
        function _RAboveSellQuoteToken(PMMState memory state, uint256 payQuoteAmount)
            internal
            pure
            returns (
                uint256 // receiveBaseToken
            )
        {
            return
                DODOMath._SolveQuadraticFunctionForTrade(
                    state.B0,
                    state.B,
                    payQuoteAmount,
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
        }
    
        // ============ Helper functions ============
    
        function adjustedTarget(PMMState memory state) internal pure {
            if (state.R == RState.BELOW_ONE) {
                state.Q0 = DODOMath._SolveQuadraticFunctionForTarget(
                    state.Q,
                    state.B.sub(state.B0),
                    state.i,
                    state.K
                );
            } else if (state.R == RState.ABOVE_ONE) {
                state.B0 = DODOMath._SolveQuadraticFunctionForTarget(
                    state.B,
                    state.Q.sub(state.Q0),
                    DecimalMath.reciprocalFloor(state.i),
                    state.K
                );
            }
        }
    
        function getMidPrice(PMMState memory state) internal pure returns (uint256) {
            if (state.R == RState.BELOW_ONE) {
                uint256 R = DecimalMath.divFloor(state.Q0.mul(state.Q0).div(state.Q), state.Q);
                R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
                return DecimalMath.divFloor(state.i, R);
            } else {
                uint256 R = DecimalMath.divFloor(state.B0.mul(state.B0).div(state.B), state.B);
                R = DecimalMath.ONE.sub(state.K).add(DecimalMath.mulFloor(state.K, R));
                return DecimalMath.mulFloor(state.i, R);
            }
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMStorage.sol
    
    
    contract DVMStorage is ReentrancyGuard {
        using SafeMath for uint256;
    
        bool public _IS_OPEN_TWAP_ = false;
    
        bool internal _DVM_INITIALIZED_;
    
        // ============ Core Address ============
    
        address public _MAINTAINER_;
    
        IERC20 public _BASE_TOKEN_;
        IERC20 public _QUOTE_TOKEN_;
    
        uint112 public _BASE_RESERVE_;
        uint112 public _QUOTE_RESERVE_;
        uint32 public _BLOCK_TIMESTAMP_LAST_;
    
        uint256 public _BASE_PRICE_CUMULATIVE_LAST_;
    
        // ============ Shares (ERC20) ============
    
        string public symbol;
        uint8 public decimals;
        string public name;
    
        uint256 public totalSupply;
        mapping(address => uint256) internal _SHARES_;
        mapping(address => mapping(address => uint256)) internal _ALLOWED_;
    
        // ================= Permit ======================
    
        bytes32 public DOMAIN_SEPARATOR;
        // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
        mapping(address => uint256) public nonces;
    
        // ============ Variables for Pricing ============
    
        uint256 public _LP_FEE_RATE_;
        IFeeRateModel public _MT_FEE_RATE_MODEL_;
        uint256 public _K_;
        uint256 public _I_;
    
    
        // ============ Helper Functions ============
    
        function getPMMState() public view returns (PMMPricing.PMMState memory state) {
            state.i = _I_;
            state.K = _K_;
            state.B = _BASE_RESERVE_;
            state.Q = _QUOTE_RESERVE_;
            state.B0 = 0; // will be calculated in adjustedTarget
            state.Q0 = 0;
            state.R = PMMPricing.RState.ABOVE_ONE;
            PMMPricing.adjustedTarget(state);
        }
    
        function getPMMStateForCall() 
            external 
            view 
            returns (
                uint256 i,
                uint256 K,
                uint256 B,
                uint256 Q,
                uint256 B0,
                uint256 Q0,
                uint256 R
            )
        {
            PMMPricing.PMMState memory state = getPMMState();
            i = state.i;
            K = state.K;
            B = state.B;
            Q = state.Q;
            B0 = state.B0;
            Q0 = state.Q0;
            R = uint256(state.R);
        }
    
        function getMidPrice() public view returns (uint256 midPrice) {
            return PMMPricing.getMidPrice(getPMMState());
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMVault.sol
    
    
    contract DVMVault is DVMStorage {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
    
        // ============ 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);
    
        // ============ View Functions ============
    
        function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve) {
            baseReserve = _BASE_RESERVE_;
            quoteReserve = _QUOTE_RESERVE_;
        }
    
        function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate) {
            lpFeeRate = _LP_FEE_RATE_;
            mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(user);
        }
    
        // ============ Asset In ============
    
        function getBaseInput() public view returns (uint256 input) {
            return _BASE_TOKEN_.balanceOf(address(this)).sub(uint256(_BASE_RESERVE_));
        }
    
        function getQuoteInput() public view returns (uint256 input) {
            return _QUOTE_TOKEN_.balanceOf(address(this)).sub(uint256(_QUOTE_RESERVE_));
        }
    
        // ============ TWAP UPDATE ===========
        
        function _twapUpdate() internal {
            uint32 blockTimestamp = uint32(block.timestamp % 2**32);
            uint32 timeElapsed = blockTimestamp - _BLOCK_TIMESTAMP_LAST_;
            if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {
                _BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;
            }
            _BLOCK_TIMESTAMP_LAST_ = blockTimestamp;
        }
    
        // ============ Set States ============
    
        function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
            require(baseReserve <= uint112(-1) && quoteReserve <= uint112(-1), "OVERFLOW");
            _BASE_RESERVE_ = uint112(baseReserve);
            _QUOTE_RESERVE_ = uint112(quoteReserve);
    
            if(_IS_OPEN_TWAP_) _twapUpdate();
        }
    
        function _sync() internal {
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            require(baseBalance <= uint112(-1) && quoteBalance <= uint112(-1), "OVERFLOW");
            if (baseBalance != _BASE_RESERVE_) {
                _BASE_RESERVE_ = uint112(baseBalance);
            }
            if (quoteBalance != _QUOTE_RESERVE_) {
                _QUOTE_RESERVE_ = uint112(quoteBalance);
            }
    
            if(_IS_OPEN_TWAP_) _twapUpdate();
        }
    
    
        function sync() external preventReentrant {
            _sync();
        }
    
        // ============ Asset Out ============
    
        function _transferBaseOut(address to, uint256 amount) internal {
            if (amount > 0) {
                _BASE_TOKEN_.safeTransfer(to, amount);
            }
        }
    
        function _transferQuoteOut(address to, uint256 amount) internal {
            if (amount > 0) {
                _QUOTE_TOKEN_.safeTransfer(to, amount);
            }
        }
    
        // ============ Shares (ERC20) ============
    
        /**
         * @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 <= _SHARES_[msg.sender], "BALANCE_NOT_ENOUGH");
    
            _SHARES_[msg.sender] = _SHARES_[msg.sender].sub(amount);
            _SHARES_[to] = _SHARES_[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 _SHARES_[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 <= _SHARES_[from], "BALANCE_NOT_ENOUGH");
            require(amount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
    
            _SHARES_[from] = _SHARES_[from].sub(amount);
            _SHARES_[to] = _SHARES_[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) {
            _approve(msg.sender, spender, amount);
            return true;
        }
    
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) private {
            _ALLOWED_[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
    
        /**
         * @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) internal {
            require(value > 1000, "MINT_INVALID");
            _SHARES_[user] = _SHARES_[user].add(value);
            totalSupply = totalSupply.add(value);
            emit Mint(user, value);
            emit Transfer(address(0), user, value);
        }
    
        function _burn(address user, uint256 value) internal {
            _SHARES_[user] = _SHARES_[user].sub(value);
            totalSupply = totalSupply.sub(value);
            emit Burn(user, value);
            emit Transfer(user, address(0), value);
        }
    
        // ============================ Permit ======================================
        
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external {
            require(deadline >= block.timestamp, "DODO_DVM_LP: EXPIRED");
            bytes32 digest = keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR,
                    keccak256(
                        abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)
                    )
                )
            );
            address recoveredAddress = ecrecover(digest, v, r, s);
            require(
                recoveredAddress != address(0) && recoveredAddress == owner,
                "DODO_DVM_LP: INVALID_SIGNATURE"
            );
            _approve(owner, spender, value);
        }
    }
    
    // File: contracts/intf/IDODOCallee.sol
    
    
    interface IDODOCallee {
        function DVMSellShareCall(
            address sender,
            uint256 burnShareAmount,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    
        function DVMFlashLoanCall(
            address sender,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    
        function DPPFlashLoanCall(
            address sender,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    
        function CPCancelCall(
            address sender,
            uint256 amount,
            bytes calldata data
        ) external;
    
    	function CPClaimBidCall(
            address sender,
            uint256 baseAmount,
            uint256 quoteAmount,
            bytes calldata data
        ) external;
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMTrader.sol
    
    
    
    contract DVMTrader is DVMVault {
        using SafeMath for uint256;
    
        // ============ Events ============
    
        event DODOSwap(
            address fromToken,
            address toToken,
            uint256 fromAmount,
            uint256 toAmount,
            address trader,
            address receiver
        );
    
        event DODOFlashLoan(
            address borrower,
            address assetTo,
            uint256 baseAmount,
            uint256 quoteAmount
        );
    
        // ============ Trade Functions ============
    
        function sellBase(address to)
            external
            preventReentrant
            returns (uint256 receiveQuoteAmount)
        {
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_));
            uint256 mtFee;
            (receiveQuoteAmount, mtFee) = querySellBase(tx.origin, baseInput);
    
            _transferQuoteOut(to, receiveQuoteAmount);
            _transferQuoteOut(_MAINTAINER_, mtFee);
            _setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this)));
    
            emit DODOSwap(
                address(_BASE_TOKEN_),
                address(_QUOTE_TOKEN_),
                baseInput,
                receiveQuoteAmount,
                msg.sender,
                to
            );
        }
    
        function sellQuote(address to)
            external
            preventReentrant
            returns (uint256 receiveBaseAmount)
        {
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_));
            uint256 mtFee;
            (receiveBaseAmount, mtFee) = querySellQuote(tx.origin, quoteInput);
    
            _transferBaseOut(to, receiveBaseAmount);
            _transferBaseOut(_MAINTAINER_, mtFee);
            _setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance);
    
            emit DODOSwap(
                address(_QUOTE_TOKEN_),
                address(_BASE_TOKEN_),
                quoteInput,
                receiveBaseAmount,
                msg.sender,
                to
            );
        }
    
        function flashLoan(
            uint256 baseAmount,
            uint256 quoteAmount,
            address assetTo,
            bytes calldata data
        ) external preventReentrant {
            _transferBaseOut(assetTo, baseAmount);
            _transferQuoteOut(assetTo, quoteAmount);
    
            if (data.length > 0)
                IDODOCallee(assetTo).DVMFlashLoanCall(msg.sender, baseAmount, quoteAmount, data);
    
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            
            // no input -> pure loss
            require(
                baseBalance >= _BASE_RESERVE_ || quoteBalance >= _QUOTE_RESERVE_,
                "FLASH_LOAN_FAILED"
            );
    
            // sell quote
            if (baseBalance < _BASE_RESERVE_) {
                uint256 quoteInput = quoteBalance.sub(uint256(_QUOTE_RESERVE_));
                (uint256 receiveBaseAmount, uint256 mtFee) = querySellQuote(tx.origin, quoteInput);
                require(uint256(_BASE_RESERVE_).sub(baseBalance) <= receiveBaseAmount, "FLASH_LOAN_FAILED");
    
                _transferBaseOut(_MAINTAINER_, mtFee);
                emit DODOSwap(
                    address(_QUOTE_TOKEN_),
                    address(_BASE_TOKEN_),
                    quoteInput,
                    receiveBaseAmount,
                    msg.sender,
                    assetTo
                );
            }
    
            // sell base
            if (quoteBalance < _QUOTE_RESERVE_) {
                uint256 baseInput = baseBalance.sub(uint256(_BASE_RESERVE_));
                (uint256 receiveQuoteAmount, uint256 mtFee) = querySellBase(tx.origin, baseInput);
                require(uint256(_QUOTE_RESERVE_).sub(quoteBalance) <= receiveQuoteAmount, "FLASH_LOAN_FAILED");
    
                _transferQuoteOut(_MAINTAINER_, mtFee);
                emit DODOSwap(
                    address(_BASE_TOKEN_),
                    address(_QUOTE_TOKEN_),
                    baseInput,
                    receiveQuoteAmount,
                    msg.sender,
                    assetTo
                );
            }
    
            _sync();
            
            emit DODOFlashLoan(msg.sender, assetTo, baseAmount, quoteAmount);
        }
    
        // ============ Query Functions ============
    
        function querySellBase(address trader, uint256 payBaseAmount)
            public
            view
            returns (uint256 receiveQuoteAmount, uint256 mtFee)
        {
            (receiveQuoteAmount, ) = PMMPricing.sellBaseToken(getPMMState(), payBaseAmount);
    
            uint256 lpFeeRate = _LP_FEE_RATE_;
            uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
            mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
            receiveQuoteAmount = receiveQuoteAmount
                .sub(DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate))
                .sub(mtFee);
        }
    
        function querySellQuote(address trader, uint256 payQuoteAmount)
            public
            view
            returns (uint256 receiveBaseAmount, uint256 mtFee)
        {
            (receiveBaseAmount, ) = PMMPricing.sellQuoteToken(getPMMState(), payQuoteAmount);
    
            uint256 lpFeeRate = _LP_FEE_RATE_;
            uint256 mtFeeRate = _MT_FEE_RATE_MODEL_.getFeeRate(trader);
            mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
            receiveBaseAmount = receiveBaseAmount
                .sub(DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate))
                .sub(mtFee);
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVMFunding.sol
    
    
    contract DVMFunding is DVMVault {
        // ============ Events ============
    
        event BuyShares(address to, uint256 increaseShares, uint256 totalShares);
    
        event SellShares(address payer, address to, uint256 decreaseShares, uint256 totalShares);
    
        // ============ Buy & Sell Shares ============
    
        // buy shares [round down]
        function buyShares(address to)
            external
            preventReentrant
            returns (
                uint256 shares,
                uint256 baseInput,
                uint256 quoteInput
            )
        {
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            uint256 baseReserve = _BASE_RESERVE_;
            uint256 quoteReserve = _QUOTE_RESERVE_;
    
            baseInput = baseBalance.sub(baseReserve);
            quoteInput = quoteBalance.sub(quoteReserve);
            require(baseInput > 0, "NO_BASE_INPUT");
    
            // Round down when withdrawing. Therefore, never be a situation occuring balance is 0 but totalsupply is not 0
            // But May Happen,reserve >0 But totalSupply = 0
            if (totalSupply == 0) {
                // case 1. initial supply
                require(baseBalance >= 10**3, "INSUFFICIENT_LIQUIDITY_MINED");
                shares = baseBalance; // 以免出现balance很大但shares很小的情况
            } else if (baseReserve > 0 && quoteReserve == 0) {
                // case 2. supply when quote reserve is 0
                shares = baseInput.mul(totalSupply).div(baseReserve);
            } else if (baseReserve > 0 && quoteReserve > 0) {
                // case 3. normal case
                uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
                uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
                uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio;
                shares = DecimalMath.mulFloor(totalSupply, mintRatio);
            }
            _mint(to, shares);
            _setReserve(baseBalance, quoteBalance);
            emit BuyShares(to, shares, _SHARES_[to]);
        }
    
        // sell shares [round down]
        function sellShares(
            uint256 shareAmount,
            address to,
            uint256 baseMinAmount,
            uint256 quoteMinAmount,
            bytes calldata data,
            uint256 deadline
        ) external preventReentrant returns (uint256 baseAmount, uint256 quoteAmount) {
            require(deadline >= block.timestamp, "TIME_EXPIRED");
            require(shareAmount <= _SHARES_[msg.sender], "DLP_NOT_ENOUGH");
            uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
            uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
            uint256 totalShares = totalSupply;
    
            baseAmount = baseBalance.mul(shareAmount).div(totalShares);
            quoteAmount = quoteBalance.mul(shareAmount).div(totalShares);
    
            require(
                baseAmount >= baseMinAmount && quoteAmount >= quoteMinAmount,
                "WITHDRAW_NOT_ENOUGH"
            );
    
            _burn(msg.sender, shareAmount);
            _transferBaseOut(to, baseAmount);
            _transferQuoteOut(to, quoteAmount);
            _sync();
    
            if (data.length > 0) {
                IDODOCallee(to).DVMSellShareCall(
                    msg.sender,
                    shareAmount,
                    baseAmount,
                    quoteAmount,
                    data
                );
            }
    
            emit SellShares(msg.sender, to, shareAmount, _SHARES_[msg.sender]);
        }
    }
    
    // File: contracts/DODOVendingMachine/impl/DVM.sol
    
    
    
    /**
     * @title DODO VendingMachine
     * @author DODO Breeder
     *
     * @notice DODOVendingMachine initialization
     */
    contract DVM is DVMTrader, DVMFunding {
        function init(
            address maintainer,
            address baseTokenAddress,
            address quoteTokenAddress,
            uint256 lpFeeRate,
            address mtFeeRateModel,
            uint256 i,
            uint256 k,
            bool isOpenTWAP
        ) external {
            require(!_DVM_INITIALIZED_, "DVM_INITIALIZED");
            _DVM_INITIALIZED_ = true;
            
            require(baseTokenAddress != quoteTokenAddress, "BASE_QUOTE_CAN_NOT_BE_SAME");
            _BASE_TOKEN_ = IERC20(baseTokenAddress);
            _QUOTE_TOKEN_ = IERC20(quoteTokenAddress);
    
            require(i > 0 && i <= 10**36);
            _I_ = i;
    
            require(k <= 10**18);
            _K_ = k;
    
            _LP_FEE_RATE_ = lpFeeRate;
            _MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel);
            _MAINTAINER_ = maintainer;
    
            _IS_OPEN_TWAP_ = isOpenTWAP;
            if(isOpenTWAP) _BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2**32);
    
            string memory connect = "_";
            string memory suffix = "DLP";
    
            name = string(abi.encodePacked(suffix, connect, addressToShortString(address(this))));
            symbol = "DLP";
            decimals = _BASE_TOKEN_.decimals();
    
            // ============================== Permit ====================================
            uint256 chainId;
            assembly {
                chainId := chainid()
            }
            DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                    0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                    keccak256(bytes(name)),
                    keccak256(bytes("1")),
                    chainId,
                    address(this)
                )
            );
            // ==========================================================================
        }
    
        function addressToShortString(address _addr) public pure returns (string memory) {
            bytes32 value = bytes32(uint256(_addr));
            bytes memory alphabet = "0123456789abcdef";
    
            bytes memory str = new bytes(8);
            for (uint256 i = 0; i < 4; i++) {
                str[i * 2] = alphabet[uint8(value[i + 12] >> 4)];
                str[1 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];
            }
            return string(str);
        }
    
        // ============ Version Control ============
        
        function version() external pure returns (string memory) {
            return "DVM 1.0.2";
        }
    }