ETH Price: $2,542.07 (+0.11%)
Gas: 0.34 Gwei

Transaction Decoder

Block:
16207800 at Dec-17-2022 11:48:35 PM +UTC
Transaction Fee:
0.013055320575225742 ETH $33.19
Gas Used:
974,534 Gas / 13.396475213 Gwei

Emitted Events:

146 InitializableERC20.Transfer( from=[Sender] 0x53c716ee18bec091c0b84594723ab912f9f0ce8a, to=ERC20MineV3, amount=99989999537037061649960 )
147 0xa901208f30ee900b166f59ad0e6157d837245b91.0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0( 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000e572c6bfb069dcec1a0863137ceed92a01b257d8 )
148 InitializableERC20.Transfer( from=ERC20MineV3, to=0xa901208f30ee900b166f59ad0e6157d837245b91, amount=99989999537037061649960 )
149 0xa901208f30ee900b166f59ad0e6157d837245b91.0xed9a567f42e0ef8986598c5257db7be662f4eaae3892286b03c5ba3a1ddf399b( 0xed9a567f42e0ef8986598c5257db7be662f4eaae3892286b03c5ba3a1ddf399b, 00000000000000000000000000000000000000000000152c77ff1936771bf628, 00000000000000000000000000000000000000000000152c77ff1936771bf628, 00000000000000000000000000000000000000000000152c77ff1936771bf628 )
150 ERC20MineV3.NewRewardToken( i=0, rewardToken=InitializableERC20 )
151 ERC20MineV3.OwnershipTransferred( previousOwner=[Receiver] DODOMineV3Proxy, newOwner=[Sender] 0x53c716ee18bec091c0b84594723ab912f9f0ce8a )
152 DODOMineV3Registry.NewMineV3( mine=ERC20MineV3, stakeToken=InitializableERC20, isLpToken=False )
153 DODOMineV3Proxy.CreateMineV3( account=[Sender] 0x53c716ee18bec091c0b84594723ab912f9f0ce8a, mineV3=ERC20MineV3 )

Account State Difference:

  Address   Before After State Difference Code
0x53C716ee...2f9f0ce8a
(Fake_Phishing462531)
0.061401560142874957 Eth
Nonce: 514
0.048346239567649215 Eth
Nonce: 515
0.013055320575225742
0x5E5a7b76...BdbA80B88
0xa901208F...837245b91
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 1317963768324164036662512590366944763043070158053469776110346941890916866494192493178704388962420821671928444092412453538307351441504939574690621593004905797903334564788987976418482784647804352212374467180470255782123723438365256074133589576604993252276605207265214314141066813397655626752908203918654463889375550285342776762158204249997140529204299597437179849411226849762426650290883426365851576831600839279789612611255821796904857546890804879527231110862498744840544065208335668341949057297349581099310334216883855846981141864383059602799100396758238130802583306209462368634063235058554928824474182474095798226637180749293805325028060668605265552409257562064625886384170683558980766980089556712501504328655974306863230863936502560482062730655623333252066592074944424116963276112410578371698656428226707203421146141365273071823964499186009672688317516887294833289908142746080763709163277499594472149046409944103326238761440655945603512280934635260285518145680992818064099998563346977630583137164793122526921858021757481270899550871285419358841515941747714266535095383124871089069506688785772284428465412747400480537618010416789223754561256614901606314936635102766562329192875632661275032697875311816828385452441109190408586210102080752191275304683981531593591129030435371253385428595991996298407734451437451141736226021282793938218760778677855802145736632947255559575424095950301967889399878094400764363571337225763395966516747960822221673757758994325319924383955148514475300661010796282042468672037373851794567152479908647269382170494237173957810049571678348121895838502459277244746229418279980869563717931860755150880365889836402788051353258379697894888981132563410222315543695561553004772094076874936040505953599669733984737574965819875912707335876838337251059254349916189832881069987944394645307711705929976396373345110664222872912137181829890862446026615929673719224370880640329926110403056264614611478606662732420575157372960321839922363138098523329879853940773926423308825215759781698395763983887483158996402302343455693014748291851566918088741158793516607318271693457714936022842930945402283459686049186702100806051984144020526537380227526098197838761068804502833119073778520829707090308650579695374455018641765193272072880167795040976710821700865209002596770567995141287452876404530358602221274515484963404005100916529025452377711647223579837535269098613088470806205452516369693994117970383270709488313773393457110741379172695938636645219173250477784900384010293501451316968015024421461672050912739817238646672886794945113010494561473213145944041688435817373544946902529173045445913630503960943755004204260326462208251561927035887458218321335120287596166979681925641182800656427294945507450786918050561333668576886080660176651449154462172275449322482187604801875001262929019160669259432135181971116223414617172883356791917600583160422723963214867342876979063597136173183662595271277877303701162155331936663747275006892482981739215653712281757019426862562347571968777474430887176742847195370298031593293687409804591463616369509360585138958586292567650640907062002143642930472429958546083319684172100441658629456057148534918715298059689139292605309600436611475155014691240635250538640327005079242982000258774945298338570407540410903770635303052258865520174073320728174323446403313874271738045750745157292334236043821103696508308222339771648388127610654211784183719629583444588249106673829741739244680994444461570117844851981280197673683320862496924240081121129116716878525159661994685644864488068251959825435713379089611829082400448902385355014464458247603621533965183510318279495147980558632821438003437303758712124799775375924673724789561831558871270372172480734883978111001817460527636410386737246322003035925057728768632638392141980050184549001910433184205347469430429779799829198272366887997419205747269851266182644480001856026683964999980931957794381618800495394128263719035140981295007882256455241245606578500086347497284872591373268805522682137642091498134103608584981443653358224887171606461935895317358504497133261310612293162546310055428105100496631606690230578721167186813224970360243191853479610285350447443964456309709993279304017274882566321188014252809531901529735681305952528017549325290450940232839477635654891998878806513842059317112681298387497025183224560266943102261429269222808524675668925018936256660117996324751256314679837971908901156130992136118580784711619993503245675319167884073090924046121244618698899835343849985558697824325353932653271106575888712857157356635801321845116195108167131890226473299078287075645534979253724295856909933288508396577401187067536125479581153988829605712008378676026870245431931160382972669264847716412890663917388076612428442484123432495935048656856517564119257734046175651267768279449160986433997060802748801019894194457304487736333046787855863637003029911379049598588292083127687103406634579634412972361680821092328244052019
0xc1454530...c0Eea36fb
0xE572c6bf...A01b257d8
0 Eth
Nonce: 0
0 Eth
Nonce: 2
From: 0 To: 497590261154554171967157698864953962584784632416744400924011105071045082886811546614300271230390500595686387
(bloXroute: Max Profit Builder)
1.766280986099930707 Eth1.767742787099930707 Eth0.001461801
0xf8ab09b3...88E852195

Execution Trace

