ETH Price: $2,156.17 (-1.70%)

Transaction Decoder

Block:
12632835 at Jun-14-2021 01:31:36 PM +UTC
Transaction Fee:
0.002540494 ETH $5.48
Gas Used:
115,477 Gas / 22 Gwei

Emitted Events:

357 DODOToken.Transfer( from=[Sender] 0x540a7a027de7241c95c2692bb3a8c321920f66d3, to=[Receiver] vDODOToken, amount=179998365476588861677 )
358 vDODOToken.MintVDODO( user=[Sender] 0x540a7a027de7241c95c2692bb3a8c321920f66d3, superior=0x171692a5...A22f27B78, mintDODO=179998365476588861677 )

Account State Difference:

  Address   Before After State Difference Code
0x43Dfc415...8ad7d4DDd
0x540A7a02...1920F66D3
0.096810656091657 Eth
Nonce: 6
0.094270162091657 Eth
Nonce: 7
0.002540494
(F2Pool Old)
1,704.232969985687682431 Eth1,704.235510479687682431 Eth0.002540494
0xc4436fBA...707Bd402A

Execution Trace

vDODOToken.mint( dodoAmount=179998365476588861677, superiorAddress=0x171692a5ad20143CD050788C731003bA22f27B78 )
  • DODOApproveProxy.claimTokens( token=0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd, who=0x540A7a027de7241C95C2692bB3a8C321920F66D3, dest=0xc4436fBAE6eBa5d95bf7d53Ae515F8A707Bd402A, amount=179998365476588861677 )
    • DODOApprove.claimTokens( token=0x43Dfc4159D86F3A37A5A4B3D4580b888ad7d4DDd, who=0x540A7a027de7241C95C2692bB3a8C321920F66D3, dest=0xc4436fBAE6eBa5d95bf7d53Ae515F8A707Bd402A, amount=179998365476588861677 )
      • DODOToken.transferFrom( from=0x540A7a027de7241C95C2692bB3a8C321920F66D3, to=0xc4436fBAE6eBa5d95bf7d53Ae515F8A707Bd402A, amount=179998365476588861677 ) => ( True )
        File 1 of 4: vDODOToken
        // 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/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/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/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/DODOToken/vDODOToken.sol
        
        
        
        interface IGovernance {
            function getLockedvDODO(address account) external view returns (uint256);
        }
        
        interface IDODOCirculationHelper {
            // Locked vDOOD not counted in circulation
            function getCirculation() external view returns (uint256);
        
            function getDodoWithdrawFeeRatio() external view returns (uint256);
        }
        
        contract vDODOToken is InitializableOwnable {
            using SafeMath for uint256;
        
            // ============ Storage(ERC20) ============
        
            string public name = "vDODO Membership Token";
            string public symbol = "vDODO";
            uint8 public decimals = 18;
            mapping(address => mapping(address => uint256)) internal _ALLOWED_;
        
            // ============ Storage ============
        
            address public immutable _DODO_TOKEN_;
            address public immutable _DODO_APPROVE_PROXY_;
            address public immutable _DODO_TEAM_;
            address public _DOOD_GOV_;
            address public _DODO_CIRCULATION_HELPER_;
        
            bool public _CAN_TRANSFER_;
        
            // staking reward parameters
            uint256 public _DODO_PER_BLOCK_;
            uint256 public constant _SUPERIOR_RATIO_ = 10**17; // 0.1
            uint256 public constant _DODO_RATIO_ = 100; // 100
            uint256 public _DODO_FEE_BURN_RATIO_;
        
            // accounting
            uint112 public alpha = 10**18; // 1
            uint112 public _TOTAL_BLOCK_DISTRIBUTION_;
            uint32 public _LAST_REWARD_BLOCK_;
        
            uint256 public _TOTAL_BLOCK_REWARD_;
            uint256 public _TOTAL_STAKING_POWER_;
            mapping(address => UserInfo) public userInfo;
        
            struct UserInfo {
                uint128 stakingPower;
                uint128 superiorSP;
                address superior;
                uint256 credit;
            }
        
            // ============ Events ============
        
            event MintVDODO(address user, address superior, uint256 mintDODO);
            event RedeemVDODO(address user, uint256 receiveDODO, uint256 burnDODO, uint256 feeDODO);
            event DonateDODO(address user, uint256 donateDODO);
            event SetCantransfer(bool allowed);
        
            event PreDeposit(uint256 dodoAmount);
            event ChangePerReward(uint256 dodoPerBlock);
            event UpdateDODOFeeBurnRatio(uint256 dodoFeeBurnRatio);
        
            event Transfer(address indexed from, address indexed to, uint256 amount);
            event Approval(address indexed owner, address indexed spender, uint256 amount);
        
            // ============ Modifiers ============
        
            modifier canTransfer() {
                require(_CAN_TRANSFER_, "vDODOToken: not allowed transfer");
                _;
            }
        
            modifier balanceEnough(address account, uint256 amount) {
                require(availableBalanceOf(account) >= amount, "vDODOToken: available amount not enough");
                _;
            }
        
            // ============ Constructor ============
        
            constructor(
                address dodoGov,
                address dodoToken,
                address dodoApproveProxy,
                address dodoTeam
            ) public {
                _DOOD_GOV_ = dodoGov;
                _DODO_TOKEN_ = dodoToken;
                _DODO_APPROVE_PROXY_ = dodoApproveProxy;
                _DODO_TEAM_ = dodoTeam;
            }
        
            // ============ Ownable Functions ============`
        
            function setCantransfer(bool allowed) public onlyOwner {
                _CAN_TRANSFER_ = allowed;
                emit SetCantransfer(allowed);
            }
        
            function changePerReward(uint256 dodoPerBlock) public onlyOwner {
                _updateAlpha();
                _DODO_PER_BLOCK_ = dodoPerBlock;
                emit ChangePerReward(dodoPerBlock);
            }
        
            function updateDODOFeeBurnRatio(uint256 dodoFeeBurnRatio) public onlyOwner {
                _DODO_FEE_BURN_RATIO_ = dodoFeeBurnRatio;
                emit UpdateDODOFeeBurnRatio(_DODO_FEE_BURN_RATIO_);
            }
        
            function updateDODOCirculationHelper(address helper) public onlyOwner {
                _DODO_CIRCULATION_HELPER_ = helper;
            }
        
            function updateGovernance(address governance) public onlyOwner {
                _DOOD_GOV_ = governance;
            }
        
            function emergencyWithdraw() public onlyOwner {
                uint256 dodoBalance = IERC20(_DODO_TOKEN_).balanceOf(address(this));
                IERC20(_DODO_TOKEN_).transfer(_OWNER_, dodoBalance);
            }
        
            // ============ Mint & Redeem & Donate ============
        
            function mint(uint256 dodoAmount, address superiorAddress) public {
                require(
                    superiorAddress != address(0) && superiorAddress != msg.sender,
                    "vDODOToken: Superior INVALID"
                );
                require(dodoAmount > 0, "vDODOToken: must mint greater than 0");
        
                UserInfo storage user = userInfo[msg.sender];
        
                if (user.superior == address(0)) {
                    require(
                        superiorAddress == _DODO_TEAM_ || userInfo[superiorAddress].superior != address(0),
                        "vDODOToken: INVALID_SUPERIOR_ADDRESS"
                    );
                    user.superior = superiorAddress;
                }
        
                _updateAlpha();
        
                IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(
                    _DODO_TOKEN_,
                    msg.sender,
                    address(this),
                    dodoAmount
                );
        
                uint256 newStakingPower = DecimalMath.divFloor(dodoAmount, alpha);
        
                _mint(user, newStakingPower);
        
                emit MintVDODO(msg.sender, superiorAddress, dodoAmount);
            }
        
            function redeem(uint256 vdodoAmount, bool all) public balanceEnough(msg.sender, vdodoAmount) {
                _updateAlpha();
                UserInfo storage user = userInfo[msg.sender];
        
                uint256 dodoAmount;
                uint256 stakingPower;
        
                if (all) {
                    stakingPower = uint256(user.stakingPower).sub(DecimalMath.divFloor(user.credit, alpha));
                    dodoAmount = DecimalMath.mulFloor(stakingPower, alpha);
                } else {
                    dodoAmount = vdodoAmount.mul(_DODO_RATIO_);
                    stakingPower = DecimalMath.divFloor(dodoAmount, alpha);
                }
        
                _redeem(user, stakingPower);
        
                (uint256 dodoReceive, uint256 burnDodoAmount, uint256 withdrawFeeDodoAmount) = getWithdrawResult(dodoAmount);
        
                IERC20(_DODO_TOKEN_).transfer(msg.sender, dodoReceive);
                
                if (burnDodoAmount > 0) {
                    IERC20(_DODO_TOKEN_).transfer(address(0), burnDodoAmount);
                }
                
                if (withdrawFeeDodoAmount > 0) {
                    alpha = uint112(
                        uint256(alpha).add(
                            DecimalMath.divFloor(withdrawFeeDodoAmount, _TOTAL_STAKING_POWER_)
                        )
                    );
                }
        
                emit RedeemVDODO(msg.sender, dodoReceive, burnDodoAmount, withdrawFeeDodoAmount);
            }
        
            function donate(uint256 dodoAmount) public {
                IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(
                    _DODO_TOKEN_,
                    msg.sender,
                    address(this),
                    dodoAmount
                );
                alpha = uint112(
                    uint256(alpha).add(DecimalMath.divFloor(dodoAmount, _TOTAL_STAKING_POWER_))
                );
                emit DonateDODO(msg.sender, dodoAmount);
            }
        
            function preDepositedBlockReward(uint256 dodoAmount) public {
                IDODOApproveProxy(_DODO_APPROVE_PROXY_).claimTokens(
                    _DODO_TOKEN_,
                    msg.sender,
                    address(this),
                    dodoAmount
                );
                _TOTAL_BLOCK_REWARD_ = _TOTAL_BLOCK_REWARD_.add(dodoAmount);
                emit PreDeposit(dodoAmount);
            }
        
            // ============ ERC20 Functions ============
        
            function totalSupply() public view returns (uint256 vDODOSupply) {
                uint256 totalDODO = IERC20(_DODO_TOKEN_).balanceOf(address(this));
                (,uint256 curDistribution) = getLatestAlpha();
                uint256 actualDODO = totalDODO.sub(_TOTAL_BLOCK_REWARD_.sub(curDistribution.add(_TOTAL_BLOCK_DISTRIBUTION_)));
                vDODOSupply = actualDODO / _DODO_RATIO_;
            }
            
            function balanceOf(address account) public view returns (uint256 vDODOAmount) {
                vDODOAmount = dodoBalanceOf(account) / _DODO_RATIO_;
            }
        
            function transfer(address to, uint256 vDODOAmount) public returns (bool) {
                _updateAlpha();
                _transfer(msg.sender, to, vDODOAmount);
                return true;
            }
        
            function approve(address spender, uint256 vDODOAmount) canTransfer public returns (bool) {
                _ALLOWED_[msg.sender][spender] = vDODOAmount;
                emit Approval(msg.sender, spender, vDODOAmount);
                return true;
            }
        
            function transferFrom(
                address from,
                address to,
                uint256 vDODOAmount
            ) public returns (bool) {
                require(vDODOAmount <= _ALLOWED_[from][msg.sender], "ALLOWANCE_NOT_ENOUGH");
                _updateAlpha();
                _transfer(from, to, vDODOAmount);
                _ALLOWED_[from][msg.sender] = _ALLOWED_[from][msg.sender].sub(vDODOAmount);
                return true;
            }
        
            function allowance(address owner, address spender) public view returns (uint256) {
                return _ALLOWED_[owner][spender];
            }
        
            // ============ Helper Functions ============
        
            function getLatestAlpha() public view returns (uint256 newAlpha, uint256 curDistribution) {
                if (_LAST_REWARD_BLOCK_ == 0) {
                    curDistribution = 0;
                } else {
                    curDistribution = _DODO_PER_BLOCK_ * (block.number - _LAST_REWARD_BLOCK_);
                }
                if (_TOTAL_STAKING_POWER_ > 0) {
                    newAlpha = uint256(alpha).add(DecimalMath.divFloor(curDistribution, _TOTAL_STAKING_POWER_));
                } else {
                    newAlpha = alpha;
                }
            }
        
            function availableBalanceOf(address account) public view returns (uint256 vDODOAmount) {
                if (_DOOD_GOV_ == address(0)) {
                    vDODOAmount = balanceOf(account);
                } else {
                    uint256 lockedvDODOAmount = IGovernance(_DOOD_GOV_).getLockedvDODO(account);
                    vDODOAmount = balanceOf(account).sub(lockedvDODOAmount);
                }
            }
        
            function dodoBalanceOf(address account) public view returns (uint256 dodoAmount) {
                UserInfo memory user = userInfo[account];
                (uint256 newAlpha,) = getLatestAlpha();
                uint256 nominalDodo =  DecimalMath.mulFloor(uint256(user.stakingPower), newAlpha);
                if(nominalDodo > user.credit) {
                    dodoAmount = nominalDodo - user.credit;
                }else {
                    dodoAmount = 0;
                }
            }
        
            function getWithdrawResult(uint256 dodoAmount)
                public
                view
                returns (
                    uint256 dodoReceive,
                    uint256 burnDodoAmount,
                    uint256 withdrawFeeDodoAmount
                )
            {
                uint256 feeRatio =
                    IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getDodoWithdrawFeeRatio();
        
                withdrawFeeDodoAmount = DecimalMath.mulFloor(dodoAmount, feeRatio);
                dodoReceive = dodoAmount.sub(withdrawFeeDodoAmount);
        
                burnDodoAmount = DecimalMath.mulFloor(withdrawFeeDodoAmount, _DODO_FEE_BURN_RATIO_);
                withdrawFeeDodoAmount = withdrawFeeDodoAmount.sub(burnDodoAmount);
            }
        
            function getDODOWithdrawFeeRatio() public view returns (uint256 feeRatio) {
                feeRatio = IDODOCirculationHelper(_DODO_CIRCULATION_HELPER_).getDodoWithdrawFeeRatio();
            }
        
            function getSuperior(address account) public view returns (address superior) {
                return userInfo[account].superior;
            }
        
            // ============ Internal Functions ============
        
            function _updateAlpha() internal {
                (uint256 newAlpha, uint256 curDistribution) = getLatestAlpha();
                uint256 newTotalDistribution = curDistribution.add(_TOTAL_BLOCK_DISTRIBUTION_);
                require(newAlpha <= uint112(-1) && newTotalDistribution <= uint112(-1), "OVERFLOW");
                alpha = uint112(newAlpha);
                _TOTAL_BLOCK_DISTRIBUTION_ = uint112(newTotalDistribution);
                _LAST_REWARD_BLOCK_ = uint32(block.number);
            }
        
            function _mint(UserInfo storage to, uint256 stakingPower) internal {
                require(stakingPower <= uint128(-1), "OVERFLOW");
                UserInfo storage superior = userInfo[to.superior];
                uint256 superiorIncreSP = DecimalMath.mulFloor(stakingPower, _SUPERIOR_RATIO_);
                uint256 superiorIncreCredit = DecimalMath.mulFloor(superiorIncreSP, alpha);
        
                to.stakingPower = uint128(uint256(to.stakingPower).add(stakingPower));
                to.superiorSP = uint128(uint256(to.superiorSP).add(superiorIncreSP));
        
                superior.stakingPower = uint128(uint256(superior.stakingPower).add(superiorIncreSP));
                superior.credit = uint128(uint256(superior.credit).add(superiorIncreCredit));
        
                _TOTAL_STAKING_POWER_ = _TOTAL_STAKING_POWER_.add(stakingPower).add(superiorIncreSP);
            }
        
            function _redeem(UserInfo storage from, uint256 stakingPower) internal {
                from.stakingPower = uint128(uint256(from.stakingPower).sub(stakingPower));
        
                // superior decrease sp = min(stakingPower*0.1, from.superiorSP)
                uint256 superiorDecreSP = DecimalMath.mulFloor(stakingPower, _SUPERIOR_RATIO_);
                superiorDecreSP = from.superiorSP <= superiorDecreSP ? from.superiorSP : superiorDecreSP;
                from.superiorSP = uint128(uint256(from.superiorSP).sub(superiorDecreSP));
        
                UserInfo storage superior = userInfo[from.superior];
                uint256 creditSP = DecimalMath.divFloor(superior.credit, alpha);
        
                if (superiorDecreSP >= creditSP) {
                    superior.credit = 0;
                    superior.stakingPower = uint128(uint256(superior.stakingPower).sub(creditSP));
                } else {
                    superior.credit = uint128(
                        uint256(superior.credit).sub(DecimalMath.mulFloor(superiorDecreSP, alpha))
                    );
                    superior.stakingPower = uint128(uint256(superior.stakingPower).sub(superiorDecreSP));
                }
        
                _TOTAL_STAKING_POWER_ = _TOTAL_STAKING_POWER_.sub(stakingPower).sub(superiorDecreSP);
            }
        
            function _transfer(
                address from,
                address to,
                uint256 vDODOAmount
            ) internal canTransfer balanceEnough(from, vDODOAmount) {
                require(from != address(0), "transfer from the zero address");
                require(to != address(0), "transfer to the zero address");
                require(from != to, "transfer from same with to");
        
                uint256 stakingPower = DecimalMath.divFloor(vDODOAmount * _DODO_RATIO_, alpha);
        
                UserInfo storage fromUser = userInfo[from];
                UserInfo storage toUser = userInfo[to];
        
                _redeem(fromUser, stakingPower);
                _mint(toUser, stakingPower);
        
                emit Transfer(from, to, vDODOAmount);
            }
        }

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

        File 3 of 4: 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 4 of 4: 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_;
            }
        }