DODOMineV3Proxy.createDODOMineV3( stakeToken=0xc145453081a1577E9A14294dbF093a1c0Eea36fb, isLpToken=False, rewardTokens=[0xc145453081a1577E9A14294dbF093a1c0Eea36fb], rewardPerBlock=[1504629629629630], startBlock=[16207819], endBlock=[82662711] ) => ( newMineV3=0xE572c6bfb069dcEC1a0863137cEEd92A01b257d8 )
  • CloneFactory.clone( prototype=0xD57f29B297e33c977e2186a751414BFeD6A38c5a ) => ( proxy=0xE572c6bfb069dcEC1a0863137cEEd92A01b257d8 )
    • ERC20MineV3.3d602d80( )
    • ERC20MineV3.init( owner=0x0d9685D4037580F68D9F77B08971f17E1000bBdc, token=0xc145453081a1577E9A14294dbF093a1c0Eea36fb )
      • ERC20MineV3.init( owner=0x0d9685D4037580F68D9F77B08971f17E1000bBdc, token=0xc145453081a1577E9A14294dbF093a1c0Eea36fb )
      • DODOApproveProxy.claimTokens( token=0xc145453081a1577E9A14294dbF093a1c0Eea36fb, who=0x53C716ee18bEc091c0B84594723Ab912f9f0ce8a, dest=0xE572c6bfb069dcEC1a0863137cEEd92A01b257d8, amount=99989999537037061649960 )
        • DODOApprove.claimTokens( token=0xc145453081a1577E9A14294dbF093a1c0Eea36fb, who=0x53C716ee18bEc091c0B84594723Ab912f9f0ce8a, dest=0xE572c6bfb069dcEC1a0863137cEEd92A01b257d8, amount=99989999537037061649960 )
          • InitializableERC20.transferFrom( from=0x53C716ee18bEc091c0B84594723Ab912f9f0ce8a, to=0xE572c6bfb069dcEC1a0863137cEEd92A01b257d8, amount=99989999537037061649960 ) => ( True )
            • InitializableERC20.transferFrom( from=0x53C716ee18bEc091c0B84594723Ab912f9f0ce8a, to=0xE572c6bfb069dcEC1a0863137cEEd92A01b257d8, amount=99989999537037061649960 ) => ( True )
            • ERC20MineV3.addRewardToken( rewardToken=0xc145453081a1577E9A14294dbF093a1c0Eea36fb, rewardPerBlock=1504629629629630, startBlock=16207819, endBlock=82662711 )
              • ERC20MineV3.addRewardToken( rewardToken=0xc145453081a1577E9A14294dbF093a1c0Eea36fb, rewardPerBlock=1504629629629630, startBlock=16207819, endBlock=82662711 )
                • 0xa901208f30ee900b166f59ad0e6157d837245b91.60806040( )
                • InitializableERC20.transfer( to=0xa901208F30ee900B166F59AD0E6157d837245b91, amount=99989999537037061649960 ) => ( True )
                  • InitializableERC20.transfer( to=0xa901208F30ee900B166F59AD0E6157d837245b91, amount=99989999537037061649960 ) => ( True )
                  • 0xa901208f30ee900b166f59ad0e6157d837245b91.CALL( )
                    • InitializableERC20.balanceOf( owner=0xa901208F30ee900B166F59AD0E6157d837245b91 ) => ( balance=99989999537037061649960 )
                      • InitializableERC20.balanceOf( owner=0xa901208F30ee900B166F59AD0E6157d837245b91 ) => ( balance=99989999537037061649960 )
                      • ERC20MineV3.directTransferOwnership( newOwner=0x53C716ee18bEc091c0B84594723Ab912f9f0ce8a )
                      • DODOMineV3Registry.addMineV3( mine=0xE572c6bfb069dcEC1a0863137cEEd92A01b257d8, isLpToken=False, stakeToken=0xc145453081a1577E9A14294dbF093a1c0Eea36fb )
                        File 1 of 9: DODOMineV3Proxy
                        // 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/intf/IDODOApprove.sol
                        
                        interface IDODOApprove {
                            function claimTokens(address token,address who,address dest,uint256 amount) external;
                            function getDODOProxy() external view returns (address);
                        }
                        
                        // File: contracts/SmartRoute/DODOApproveProxy.sol
                        
                        
                        interface IDODOApproveProxy {
                            function isAllowedProxy(address _proxy) external view returns (bool);
                            function claimTokens(address token,address who,address dest,uint256 amount) external;
                        }
                        
                        /**
                         * @title DODOApproveProxy
                         * @author DODO Breeder
                         *
                         * @notice Allow different version dodoproxy to claim from DODOApprove
                         */
                        contract DODOApproveProxy is InitializableOwnable {
                            
                            // ============ Storage ============
                            uint256 private constant _TIMELOCK_DURATION_ = 3 days;
                            mapping (address => bool) public _IS_ALLOWED_PROXY_;
                            uint256 public _TIMELOCK_;
                            address public _PENDING_ADD_DODO_PROXY_;
                            address public immutable _DODO_APPROVE_;
                        
                            // ============ Modifiers ============
                            modifier notLocked() {
                                require(
                                    _TIMELOCK_ <= block.timestamp,
                                    "SetProxy is timelocked"
                                );
                                _;
                            }
                        
                            constructor(address dodoApporve) public {
                                _DODO_APPROVE_ = dodoApporve;
                            }
                        
                            function init(address owner, address[] memory proxies) external {
                                initOwner(owner);
                                for(uint i = 0; i < proxies.length; i++) 
                                    _IS_ALLOWED_PROXY_[proxies[i]] = true;
                            }
                        
                            function unlockAddProxy(address newDodoProxy) public onlyOwner {
                                _TIMELOCK_ = block.timestamp + _TIMELOCK_DURATION_;
                                _PENDING_ADD_DODO_PROXY_ = newDodoProxy;
                            }
                        
                            function lockAddProxy() public onlyOwner {
                               _PENDING_ADD_DODO_PROXY_ = address(0);
                               _TIMELOCK_ = 0;
                            }
                        
                        
                            function addDODOProxy() external onlyOwner notLocked() {
                                _IS_ALLOWED_PROXY_[_PENDING_ADD_DODO_PROXY_] = true;
                                lockAddProxy();
                            }
                        
                            function removeDODOProxy (address oldDodoProxy) public onlyOwner {
                                _IS_ALLOWED_PROXY_[oldDodoProxy] = false;
                            }
                            
                            function claimTokens(
                                address token,
                                address who,
                                address dest,
                                uint256 amount
                            ) external {
                                require(_IS_ALLOWED_PROXY_[msg.sender], "DODOApproveProxy:Access restricted");
                                IDODOApprove(_DODO_APPROVE_).claimTokens(
                                    token,
                                    who,
                                    dest,
                                    amount
                                );
                            }
                        
                            function isAllowedProxy(address _proxy) external view returns (bool) {
                                return _IS_ALLOWED_PROXY_[_proxy];
                            }
                        }
                        
                        // File: contracts/lib/Ownable.sol
                        
                        /**
                         * @title Ownable
                         * @author DODO Breeder
                         *
                         * @notice Ownership related functions
                         */
                        contract Ownable {
                            address public _OWNER_;
                            address public _NEW_OWNER_;
                        
                            // ============ Events ============
                        
                            event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
                        
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                        
                            // ============ Modifiers ============
                        
                            modifier onlyOwner() {
                                require(msg.sender == _OWNER_, "NOT_OWNER");
                                _;
                            }
                        
                            // ============ Functions ============
                        
                            constructor() internal {
                                _OWNER_ = msg.sender;
                                emit OwnershipTransferred(address(0), _OWNER_);
                            }
                        
                            function transferOwnership(address newOwner) external virtual onlyOwner {
                                emit OwnershipTransferPrepared(_OWNER_, newOwner);
                                _NEW_OWNER_ = newOwner;
                            }
                        
                            function claimOwnership() external {
                                require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                                emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                                _OWNER_ = _NEW_OWNER_;
                                _NEW_OWNER_ = address(0);
                            }
                        }
                        
                        // File: contracts/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/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/DODOToken/DODOMineV3/RewardVault.sol
                        
                        
                        interface IRewardVault {
                            function reward(address to, uint256 amount) external;
                            function withdrawLeftOver(address to, uint256 amount) external; 
                            function syncValue() external;
                            function _TOTAL_REWARD_() external view returns(uint256);
                        }
                        
                        contract RewardVault is Ownable {
                            using SafeERC20 for IERC20;
                            using SafeMath for uint256;
                        
                            uint256 public _REWARD_RESERVE_;
                            uint256 public _TOTAL_REWARD_;
                            address public _REWARD_TOKEN_;
                        
                            // ============ Event =============
                            event DepositReward(uint256 totalReward, uint256 inputReward, uint256 rewardReserve);
                        
                            constructor(address _rewardToken) public {
                                _REWARD_TOKEN_ = _rewardToken;
                            }
                        
                            function reward(address to, uint256 amount) external onlyOwner {
                                require(_REWARD_RESERVE_ >= amount, "VAULT_NOT_ENOUGH");
                                _REWARD_RESERVE_ = _REWARD_RESERVE_.sub(amount);
                                IERC20(_REWARD_TOKEN_).safeTransfer(to, amount);
                            }
                        
                            function withdrawLeftOver(address to,uint256 amount) external onlyOwner {
                                require(_REWARD_RESERVE_ >= amount, "VAULT_NOT_ENOUGH");
                                _REWARD_RESERVE_ = _REWARD_RESERVE_.sub(amount);
                                IERC20(_REWARD_TOKEN_).safeTransfer(to, amount);
                            }
                        
                            function syncValue() external {
                                uint256 rewardBalance = IERC20(_REWARD_TOKEN_).balanceOf(address(this));
                                uint256 rewardInput = rewardBalance.sub(_REWARD_RESERVE_);
                        
                                _TOTAL_REWARD_ = _TOTAL_REWARD_.add(rewardInput);
                                _REWARD_RESERVE_ = rewardBalance;
                        
                                emit DepositReward(_TOTAL_REWARD_, rewardInput, _REWARD_RESERVE_);
                            }
                        }
                        
                        // File: contracts/Factory/Registries/DODOMineV3Registry.sol
                        
                        
                        interface IDODOMineV3Registry {
                            function addMineV3(
                                address mine,
                                bool isLpToken,
                                address stakeToken
                            ) external;
                        }
                        
                        /**
                         * @title DODOMineV3 Registry
                         * @author DODO Breeder
                         *
                         * @notice Register DODOMineV3 Pools 
                         */
                        contract DODOMineV3Registry is InitializableOwnable, IDODOMineV3Registry {
                        
                            mapping (address => bool) public isAdminListed;
                            
                            // ============ Registry ============
                            // minePool -> stakeToken
                            mapping(address => address) public _MINE_REGISTRY_;
                            // lpToken -> minePool
                            mapping(address => address[]) public _LP_REGISTRY_;
                            // singleToken -> minePool
                            mapping(address => address[]) public _SINGLE_REGISTRY_;
                        
                        
                            // ============ Events ============
                            event NewMineV3(address mine, address stakeToken, bool isLpToken);
                            event RemoveMineV3(address mine, address stakeToken);
                            event addAdmin(address admin);
                            event removeAdmin(address admin);
                        
                        
                            function addMineV3(
                                address mine,
                                bool isLpToken,
                                address stakeToken
                            ) override external {
                                require(isAdminListed[msg.sender], "ACCESS_DENIED");
                                _MINE_REGISTRY_[mine] = stakeToken;
                                if(isLpToken) {
                                    _LP_REGISTRY_[stakeToken].push(mine);
                                }else {
                                    _SINGLE_REGISTRY_[stakeToken].push(mine);
                                }
                        
                                emit NewMineV3(mine, stakeToken, isLpToken);
                            }
                        
                            // ============ Admin Operation Functions ============
                        
                            function removeMineV3(
                                address mine,
                                bool isLpToken,
                                address stakeToken
                            ) external onlyOwner {
                                _MINE_REGISTRY_[mine] = address(0);
                                if(isLpToken) {
                                    uint256 len = _LP_REGISTRY_[stakeToken].length;
                                    for (uint256 i = 0; i < len; i++) {
                                        if (mine == _LP_REGISTRY_[stakeToken][i]) {
                                            if(i != len - 1) {
                                                _LP_REGISTRY_[stakeToken][i] = _LP_REGISTRY_[stakeToken][len - 1];
                                            }
                                            _LP_REGISTRY_[stakeToken].pop();
                                            break;
                                        }
                                    }
                                }else {
                                    uint256 len = _SINGLE_REGISTRY_[stakeToken].length;
                                    for (uint256 i = 0; i < len; i++) {
                                        if (mine == _SINGLE_REGISTRY_[stakeToken][i]) {
                                            if(i != len - 1) {
                                                _SINGLE_REGISTRY_[stakeToken][i] = _SINGLE_REGISTRY_[stakeToken][len - 1];
                                            }
                                            _SINGLE_REGISTRY_[stakeToken].pop();
                                            break;
                                        }
                                    }
                                }
                        
                                emit RemoveMineV3(mine, stakeToken);
                            }
                        
                            function addAdminList (address contractAddr) external onlyOwner {
                                isAdminListed[contractAddr] = true;
                                emit addAdmin(contractAddr);
                            }
                        
                            function removeAdminList (address contractAddr) external onlyOwner {
                                isAdminListed[contractAddr] = false;
                                emit removeAdmin(contractAddr);
                            }
                        }
                        
                        // File: contracts/lib/CloneFactory.sol
                        
                        interface ICloneFactory {
                            function clone(address prototype) external returns (address proxy);
                        }
                        
                        // introduction of proxy mode design: https://docs.openzeppelin.com/upgrades/2.8/
                        // minimum implementation of transparent proxy: https://eips.ethereum.org/EIPS/eip-1167
                        
                        contract CloneFactory is ICloneFactory {
                            function clone(address prototype) external override returns (address proxy) {
                                bytes20 targetBytes = bytes20(prototype);
                                assembly {
                                    let clone := mload(0x40)
                                    mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                                    mstore(add(clone, 0x14), targetBytes)
                                    mstore(
                                        add(clone, 0x28),
                                        0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
                                    )
                                    proxy := create(0, clone, 0x37)
                                }
                                return proxy;
                            }
                        }
                        
                        // File: contracts/SmartRoute/proxies/DODOMineV3Proxy.sol
                        
                        interface IMineV3 {
                            function init(address owner, address token) external;
                        
                            function addRewardToken(
                                address rewardToken,
                                uint256 rewardPerBlock,
                                uint256 startBlock,
                                uint256 endBlock
                            ) external;
                        
                            function directTransferOwnership(address newOwner) external;
                        
                            function getVaultByRewardToken(address rewardToken) external view returns(address);
                        }
                        
                        /**
                         * @title DODOMineV3 Proxy
                         * @author DODO Breeder
                         *
                         * @notice Create And Register DODOMineV3 Contracts 
                         */
                        contract DODOMineV3Proxy is InitializableOwnable {
                            using SafeMath for uint256;
                            // ============ Templates ============
                        
                            address public immutable _CLONE_FACTORY_;
                            address public immutable _DODO_APPROVE_PROXY_;
                            address public immutable _DODO_MINEV3_REGISTRY_;
                            address public _MINEV3_TEMPLATE_;
                        
                        
                            // ============ Events ============
                            event DepositRewardToVault(address mine, address rewardToken, uint256 amount);
                            event DepositRewardToMine(address mine, address rewardToken, uint256 amount);
                            event CreateMineV3(address account, address mineV3);
                            event ChangeMineV3Template(address mineV3);
                        
                            constructor(
                                address cloneFactory,
                                address mineTemplate,
                                address dodoApproveProxy,
                                address dodoMineV3Registry
                            ) public {
                                _CLONE_FACTORY_ = cloneFactory;
                                _MINEV3_TEMPLATE_ = mineTemplate;
                                _DODO_APPROVE_PROXY_ = dodoApproveProxy;
                                _DODO_MINEV3_REGISTRY_ = dodoMineV3Registry;
                            }
                        
                            // ============ Functions ============
                        
                            function createDODOMineV3(
                                address stakeToken,
                                bool isLpToken,
                                address[] memory rewardTokens,
                                uint256[] memory rewardPerBlock,
                                uint256[] memory startBlock,
                                uint256[] memory endBlock
                            ) external returns (address newMineV3) {
                                require(rewardTokens.length > 0, "REWARD_EMPTY");
                                require(rewardTokens.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH");
                                require(startBlock.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH");
                                require(endBlock.length == rewardPerBlock.length, "REWARD_PARAM_NOT_MATCH");
                        
                                newMineV3 = ICloneFactory(_CLONE_FACTORY_).clone(_MINEV3_TEMPLATE_);
                        
                                IMineV3(newMineV3).init(address(this), stakeToken);
                        
                                for(uint i = 0; i<rewardTokens.length; i++) {
                                    uint256 rewardAmount = rewardPerBlock[i].mul(endBlock[i].sub(startBlock[i]));
                                    IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(rewardTokens[i], msg.sender, newMineV3, rewardAmount);
                                    IMineV3(newMineV3).addRewardToken(
                                        rewardTokens[i],
                                        rewardPerBlock[i],
                                        startBlock[i],
                                        endBlock[i]
                                    );
                                }
                        
                                IMineV3(newMineV3).directTransferOwnership(msg.sender);
                        
                                IDODOMineV3Registry(_DODO_MINEV3_REGISTRY_).addMineV3(newMineV3, isLpToken, stakeToken);
                        
                                emit CreateMineV3(msg.sender, newMineV3);
                            }
                        
                            function depositRewardToVault(
                                address mineV3,
                                address rewardToken,
                                uint256 amount
                            ) external {    
                                address rewardVault = IMineV3(mineV3).getVaultByRewardToken(rewardToken);
                                IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(rewardToken, msg.sender, rewardVault, amount);
                                IRewardVault(rewardVault).syncValue();
                        
                                emit DepositRewardToVault(mineV3,rewardToken,amount);
                            }
                        
                            function depositRewardToMine(
                                address mineV3,
                                address rewardToken,
                                uint256 amount
                            ) external {
                                require(mineV3 != address(0), "MINE_EMPTY");
                                IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(rewardToken, msg.sender, mineV3, amount);
                        
                                emit DepositRewardToMine(mineV3,rewardToken,amount);
                            }
                        
                            // ============ Admin Operation Functions ============
                            
                            function updateMineV3Template(address _newMineV3Template) external onlyOwner {
                                _MINEV3_TEMPLATE_ = _newMineV3Template;
                                emit ChangeMineV3Template(_newMineV3Template);
                            }
                        }

                        File 2 of 9: ERC20MineV3
                        // File: contracts/intf/IERC20.sol
                        
                        // This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
                        // SPDX-License-Identifier: MIT
                        
                        pragma solidity 0.6.9;
                        pragma experimental ABIEncoderV2;
                        
                        /**
                         * @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/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/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/InitializableOwnable.sol
                        
                        /**
                         * @title Ownable
                         * @author DODO Breeder
                         *
                         * @notice Ownership related functions
                         */
                        contract InitializableOwnable {
                            address public _OWNER_;
                            address public _NEW_OWNER_;
                            bool internal _INITIALIZED_;
                        
                            // ============ Events ============
                        
                            event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
                        
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                        
                            // ============ Modifiers ============
                        
                            modifier notInitialized() {
                                require(!_INITIALIZED_, "DODO_INITIALIZED");
                                _;
                            }
                        
                            modifier onlyOwner() {
                                require(msg.sender == _OWNER_, "NOT_OWNER");
                                _;
                            }
                        
                            // ============ Functions ============
                        
                            function initOwner(address newOwner) public notInitialized {
                                _INITIALIZED_ = true;
                                _OWNER_ = newOwner;
                            }
                        
                            function transferOwnership(address newOwner) public onlyOwner {
                                emit OwnershipTransferPrepared(_OWNER_, newOwner);
                                _NEW_OWNER_ = newOwner;
                            }
                        
                            function claimOwnership() public {
                                require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                                emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                                _OWNER_ = _NEW_OWNER_;
                                _NEW_OWNER_ = address(0);
                            }
                        }
                        
                        // File: contracts/lib/Ownable.sol
                        
                        /**
                         * @title Ownable
                         * @author DODO Breeder
                         *
                         * @notice Ownership related functions
                         */
                        contract Ownable {
                            address public _OWNER_;
                            address public _NEW_OWNER_;
                        
                            // ============ Events ============
                        
                            event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
                        
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                        
                            // ============ Modifiers ============
                        
                            modifier onlyOwner() {
                                require(msg.sender == _OWNER_, "NOT_OWNER");
                                _;
                            }
                        
                            // ============ Functions ============
                        
                            constructor() internal {
                                _OWNER_ = msg.sender;
                                emit OwnershipTransferred(address(0), _OWNER_);
                            }
                        
                            function transferOwnership(address newOwner) external virtual onlyOwner {
                                emit OwnershipTransferPrepared(_OWNER_, newOwner);
                                _NEW_OWNER_ = newOwner;
                            }
                        
                            function claimOwnership() external {
                                require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                                emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                                _OWNER_ = _NEW_OWNER_;
                                _NEW_OWNER_ = address(0);
                            }
                        }
                        
                        // File: contracts/DODOToken/DODOMineV3/RewardVault.sol
                        
                        
                        interface IRewardVault {
                            function reward(address to, uint256 amount) external;
                            function withdrawLeftOver(address to, uint256 amount) external; 
                            function syncValue() external;
                            function _TOTAL_REWARD_() external view returns(uint256);
                        }
                        
                        contract RewardVault is Ownable {
                            using SafeERC20 for IERC20;
                            using SafeMath for uint256;
                        
                            uint256 public _REWARD_RESERVE_;
                            uint256 public _TOTAL_REWARD_;
                            address public _REWARD_TOKEN_;
                        
                            // ============ Event =============
                            event DepositReward(uint256 totalReward, uint256 inputReward, uint256 rewardReserve);
                        
                            constructor(address _rewardToken) public {
                                _REWARD_TOKEN_ = _rewardToken;
                            }
                        
                            function reward(address to, uint256 amount) external onlyOwner {
                                require(_REWARD_RESERVE_ >= amount, "VAULT_NOT_ENOUGH");
                                _REWARD_RESERVE_ = _REWARD_RESERVE_.sub(amount);
                                IERC20(_REWARD_TOKEN_).safeTransfer(to, amount);
                            }
                        
                            function withdrawLeftOver(address to,uint256 amount) external onlyOwner {
                                require(_REWARD_RESERVE_ >= amount, "VAULT_NOT_ENOUGH");
                                _REWARD_RESERVE_ = _REWARD_RESERVE_.sub(amount);
                                IERC20(_REWARD_TOKEN_).safeTransfer(to, amount);
                            }
                        
                            function syncValue() external {
                                uint256 rewardBalance = IERC20(_REWARD_TOKEN_).balanceOf(address(this));
                                uint256 rewardInput = rewardBalance.sub(_REWARD_RESERVE_);
                        
                                _TOTAL_REWARD_ = _TOTAL_REWARD_.add(rewardInput);
                                _REWARD_RESERVE_ = rewardBalance;
                        
                                emit DepositReward(_TOTAL_REWARD_, rewardInput, _REWARD_RESERVE_);
                            }
                        }
                        
                        // File: contracts/DODOToken/DODOMineV3/BaseMine.sol
                        
                        
                        
                        contract BaseMine is InitializableOwnable {
                            using SafeERC20 for IERC20;
                            using SafeMath for uint256;
                        
                            // ============ Storage ============
                        
                            struct RewardTokenInfo {
                                address rewardToken;
                                uint256 startBlock;
                                uint256 endBlock;
                                address rewardVault;
                                uint256 rewardPerBlock;
                                uint256 accRewardPerShare;
                                uint256 lastRewardBlock;
                                uint256 workThroughReward;
                                uint256 lastFlagBlock;
                                mapping(address => uint256) userRewardPerSharePaid;
                                mapping(address => uint256) userRewards;
                            }
                        
                            RewardTokenInfo[] public rewardTokenInfos;
                        
                            uint256 internal _totalSupply;
                            mapping(address => uint256) internal _balances;
                        
                            // ============ Event =============
                        
                            event Claim(uint256 indexed i, address indexed user, uint256 reward);
                            event UpdateReward(uint256 indexed i, uint256 rewardPerBlock);
                            event UpdateEndBlock(uint256 indexed i, uint256 endBlock);
                            event NewRewardToken(uint256 indexed i, address rewardToken);
                            event RemoveRewardToken(address rewardToken);
                            event WithdrawLeftOver(address owner, uint256 i);
                        
                            // ============ View  ============
                        
                            function getPendingReward(address user, uint256 i) public view returns (uint256) {
                                require(i<rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                uint256 accRewardPerShare = rt.accRewardPerShare;
                                if (rt.lastRewardBlock != block.number) {
                                    accRewardPerShare = _getAccRewardPerShare(i);
                                }
                                return
                                    DecimalMath.mulFloor(
                                        balanceOf(user), 
                                        accRewardPerShare.sub(rt.userRewardPerSharePaid[user])
                                    ).add(rt.userRewards[user]);
                            }
                        
                            function getPendingRewardByToken(address user, address rewardToken) external view returns (uint256) {
                                return getPendingReward(user, getIdByRewardToken(rewardToken));
                            }
                        
                            function totalSupply() public view returns (uint256) {
                                return _totalSupply;
                            }
                        
                            function balanceOf(address user) public view returns (uint256) {
                                return _balances[user];
                            }
                        
                            function getRewardTokenById(uint256 i) external view returns (address) {
                                require(i<rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                RewardTokenInfo memory rt = rewardTokenInfos[i];
                                return rt.rewardToken;
                            }
                        
                            function getIdByRewardToken(address rewardToken) public view returns(uint256) {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    if (rewardToken == rewardTokenInfos[i].rewardToken) {
                                        return i;
                                    }
                                }
                                require(false, "DODOMineV3: TOKEN_NOT_FOUND");
                            }
                        
                            function getRewardNum() external view returns(uint256) {
                                return rewardTokenInfos.length;
                            }
                        
                            function getVaultByRewardToken(address rewardToken) public view returns(address) {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    if (rewardToken == rewardTokenInfos[i].rewardToken) {
                                        return rewardTokenInfos[i].rewardVault;
                                    }
                                }
                                require(false, "DODOMineV3: TOKEN_NOT_FOUND");
                            }
                        
                            function getVaultDebtByRewardToken(address rewardToken) public view returns(uint256) {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    if (rewardToken == rewardTokenInfos[i].rewardToken) {
                                        uint256 totalDepositReward = IRewardVault(rewardTokenInfos[i].rewardVault)._TOTAL_REWARD_();
                                        uint256 gap = rewardTokenInfos[i].endBlock.sub(rewardTokenInfos[i].lastFlagBlock);
                                        uint256 totalReward = rewardTokenInfos[i].workThroughReward.add(gap.mul(rewardTokenInfos[i].rewardPerBlock));
                                        if(totalDepositReward >= totalReward) {
                                            return 0;
                                        }else {
                                            return totalReward.sub(totalDepositReward);
                                        }
                                    }
                                }
                                require(false, "DODOMineV3: TOKEN_NOT_FOUND");
                            }
                        
                            // ============ Claim ============
                        
                            function claimReward(uint256 i) public {
                                require(i<rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                _updateReward(msg.sender, i);
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                uint256 reward = rt.userRewards[msg.sender];
                                if (reward > 0) {
                                    rt.userRewards[msg.sender] = 0;
                                    IRewardVault(rt.rewardVault).reward(msg.sender, reward);
                                    emit Claim(i, msg.sender, reward);
                                }
                            }
                        
                            function claimAllRewards() external {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    claimReward(i);
                                }
                            }
                        
                            // =============== Ownable  ================
                        
                            function addRewardToken(
                                address rewardToken,
                                uint256 rewardPerBlock,
                                uint256 startBlock,
                                uint256 endBlock
                            ) external onlyOwner {
                                require(rewardToken != address(0), "DODOMineV3: TOKEN_INVALID");
                                require(startBlock > block.number, "DODOMineV3: START_BLOCK_INVALID");
                                require(endBlock > startBlock, "DODOMineV3: DURATION_INVALID");
                        
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    require(
                                        rewardToken != rewardTokenInfos[i].rewardToken,
                                        "DODOMineV3: TOKEN_ALREADY_ADDED"
                                    );
                                }
                        
                                RewardTokenInfo storage rt = rewardTokenInfos.push();
                                rt.rewardToken = rewardToken;
                                rt.startBlock = startBlock;
                                rt.lastFlagBlock = startBlock;
                                rt.endBlock = endBlock;
                                rt.rewardPerBlock = rewardPerBlock;
                                rt.rewardVault = address(new RewardVault(rewardToken));
                        
                                uint256 rewardAmount = rewardPerBlock.mul(endBlock.sub(startBlock));
                                IERC20(rewardToken).safeTransfer(rt.rewardVault, rewardAmount);
                                RewardVault(rt.rewardVault).syncValue();
                        
                                emit NewRewardToken(len, rewardToken);
                            }
                        
                            function setEndBlock(uint256 i, uint256 newEndBlock)
                                external
                                onlyOwner
                            {
                                require(i < rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                _updateReward(address(0), i);
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                        
                        
                                uint256 totalDepositReward = RewardVault(rt.rewardVault)._TOTAL_REWARD_();
                                uint256 gap = newEndBlock.sub(rt.lastFlagBlock);
                                uint256 totalReward = rt.workThroughReward.add(gap.mul(rt.rewardPerBlock));
                                require(totalDepositReward >= totalReward, "DODOMineV3: REWARD_NOT_ENOUGH");
                        
                                require(block.number < newEndBlock, "DODOMineV3: END_BLOCK_INVALID");
                                require(block.number > rt.startBlock, "DODOMineV3: NOT_START");
                                require(block.number < rt.endBlock, "DODOMineV3: ALREADY_CLOSE");
                        
                                rt.endBlock = newEndBlock;
                                emit UpdateEndBlock(i, newEndBlock);
                            }
                        
                            function setReward(uint256 i, uint256 newRewardPerBlock)
                                external
                                onlyOwner
                            {
                                require(i < rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                _updateReward(address(0), i);
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                
                                require(block.number < rt.endBlock, "DODOMineV3: ALREADY_CLOSE");
                                
                                rt.workThroughReward = rt.workThroughReward.add((block.number.sub(rt.lastFlagBlock)).mul(rt.rewardPerBlock));
                                rt.rewardPerBlock = newRewardPerBlock;
                                rt.lastFlagBlock = block.number;
                        
                                uint256 totalDepositReward = RewardVault(rt.rewardVault)._TOTAL_REWARD_();
                                uint256 gap = rt.endBlock.sub(block.number);
                                uint256 totalReward = rt.workThroughReward.add(gap.mul(newRewardPerBlock));
                                require(totalDepositReward >= totalReward, "DODOMineV3: REWARD_NOT_ENOUGH");
                        
                                emit UpdateReward(i, newRewardPerBlock);
                            }
                        
                            function withdrawLeftOver(uint256 i, uint256 amount) external onlyOwner {
                                require(i < rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                require(block.number > rt.endBlock, "DODOMineV3: MINING_NOT_FINISHED");
                                
                                uint256 gap = rt.endBlock.sub(rt.lastFlagBlock);
                                uint256 totalReward = rt.workThroughReward.add(gap.mul(rt.rewardPerBlock));
                                uint256 totalDepositReward = IRewardVault(rt.rewardVault)._TOTAL_REWARD_();
                                require(amount <= totalDepositReward.sub(totalReward), "DODOMineV3: NOT_ENOUGH");
                        
                                IRewardVault(rt.rewardVault).withdrawLeftOver(msg.sender,amount);
                        
                                emit WithdrawLeftOver(msg.sender, i);
                            }
                        
                        
                            function directTransferOwnership(address newOwner) external onlyOwner {
                                require(newOwner != address(0), "DODOMineV3: ZERO_ADDRESS");
                                emit OwnershipTransferred(_OWNER_, newOwner);
                                _OWNER_ = newOwner;
                            }
                        
                            // ============ Internal  ============
                        
                            function _updateReward(address user, uint256 i) internal {
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                if (rt.lastRewardBlock != block.number){
                                    rt.accRewardPerShare = _getAccRewardPerShare(i);
                                    rt.lastRewardBlock = block.number;
                                }
                                if (user != address(0)) {
                                    rt.userRewards[user] = getPendingReward(user, i);
                                    rt.userRewardPerSharePaid[user] = rt.accRewardPerShare;
                                }
                            }
                        
                            function _updateAllReward(address user) internal {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    _updateReward(user, i);
                                }
                            }
                        
                            function _getUnrewardBlockNum(uint256 i) internal view returns (uint256) {
                                RewardTokenInfo memory rt = rewardTokenInfos[i];
                                if (block.number < rt.startBlock || rt.lastRewardBlock > rt.endBlock) {
                                    return 0;
                                }
                                uint256 start = rt.lastRewardBlock < rt.startBlock ? rt.startBlock : rt.lastRewardBlock;
                                uint256 end = rt.endBlock < block.number ? rt.endBlock : block.number;
                                return end.sub(start);
                            }
                        
                            function _getAccRewardPerShare(uint256 i) internal view returns (uint256) {
                                RewardTokenInfo memory rt = rewardTokenInfos[i];
                                if (totalSupply() == 0) {
                                    return rt.accRewardPerShare;
                                }
                                return
                                    rt.accRewardPerShare.add(
                                        DecimalMath.divFloor(_getUnrewardBlockNum(i).mul(rt.rewardPerBlock), totalSupply())
                                    );
                            }
                        
                        }
                        
                        // File: contracts/DODOToken/DODOMineV3/ERC20MineV3.sol
                        
                        
                        
                        contract ERC20MineV3 is ReentrancyGuard, BaseMine {
                            using SafeERC20 for IERC20;
                            using SafeMath for uint256;
                        
                            // ============ Storage ============
                        
                            address public _TOKEN_;
                        
                            function init(address owner, address token) external {
                                super.initOwner(owner);
                                _TOKEN_ = token;
                            }
                        
                            // ============ Event  ============
                        
                            event Deposit(address indexed user, uint256 amount);
                            event Withdraw(address indexed user, uint256 amount);
                        
                            // ============ Deposit && Withdraw && Exit ============
                        
                            function deposit(uint256 amount) external preventReentrant {
                                require(amount > 0, "DODOMineV3: CANNOT_DEPOSIT_ZERO");
                        
                                _updateAllReward(msg.sender);
                        
                                uint256 erc20OriginBalance = IERC20(_TOKEN_).balanceOf(address(this));
                                IERC20(_TOKEN_).safeTransferFrom(msg.sender, address(this), amount);
                                uint256 actualStakeAmount = IERC20(_TOKEN_).balanceOf(address(this)).sub(erc20OriginBalance);
                                
                                _totalSupply = _totalSupply.add(actualStakeAmount);
                                _balances[msg.sender] = _balances[msg.sender].add(actualStakeAmount);
                        
                                emit Deposit(msg.sender, actualStakeAmount);
                            }
                        
                            function withdraw(uint256 amount) external preventReentrant {
                                require(amount > 0, "DODOMineV3: CANNOT_WITHDRAW_ZERO");
                        
                                _updateAllReward(msg.sender);
                                _totalSupply = _totalSupply.sub(amount);
                                _balances[msg.sender] = _balances[msg.sender].sub(amount);
                                IERC20(_TOKEN_).safeTransfer(msg.sender, amount);
                        
                                emit Withdraw(msg.sender, amount);
                            }
                        }

                        File 3 of 9: InitializableERC20
                        /**
                         *Submitted for verification at BscScan.com on 2021-07-01
                        */
                        
                        // File: contracts/lib/SafeMath.sol
                        
                        /*
                        
                            Copyright 2020 DODO ZOO.
                            SPDX-License-Identifier: Apache-2.0
                        
                        */
                        
                        pragma solidity 0.6.9;
                        
                        
                        /**
                         * @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/external/ERC20/InitializableERC20.sol
                        
                        
                        
                        contract InitializableERC20 {
                            using SafeMath for uint256;
                        
                            string public name;
                            uint8 public decimals;
                            string public symbol;
                            uint256 public totalSupply;
                        
                            bool public initialized;
                        
                            mapping(address => uint256) balances;
                            mapping(address => mapping(address => uint256)) internal allowed;
                        
                            event Transfer(address indexed from, address indexed to, uint256 amount);
                            event Approval(address indexed owner, address indexed spender, uint256 amount);
                        
                            function init(
                                address _creator,
                                uint256 _totalSupply,
                                string memory _name,
                                string memory _symbol,
                                uint8 _decimals
                            ) public {
                                require(!initialized, "TOKEN_INITIALIZED");
                                initialized = true;
                                totalSupply = _totalSupply;
                                balances[_creator] = _totalSupply;
                                name = _name;
                                symbol = _symbol;
                                decimals = _decimals;
                                emit Transfer(address(0), _creator, _totalSupply);
                            }
                        
                            function transfer(address to, uint256 amount) public returns (bool) {
                                require(to != address(0), "TO_ADDRESS_IS_EMPTY");
                                require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH");
                        
                                balances[msg.sender] = balances[msg.sender].sub(amount);
                                balances[to] = balances[to].add(amount);
                                emit Transfer(msg.sender, to, amount);
                                return true;
                            }
                        
                            function balanceOf(address owner) public view returns (uint256 balance) {
                                return balances[owner];
                            }
                        
                            function transferFrom(
                                address from,
                                address to,
                                uint256 amount
                            ) public returns (bool) {
                                require(to != address(0), "TO_ADDRESS_IS_EMPTY");
                                require(amount <= balances[from], "BALANCE_NOT_ENOUGH");
                                require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
                        
                                balances[from] = balances[from].sub(amount);
                                balances[to] = balances[to].add(amount);
                                allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount);
                                emit Transfer(from, to, amount);
                                return true;
                            }
                        
                            function approve(address spender, uint256 amount) public returns (bool) {
                                allowed[msg.sender][spender] = amount;
                                emit Approval(msg.sender, spender, amount);
                                return true;
                            }
                        
                            function allowance(address owner, address spender) public view returns (uint256) {
                                return allowed[owner][spender];
                            }
                        }

                        File 4 of 9: DODOMineV3Registry
                        // 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/Factory/Registries/DODOMineV3Registry.sol
                        
                        
                        
                        interface IDODOMineV3Registry {
                            function addMineV3(
                                address mine,
                                bool isLpToken,
                                address stakeToken
                            ) external;
                        }
                        
                        /**
                         * @title DODOMineV3 Registry
                         * @author DODO Breeder
                         *
                         * @notice Register DODOMineV3 Pools 
                         */
                        contract DODOMineV3Registry is InitializableOwnable, IDODOMineV3Registry {
                        
                            mapping (address => bool) public isAdminListed;
                            
                            // ============ Registry ============
                            // minePool -> stakeToken
                            mapping(address => address) public _MINE_REGISTRY_;
                            // lpToken -> minePool
                            mapping(address => address[]) public _LP_REGISTRY_;
                            // singleToken -> minePool
                            mapping(address => address[]) public _SINGLE_REGISTRY_;
                        
                        
                            // ============ Events ============
                            event NewMineV3(address mine, address stakeToken, bool isLpToken);
                            event RemoveMineV3(address mine, address stakeToken);
                            event addAdmin(address admin);
                            event removeAdmin(address admin);
                        
                        
                            function addMineV3(
                                address mine,
                                bool isLpToken,
                                address stakeToken
                            ) override external {
                                require(isAdminListed[msg.sender], "ACCESS_DENIED");
                                _MINE_REGISTRY_[mine] = stakeToken;
                                if(isLpToken) {
                                    _LP_REGISTRY_[stakeToken].push(mine);
                                }else {
                                    _SINGLE_REGISTRY_[stakeToken].push(mine);
                                }
                        
                                emit NewMineV3(mine, stakeToken, isLpToken);
                            }
                        
                            // ============ Admin Operation Functions ============
                        
                            function removeMineV3(
                                address mine,
                                bool isLpToken,
                                address stakeToken
                            ) external onlyOwner {
                                _MINE_REGISTRY_[mine] = address(0);
                                if(isLpToken) {
                                    uint256 len = _LP_REGISTRY_[stakeToken].length;
                                    for (uint256 i = 0; i < len; i++) {
                                        if (mine == _LP_REGISTRY_[stakeToken][i]) {
                                            if(i != len - 1) {
                                                _LP_REGISTRY_[stakeToken][i] = _LP_REGISTRY_[stakeToken][len - 1];
                                            }
                                            _LP_REGISTRY_[stakeToken].pop();
                                            break;
                                        }
                                    }
                                }else {
                                    uint256 len = _SINGLE_REGISTRY_[stakeToken].length;
                                    for (uint256 i = 0; i < len; i++) {
                                        if (mine == _SINGLE_REGISTRY_[stakeToken][i]) {
                                            if(i != len - 1) {
                                                _SINGLE_REGISTRY_[stakeToken][i] = _SINGLE_REGISTRY_[stakeToken][len - 1];
                                            }
                                            _SINGLE_REGISTRY_[stakeToken].pop();
                                            break;
                                        }
                                    }
                                }
                        
                                emit RemoveMineV3(mine, stakeToken);
                            }
                        
                            function addAdminList (address contractAddr) external onlyOwner {
                                isAdminListed[contractAddr] = true;
                                emit addAdmin(contractAddr);
                            }
                        
                            function removeAdminList (address contractAddr) external onlyOwner {
                                isAdminListed[contractAddr] = false;
                                emit removeAdmin(contractAddr);
                            }
                        }

                        File 5 of 9: CloneFactory
                        /*
                        
                            Copyright 2020 DODO ZOO.
                            SPDX-License-Identifier: Apache-2.0
                        
                        */
                        
                        pragma solidity 0.6.9;
                        pragma experimental ABIEncoderV2;
                        
                        interface ICloneFactory {
                            function clone(address prototype) external returns (address proxy);
                        }
                        
                        // introduction of proxy mode design: https://docs.openzeppelin.com/upgrades/2.8/
                        // minimum implementation of transparent proxy: https://eips.ethereum.org/EIPS/eip-1167
                        
                        contract CloneFactory is ICloneFactory {
                            function clone(address prototype) external override returns (address proxy) {
                                bytes20 targetBytes = bytes20(prototype);
                                assembly {
                                    let clone := mload(0x40)
                                    mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                                    mstore(add(clone, 0x14), targetBytes)
                                    mstore(
                                        add(clone, 0x28),
                                        0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
                                    )
                                    proxy := create(0, clone, 0x37)
                                }
                                return proxy;
                            }
                        }

                        File 6 of 9: ERC20MineV3
                        // File: contracts/intf/IERC20.sol
                        
                        // This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol
                        // SPDX-License-Identifier: MIT
                        
                        pragma solidity 0.6.9;
                        pragma experimental ABIEncoderV2;
                        
                        /**
                         * @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/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/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/InitializableOwnable.sol
                        
                        /**
                         * @title Ownable
                         * @author DODO Breeder
                         *
                         * @notice Ownership related functions
                         */
                        contract InitializableOwnable {
                            address public _OWNER_;
                            address public _NEW_OWNER_;
                            bool internal _INITIALIZED_;
                        
                            // ============ Events ============
                        
                            event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
                        
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                        
                            // ============ Modifiers ============
                        
                            modifier notInitialized() {
                                require(!_INITIALIZED_, "DODO_INITIALIZED");
                                _;
                            }
                        
                            modifier onlyOwner() {
                                require(msg.sender == _OWNER_, "NOT_OWNER");
                                _;
                            }
                        
                            // ============ Functions ============
                        
                            function initOwner(address newOwner) public notInitialized {
                                _INITIALIZED_ = true;
                                _OWNER_ = newOwner;
                            }
                        
                            function transferOwnership(address newOwner) public onlyOwner {
                                emit OwnershipTransferPrepared(_OWNER_, newOwner);
                                _NEW_OWNER_ = newOwner;
                            }
                        
                            function claimOwnership() public {
                                require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                                emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                                _OWNER_ = _NEW_OWNER_;
                                _NEW_OWNER_ = address(0);
                            }
                        }
                        
                        // File: contracts/lib/Ownable.sol
                        
                        /**
                         * @title Ownable
                         * @author DODO Breeder
                         *
                         * @notice Ownership related functions
                         */
                        contract Ownable {
                            address public _OWNER_;
                            address public _NEW_OWNER_;
                        
                            // ============ Events ============
                        
                            event OwnershipTransferPrepared(address indexed previousOwner, address indexed newOwner);
                        
                            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                        
                            // ============ Modifiers ============
                        
                            modifier onlyOwner() {
                                require(msg.sender == _OWNER_, "NOT_OWNER");
                                _;
                            }
                        
                            // ============ Functions ============
                        
                            constructor() internal {
                                _OWNER_ = msg.sender;
                                emit OwnershipTransferred(address(0), _OWNER_);
                            }
                        
                            function transferOwnership(address newOwner) external virtual onlyOwner {
                                emit OwnershipTransferPrepared(_OWNER_, newOwner);
                                _NEW_OWNER_ = newOwner;
                            }
                        
                            function claimOwnership() external {
                                require(msg.sender == _NEW_OWNER_, "INVALID_CLAIM");
                                emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
                                _OWNER_ = _NEW_OWNER_;
                                _NEW_OWNER_ = address(0);
                            }
                        }
                        
                        // File: contracts/DODOToken/DODOMineV3/RewardVault.sol
                        
                        
                        interface IRewardVault {
                            function reward(address to, uint256 amount) external;
                            function withdrawLeftOver(address to, uint256 amount) external; 
                            function syncValue() external;
                            function _TOTAL_REWARD_() external view returns(uint256);
                        }
                        
                        contract RewardVault is Ownable {
                            using SafeERC20 for IERC20;
                            using SafeMath for uint256;
                        
                            uint256 public _REWARD_RESERVE_;
                            uint256 public _TOTAL_REWARD_;
                            address public _REWARD_TOKEN_;
                        
                            // ============ Event =============
                            event DepositReward(uint256 totalReward, uint256 inputReward, uint256 rewardReserve);
                        
                            constructor(address _rewardToken) public {
                                _REWARD_TOKEN_ = _rewardToken;
                            }
                        
                            function reward(address to, uint256 amount) external onlyOwner {
                                require(_REWARD_RESERVE_ >= amount, "VAULT_NOT_ENOUGH");
                                _REWARD_RESERVE_ = _REWARD_RESERVE_.sub(amount);
                                IERC20(_REWARD_TOKEN_).safeTransfer(to, amount);
                            }
                        
                            function withdrawLeftOver(address to,uint256 amount) external onlyOwner {
                                require(_REWARD_RESERVE_ >= amount, "VAULT_NOT_ENOUGH");
                                _REWARD_RESERVE_ = _REWARD_RESERVE_.sub(amount);
                                IERC20(_REWARD_TOKEN_).safeTransfer(to, amount);
                            }
                        
                            function syncValue() external {
                                uint256 rewardBalance = IERC20(_REWARD_TOKEN_).balanceOf(address(this));
                                uint256 rewardInput = rewardBalance.sub(_REWARD_RESERVE_);
                        
                                _TOTAL_REWARD_ = _TOTAL_REWARD_.add(rewardInput);
                                _REWARD_RESERVE_ = rewardBalance;
                        
                                emit DepositReward(_TOTAL_REWARD_, rewardInput, _REWARD_RESERVE_);
                            }
                        }
                        
                        // File: contracts/DODOToken/DODOMineV3/BaseMine.sol
                        
                        
                        
                        contract BaseMine is InitializableOwnable {
                            using SafeERC20 for IERC20;
                            using SafeMath for uint256;
                        
                            // ============ Storage ============
                        
                            struct RewardTokenInfo {
                                address rewardToken;
                                uint256 startBlock;
                                uint256 endBlock;
                                address rewardVault;
                                uint256 rewardPerBlock;
                                uint256 accRewardPerShare;
                                uint256 lastRewardBlock;
                                uint256 workThroughReward;
                                uint256 lastFlagBlock;
                                mapping(address => uint256) userRewardPerSharePaid;
                                mapping(address => uint256) userRewards;
                            }
                        
                            RewardTokenInfo[] public rewardTokenInfos;
                        
                            uint256 internal _totalSupply;
                            mapping(address => uint256) internal _balances;
                        
                            // ============ Event =============
                        
                            event Claim(uint256 indexed i, address indexed user, uint256 reward);
                            event UpdateReward(uint256 indexed i, uint256 rewardPerBlock);
                            event UpdateEndBlock(uint256 indexed i, uint256 endBlock);
                            event NewRewardToken(uint256 indexed i, address rewardToken);
                            event RemoveRewardToken(address rewardToken);
                            event WithdrawLeftOver(address owner, uint256 i);
                        
                            // ============ View  ============
                        
                            function getPendingReward(address user, uint256 i) public view returns (uint256) {
                                require(i<rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                uint256 accRewardPerShare = rt.accRewardPerShare;
                                if (rt.lastRewardBlock != block.number) {
                                    accRewardPerShare = _getAccRewardPerShare(i);
                                }
                                return
                                    DecimalMath.mulFloor(
                                        balanceOf(user), 
                                        accRewardPerShare.sub(rt.userRewardPerSharePaid[user])
                                    ).add(rt.userRewards[user]);
                            }
                        
                            function getPendingRewardByToken(address user, address rewardToken) external view returns (uint256) {
                                return getPendingReward(user, getIdByRewardToken(rewardToken));
                            }
                        
                            function totalSupply() public view returns (uint256) {
                                return _totalSupply;
                            }
                        
                            function balanceOf(address user) public view returns (uint256) {
                                return _balances[user];
                            }
                        
                            function getRewardTokenById(uint256 i) external view returns (address) {
                                require(i<rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                RewardTokenInfo memory rt = rewardTokenInfos[i];
                                return rt.rewardToken;
                            }
                        
                            function getIdByRewardToken(address rewardToken) public view returns(uint256) {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    if (rewardToken == rewardTokenInfos[i].rewardToken) {
                                        return i;
                                    }
                                }
                                require(false, "DODOMineV3: TOKEN_NOT_FOUND");
                            }
                        
                            function getRewardNum() external view returns(uint256) {
                                return rewardTokenInfos.length;
                            }
                        
                            function getVaultByRewardToken(address rewardToken) public view returns(address) {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    if (rewardToken == rewardTokenInfos[i].rewardToken) {
                                        return rewardTokenInfos[i].rewardVault;
                                    }
                                }
                                require(false, "DODOMineV3: TOKEN_NOT_FOUND");
                            }
                        
                            function getVaultDebtByRewardToken(address rewardToken) public view returns(uint256) {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    if (rewardToken == rewardTokenInfos[i].rewardToken) {
                                        uint256 totalDepositReward = IRewardVault(rewardTokenInfos[i].rewardVault)._TOTAL_REWARD_();
                                        uint256 gap = rewardTokenInfos[i].endBlock.sub(rewardTokenInfos[i].lastFlagBlock);
                                        uint256 totalReward = rewardTokenInfos[i].workThroughReward.add(gap.mul(rewardTokenInfos[i].rewardPerBlock));
                                        if(totalDepositReward >= totalReward) {
                                            return 0;
                                        }else {
                                            return totalReward.sub(totalDepositReward);
                                        }
                                    }
                                }
                                require(false, "DODOMineV3: TOKEN_NOT_FOUND");
                            }
                        
                            // ============ Claim ============
                        
                            function claimReward(uint256 i) public {
                                require(i<rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                _updateReward(msg.sender, i);
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                uint256 reward = rt.userRewards[msg.sender];
                                if (reward > 0) {
                                    rt.userRewards[msg.sender] = 0;
                                    IRewardVault(rt.rewardVault).reward(msg.sender, reward);
                                    emit Claim(i, msg.sender, reward);
                                }
                            }
                        
                            function claimAllRewards() external {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    claimReward(i);
                                }
                            }
                        
                            // =============== Ownable  ================
                        
                            function addRewardToken(
                                address rewardToken,
                                uint256 rewardPerBlock,
                                uint256 startBlock,
                                uint256 endBlock
                            ) external onlyOwner {
                                require(rewardToken != address(0), "DODOMineV3: TOKEN_INVALID");
                                require(startBlock > block.number, "DODOMineV3: START_BLOCK_INVALID");
                                require(endBlock > startBlock, "DODOMineV3: DURATION_INVALID");
                        
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    require(
                                        rewardToken != rewardTokenInfos[i].rewardToken,
                                        "DODOMineV3: TOKEN_ALREADY_ADDED"
                                    );
                                }
                        
                                RewardTokenInfo storage rt = rewardTokenInfos.push();
                                rt.rewardToken = rewardToken;
                                rt.startBlock = startBlock;
                                rt.lastFlagBlock = startBlock;
                                rt.endBlock = endBlock;
                                rt.rewardPerBlock = rewardPerBlock;
                                rt.rewardVault = address(new RewardVault(rewardToken));
                        
                                uint256 rewardAmount = rewardPerBlock.mul(endBlock.sub(startBlock));
                                IERC20(rewardToken).safeTransfer(rt.rewardVault, rewardAmount);
                                RewardVault(rt.rewardVault).syncValue();
                        
                                emit NewRewardToken(len, rewardToken);
                            }
                        
                            function setEndBlock(uint256 i, uint256 newEndBlock)
                                external
                                onlyOwner
                            {
                                require(i < rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                _updateReward(address(0), i);
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                        
                        
                                uint256 totalDepositReward = RewardVault(rt.rewardVault)._TOTAL_REWARD_();
                                uint256 gap = newEndBlock.sub(rt.lastFlagBlock);
                                uint256 totalReward = rt.workThroughReward.add(gap.mul(rt.rewardPerBlock));
                                require(totalDepositReward >= totalReward, "DODOMineV3: REWARD_NOT_ENOUGH");
                        
                                require(block.number < newEndBlock, "DODOMineV3: END_BLOCK_INVALID");
                                require(block.number > rt.startBlock, "DODOMineV3: NOT_START");
                                require(block.number < rt.endBlock, "DODOMineV3: ALREADY_CLOSE");
                        
                                rt.endBlock = newEndBlock;
                                emit UpdateEndBlock(i, newEndBlock);
                            }
                        
                            function setReward(uint256 i, uint256 newRewardPerBlock)
                                external
                                onlyOwner
                            {
                                require(i < rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                _updateReward(address(0), i);
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                
                                require(block.number < rt.endBlock, "DODOMineV3: ALREADY_CLOSE");
                                
                                rt.workThroughReward = rt.workThroughReward.add((block.number.sub(rt.lastFlagBlock)).mul(rt.rewardPerBlock));
                                rt.rewardPerBlock = newRewardPerBlock;
                                rt.lastFlagBlock = block.number;
                        
                                uint256 totalDepositReward = RewardVault(rt.rewardVault)._TOTAL_REWARD_();
                                uint256 gap = rt.endBlock.sub(block.number);
                                uint256 totalReward = rt.workThroughReward.add(gap.mul(newRewardPerBlock));
                                require(totalDepositReward >= totalReward, "DODOMineV3: REWARD_NOT_ENOUGH");
                        
                                emit UpdateReward(i, newRewardPerBlock);
                            }
                        
                            function withdrawLeftOver(uint256 i, uint256 amount) external onlyOwner {
                                require(i < rewardTokenInfos.length, "DODOMineV3: REWARD_ID_NOT_FOUND");
                                
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                require(block.number > rt.endBlock, "DODOMineV3: MINING_NOT_FINISHED");
                                
                                uint256 gap = rt.endBlock.sub(rt.lastFlagBlock);
                                uint256 totalReward = rt.workThroughReward.add(gap.mul(rt.rewardPerBlock));
                                uint256 totalDepositReward = IRewardVault(rt.rewardVault)._TOTAL_REWARD_();
                                require(amount <= totalDepositReward.sub(totalReward), "DODOMineV3: NOT_ENOUGH");
                        
                                IRewardVault(rt.rewardVault).withdrawLeftOver(msg.sender,amount);
                        
                                emit WithdrawLeftOver(msg.sender, i);
                            }
                        
                        
                            function directTransferOwnership(address newOwner) external onlyOwner {
                                require(newOwner != address(0), "DODOMineV3: ZERO_ADDRESS");
                                emit OwnershipTransferred(_OWNER_, newOwner);
                                _OWNER_ = newOwner;
                            }
                        
                            // ============ Internal  ============
                        
                            function _updateReward(address user, uint256 i) internal {
                                RewardTokenInfo storage rt = rewardTokenInfos[i];
                                if (rt.lastRewardBlock != block.number){
                                    rt.accRewardPerShare = _getAccRewardPerShare(i);
                                    rt.lastRewardBlock = block.number;
                                }
                                if (user != address(0)) {
                                    rt.userRewards[user] = getPendingReward(user, i);
                                    rt.userRewardPerSharePaid[user] = rt.accRewardPerShare;
                                }
                            }
                        
                            function _updateAllReward(address user) internal {
                                uint256 len = rewardTokenInfos.length;
                                for (uint256 i = 0; i < len; i++) {
                                    _updateReward(user, i);
                                }
                            }
                        
                            function _getUnrewardBlockNum(uint256 i) internal view returns (uint256) {
                                RewardTokenInfo memory rt = rewardTokenInfos[i];
                                if (block.number < rt.startBlock || rt.lastRewardBlock > rt.endBlock) {
                                    return 0;
                                }
                                uint256 start = rt.lastRewardBlock < rt.startBlock ? rt.startBlock : rt.lastRewardBlock;
                                uint256 end = rt.endBlock < block.number ? rt.endBlock : block.number;
                                return end.sub(start);
                            }
                        
                            function _getAccRewardPerShare(uint256 i) internal view returns (uint256) {
                                RewardTokenInfo memory rt = rewardTokenInfos[i];
                                if (totalSupply() == 0) {
                                    return rt.accRewardPerShare;
                                }
                                return
                                    rt.accRewardPerShare.add(
                                        DecimalMath.divFloor(_getUnrewardBlockNum(i).mul(rt.rewardPerBlock), totalSupply())
                                    );
                            }
                        
                        }
                        
                        // File: contracts/DODOToken/DODOMineV3/ERC20MineV3.sol
                        
                        
                        
                        contract ERC20MineV3 is ReentrancyGuard, BaseMine {
                            using SafeERC20 for IERC20;
                            using SafeMath for uint256;
                        
                            // ============ Storage ============
                        
                            address public _TOKEN_;
                        
                            function init(address owner, address token) external {
                                super.initOwner(owner);
                                _TOKEN_ = token;
                            }
                        
                            // ============ Event  ============
                        
                            event Deposit(address indexed user, uint256 amount);
                            event Withdraw(address indexed user, uint256 amount);
                        
                            // ============ Deposit && Withdraw && Exit ============
                        
                            function deposit(uint256 amount) external preventReentrant {
                                require(amount > 0, "DODOMineV3: CANNOT_DEPOSIT_ZERO");
                        
                                _updateAllReward(msg.sender);
                        
                                uint256 erc20OriginBalance = IERC20(_TOKEN_).balanceOf(address(this));
                                IERC20(_TOKEN_).safeTransferFrom(msg.sender, address(this), amount);
                                uint256 actualStakeAmount = IERC20(_TOKEN_).balanceOf(address(this)).sub(erc20OriginBalance);
                                
                                _totalSupply = _totalSupply.add(actualStakeAmount);
                                _balances[msg.sender] = _balances[msg.sender].add(actualStakeAmount);
                        
                                emit Deposit(msg.sender, actualStakeAmount);
                            }
                        
                            function withdraw(uint256 amount) external preventReentrant {
                                require(amount > 0, "DODOMineV3: CANNOT_WITHDRAW_ZERO");
                        
                                _updateAllReward(msg.sender);
                                _totalSupply = _totalSupply.sub(amount);
                                _balances[msg.sender] = _balances[msg.sender].sub(amount);
                                IERC20(_TOKEN_).safeTransfer(msg.sender, amount);
                        
                                emit Withdraw(msg.sender, amount);
                            }
                        }

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

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

                        File 9 of 9: InitializableERC20
                        /**
                         *Submitted for verification at BscScan.com on 2021-07-01
                        */
                        
                        // File: contracts/lib/SafeMath.sol
                        
                        /*
                        
                            Copyright 2020 DODO ZOO.
                            SPDX-License-Identifier: Apache-2.0
                        
                        */
                        
                        pragma solidity 0.6.9;
                        
                        
                        /**
                         * @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/external/ERC20/InitializableERC20.sol
                        
                        
                        
                        contract InitializableERC20 {
                            using SafeMath for uint256;
                        
                            string public name;
                            uint8 public decimals;
                            string public symbol;
                            uint256 public totalSupply;
                        
                            bool public initialized;
                        
                            mapping(address => uint256) balances;
                            mapping(address => mapping(address => uint256)) internal allowed;
                        
                            event Transfer(address indexed from, address indexed to, uint256 amount);
                            event Approval(address indexed owner, address indexed spender, uint256 amount);
                        
                            function init(
                                address _creator,
                                uint256 _totalSupply,
                                string memory _name,
                                string memory _symbol,
                                uint8 _decimals
                            ) public {
                                require(!initialized, "TOKEN_INITIALIZED");
                                initialized = true;
                                totalSupply = _totalSupply;
                                balances[_creator] = _totalSupply;
                                name = _name;
                                symbol = _symbol;
                                decimals = _decimals;
                                emit Transfer(address(0), _creator, _totalSupply);
                            }
                        
                            function transfer(address to, uint256 amount) public returns (bool) {
                                require(to != address(0), "TO_ADDRESS_IS_EMPTY");
                                require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH");
                        
                                balances[msg.sender] = balances[msg.sender].sub(amount);
                                balances[to] = balances[to].add(amount);
                                emit Transfer(msg.sender, to, amount);
                                return true;
                            }
                        
                            function balanceOf(address owner) public view returns (uint256 balance) {
                                return balances[owner];
                            }
                        
                            function transferFrom(
                                address from,
                                address to,
                                uint256 amount
                            ) public returns (bool) {
                                require(to != address(0), "TO_ADDRESS_IS_EMPTY");
                                require(amount <= balances[from], "BALANCE_NOT_ENOUGH");
                                require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
                        
                                balances[from] = balances[from].sub(amount);
                                balances[to] = balances[to].add(amount);
                                allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount);
                                emit Transfer(from, to, amount);
                                return true;
                            }
                        
                            function approve(address spender, uint256 amount) public returns (bool) {
                                allowed[msg.sender][spender] = amount;
                                emit Approval(msg.sender, spender, amount);
                                return true;
                            }
                        
                            function allowance(address owner, address spender) public view returns (uint256) {
                                return allowed[owner][spender];
                            }
                        }