AVAX Price: $22.80 (+10.54%)
Gas: 1 nAVAX
 

Overview

AVAX Balance

Avalanche C-Chain LogoAvalanche C-Chain LogoAvalanche C-Chain Logo34.249547396169064002 AVAX

AVAX Value

$780.77 (@ $22.80/AVAX)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Swap136078572022-04-19 3:48:441100 days ago1650340124IN
0x160020B0...BcF2062FF
0 AVAX0.0209634742.76095328
Swap136050592022-04-19 2:14:261100 days ago1650334466IN
0x160020B0...BcF2062FF
0 AVAX0.0165193957.66878279
Swap136008382022-04-18 23:51:501100 days ago1650325910IN
0x160020B0...BcF2062FF
0 AVAX0.0273887760.82299277
Swap136008052022-04-18 23:50:441100 days ago1650325844IN
0x160020B0...BcF2062FF
0 AVAX0.029159663.55929425
Swap136007782022-04-18 23:49:441100 days ago1650325784IN
0x160020B0...BcF2062FF
0 AVAX0.0282473462.7329829
Swap136007582022-04-18 23:49:041100 days ago1650325744IN
0x160020B0...BcF2062FF
0 AVAX0.0304945364.08057367
Swap136003352022-04-18 23:34:521100 days ago1650324892IN
0x160020B0...BcF2062FF
0 AVAX0.01798559.25397641
Swap135717302022-04-18 7:29:121101 days ago1650266952IN
0x160020B0...BcF2062FF
0 AVAX0.0228734771.50195666
Swap135668352022-04-18 4:44:121101 days ago1650257052IN
0x160020B0...BcF2062FF
0 AVAX0.024772385.87689279
External Swap135611812022-04-18 1:33:001101 days ago1650245580IN
0x160020B0...BcF2062FF
0 AVAX0.0358106543.56135125
Swap134894932022-04-16 10:00:311103 days ago1650103231IN
0x160020B0...BcF2062FF
0 AVAX0.0159094332.45100734
Swap134852912022-04-16 7:39:351103 days ago1650094775IN
0x160020B0...BcF2062FF
0 AVAX0.0184594338.78888993
Swap134816642022-04-16 5:37:451103 days ago1650087465IN
0x160020B0...BcF2062FF
0 AVAX0.0017282867.23792234
Swap134651842022-04-15 20:25:441103 days ago1650054344IN
0x160020B0...BcF2062FF
0 AVAX0.0206460267.57093056
External Swap134514902022-04-15 12:41:031104 days ago1650026463IN
0x160020B0...BcF2062FF
0 AVAX0.02899678.27113024
Swap133899752022-04-14 2:24:561105 days ago1649903096IN
0x160020B0...BcF2062FF
0 AVAX0.0169981755.99740002
Swap133525692022-04-13 5:25:121106 days ago1649827512IN
0x160020B0...BcF2062FF
0 AVAX0.0351103171.61744941
Swap133398582022-04-12 22:17:081106 days ago1649801828IN
0x160020B0...BcF2062FF
0 AVAX0.0277509296.87370324
External Swap133297822022-04-12 16:38:031106 days ago1649781483IN
0x160020B0...BcF2062FF
0 AVAX0.0243552587.51785849
External Swap133199672022-04-12 11:08:111107 days ago1649761691IN
0x160020B0...BcF2062FF
0.33198593 AVAX0.0169736762
Swap133174692022-04-12 9:44:281107 days ago1649756668IN
0x160020B0...BcF2062FF
0 AVAX0.0076576927.001
Swap133174332022-04-12 9:43:151107 days ago1649756595IN
0x160020B0...BcF2062FF
30 AVAX0.007744227.27035117
External Swap132897092022-04-11 18:09:581107 days ago1649700598IN
0x160020B0...BcF2062FF
0 AVAX0.06398404123.27074166
External Swap132896752022-04-11 18:08:471107 days ago1649700527IN
0x160020B0...BcF2062FF
0 AVAX0.05868242120.21834914
External Swap132896622022-04-11 18:08:211107 days ago1649700501IN
0x160020B0...BcF2062FF
0 AVAX0.05876769116.33205976
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
136078572022-04-19 3:48:441100 days ago1650340124
0x160020B0...BcF2062FF
4.12577629 AVAX
136078572022-04-19 3:48:441100 days ago1650340124
0x160020B0...BcF2062FF
4.12577629 AVAX
135717302022-04-18 7:29:121101 days ago1650266952
0x160020B0...BcF2062FF
0.17861335 AVAX
135717302022-04-18 7:29:121101 days ago1650266952
0x160020B0...BcF2062FF
0.17861335 AVAX
134894932022-04-16 10:00:311103 days ago1650103231
0x160020B0...BcF2062FF
5.81729254 AVAX
134894932022-04-16 10:00:311103 days ago1650103231
0x160020B0...BcF2062FF
5.81729254 AVAX
133525692022-04-13 5:25:121106 days ago1649827512
0x160020B0...BcF2062FF
2.86453198 AVAX
133525692022-04-13 5:25:121106 days ago1649827512
0x160020B0...BcF2062FF
2.86453198 AVAX
133297822022-04-12 16:38:031106 days ago1649781483
0x160020B0...BcF2062FF
0.37620779 AVAX
133297822022-04-12 16:38:031106 days ago1649781483
0x160020B0...BcF2062FF
0.37620779 AVAX
133199672022-04-12 11:08:111107 days ago1649761691
0x160020B0...BcF2062FF
0.33198593 AVAX
133174332022-04-12 9:43:151107 days ago1649756595
0x160020B0...BcF2062FF
30 AVAX
132847792022-04-11 15:23:381107 days ago1649690618
0x160020B0...BcF2062FF
0.43193181 AVAX
132847792022-04-11 15:23:381107 days ago1649690618
0x160020B0...BcF2062FF
0.43193181 AVAX
132649352022-04-11 4:16:361108 days ago1649650596
0x160020B0...BcF2062FF
6 AVAX
132411832022-04-10 14:57:411108 days ago1649602661
0x160020B0...BcF2062FF
0.16134069 AVAX
132411832022-04-10 14:57:411108 days ago1649602661
0x160020B0...BcF2062FF
0.16134069 AVAX
132411472022-04-10 14:56:281108 days ago1649602588
0x160020B0...BcF2062FF
0.26233561 AVAX
132411472022-04-10 14:56:281108 days ago1649602588
0x160020B0...BcF2062FF
0.26233561 AVAX
131978092022-04-09 14:28:071109 days ago1649514487
0x160020B0...BcF2062FF
5.71410577 AVAX
131978092022-04-09 14:28:071109 days ago1649514487
0x160020B0...BcF2062FF
5.71410577 AVAX
131791752022-04-09 4:00:111110 days ago1649476811
0x160020B0...BcF2062FF
1,068 AVAX
129969342022-04-04 20:35:391114 days ago1649104539
0x160020B0...BcF2062FF
2 AVAX
129484752022-04-03 16:46:451115 days ago1649004405
0x160020B0...BcF2062FF
2.3151116 AVAX
129313612022-04-03 6:59:551116 days ago1648969195
0x160020B0...BcF2062FF
0.32922856 AVAX
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WooRouter

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 58 : WooRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './interfaces/IWooPP.sol';
import './interfaces/IWETH.sol';
import './interfaces/IWooRouter.sol';

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

/// @title Woo Router implementation.
/// @notice Router for stateless execution of swaps against Woo private pool.
contract WooRouter is IWooRouter, Ownable, ReentrancyGuard {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    /* ----- Constant variables ----- */

    // Erc20 placeholder address for native tokens (e.g. eth, bnb, matic, etc)
    address constant ETH_PLACEHOLDER_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /* ----- State variables ----- */

    // Wrapper for native tokens (e.g. eth, bnb, matic, etc)
    // BSC WBNB: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
    address public immutable override WETH;

    IWooPP public override wooPool;

    mapping(address => bool) public isWhitelisted;

    address public quoteToken;

    /* ----- Callback Function ----- */

    receive() external payable {
        // only accept ETH from WETH or whitelisted external swaps.
        assert(msg.sender == WETH || isWhitelisted[msg.sender]);
    }

    /* ----- Query & swap APIs ----- */

    constructor(address weth, address newPool) public {
        require(weth != address(0), 'WooRouter: weth_ZERO_ADDR');
        WETH = weth;
        setPool(newPool);
    }

    /// @inheritdoc IWooRouter
    function querySwap(
        address fromToken,
        address toToken,
        uint256 fromAmount
    ) external view override returns (uint256 toAmount) {
        require(fromToken != address(0), 'WooRouter: fromToken_ADDR_ZERO');
        require(toToken != address(0), 'WooRouter: toToken_ADDR_ZERO');
        fromToken = (fromToken == ETH_PLACEHOLDER_ADDR) ? WETH : fromToken;
        toToken = (toToken == ETH_PLACEHOLDER_ADDR) ? WETH : toToken;
        if (fromToken == quoteToken) {
            toAmount = wooPool.querySellQuote(toToken, fromAmount);
        } else if (toToken == quoteToken) {
            toAmount = wooPool.querySellBase(fromToken, fromAmount);
        } else {
            uint256 quoteAmount = wooPool.querySellBase(fromToken, fromAmount);
            toAmount = wooPool.querySellQuote(toToken, quoteAmount);
        }
    }

    /// @inheritdoc IWooRouter
    function swap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address payable to,
        address rebateTo
    ) external payable override nonReentrant returns (uint256 realToAmount) {
        require(fromToken != address(0), 'WooRouter: fromToken_ADDR_ZERO');
        require(toToken != address(0), 'WooRouter: toToken_ADDR_ZERO');
        require(to != address(0), 'WooRouter: to_ADDR_ZERO');

        bool isFromETH = fromToken == ETH_PLACEHOLDER_ADDR;
        bool isToETH = toToken == ETH_PLACEHOLDER_ADDR;
        fromToken = isFromETH ? WETH : fromToken;
        toToken = isToETH ? WETH : toToken;

        // Step 1: transfer the source tokens to WooRouter
        if (isFromETH) {
            require(fromAmount <= msg.value, 'WooRouter: fromAmount_INVALID');
            IWETH(WETH).deposit{value: msg.value}();
        } else {
            TransferHelper.safeTransferFrom(fromToken, msg.sender, address(this), fromAmount);
        }

        // Step 2: swap and transfer
        TransferHelper.safeApprove(fromToken, address(wooPool), fromAmount);
        if (fromToken == quoteToken) {
            // case 1: quoteToken --> baseToken
            realToAmount = _sellQuoteAndTransfer(isToETH, toToken, fromAmount, minToAmount, to, rebateTo);
        } else if (toToken == quoteToken) {
            // case 2: fromToken --> quoteToken
            realToAmount = wooPool.sellBase(fromToken, fromAmount, minToAmount, to, rebateTo);
        } else {
            // case 3: fromToken --> quoteToken --> toToken
            uint256 quoteAmount = wooPool.sellBase(fromToken, fromAmount, 0, address(this), rebateTo);
            TransferHelper.safeApprove(quoteToken, address(wooPool), quoteAmount);
            realToAmount = _sellQuoteAndTransfer(isToETH, toToken, quoteAmount, minToAmount, to, rebateTo);
        }

        // Step 3: firing event
        emit WooRouterSwap(
            SwapType.WooSwap,
            isFromETH ? ETH_PLACEHOLDER_ADDR : fromToken,
            isToETH ? ETH_PLACEHOLDER_ADDR : toToken,
            fromAmount,
            realToAmount,
            msg.sender,
            to,
            rebateTo
        );
    }

    /// @inheritdoc IWooRouter
    function externalSwap(
        address approveTarget,
        address swapTarget,
        address fromToken,
        address toToken,
        uint256 fromAmount,
        address payable to,
        bytes calldata data
    ) external payable override {
        externalSwap(approveTarget, swapTarget, fromToken, toToken, fromAmount, 0, to, data);
    }

    /// @inheritdoc IWooRouter
    function externalSwap(
        address approveTarget,
        address swapTarget,
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address payable to,
        bytes calldata data
    ) public payable override nonReentrant returns (uint256 realToAmount) {
        require(approveTarget != address(0), 'WooRouter: approveTarget_ADDR_ZERO');
        require(swapTarget != address(0), 'WooRouter: swapTarget_ADDR_ZERO');
        require(fromToken != address(0), 'WooRouter: fromToken_ADDR_ZERO');
        require(toToken != address(0), 'WooRouter: toToken_ADDR_ZERO');
        require(to != address(0), 'WooRouter: to_ADDR_ZERO');
        require(isWhitelisted[approveTarget], 'WooRouter: APPROVE_TARGET_NOT_ALLOWED');
        require(isWhitelisted[swapTarget], 'WooRouter: SWAP_TARGET_NOT_ALLOWED');

        uint256 preBalance = _generalBalanceOf(toToken, address(this));
        _internalFallbackSwap(approveTarget, swapTarget, fromToken, fromAmount, data);
        uint256 postBalance = _generalBalanceOf(toToken, address(this));

        require(preBalance <= postBalance, 'WooRouter: balance_ERROR');
        realToAmount = postBalance.sub(preBalance);
        require(realToAmount >= minToAmount, 'WooRouter: realToAmount_NOT_ENOUGH');
        _generalTransfer(toToken, to, realToAmount);

        emit WooRouterSwap(SwapType.DodoSwap, fromToken, toToken, fromAmount, realToAmount, msg.sender, to, address(0));
    }

    /* ----- External Functions ---- */

    /// @dev query the swap price for baseToken -> quoteToken.
    /// @param baseToken the base token to sell
    /// @param baseAmount the amout of base token to sell
    /// @return quoteAmount the amount of swapped quote token
    function querySellBase(address baseToken, uint256 baseAmount) external view returns (uint256 quoteAmount) {
        require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO');
        baseToken = (baseToken == ETH_PLACEHOLDER_ADDR) ? WETH : baseToken;
        quoteAmount = wooPool.querySellBase(baseToken, baseAmount);
    }

    /// @dev query the swap price for quoteToken -> baseToken.
    /// @param baseToken the base token to swap
    /// @param quoteAmount the amount of quote token to swap
    /// @return baseAmount the amount of base token after swap
    function querySellQuote(address baseToken, uint256 quoteAmount) external view returns (uint256 baseAmount) {
        require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO');
        baseToken = (baseToken == ETH_PLACEHOLDER_ADDR) ? WETH : baseToken;
        baseAmount = wooPool.querySellQuote(baseToken, quoteAmount);
    }

    /// @dev swap baseToken -> quoteToken
    /// @param baseToken the base token
    /// @param baseAmount the amount of base token to sell
    /// @param minQuoteAmount the minimum quote amount to receive
    /// @param to the destination address
    /// @param rebateTo the rebate address
    /// @return realQuoteAmount the exact received amount of quote token
    function sellBase(
        address baseToken,
        uint256 baseAmount,
        uint256 minQuoteAmount,
        address to,
        address rebateTo
    ) external nonReentrant returns (uint256 realQuoteAmount) {
        require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO');
        require(to != address(0), 'WooRouter: to_ADDR_ZERO');
        TransferHelper.safeTransferFrom(baseToken, msg.sender, address(this), baseAmount);
        TransferHelper.safeApprove(baseToken, address(wooPool), baseAmount);
        realQuoteAmount = wooPool.sellBase(baseToken, baseAmount, minQuoteAmount, to, rebateTo);
        emit WooRouterSwap(
            SwapType.WooSwap,
            baseToken,
            quoteToken,
            baseAmount,
            realQuoteAmount,
            msg.sender,
            to,
            rebateTo
        );
    }

    /// @dev swap quoteToken -> baseToken
    /// @param baseToken the base token to receive
    /// @param quoteAmount the amount of quote token to sell
    /// @param minBaseAmount the minimum amount of base token for swap
    /// @param to the destination address
    /// @param rebateTo the address for the rebate
    /// @return realBaseAmount the exact received amount of base token to receive
    function sellQuote(
        address baseToken,
        uint256 quoteAmount,
        uint256 minBaseAmount,
        address to,
        address rebateTo
    ) external nonReentrant returns (uint256 realBaseAmount) {
        require(baseToken != address(0), 'WooRouter: baseToken_ADDR_ZERO');
        require(to != address(0), 'WooRouter: to_ADDR_ZERO');
        TransferHelper.safeTransferFrom(quoteToken, msg.sender, address(this), quoteAmount);
        TransferHelper.safeApprove(quoteToken, address(wooPool), quoteAmount);
        realBaseAmount = wooPool.sellQuote(baseToken, quoteAmount, minBaseAmount, to, rebateTo);
        emit WooRouterSwap(
            SwapType.WooSwap,
            quoteToken,
            baseToken,
            quoteAmount,
            realBaseAmount,
            msg.sender,
            to,
            rebateTo
        );
    }

    /* ----- Admin functions ----- */

    /// @dev Rescue the specified funds when stuck happen
    /// @param token token address
    /// @param amount amount of token to rescue
    function rescueFunds(address token, uint256 amount) external nonReentrant onlyOwner {
        require(token != address(0), 'WooRouter: token_ADDR_ZERO');
        TransferHelper.safeTransfer(token, msg.sender, amount);
    }

    /// @dev Set wooPool from newPool
    /// @param newPool Wooracle address
    function setPool(address newPool) public nonReentrant onlyOwner {
        require(newPool != address(0), 'WooRouter: newPool_ADDR_ZERO');
        wooPool = IWooPP(newPool);
        quoteToken = wooPool.quoteToken();
        require(quoteToken != address(0), 'WooRouter: quoteToken_ADDR_ZERO');
        emit WooPoolChanged(newPool);
    }

    /// @dev Add target address into whitelist
    /// @param target address that approved by WooRouter
    /// @param whitelisted approve to using WooRouter or not
    function setWhitelisted(address target, bool whitelisted) external nonReentrant onlyOwner {
        require(target != address(0), 'WooRouter: target_ADDR_ZERO');
        isWhitelisted[target] = whitelisted;
    }

    /* ----- Private Function ----- */

    function _sellQuoteAndTransfer(
        bool isToETH,
        address toToken,
        uint256 quoteAmount,
        uint256 minToAmount,
        address payable to,
        address rebateTo
    ) private returns (uint256 realToAmount) {
        if (isToETH) {
            realToAmount = wooPool.sellQuote(toToken, quoteAmount, minToAmount, address(this), rebateTo);
            IWETH(WETH).withdraw(realToAmount);
            require(to != address(0), 'WooRouter: to_ZERO_ADDR');
            TransferHelper.safeTransferETH(to, realToAmount);
        } else {
            realToAmount = wooPool.sellQuote(toToken, quoteAmount, minToAmount, to, rebateTo);
        }
    }

    function _internalFallbackSwap(
        address approveTarget,
        address swapTarget,
        address fromToken,
        uint256 fromAmount,
        bytes calldata data
    ) private {
        require(isWhitelisted[approveTarget], 'WooRouter: APPROVE_TARGET_NOT_ALLOWED');
        require(isWhitelisted[swapTarget], 'WooRouter: SWAP_TARGET_NOT_ALLOWED');

        if (fromToken != ETH_PLACEHOLDER_ADDR) {
            TransferHelper.safeTransferFrom(fromToken, msg.sender, address(this), fromAmount);
            TransferHelper.safeApprove(fromToken, approveTarget, fromAmount);
        } else {
            require(fromAmount <= msg.value, 'WooRouter: fromAmount_INVALID');
        }

        (bool success, ) = swapTarget.call{value: fromToken == ETH_PLACEHOLDER_ADDR ? fromAmount : 0}(data);
        require(success, 'WooRouter: FALLBACK_SWAP_FAILED');
    }

    function _generalTransfer(
        address token,
        address payable to,
        uint256 amount
    ) private {
        if (amount > 0) {
            if (token == ETH_PLACEHOLDER_ADDR) {
                TransferHelper.safeTransferETH(to, amount);
            } else {
                TransferHelper.safeTransfer(token, to, amount);
            }
        }
    }

    function _generalBalanceOf(address token, address who) private view returns (uint256) {
        return token == ETH_PLACEHOLDER_ADDR ? who.balance : IERC20(token).balanceOf(who);
    }
}

File 2 of 58 : StrategyAlpaca.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../../../interfaces/Alpaca/IAlpacaVault.sol';
import '../../../interfaces/Alpaca/IFairLaunch.sol';
import '../../../interfaces/PancakeSwap/IPancakeRouter.sol';
import '../../../interfaces/IWooAccessManager.sol';
import '../../../interfaces/IStrategy.sol';
import '../../../interfaces/IWETH.sol';
import '../BaseStrategy.sol';

contract StrategyAlpaca is BaseStrategy {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    /* ----- State Variables ----- */

    address public alpacaVault;
    address public fairLaunch;
    uint256 public immutable pid;

    address[] public rewardToWantRoute;

    /* ----- Constant Variables ----- */

    address public constant wrappedEther = address(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
    address public constant reward = address(0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F);
    address public constant uniRouter = address(0x10ED43C718714eb63d5aA57B78B54704E256024E);

    constructor(
        address initVault,
        address initAccessManager,
        address initAlpacaVault,
        address initFairLaunch,
        uint256 initPid,
        address[] memory initRewardToWantRoute
    ) public BaseStrategy(initVault, initAccessManager) {
        (address stakeToken, , , , ) = IFairLaunch(initFairLaunch).poolInfo(initPid);
        require(stakeToken == initAlpacaVault, 'StrategyAlpaca: wrong_initPid');
        alpacaVault = initAlpacaVault;
        fairLaunch = initFairLaunch;
        pid = initPid;
        rewardToWantRoute = initRewardToWantRoute;

        _giveAllowances();
    }

    /* ----- Public Functions ----- */

    function harvest() public override whenNotPaused {
        require(msg.sender == tx.origin || msg.sender == address(vault), 'StrategyAlpaca: EOA_or_vault');

        (uint256 amount, , , ) = IFairLaunch(fairLaunch).userInfo(pid, address(this));
        if (amount > 0) {
            IFairLaunch(fairLaunch).harvest(pid);
        }

        uint256 rewardBalance = IERC20(reward).balanceOf(address(this));
        if (rewardBalance > 0) {
            uint256 wantBalBefore = IERC20(want).balanceOf(address(this));
            IPancakeRouter(uniRouter).swapExactTokensForTokens(
                rewardBalance,
                0,
                rewardToWantRoute,
                address(this),
                now.add(600)
            );
            uint256 wantBalAfter = IERC20(want).balanceOf(address(this));
            uint256 perfAmount = wantBalAfter.sub(wantBalBefore);
            chargePerformanceFee(perfAmount);
        }

        deposit();
    }

    function deposit() public override whenNotPaused nonReentrant {
        uint256 wantBalance = IERC20(want).balanceOf(address(this));

        if (wantBalance > 0) {
            if (want == wrappedEther) {
                IWETH(wrappedEther).withdraw(wantBalance);
                IAlpacaVault(alpacaVault).deposit{value: wantBalance}(wantBalance);
            } else {
                IAlpacaVault(alpacaVault).deposit(wantBalance);
            }
            IFairLaunch(fairLaunch).deposit(address(this), pid, IAlpacaVault(alpacaVault).balanceOf(address(this)));
        }
    }

    function withdraw(uint256 amount) public override nonReentrant {
        require(msg.sender == address(vault), 'StrategyAlpaca: not_vault');
        require(amount > 0, 'StrategyAlpaca: amount_ZERO');

        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance < amount) {
            uint256 ibAmount = amount.mul(IAlpacaVault(alpacaVault).totalSupply()).div(
                IAlpacaVault(alpacaVault).totalToken()
            );
            IFairLaunch(fairLaunch).withdraw(address(this), pid, ibAmount);
            IAlpacaVault(alpacaVault).withdraw(IERC20(alpacaVault).balanceOf(address(this)));
            if (want == wrappedEther) {
                _wrapEther();
            }
            wantBalance = IERC20(want).balanceOf(address(this));
        }

        // just in case the decimal precision for the very left staking amount
        uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance;

        uint256 fee = chargeWithdrawalFee(withdrawAmount);
        if (withdrawAmount > fee) {
            TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee));
        }
    }

    function balanceOfPool() public view override returns (uint256) {
        (uint256 amount, , , ) = IFairLaunch(fairLaunch).userInfo(pid, address(this));

        return amount.mul(IAlpacaVault(alpacaVault).totalToken()).div(IAlpacaVault(alpacaVault).totalSupply());
    }

    /* ----- Private Functions ----- */

    function _giveAllowances() internal override {
        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(reward, uniRouter, uint256(-1));
        TransferHelper.safeApprove(want, alpacaVault, 0);
        TransferHelper.safeApprove(want, alpacaVault, uint256(-1));
        TransferHelper.safeApprove(alpacaVault, fairLaunch, 0);
        TransferHelper.safeApprove(alpacaVault, fairLaunch, uint256(-1));
    }

    function _removeAllowances() internal override {
        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(want, alpacaVault, 0);
        TransferHelper.safeApprove(alpacaVault, fairLaunch, 0);
    }

    function _withdrawAll() private {
        uint256 amount = balanceOfPool();
        uint256 ibAmount = amount.mul(IAlpacaVault(alpacaVault).totalSupply()).div(
            IAlpacaVault(alpacaVault).totalToken()
        );
        IFairLaunch(fairLaunch).withdraw(address(this), pid, ibAmount);
        IAlpacaVault(alpacaVault).withdraw(IERC20(alpacaVault).balanceOf(address(this)));
        if (want == wrappedEther) {
            _wrapEther();
        }
    }

    function _wrapEther() private {
        // NOTE: alpaca vault withdrawal returns the native BNB token; so wrapEther is required.
        uint256 etherBalance = address(this).balance;
        if (etherBalance > 0) {
            IWETH(wrappedEther).deposit{value: etherBalance}();
        }
    }

    /* ----- Admin Functions ----- */

    function retireStrat() external override {
        require(msg.sender == vault, '!vault');
        _withdrawAll();
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }

    function emergencyExit() external override onlyAdmin {
        _withdrawAll();
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }

    receive() external payable {}
}

File 3 of 58 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @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);

    /**
     * @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);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 4 of 58 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.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 IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    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));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @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. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "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 5 of 58 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 6 of 58 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../utils/Context.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

File 7 of 58 : Pausable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor () internal {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 8 of 58 : TransferHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeApprove: approve failed'
        );
    }

    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeTransfer: transfer failed'
        );
    }

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::transferFrom: transferFrom failed'
        );
    }

    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
    }
}

File 9 of 58 : IAlpacaVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IAlpacaVault {
    function balanceOf(address account) external view returns (uint256);

    function totalToken() external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function deposit(uint256 amountToken) external payable;

    function withdraw(uint256 share) external;
}

File 10 of 58 : IFairLaunch.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IFairLaunch {
    function deposit(
        address _for,
        uint256 pid,
        uint256 _amount
    ) external; // staking

    function withdraw(
        address _for,
        uint256 _pid,
        uint256 _amount
    ) external; // unstaking

    function harvest(uint256 _pid) external;

    function pendingAlpaca(uint256 _pid, address _user) external returns (uint256);

    function userInfo(uint256, address)
        external
        view
        returns (
            uint256 amount,
            uint256 rewardDebt,
            uint256 bonusDebt,
            uint256 fundedBy
        );

    function poolInfo(uint256)
        external
        view
        returns (
            address stakeToken,
            uint256 allocPoint,
            uint256 lastRewardBlock,
            uint256 accAlpacaPerShare,
            uint256 accAlpacaPerShareTilBonusEnd
        );
}

File 11 of 58 : IPancakeRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IPancakeRouter {
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );
}

File 12 of 58 : IWooAccessManager.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// @title Reward manager interface for WooFi Swap.
/// @notice this is for swap rebate or potential incentive program
interface IWooAccessManager {
    /* ----- Events ----- */

    event FeeAdminUpdated(address indexed feeAdmin, bool flag);

    event VaultAdminUpdated(address indexed vaultAdmin, bool flag);

    event RebateAdminUpdated(address indexed rebateAdmin, bool flag);

    event ZeroFeeVaultUpdated(address indexed vault, bool flag);

    /* ----- External Functions ----- */

    function isFeeAdmin(address feeAdmin) external returns (bool);

    function isVaultAdmin(address vaultAdmin) external returns (bool);

    function isRebateAdmin(address rebateAdmin) external returns (bool);

    function isZeroFeeVault(address vault) external returns (bool);

    /* ----- Admin Functions ----- */

    /// @notice Sets feeAdmin
    function setFeeAdmin(address feeAdmin, bool flag) external;

    /// @notice Batch sets feeAdmin
    function batchSetFeeAdmin(address[] calldata feeAdmins, bool[] calldata flags) external;

    /// @notice Sets vaultAdmin
    function setVaultAdmin(address vaultAdmin, bool flag) external;

    /// @notice Batch sets vaultAdmin
    function batchSetVaultAdmin(address[] calldata vaultAdmins, bool[] calldata flags) external;

    /// @notice Sets rebateAdmin
    function setRebateAdmin(address rebateAdmin, bool flag) external;

    /// @notice Batch sets rebateAdmin
    function batchSetRebateAdmin(address[] calldata rebateAdmins, bool[] calldata flags) external;

    /// @notice Sets zeroFeeVault
    function setZeroFeeVault(address vault, bool flag) external;

    /// @notice Batch sets zeroFeeVault
    function batchSetZeroFeeVault(address[] calldata vaults, bool[] calldata flags) external;
}

File 13 of 58 : IStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IStrategy {
    function vault() external view returns (address);

    function want() external view returns (address);

    function beforeDeposit() external;

    function deposit() external;

    function withdraw(uint256) external;

    function balanceOf() external view returns (uint256);

    function balanceOfWant() external view returns (uint256);

    function balanceOfPool() external view returns (uint256);

    function harvest() external;

    function retireStrat() external;

    function emergencyExit() external;

    function paused() external view returns (bool);

    function inCaseTokensGetStuck(address stuckToken) external;
}

File 14 of 58 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/// @title Wrapped ETH.
/// BSC: https://bscscan.com/address/0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c#code
interface IWETH {
    /// @dev Deposit ETH into WETH
    function deposit() external payable;

    /// @dev Transfer WETH to receiver
    /// @param to address of WETH receiver
    /// @param value amount of WETH to transfer
    /// @return get true when succeed, else false
    function transfer(address to, uint256 value) external returns (bool);

    /// @dev Withdraw WETH to ETH
    function withdraw(uint256) external;
}

File 15 of 58 : BaseStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../../interfaces/PancakeSwap/IMasterChef.sol';
import '../../interfaces/IWooAccessManager.sol';
import '../../interfaces/IStrategy.sol';
import '../../interfaces/IVault.sol';

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 * Base strategy abstract contract for:
 *  - vault and access manager setup
 *  - fees management
 *  - pause / unpause
 */
abstract contract BaseStrategy is Ownable, Pausable, IStrategy, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    /* ----- State Variables ----- */

    address public override want;
    address public immutable override vault;

    bool public harvestOnDeposit = true;

    /* ----- Constant Variables ----- */

    uint256 public constant FEE_MAX = 10000;
    uint256 public performanceFee = 300; // 1 in 10000th: 100: 1%, 300: 3%
    uint256 public withdrawalFee = 0; // 1 in 10000th: 1: 0.01%, 10: 0.1%
    address public performanceTreasury;
    address public withdrawalTreasury;

    IWooAccessManager public accessManager;

    event PerformanceFeeUpdated(uint256 newFee);
    event WithdrawalFeeUpdated(uint256 newFee);

    constructor(address initVault, address initAccessManager) public {
        require(initVault != address(0), 'BaseStrategy: initVault_ZERO_ADDR');
        require(initAccessManager != address(0), 'BaseStrategy: initAccessManager_ZERO_ADDR');
        vault = initVault;
        accessManager = IWooAccessManager(initAccessManager);
        want = IVault(initVault).want();
    }

    modifier onlyAdmin() {
        require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'BaseStrategy: NOT_ADMIN');
        _;
    }

    /* ----- Public Functions ----- */

    function beforeDeposit() public virtual override {
        require(msg.sender == address(vault), 'BaseStrategy: NOT_VAULT');
        if (harvestOnDeposit) {
            harvest();
        }
    }

    function balanceOf() public view override returns (uint256) {
        return balanceOfWant().add(balanceOfPool());
    }

    function balanceOfWant() public view override returns (uint256) {
        return IERC20(want).balanceOf(address(this));
    }

    /* ----- Internal Functions ----- */

    function chargePerformanceFee(uint256 amount) internal returns (uint256) {
        uint256 fee = amount.mul(performanceFee).div(FEE_MAX);
        if (fee > 0) {
            TransferHelper.safeTransfer(want, performanceTreasury, fee);
        }
        return fee;
    }

    function chargeWithdrawalFee(uint256 amount) internal returns (uint256) {
        uint256 fee = amount.mul(withdrawalFee).div(FEE_MAX);
        if (fee > 0) {
            TransferHelper.safeTransfer(want, withdrawalTreasury, fee);
        }
        return fee;
    }

    /* ----- Abstract Method ----- */

    function balanceOfPool() public view virtual override returns (uint256);

    function deposit() public virtual override;

    function withdraw(uint256 amount) external virtual override;

    function harvest() public virtual override;

    function retireStrat() external virtual override;

    function emergencyExit() external virtual override;

    function _giveAllowances() internal virtual;

    function _removeAllowances() internal virtual;

    /* ----- Admin Functions ----- */

    function setPerformanceFee(uint256 fee) external onlyAdmin {
        require(fee <= FEE_MAX, 'BaseStrategy: fee_EXCCEEDS_MAX');
        performanceFee = fee;
        emit PerformanceFeeUpdated(fee);
    }

    function setWithdrawalFee(uint256 fee) external onlyAdmin {
        require(fee <= FEE_MAX, 'BaseStrategy: fee_EXCCEEDS_MAX');
        require(fee <= 500, 'BaseStrategy: fee_EXCCEEDS_5%'); // less than 5%
        withdrawalFee = fee;
        emit WithdrawalFeeUpdated(fee);
    }

    function setPerformanceTreasury(address treasury) external onlyAdmin {
        require(treasury != address(0), 'BaseStrategy: treasury_ZERO_ADDR');
        performanceTreasury = treasury;
    }

    function setWithdrawalTreasury(address treasury) external onlyAdmin {
        require(treasury != address(0), 'BaseStrategy: treasury_ZERO_ADDR');
        withdrawalTreasury = treasury;
    }

    function setHarvestOnDeposit(bool newHarvestOnDeposit) external onlyAdmin {
        harvestOnDeposit = newHarvestOnDeposit;
    }

    function pause() public onlyAdmin {
        _pause();
        _removeAllowances();
    }

    function unpause() external onlyAdmin {
        _unpause();
        _giveAllowances();
        deposit();
    }

    function paused() public view override(IStrategy, Pausable) returns (bool) {
        return Pausable.paused();
    }

    function inCaseTokensGetStuck(address stuckToken) external override onlyAdmin {
        require(stuckToken != address(want), 'BaseStrategy: stuckToken_NOT_WANT');
        require(stuckToken != address(0), 'BaseStrategy: stuckToken_ZERO_ADDR');

        uint256 amount = IERC20(stuckToken).balanceOf(address(this));
        if (amount > 0) {
            TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
        }
    }

    function inCaseNativeTokensGetStuck() external onlyAdmin {
        // NOTE: vault never needs native tokens to do the yield farming;
        // This native token balance indicates a user's incorrect transfer.
        if (address(this).balance > 0) {
            TransferHelper.safeTransferETH(msg.sender, address(this).balance);
        }
    }
}

File 16 of 58 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 17 of 58 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 18 of 58 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () internal {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 19 of 58 : IMasterChef.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IMasterChef {
    function deposit(uint256 pid, uint256 amount) external;

    function withdraw(uint256 pid, uint256 amount) external;

    function enterStaking(uint256 amount) external;

    function leaveStaking(uint256 amount) external;

    function emergencyWithdraw(uint256 pid) external;

    function pendingCake(uint256 pid, address user) external view returns (uint256);

    function poolInfo(uint256 pid)
        external
        view
        returns (
            address,
            uint256,
            uint256,
            uint256
        );

    function userInfo(uint256 pid, address user) external view returns (uint256, uint256);
}

File 20 of 58 : IVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IVault {
    function want() external view returns (address);

    function deposit(uint256 amount) external payable;

    function withdraw(uint256 shares) external;

    function earn() external;

    function available() external view returns (uint256);

    function balance() external view returns (uint256);

    function getPricePerFullShare() external view returns (uint256);
}

File 21 of 58 : VoidStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import './BaseStrategy.sol';

contract VoidStrategy is BaseStrategy {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    constructor(address initVault, address initAccessManager) public BaseStrategy(initVault, initAccessManager) {
        _giveAllowances();
    }

    /* ----- External Functions ----- */

    function withdraw(uint256 amount) external override nonReentrant {
        require(msg.sender == vault, 'VoidStrategy: NOT_VAULT');

        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance;

        uint256 fee = chargeWithdrawalFee(withdrawAmount);
        if (withdrawAmount > fee) {
            TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee));
        }
    }

    function harvest() public override whenNotPaused {
        require(msg.sender == tx.origin || msg.sender == vault, 'VoidStrategy: EOA_OR_VAULT');
        deposit();
    }

    function deposit() public override whenNotPaused nonReentrant {}

    function balanceOfPool() public view override returns (uint256) {
        return 0;
    }

    /* ----- Private Functions ----- */

    function _giveAllowances() internal override {}

    function _removeAllowances() internal override {}

    function retireStrat() external override {
        require(msg.sender == vault, '!vault');
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }

    function emergencyExit() external override onlyAdmin {
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }
}

File 22 of 58 : WooVaultManager.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './libraries/InitializableOwnable.sol';
import './libraries/DecimalMath.sol';
import './interfaces/IWooracle.sol';
import './interfaces/IWooVaultManager.sol';
import './interfaces/IWooGuardian.sol';
import './interfaces/AggregatorV3Interface.sol';
import './interfaces/IWooAccessManager.sol';

import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/utils/EnumerableSet.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

contract WooVaultManager is InitializableOwnable, ReentrancyGuard, IWooVaultManager {
    using SafeMath for uint256;
    using DecimalMath for uint256;
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(address => uint256) public override vaultWeight;
    uint256 public totalWeight;

    IWooPP private wooPP;

    address public immutable override quoteToken; // USDT
    address public immutable rewardToken; // WOO

    EnumerableSet.AddressSet private vaultSet;

    IWooAccessManager public accessManager;

    /* ----- Modifiers ----- */

    modifier onlyAdmin() {
        require(msg.sender == _OWNER_ || accessManager.isVaultAdmin(msg.sender), 'WooVaultManager: NOT_ADMIN');
        _;
    }

    constructor(
        address newQuoteToken,
        address newRewardToken,
        address newAccessManager
    ) public {
        require(newQuoteToken != address(0), 'WooVaultManager: INVALID_QUOTE');
        require(newRewardToken != address(0), 'WooVaultManager: INVALID_RAWARD_TOKEN');
        initOwner(msg.sender);
        quoteToken = newQuoteToken;
        rewardToken = newRewardToken;
        accessManager = IWooAccessManager(newAccessManager);
    }

    function allVaults() external view override returns (address[] memory) {
        address[] memory vaults = new address[](vaultSet.length());
        for (uint256 i = 0; i < vaultSet.length(); ++i) {
            vaults[i] = vaultSet.at(i);
        }
        return vaults;
    }

    function addReward(uint256 amount) external override nonReentrant {
        if (amount == 0) {
            return;
        }

        uint256 balanceBefore = IERC20(quoteToken).balanceOf(address(this));
        TransferHelper.safeTransferFrom(quoteToken, msg.sender, address(this), amount);
        uint256 balanceAfter = IERC20(quoteToken).balanceOf(address(this));
        require(balanceAfter.sub(balanceBefore) >= amount, 'WooVaultManager: amount INSUFF');
    }

    function pendingReward(address vaultAddr) external view override returns (uint256) {
        require(vaultAddr != address(0), 'WooVaultManager: vaultAddr_ZERO_ADDR');
        uint256 totalReward = IERC20(quoteToken).balanceOf(address(this));
        return totalReward.mul(vaultWeight[vaultAddr]).div(totalWeight);
    }

    function pendingAllReward() external view override returns (uint256) {
        return IERC20(quoteToken).balanceOf(address(this));
    }

    // ----------- Admin Functions ------------- //

    function setVaultWeight(address vaultAddr, uint256 weight) external override onlyAdmin {
        require(vaultAddr != address(0), 'WooVaultManager: vaultAddr_ZERO_ADDR');

        // NOTE: First clear all the pending reward if > 100u to keep the things fair
        if (IERC20(quoteToken).balanceOf(address(this)) >= 1e20) {
            distributeAllReward();
        }

        uint256 prevWeight = vaultWeight[vaultAddr];
        vaultWeight[vaultAddr] = weight;
        totalWeight = totalWeight.add(weight).sub(prevWeight);

        if (weight == 0) {
            vaultSet.remove(vaultAddr);
        } else {
            vaultSet.add(vaultAddr);
        }

        emit VaultWeightUpdated(vaultAddr, weight);
    }

    function distributeAllReward() public override onlyAdmin {
        uint256 totalRewardInQuote = IERC20(quoteToken).balanceOf(address(this));
        if (totalRewardInQuote == 0 || totalWeight == 0) {
            return;
        }

        uint256 balanceBefore = IERC20(rewardToken).balanceOf(address(this));
        TransferHelper.safeApprove(quoteToken, address(wooPP), totalRewardInQuote);
        uint256 wooAmount = IWooPP(wooPP).sellQuote(rewardToken, totalRewardInQuote, 0, address(this), address(0));
        uint256 balanceAfter = IERC20(rewardToken).balanceOf(address(this));
        require(balanceAfter.sub(balanceBefore) >= wooAmount, 'WooVaultManager: woo amount INSUFF');

        for (uint256 i = 0; i < vaultSet.length(); ++i) {
            address vaultAddr = vaultSet.at(i);
            uint256 vaultAmount = wooAmount.mul(vaultWeight[vaultAddr]).div(totalWeight);
            if (vaultAmount > 0) {
                TransferHelper.safeTransfer(rewardToken, vaultAddr, vaultAmount);
            }
            emit RewardDistributed(vaultAddr, vaultAmount);
        }
    }

    function setWooPP(address newWooPP) external onlyAdmin {
        require(newWooPP != address(0), 'WooVaultManager: newWooPP_ZERO_ADDR');
        wooPP = IWooPP(newWooPP);
        require(wooPP.quoteToken() == quoteToken, 'WooVaultManager: wooPP_quote_token_INVALID');
    }

    function setAccessManager(address newAccessManager) external onlyOwner {
        require(newAccessManager != address(0), 'WooVaultManager: newAccessManager_ZERO_ADDR');
        accessManager = IWooAccessManager(newAccessManager);
    }

    function emergencyWithdraw(address token, address to) public onlyOwner {
        require(token != address(0), 'WooVaultManager: token_ZERO_ADDR');
        require(to != address(0), 'WooVaultManager: to_ZERO_ADDR');
        TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
    }
}

File 23 of 58 : InitializableOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/**
 * @title Ownable initializable contract.
 *
 * @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_, 'InitializableOwnable: SHOULD_NOT_BE_INITIALIZED');
        _;
    }

    modifier onlyOwner() {
        require(msg.sender == _OWNER_, 'InitializableOwnable: NOT_OWNER');
        _;
    }

    // ============ Functions ============

    /// @dev Init _OWNER_ from newOwner and set _INITIALIZED_ as true
    /// @param newOwner new owner address
    function initOwner(address newOwner) public notInitialized {
        _INITIALIZED_ = true;
        _OWNER_ = newOwner;
    }

    /// @dev Set _NEW_OWNER_ from newOwner
    /// @param newOwner new owner address
    function transferOwnership(address newOwner) public onlyOwner {
        emit OwnershipTransferPrepared(_OWNER_, newOwner);
        _NEW_OWNER_ = newOwner;
    }

    /// @dev Set _OWNER_ from _NEW_OWNER_ and set _NEW_OWNER_ equal zero address
    function claimOwnership() public {
        require(msg.sender == _NEW_OWNER_, 'InitializableOwnable: INVALID_CLAIM');
        emit OwnershipTransferred(_OWNER_, _NEW_OWNER_);
        _OWNER_ = _NEW_OWNER_;
        _NEW_OWNER_ = address(0);
    }
}

File 24 of 58 : DecimalMath.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

import '@openzeppelin/contracts/math/SafeMath.sol';

/**
 * @title DecimalMath
 *
 * @notice Functions for fixed point number with 18 decimals
 */
library DecimalMath {
    using SafeMath for uint256;

    uint256 internal constant ONE = 10**18;
    uint256 internal constant TWO = 2 * 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 _divCeil(target.mul(d), 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 _divCeil(target.mul(10**18), d);
    }

    function reciprocalFloor(uint256 target) internal pure returns (uint256) {
        return uint256(10**36).div(target);
    }

    function reciprocalCeil(uint256 target) internal pure returns (uint256) {
        return _divCeil(uint256(10**36), target);
    }

    function _divCeil(uint256 a, uint256 b) private pure returns (uint256) {
        uint256 quotient = a.div(b);
        uint256 remainder = a - quotient * b;
        if (remainder > 0) {
            return quotient + 1;
        } else {
            return quotient;
        }
    }
}

File 25 of 58 : IWooracle.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// @title The oracle interface by Woo.Network.
/// @notice update and posted the latest price info by Woo.
interface IWooracle {
    /// @dev the quote token for Wooracle's pricing.
    /// @return the quote token
    function quoteToken() external view returns (address);

    /// @dev the price for the given base token
    /// @param base baseToken address
    /// @return priceNow the current price of base token
    /// @return feasible whether the current price is feasible and valid
    function price(address base) external view returns (uint256 priceNow, bool feasible);

    function getPrice(address base) external view returns (uint256);

    function getSpread(address base) external view returns (uint256);

    function getCoeff(address base) external view returns (uint256);

    /// @dev returns the state for the given base token.
    /// @param base baseToken address
    /// @return priceNow the current price of base token
    /// @return spreadNow the current spread of base token
    /// @return coeffNow the slippage coefficient of base token
    /// @return feasible whether the current state is feasible and valid
    function state(address base)
        external
        view
        returns (
            uint256 priceNow,
            uint256 spreadNow,
            uint256 coeffNow,
            bool feasible
        );

    /// @dev returns the last updated timestamp
    /// @return the last updated timestamp
    function timestamp() external view returns (uint256);

    /// @dev returns whether the base token price is valid.
    /// @param base baseToken address
    /// @return whether the base token price is valid.
    function isFeasible(address base) external view returns (bool);
}

File 26 of 58 : IWooVaultManager.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// @title Vault reward manager interface for WooFi Swap.
interface IWooVaultManager {
    event VaultWeightUpdated(address indexed vaultAddr, uint256 weight);
    event RewardDistributed(address indexed vaultAddr, uint256 amount);

    /// @dev Gets the reward weight for the given vault.
    /// @param vaultAddr the vault address
    /// @return The weight of the given vault.
    function vaultWeight(address vaultAddr) external view returns (uint256);

    /// @dev Sets the reward weight for the given vault.
    /// @param vaultAddr the vault address
    /// @param weight the vault weight
    function setVaultWeight(address vaultAddr, uint256 weight) external;

    /// @dev Adds the reward quote amount.
    /// Note: The reward will be stored in this manager contract for
    ///       further weight adjusted distribution.
    /// @param quoteAmount the reward amount in quote token.
    function addReward(uint256 quoteAmount) external;

    /// @dev Pending amount in quote token for the given vault.
    /// @param vaultAddr the vault address
    function pendingReward(address vaultAddr) external view returns (uint256);

    /// @dev All pending amount in quote token.
    /// @return the total pending reward
    function pendingAllReward() external view returns (uint256);

    /// @dev Distributes the reward to all the vaults based on the weights.
    function distributeAllReward() external;

    /// @dev All the vaults
    /// @return the vault address array
    function allVaults() external view returns (address[] memory);

    /// @dev get the quote token address
    /// @return address of quote token
    function quoteToken() external view returns (address);
}

File 27 of 58 : IWooGuardian.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import '../interfaces/IWooPP.sol';

/// @title Guardian interface to ensure the trading price and volume correct
interface IWooGuardian {
    event ChainlinkRefOracleUpdated(address indexed token, address indexed chainlinkRefOracle);

    /* ----- Main check APIs ----- */

    function checkSwapPrice(
        uint256 price,
        address fromToken,
        address toToken
    ) external view;

    function checkInputAmount(address token, uint256 inputAmount) external view;

    function checkSwapAmount(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 toAmount
    ) external view;
}

File 28 of 58 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

interface AggregatorV3Interface {
    function decimals() external view returns (uint8);

    function description() external view returns (string memory);

    function version() external view returns (uint256);

    /// getRoundData and latestRoundData should both raise "No data present"
    /// if they do not have data to report, instead of returning unset values
    /// which could be misinterpreted as actual reported values.
    function getRoundData(uint80 _roundId)
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );

    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );
}

File 29 of 58 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}

File 30 of 58 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

File 31 of 58 : IWooPP.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// @title Woo private pool for swap.
/// @notice Use this contract to directly interfact with woo's synthetic proactive
///         marketing making pool.
/// @author woo.network
interface IWooPP {
    /* ----- Type declarations ----- */

    /// @dev struct info to store the token info
    struct TokenInfo {
        uint112 reserve; // Token balance
        uint112 threshold; // Threshold for reserve update
        uint32 lastResetTimestamp; // Timestamp for last param update
        uint64 R; // Rebalance coefficient [0, 1]
        uint112 target; // Targeted balance for pricing
        bool isValid; // is this token info valid
    }

    /* ----- Events ----- */

    event StrategistUpdated(address indexed strategist, bool flag);
    event FeeManagerUpdated(address indexed newFeeManager);
    event RewardManagerUpdated(address indexed newRewardManager);
    event WooracleUpdated(address indexed newWooracle);
    event WooGuardianUpdated(address indexed newWooGuardian);
    event ParametersUpdated(address indexed baseToken, uint256 newThreshold, uint256 newR);
    event Withdraw(address indexed token, address indexed to, uint256 amount);
    event WooSwap(
        address indexed fromToken,
        address indexed toToken,
        uint256 fromAmount,
        uint256 toAmount,
        address from,
        address indexed to,
        address rebateTo
    );

    /* ----- External Functions ----- */

    /// @dev Swap baseToken into quoteToken
    /// @param baseToken the base token
    /// @param baseAmount amount of baseToken that user want to swap
    /// @param minQuoteAmount minimum amount of quoteToken that user accept to receive
    /// @param to quoteToken receiver address
    /// @param rebateTo the wallet address for rebate
    /// @return quoteAmount the swapped amount of quote token
    function sellBase(
        address baseToken,
        uint256 baseAmount,
        uint256 minQuoteAmount,
        address to,
        address rebateTo
    ) external returns (uint256 quoteAmount);

    /// @dev Swap quoteToken into baseToken
    /// @param baseToken the base token
    /// @param quoteAmount amount of quoteToken that user want to swap
    /// @param minBaseAmount minimum amount of baseToken that user accept to receive
    /// @param to baseToken receiver address
    /// @param rebateTo the wallet address for rebate
    /// @return baseAmount the swapped amount of base token
    function sellQuote(
        address baseToken,
        uint256 quoteAmount,
        uint256 minBaseAmount,
        address to,
        address rebateTo
    ) external returns (uint256 baseAmount);

    /// @dev Query the amount for selling the base token amount.
    /// @param baseToken the base token to sell
    /// @param baseAmount the amount to sell
    /// @return quoteAmount the swapped quote amount
    function querySellBase(address baseToken, uint256 baseAmount) external view returns (uint256 quoteAmount);

    /// @dev Query the amount for selling the quote token.
    /// @param baseToken the base token to receive (buy)
    /// @param quoteAmount the amount to sell
    /// @return baseAmount the swapped base token amount
    function querySellQuote(address baseToken, uint256 quoteAmount) external view returns (uint256 baseAmount);

    /// @dev get the quote token address
    /// @return address of quote token
    function quoteToken() external view returns (address);
}

File 32 of 58 : WooStakingVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import './libraries/DecimalMath.sol';
import './interfaces/IWooAccessManager.sol';

contract WooStakingVault is ERC20, Ownable, ReentrancyGuard, Pausable {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;
    using DecimalMath for uint256;

    struct UserInfo {
        uint256 reserveAmount; // amount of stakedToken user reverseWithdraw
        uint256 lastReserveWithdrawTime; // keeps track of reverseWithdraw time for potential penalty
    }

    /* ----- Events ----- */

    event Deposit(address indexed user, uint256 depositAmount, uint256 mintShares);
    event ReserveWithdraw(address indexed user, uint256 reserveAmount, uint256 burnShares);
    event Withdraw(address indexed user, uint256 withdrawAmount, uint256 withdrawFee);
    event InstantWithdraw(address indexed user, uint256 withdrawAmount, uint256 withdrawFee);
    event RewardAdded(
        address indexed sender,
        uint256 balanceBefore,
        uint256 sharePriceBefore,
        uint256 balanceAfter,
        uint256 sharePriceAfter
    );

    /* ----- State variables ----- */

    IERC20 public immutable stakedToken;
    mapping(address => uint256) public costSharePrice;
    mapping(address => UserInfo) public userInfo;

    uint256 public totalReserveAmount = 0; // affected by reserveWithdraw and withdraw
    uint256 public withdrawFeePeriod = 7 days;
    uint256 public withdrawFee = 500; // 5% (10000 as denominator)

    address public treasury;
    IWooAccessManager public wooAccessManager;

    /* ----- Constant variables ----- */

    uint256 public constant MAX_WITHDRAW_FEE_PERIOD = 7 days;
    uint256 public constant MAX_WITHDRAW_FEE = 500; // 5% (10000 as denominator)

    constructor(
        address initialStakedToken,
        address initialTreasury,
        address initialWooAccessManager
    )
        public
        ERC20(
            string(abi.encodePacked('Interest Bearing ', ERC20(initialStakedToken).name())),
            string(abi.encodePacked('x', ERC20(initialStakedToken).symbol()))
        )
    {
        require(initialStakedToken != address(0), 'WooStakingVault: initialStakedToken_ZERO_ADDR');
        require(initialTreasury != address(0), 'WooStakingVault: initialTreasury_ZERO_ADDR');
        require(initialWooAccessManager != address(0), 'WooStakingVault: initialWooAccessManager_ZERO_ADDR');

        stakedToken = IERC20(initialStakedToken);
        treasury = initialTreasury;
        wooAccessManager = IWooAccessManager(initialWooAccessManager);
    }

    /* ----- External Functions ----- */

    function deposit(uint256 amount) external nonReentrant whenNotPaused {
        require(amount > 0, 'WooStakingVault: amount_CAN_NOT_BE_ZERO');

        uint256 balanceBefore = balance();
        TransferHelper.safeTransferFrom(address(stakedToken), msg.sender, address(this), amount);
        uint256 balanceAfter = balance();
        amount = balanceAfter.sub(balanceBefore);

        uint256 xTotalSupply = totalSupply();
        uint256 shares = xTotalSupply == 0 ? amount : amount.mul(xTotalSupply).div(balanceBefore);

        // must be executed before _mint
        _updateCostSharePrice(amount, shares);

        _mint(msg.sender, shares);

        emit Deposit(msg.sender, amount, shares);
    }

    function reserveWithdraw(uint256 shares) external nonReentrant {
        require(shares > 0, 'WooStakingVault: shares_CAN_NOT_BE_ZERO');
        require(shares <= balanceOf(msg.sender), 'WooStakingVault: shares exceed balance');

        uint256 currentReserveAmount = shares.mulFloor(getPricePerFullShare()); // calculate reserveAmount before _burn
        uint256 poolBalance = balance();
        if (poolBalance < currentReserveAmount) {
            // in case reserve amount exceeds pool balance
            currentReserveAmount = poolBalance;
        }
        _burn(msg.sender, shares);

        totalReserveAmount = totalReserveAmount.add(currentReserveAmount);

        UserInfo storage user = userInfo[msg.sender];
        user.reserveAmount = user.reserveAmount.add(currentReserveAmount);
        user.lastReserveWithdrawTime = block.timestamp;

        emit ReserveWithdraw(msg.sender, currentReserveAmount, shares);
    }

    function withdraw() external nonReentrant {
        UserInfo storage user = userInfo[msg.sender];

        uint256 withdrawAmount = user.reserveAmount;
        require(withdrawAmount > 0, 'WooStakingVault: withdrawAmount_CAN_NOT_BE_ZERO');

        uint256 fee = 0;
        if (block.timestamp < user.lastReserveWithdrawTime.add(withdrawFeePeriod)) {
            fee = withdrawAmount.mul(withdrawFee).div(10000);
            if (fee > 0) {
                TransferHelper.safeTransfer(address(stakedToken), treasury, fee);
            }
        }
        uint256 withdrawAmountAfterFee = withdrawAmount.sub(fee);

        user.reserveAmount = 0;
        totalReserveAmount = totalReserveAmount.sub(withdrawAmount);
        TransferHelper.safeTransfer(address(stakedToken), msg.sender, withdrawAmountAfterFee);

        emit Withdraw(msg.sender, withdrawAmount, fee);
    }

    function instantWithdraw(uint256 shares) external nonReentrant {
        require(shares > 0, 'WooStakingVault: shares_CAN_NOT_BE_ZERO');
        require(shares <= balanceOf(msg.sender), 'WooStakingVault: shares exceed balance');

        uint256 withdrawAmount = shares.mulFloor(getPricePerFullShare());

        uint256 poolBalance = balance();
        if (poolBalance < withdrawAmount) {
            withdrawAmount = poolBalance;
        }

        _burn(msg.sender, shares);

        uint256 fee = wooAccessManager.isZeroFeeVault(msg.sender) ? 0 : withdrawAmount.mul(withdrawFee).div(10000);
        if (fee > 0) {
            TransferHelper.safeTransfer(address(stakedToken), treasury, fee);
        }
        uint256 withdrawAmountAfterFee = withdrawAmount.sub(fee);

        TransferHelper.safeTransfer(address(stakedToken), msg.sender, withdrawAmountAfterFee);

        emit InstantWithdraw(msg.sender, withdrawAmount, fee);
    }

    function addReward(uint256 amount) external whenNotPaused {
        // Note: this method is only for adding Woo reward. Users may not call this method to deposit woo token.
        require(amount > 0, 'WooStakingVault: amount_CAN_NOT_BE_ZERO');
        uint256 balanceBefore = balance();
        uint256 sharePriceBefore = getPricePerFullShare();
        TransferHelper.safeTransferFrom(address(stakedToken), msg.sender, address(this), amount);
        uint256 balanceAfter = balance();
        uint256 sharePriceAfter = getPricePerFullShare();

        emit RewardAdded(msg.sender, balanceBefore, sharePriceBefore, balanceAfter, sharePriceAfter);
    }

    /* ----- Public Functions ----- */

    function getPricePerFullShare() public view returns (uint256) {
        if (totalSupply() == 0) {
            return 1e18;
        }
        return balance().divFloor(totalSupply());
    }

    function balance() public view returns (uint256) {
        return stakedToken.balanceOf(address(this)).sub(totalReserveAmount);
    }

    /* ----- Private Functions ----- */

    function _updateCostSharePrice(uint256 amount, uint256 shares) private {
        uint256 sharesBefore = balanceOf(msg.sender);
        uint256 costBefore = costSharePrice[msg.sender];
        uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares));

        costSharePrice[msg.sender] = costAfter;
    }

    /* ----- Admin Functions ----- */

    /// @notice Sets withdraw fee period
    /// @dev Only callable by the contract owner.
    function setWithdrawFeePeriod(uint256 newWithdrawFeePeriod) external onlyOwner {
        require(
            newWithdrawFeePeriod <= MAX_WITHDRAW_FEE_PERIOD,
            'WooStakingVault: newWithdrawFeePeriod>MAX_WITHDRAW_FEE_PERIOD'
        );
        withdrawFeePeriod = newWithdrawFeePeriod;
    }

    /// @notice Sets withdraw fee
    /// @dev Only callable by the contract owner.
    function setWithdrawFee(uint256 newWithdrawFee) external onlyOwner {
        require(newWithdrawFee <= MAX_WITHDRAW_FEE, 'WooStakingVault: newWithdrawFee>MAX_WITHDRAW_FEE');
        withdrawFee = newWithdrawFee;
    }

    /// @notice Sets treasury address
    /// @dev Only callable by the contract owner.
    function setTreasury(address newTreasury) external onlyOwner {
        require(newTreasury != address(0), 'WooStakingVault: newTreasury_ZERO_ADDR');
        treasury = newTreasury;
    }

    /// @notice Sets WooAccessManager
    /// @dev Only callable by the contract owner.
    function setWooAccessManager(address newWooAccessManager) external onlyOwner {
        require(newWooAccessManager != address(0), 'WooStakingVault: newWooAccessManager_ZERO_ADDR');
        wooAccessManager = IWooAccessManager(newWooAccessManager);
    }

    /**
        @notice Rescues random funds stuck.
        This method only saves the irrelevant tokens just in case users deposited in mistake.
        It cannot transfer any of user staked tokens.
    */
    function inCaseTokensGetStuck(address stuckToken) external onlyOwner {
        require(stuckToken != address(0), 'WooStakingVault: stuckToken_ZERO_ADDR');
        require(stuckToken != address(stakedToken), 'WooStakingVault: stuckToken_CAN_NOT_BE_stakedToken');

        uint256 amount = IERC20(stuckToken).balanceOf(address(this));
        TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
    }

    /// @notice Pause the contract.
    function pause() external onlyOwner {
        super._pause();
    }

    /// @notice Restart the contract.
    function unpause() external onlyOwner {
        super._unpause();
    }
}

File 33 of 58 : WooRebateManager.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './libraries/InitializableOwnable.sol';
import './libraries/DecimalMath.sol';
import './interfaces/IWooracle.sol';
import './interfaces/IWooRebateManager.sol';
import './interfaces/IWooGuardian.sol';
import './interfaces/AggregatorV3Interface.sol';
import './interfaces/IWooAccessManager.sol';

import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

contract WooRebateManager is InitializableOwnable, ReentrancyGuard, IWooRebateManager {
    using SafeMath for uint256;
    using DecimalMath for uint256;
    using SafeERC20 for IERC20;

    // Note: this is the percent rate of the total swap fee (not the swap volume)
    // decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01%
    //
    // e.g. suppose:
    //   rebateRate = 1e17 (10%), so the rebate amount is total_swap_fee * 10%.
    mapping(address => uint256) public override rebateRate;

    // pending rebate amount in quote token
    mapping(address => uint256) public pendingRebate;

    IWooPP private wooPP;

    address public immutable override quoteToken; // USDT
    address public immutable rewardToken; // WOO

    IWooAccessManager public accessManager;

    /* ----- Modifiers ----- */

    modifier onlyAdmin() {
        require(msg.sender == _OWNER_ || accessManager.isRebateAdmin(msg.sender), 'WooRebateManager: NOT_ADMIN');
        _;
    }

    constructor(
        address newQuoteToken,
        address newRewardToken,
        address newAccessManager
    ) public {
        require(newQuoteToken != address(0), 'WooRebateManager: INVALID_QUOTE');
        require(newRewardToken != address(0), 'WooRebateManager: INVALID_REWARD_TOKEN');
        initOwner(msg.sender);
        quoteToken = newQuoteToken;
        rewardToken = newRewardToken;
        accessManager = IWooAccessManager(newAccessManager);
    }

    function pendingRebateInUSDT(address brokerAddr) external view override returns (uint256) {
        require(brokerAddr != address(0), 'WooRebateManager: zero_brokerAddr');
        return pendingRebate[brokerAddr];
    }

    function pendingRebateInWOO(address brokerAddr) external view override returns (uint256) {
        require(brokerAddr != address(0), 'WooRebateManager: zero_brokerAddr');
        return wooPP.querySellQuote(rewardToken, pendingRebate[brokerAddr]);
    }

    function claimRebate() external override nonReentrant {
        require(pendingRebate[msg.sender] > 0, 'WooRebateManager: NO_pending_rebate');

        uint256 quoteAmount = pendingRebate[msg.sender];
        // Note: set the pending rebate early to make external interactions safe.
        pendingRebate[msg.sender] = 0;

        uint256 balanceBefore = IERC20(rewardToken).balanceOf(address(this));
        TransferHelper.safeApprove(quoteToken, address(wooPP), quoteAmount);
        uint256 wooAmount = wooPP.sellQuote(rewardToken, quoteAmount, 0, address(this), address(0));
        uint256 balanceAfter = IERC20(rewardToken).balanceOf(address(this));
        require(balanceAfter.sub(balanceBefore) >= wooAmount, 'WooRebateManager: woo amount INSUFF');

        if (wooAmount > 0) {
            TransferHelper.safeTransfer(rewardToken, msg.sender, wooAmount);
        }

        emit ClaimReward(msg.sender, wooAmount);
    }

    /* ----- Admin Functions ----- */

    function addRebate(address brokerAddr, uint256 amountInUSDT) external override nonReentrant onlyAdmin {
        if (brokerAddr == address(0)) {
            return;
        }
        pendingRebate[brokerAddr] = amountInUSDT.add(pendingRebate[brokerAddr]);
    }

    function setRebateRate(address brokerAddr, uint256 rate) external override onlyAdmin {
        require(brokerAddr != address(0), 'WooRebateManager: brokerAddr_ZERO_ADDR');
        require(rate <= 1e18, 'WooRebateManager: INVALID_USER_REWARD_RATE'); // rate <= 100%
        rebateRate[brokerAddr] = rate;
        emit RebateRateUpdated(brokerAddr, rate);
    }

    function setWooPP(address newWooPP) external onlyAdmin {
        require(newWooPP != address(0), 'WooRebateManager: wooPP_ZERO_ADDR');
        wooPP = IWooPP(newWooPP);
        require(wooPP.quoteToken() == quoteToken, 'WooRebateManager: wooPP_quote_token_INVALID');
    }

    function setAccessManager(address newAccessManager) external onlyOwner {
        require(newAccessManager != address(0), 'WooRebateManager: newAccessManager_ZERO_ADDR');
        accessManager = IWooAccessManager(newAccessManager);
    }

    function emergencyWithdraw(address token, address to) public onlyOwner {
        require(token != address(0), 'WooRebateManager: token_ZERO_ADDR');
        require(to != address(0), 'WooRebateManager: to_ZERO_ADDR');
        uint256 amount = IERC20(token).balanceOf(address(this));
        TransferHelper.safeTransfer(token, to, amount);
        emit Withdraw(token, to, amount);
    }
}

File 34 of 58 : IWooRebateManager.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// @title Rebate manager interface for WooFi Swap.
/// @notice this is for swap rebate or potential incentive program

interface IWooRebateManager {
    event Withdraw(address indexed token, address indexed to, uint256 amount);
    event RebateRateUpdated(address indexed brokerAddr, uint256 rate);
    event ClaimReward(address indexed brokerAddr, uint256 amount);

    /// @dev Gets the rebate rate for the given broker.
    /// Note: decimal: 18;  1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01%
    /// @param brokerAddr the address for rebate
    /// @return The rebate rate (decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01%)
    function rebateRate(address brokerAddr) external view returns (uint256);

    /// @dev set the rebate rate
    /// @param brokerAddr the rebate address
    /// @param rate the rebate rate
    function setRebateRate(address brokerAddr, uint256 rate) external;

    /// @dev adds the pending reward for the given user.
    /// @param brokerAddr the address for rebate
    /// @param amountInUSD the pending reward amount
    function addRebate(address brokerAddr, uint256 amountInUSD) external;

    /// @dev Pending amount in $woo.
    /// @param brokerAddr the address for rebate
    function pendingRebateInWOO(address brokerAddr) external view returns (uint256);

    /// @dev Pending amount in $woo.
    /// @param brokerAddr the address for rebate
    function pendingRebateInUSDT(address brokerAddr) external view returns (uint256);

    /// @dev Claims the reward ($woo token will be distributed)
    function claimRebate() external;

    /// @dev get the quote token address
    /// @return address of quote token
    function quoteToken() external view returns (address);
}

File 35 of 58 : IWooRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import '../interfaces/IWooPP.sol';

/// @title Woo router interface
/// @notice functions to interface with WooFi swap
interface IWooRouter {
    /* ----- Type declarations ----- */

    enum SwapType {
        WooSwap,
        DodoSwap
    }

    /* ----- Events ----- */

    event WooRouterSwap(
        SwapType swapType,
        address indexed fromToken,
        address indexed toToken,
        uint256 fromAmount,
        uint256 toAmount,
        address from,
        address indexed to,
        address rebateTo
    );

    event WooPoolChanged(address newPool);

    /* ----- Router properties ----- */

    function WETH() external pure returns (address);

    function wooPool() external pure returns (IWooPP);

    /* ----- Main query & swap APIs ----- */

    /// @dev query the amount to swap fromToken -> toToken
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of fromToken to swap
    /// @return toAmount the predicted amount to receive
    function querySwap(
        address fromToken,
        address toToken,
        uint256 fromAmount
    ) external view returns (uint256 toAmount);

    /// @dev swap fromToken -> toToken
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of fromToken to swap
    /// @param minToAmount the amount of fromToken to swap
    /// @param to the destination address
    /// @param rebateTo the rebate address (optional, can be 0)
    /// @return realToAmount the amount of toToken to receive
    function swap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address payable to,
        address rebateTo
    ) external payable returns (uint256 realToAmount);

    /* ----- 3rd party DEX swap ----- */

    /// @dev swap fromToken -> toToken via an external 3rd swap
    /// @param approveTarget the contract address for token transfer approval
    /// @param swapTarget the contract address for swap
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of fromToken to swap
    /// @param to the destination address
    /// @param data call data for external call
    function externalSwap(
        address approveTarget,
        address swapTarget,
        address fromToken,
        address toToken,
        uint256 fromAmount,
        address payable to,
        bytes calldata data
    ) external payable;

    /// @dev swap fromToken -> toToken via an external 3rd swap
    /// @param approveTarget the contract address for token transfer approval
    /// @param swapTarget the contract address for swap
    /// @param fromToken the from token
    /// @param toToken the to token
    /// @param fromAmount the amount of fromToken to swap
    /// @param minToAmount the min amount of swapped toToken
    /// @param to the destination address
    /// @param data call data for external call
    function externalSwap(
        address approveTarget,
        address swapTarget,
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address payable to,
        bytes calldata data
    ) external payable returns (uint256 realToAmount);
}

File 36 of 58 : WooPP.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './libraries/InitializableOwnable.sol';
import './libraries/DecimalMath.sol';
import './interfaces/IWooracle.sol';
import './interfaces/IWooPP.sol';
import './interfaces/IWooFeeManager.sol';
import './interfaces/IWooGuardian.sol';
import './interfaces/AggregatorV3Interface.sol';

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

/// @title Woo private pool for swaping.
/// @notice the implementation class for interface IWooPP, mainly for query and swap tokens.
contract WooPP is InitializableOwnable, ReentrancyGuard, Pausable, IWooPP {
    /* ----- Type declarations ----- */

    using SafeMath for uint256;
    using DecimalMath for uint256;
    using SafeERC20 for IERC20;

    /* ----- State variables ----- */

    mapping(address => TokenInfo) public tokenInfo;
    mapping(address => bool) public isStrategist;

    /// @inheritdoc IWooPP
    address public immutable override quoteToken;
    address public wooracle;
    IWooGuardian public wooGuardian;
    IWooFeeManager public feeManager;
    string public pairsInfo; // e.g. BNB/ETH/BTCB/WOO-USDT

    /* ----- Modifiers ----- */

    modifier onlyStrategist() {
        require(msg.sender == _OWNER_ || isStrategist[msg.sender], 'WooPP: NOT_STRATEGIST');
        _;
    }

    constructor(
        address newQuoteToken,
        address newWooracle,
        address newFeeManager,
        address newWooGuardian
    ) public {
        require(newQuoteToken != address(0), 'WooPP: INVALID_QUOTE');
        require(newWooracle != address(0), 'WooPP: newWooracle_ZERO_ADDR');
        require(newFeeManager != address(0), 'WooPP: newFeeManager_ZERO_ADDR');
        require(newWooGuardian != address(0), 'WooPP: newWooGuardian_ZERO_ADDR');

        initOwner(msg.sender);
        quoteToken = newQuoteToken;
        wooracle = newWooracle;
        feeManager = IWooFeeManager(newFeeManager);
        require(feeManager.quoteToken() == newQuoteToken, 'WooPP: feeManager_quoteToken_INVALID');
        wooGuardian = IWooGuardian(newWooGuardian);

        TokenInfo storage quoteInfo = tokenInfo[newQuoteToken];
        quoteInfo.isValid = true;
    }

    /* ----- External Functions ----- */

    /// @inheritdoc IWooPP
    function querySellBase(address baseToken, uint256 baseAmount)
        external
        view
        override
        whenNotPaused
        returns (uint256 quoteAmount)
    {
        require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR');
        require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken');
        wooGuardian.checkInputAmount(baseToken, baseAmount);

        TokenInfo memory baseInfo = tokenInfo[baseToken];
        require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST');
        TokenInfo memory quoteInfo = tokenInfo[quoteToken];
        _autoUpdate(baseToken, baseInfo, quoteInfo);

        quoteAmount = getQuoteAmountSellBase(baseToken, baseAmount, baseInfo, quoteInfo);
        wooGuardian.checkSwapAmount(baseToken, quoteToken, baseAmount, quoteAmount);
        uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken));
        quoteAmount = quoteAmount.sub(lpFee);

        require(quoteAmount <= IERC20(quoteToken).balanceOf(address(this)), 'WooPP: INSUFF_QUOTE');
    }

    /// @inheritdoc IWooPP
    function querySellQuote(address baseToken, uint256 quoteAmount)
        external
        view
        override
        whenNotPaused
        returns (uint256 baseAmount)
    {
        require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR');
        require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken');
        wooGuardian.checkInputAmount(quoteToken, quoteAmount);

        TokenInfo memory baseInfo = tokenInfo[baseToken];
        require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST');
        TokenInfo memory quoteInfo = tokenInfo[quoteToken];
        _autoUpdate(baseToken, baseInfo, quoteInfo);

        uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken));
        quoteAmount = quoteAmount.sub(lpFee);
        baseAmount = getBaseAmountSellQuote(baseToken, quoteAmount, baseInfo, quoteInfo);
        wooGuardian.checkSwapAmount(quoteToken, baseToken, quoteAmount, baseAmount);

        require(baseAmount <= IERC20(baseToken).balanceOf(address(this)), 'WooPP: INSUFF_BASE');
    }

    /// @inheritdoc IWooPP
    function sellBase(
        address baseToken,
        uint256 baseAmount,
        uint256 minQuoteAmount,
        address to,
        address rebateTo
    ) external override nonReentrant whenNotPaused returns (uint256 quoteAmount) {
        require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR');
        require(to != address(0), 'WooPP: to_ZERO_ADDR');
        require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken');
        wooGuardian.checkInputAmount(baseToken, baseAmount);

        address from = msg.sender;
        TokenInfo memory baseInfo = tokenInfo[baseToken];
        require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST');
        TokenInfo memory quoteInfo = tokenInfo[quoteToken];
        _autoUpdate(baseToken, baseInfo, quoteInfo);

        TransferHelper.safeTransferFrom(baseToken, from, address(this), baseAmount);

        quoteAmount = getQuoteAmountSellBase(baseToken, baseAmount, baseInfo, quoteInfo);
        wooGuardian.checkSwapAmount(baseToken, quoteToken, baseAmount, quoteAmount);

        uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken));
        quoteAmount = quoteAmount.sub(lpFee);
        require(quoteAmount >= minQuoteAmount, 'WooPP: quoteAmount<minQuoteAmount');

        TransferHelper.safeApprove(quoteToken, address(feeManager), lpFee);
        feeManager.collectFee(lpFee, rebateTo);

        uint256 balanceBefore = IERC20(quoteToken).balanceOf(to);
        TransferHelper.safeTransfer(quoteToken, to, quoteAmount);
        require(IERC20(quoteToken).balanceOf(to).sub(balanceBefore) >= minQuoteAmount, 'WooPP: INSUFF_OUTPUT_AMOUNT');

        _updateReserve(baseToken, baseInfo, quoteInfo);

        tokenInfo[baseToken] = baseInfo;
        tokenInfo[quoteToken] = quoteInfo;

        emit WooSwap(baseToken, quoteToken, baseAmount, quoteAmount, from, to, rebateTo);
    }

    /// @inheritdoc IWooPP
    function sellQuote(
        address baseToken,
        uint256 quoteAmount,
        uint256 minBaseAmount,
        address to,
        address rebateTo
    ) external override nonReentrant whenNotPaused returns (uint256 baseAmount) {
        require(baseToken != address(0), 'WooPP: baseToken_ZERO_ADDR');
        require(to != address(0), 'WooPP: to_ZERO_ADDR');
        require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken');
        wooGuardian.checkInputAmount(quoteToken, quoteAmount);

        address from = msg.sender;
        TokenInfo memory baseInfo = tokenInfo[baseToken];
        require(baseInfo.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST');
        TokenInfo memory quoteInfo = tokenInfo[quoteToken];
        _autoUpdate(baseToken, baseInfo, quoteInfo);

        TransferHelper.safeTransferFrom(quoteToken, from, address(this), quoteAmount);

        uint256 lpFee = quoteAmount.mulCeil(feeManager.feeRate(baseToken));
        quoteAmount = quoteAmount.sub(lpFee);
        baseAmount = getBaseAmountSellQuote(baseToken, quoteAmount, baseInfo, quoteInfo);
        require(baseAmount >= minBaseAmount, 'WooPP: baseAmount<minBaseAmount');

        TransferHelper.safeApprove(quoteToken, address(feeManager), lpFee);
        feeManager.collectFee(lpFee, rebateTo);

        wooGuardian.checkSwapAmount(quoteToken, baseToken, quoteAmount, baseAmount);

        uint256 balanceBefore = IERC20(baseToken).balanceOf(to);
        TransferHelper.safeTransfer(baseToken, to, baseAmount);
        require(IERC20(baseToken).balanceOf(to).sub(balanceBefore) >= minBaseAmount, 'WooPP: INSUFF_OUTPUT_AMOUNT');

        _updateReserve(baseToken, baseInfo, quoteInfo);

        tokenInfo[baseToken] = baseInfo;
        tokenInfo[quoteToken] = quoteInfo;

        emit WooSwap(quoteToken, baseToken, quoteAmount.add(lpFee), baseAmount, from, to, rebateTo);
    }

    /// @dev Set the pairsInfo
    /// @param newPairsInfo the pairs info to set
    function setPairsInfo(string calldata newPairsInfo) external nonReentrant onlyStrategist {
        pairsInfo = newPairsInfo;
    }

    /// @dev Get the pool's balance of token
    /// @param token the token pool to query
    function poolSize(address token) external view returns (uint256) {
        return IERC20(token).balanceOf(address(this));
    }

    /// @dev Set wooracle from newWooracle
    /// @param newWooracle Wooracle address
    function setWooracle(address newWooracle) external nonReentrant onlyStrategist {
        require(newWooracle != address(0), 'WooPP: newWooracle_ZERO_ADDR');
        wooracle = newWooracle;
        emit WooracleUpdated(newWooracle);
    }

    /// @dev Set wooGuardian from newWooGuardian
    /// @param newWooGuardian WooGuardian address
    function setWooGuardian(address newWooGuardian) external nonReentrant onlyStrategist {
        require(newWooGuardian != address(0), 'WooPP: newWooGuardian_ZERO_ADDR');
        wooGuardian = IWooGuardian(newWooGuardian);
        emit WooGuardianUpdated(newWooGuardian);
    }

    /// @dev Set the feeManager.
    /// @param newFeeManager the fee manager
    function setFeeManager(address newFeeManager) external nonReentrant onlyStrategist {
        require(newFeeManager != address(0), 'WooPP: newFeeManager_ZERO_ADDR');
        feeManager = IWooFeeManager(newFeeManager);
        require(feeManager.quoteToken() == quoteToken, 'WooPP: feeManager_quoteToken_INVALID');
        emit FeeManagerUpdated(newFeeManager);
    }

    /// @dev Add the base token for swap
    /// @param baseToken the base token
    /// @param threshold the balance threshold info
    /// @param R the rebalance refactor
    function addBaseToken(
        address baseToken,
        uint256 threshold,
        uint256 R
    ) external nonReentrant onlyStrategist {
        require(baseToken != address(0), 'WooPP: BASE_TOKEN_ZERO_ADDR');
        require(baseToken != quoteToken, 'WooPP: baseToken==quoteToken');
        require(threshold <= type(uint112).max, 'WooPP: THRESHOLD_OUT_OF_RANGE');
        require(R <= 1e18, 'WooPP: R_OUT_OF_RANGE');

        TokenInfo memory info = tokenInfo[baseToken];
        require(!info.isValid, 'WooPP: TOKEN_ALREADY_EXISTS');

        info.threshold = uint112(threshold);
        info.R = uint64(R);
        info.target = max(info.threshold, info.target);
        info.isValid = true;

        tokenInfo[baseToken] = info;

        emit ParametersUpdated(baseToken, threshold, R);
    }

    /// @dev Remove the base token
    /// @param baseToken the base token
    function removeBaseToken(address baseToken) external nonReentrant onlyStrategist {
        require(baseToken != address(0), 'WooPP: BASE_TOKEN_ZERO_ADDR');
        require(tokenInfo[baseToken].isValid, 'WooPP: TOKEN_DOES_NOT_EXIST');
        delete tokenInfo[baseToken];
        emit ParametersUpdated(baseToken, 0, 0);
    }

    /// @dev Tune the token params
    /// @param token the token to tune
    /// @param newThreshold the new balance threshold info
    /// @param newR the new rebalance refactor
    function tuneParameters(
        address token,
        uint256 newThreshold,
        uint256 newR
    ) external nonReentrant onlyStrategist {
        require(token != address(0), 'WooPP: token_ZERO_ADDR');
        require(newThreshold <= type(uint112).max, 'WooPP: THRESHOLD_OUT_OF_RANGE');
        require(newR <= 1e18, 'WooPP: R>1');

        TokenInfo memory info = tokenInfo[token];
        require(info.isValid, 'WooPP: TOKEN_DOES_NOT_EXIST');

        info.threshold = uint112(newThreshold);
        info.R = uint64(newR);
        info.target = max(info.threshold, info.target);

        tokenInfo[token] = info;
        emit ParametersUpdated(token, newThreshold, newR);
    }

    /* ----- Admin Functions ----- */

    /// @dev Pause the contract.
    function pause() external onlyStrategist {
        super._pause();
    }

    /// @dev Restart the contract.
    function unpause() external onlyStrategist {
        super._unpause();
    }

    /// @dev Update the strategist info.
    /// @param strategist the strategist to set
    /// @param flag true or false
    function setStrategist(address strategist, bool flag) external nonReentrant onlyStrategist {
        require(strategist != address(0), 'WooPP: strategist_ZERO_ADDR');
        isStrategist[strategist] = flag;
        emit StrategistUpdated(strategist, flag);
    }

    /// @dev Withdraw the token.
    /// @param token the token to withdraw
    /// @param to the destination address
    /// @param amount the amount to withdraw
    function withdraw(
        address token,
        address to,
        uint256 amount
    ) public nonReentrant onlyOwner {
        require(token != address(0), 'WooPP: token_ZERO_ADDR');
        require(to != address(0), 'WooPP: to_ZERO_ADDR');
        TransferHelper.safeTransfer(token, to, amount);
        emit Withdraw(token, to, amount);
    }

    function withdrawAll(address token, address to) external onlyOwner {
        withdraw(token, to, IERC20(token).balanceOf(address(this)));
    }

    /// @dev Withdraw the token to the OWNER address
    /// @param token the token
    function withdrawAllToOwner(address token) external nonReentrant onlyStrategist {
        require(token != address(0), 'WooPP: token_ZERO_ADDR');
        uint256 amount = IERC20(token).balanceOf(address(this));
        TransferHelper.safeTransfer(token, _OWNER_, amount);
        emit Withdraw(token, _OWNER_, amount);
    }

    /* ----- Private Functions ----- */

    function _autoUpdate(
        address baseToken,
        TokenInfo memory baseInfo,
        TokenInfo memory quoteInfo
    ) private view {
        require(baseToken != address(0), 'WooPP: BASETOKEN_ZERO_ADDR');
        _updateReserve(baseToken, baseInfo, quoteInfo);

        // NOTE: only consider the least 32 bigs integer number is good engouh
        uint32 priceTimestamp = uint32(IWooracle(wooracle).timestamp());
        if (priceTimestamp != baseInfo.lastResetTimestamp) {
            baseInfo.target = max(baseInfo.threshold, baseInfo.reserve);
            baseInfo.lastResetTimestamp = priceTimestamp;
        }
        if (priceTimestamp != quoteInfo.lastResetTimestamp) {
            quoteInfo.target = max(quoteInfo.threshold, quoteInfo.reserve);
            quoteInfo.lastResetTimestamp = priceTimestamp;
        }
    }

    function _updateReserve(
        address baseToken,
        TokenInfo memory baseInfo,
        TokenInfo memory quoteInfo
    ) private view {
        uint256 baseReserve = IERC20(baseToken).balanceOf(address(this));
        uint256 quoteReserve = IERC20(quoteToken).balanceOf(address(this));
        require(baseReserve <= type(uint112).max);
        require(quoteReserve <= type(uint112).max);
        baseInfo.reserve = uint112(baseReserve);
        quoteInfo.reserve = uint112(quoteReserve);
    }

    // When baseSold >= 0 , users sold the base token
    function getQuoteAmountLowQuoteSide(
        uint256 p,
        uint256 k,
        uint256 r,
        uint256 baseAmount
    ) private pure returns (uint256) {
        // priceFactor = 1 + k * baseAmount * p * r;
        uint256 priceFactor = DecimalMath.ONE.add(k.mulCeil(baseAmount).mulCeil(p).mulCeil(r));
        // return baseAmount * p / priceFactor;
        return baseAmount.mulFloor(p).divFloor(priceFactor); // round down
    }

    // When baseSold >= 0
    function getBaseAmountLowQuoteSide(
        uint256 p,
        uint256 k,
        uint256 r,
        uint256 quoteAmount
    ) private pure returns (uint256) {
        // priceFactor = (1 - k * quoteAmount * r);
        uint256 priceFactor = DecimalMath.ONE.sub(k.mulFloor(quoteAmount).mulFloor(r));
        // return quoteAmount / p / priceFactor;
        return quoteAmount.divFloor(p).divFloor(priceFactor);
    }

    // When quoteSold >= 0
    function getBaseAmountLowBaseSide(
        uint256 p,
        uint256 k,
        uint256 r,
        uint256 quoteAmount
    ) private pure returns (uint256) {
        // priceFactor = 1 + k * quoteAmount * r;
        uint256 priceFactor = DecimalMath.ONE.add(k.mulCeil(quoteAmount).mulCeil(r));
        // return quoteAmount / p / priceFactor;
        return quoteAmount.divFloor(p).divFloor(priceFactor); // round down
    }

    // When quoteSold >= 0
    function getQuoteAmountLowBaseSide(
        uint256 p,
        uint256 k,
        uint256 r,
        uint256 baseAmount
    ) private pure returns (uint256) {
        // priceFactor = 1 - k * baseAmount * p * r;
        uint256 priceFactor = DecimalMath.ONE.sub(k.mulFloor(baseAmount).mulFloor(p).mulFloor(r));
        // return baseAmount * p / priceFactor;
        return baseAmount.mulFloor(p).divFloor(priceFactor); // round down
    }

    function getBoughtAmount(
        TokenInfo memory baseInfo,
        TokenInfo memory quoteInfo,
        uint256 p,
        uint256 k,
        bool isSellBase
    ) private pure returns (uint256 baseBought, uint256 quoteBought) {
        uint256 baseSold = 0;
        if (baseInfo.reserve < baseInfo.target) {
            baseBought = uint256(baseInfo.target).sub(uint256(baseInfo.reserve));
        } else {
            baseSold = uint256(baseInfo.reserve).sub(uint256(baseInfo.target));
        }
        uint256 quoteSold = 0;
        if (quoteInfo.reserve < quoteInfo.target) {
            quoteBought = uint256(quoteInfo.target).sub(uint256(quoteInfo.reserve));
        } else {
            quoteSold = uint256(quoteInfo.reserve).sub(uint256(quoteInfo.target));
        }

        if (baseSold.mulCeil(p) > quoteSold) {
            baseSold = baseSold.sub(quoteSold.divFloor(p));
            quoteSold = 0;
        } else {
            quoteSold = quoteSold.sub(baseSold.mulCeil(p));
            baseSold = 0;
        }

        uint256 virtualBaseBought = getBaseAmountLowBaseSide(p, k, DecimalMath.ONE, quoteSold);
        if (isSellBase == (virtualBaseBought < baseBought)) {
            baseBought = virtualBaseBought;
        }
        uint256 virtualQuoteBought = getQuoteAmountLowQuoteSide(p, k, DecimalMath.ONE, baseSold);
        if (isSellBase == (virtualQuoteBought > quoteBought)) {
            quoteBought = virtualQuoteBought;
        }
    }

    function getQuoteAmountSellBase(
        address baseToken,
        uint256 baseAmount,
        TokenInfo memory baseInfo,
        TokenInfo memory quoteInfo
    ) private view returns (uint256 quoteAmount) {
        uint256 p;
        uint256 s;
        uint256 k;
        bool isFeasible;
        (p, s, k, isFeasible) = IWooracle(wooracle).state(baseToken);
        require(isFeasible, 'WooPP: ORACLE_PRICE_NOT_FEASIBLE');

        wooGuardian.checkSwapPrice(p, baseToken, quoteToken);

        // price: p * (1 - s / 2)
        p = p.mulFloor(DecimalMath.ONE.sub(s.divCeil(DecimalMath.TWO)));

        uint256 baseBought;
        uint256 quoteBought;
        (baseBought, quoteBought) = getBoughtAmount(baseInfo, quoteInfo, p, k, true);

        if (baseBought > 0) {
            uint256 quoteSold = getQuoteAmountLowBaseSide(p, k, baseInfo.R, baseBought);
            if (baseAmount > baseBought) {
                uint256 newBaseSold = baseAmount.sub(baseBought);
                quoteAmount = quoteSold.add(getQuoteAmountLowQuoteSide(p, k, DecimalMath.ONE, newBaseSold));
            } else {
                uint256 newBaseBought = baseBought.sub(baseAmount);
                quoteAmount = quoteSold.sub(getQuoteAmountLowBaseSide(p, k, baseInfo.R, newBaseBought));
            }
        } else {
            uint256 baseSold = getBaseAmountLowQuoteSide(p, k, DecimalMath.ONE, quoteBought);
            uint256 newBaseSold = baseAmount.add(baseSold);
            uint256 newQuoteBought = getQuoteAmountLowQuoteSide(p, k, DecimalMath.ONE, newBaseSold);
            quoteAmount = newQuoteBought > quoteBought ? newQuoteBought.sub(quoteBought) : 0;
        }
    }

    function getBaseAmountSellQuote(
        address baseToken,
        uint256 quoteAmount,
        TokenInfo memory baseInfo,
        TokenInfo memory quoteInfo
    ) private view returns (uint256 baseAmount) {
        uint256 p;
        uint256 s;
        uint256 k;
        bool isFeasible;
        (p, s, k, isFeasible) = IWooracle(wooracle).state(baseToken);
        require(isFeasible, 'WooPP: ORACLE_PRICE_NOT_FEASIBLE');

        wooGuardian.checkSwapPrice(p, baseToken, quoteToken);

        // price: p * (1 + s / 2)
        p = p.mulCeil(DecimalMath.ONE.add(s.divCeil(DecimalMath.TWO)));

        uint256 baseBought;
        uint256 quoteBought;
        (baseBought, quoteBought) = getBoughtAmount(baseInfo, quoteInfo, p, k, false);

        if (quoteBought > 0) {
            uint256 baseSold = getBaseAmountLowQuoteSide(p, k, baseInfo.R, quoteBought);
            if (quoteAmount > quoteBought) {
                uint256 newQuoteSold = quoteAmount.sub(quoteBought);
                baseAmount = baseSold.add(getBaseAmountLowBaseSide(p, k, DecimalMath.ONE, newQuoteSold));
            } else {
                uint256 newQuoteBought = quoteBought.sub(quoteAmount);
                baseAmount = baseSold.sub(getBaseAmountLowQuoteSide(p, k, baseInfo.R, newQuoteBought));
            }
        } else {
            uint256 quoteSold = getQuoteAmountLowBaseSide(p, k, DecimalMath.ONE, baseBought);
            uint256 newQuoteSold = quoteAmount.add(quoteSold);
            uint256 newBaseBought = getBaseAmountLowBaseSide(p, k, DecimalMath.ONE, newQuoteSold);
            baseAmount = newBaseBought > baseBought ? newBaseBought.sub(baseBought) : 0;
        }
    }

    function max(uint112 a, uint112 b) private pure returns (uint112) {
        return a >= b ? a : b;
    }
}

File 37 of 58 : IWooFeeManager.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// @title Contract to collect transaction fee of Woo private pool.
interface IWooFeeManager {
    /* ----- Events ----- */

    event FeeRateUpdated(address indexed token, uint256 newFeeRate);
    event Withdraw(address indexed token, address indexed to, uint256 amount);

    /* ----- External Functions ----- */

    /// @dev fee rate for the given base token:
    /// NOTE: fee rate decimal 18: 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01%
    /// @param token the base token
    /// @return the fee rate
    function feeRate(address token) external view returns (uint256);

    /// @dev Sets the fee rate for the given token
    /// @param token the base token
    /// @param newFeeRate the new fee rate
    function setFeeRate(address token, uint256 newFeeRate) external;

    /// @dev Collects the swap fee to the given brokder address.
    /// @param amount the swap fee amount
    /// @param brokerAddr the broker address to rebate to
    function collectFee(uint256 amount, address brokerAddr) external;

    /// @dev get the quote token address
    /// @return address of quote token
    function quoteToken() external view returns (address);

    /// @dev Collects the fee and distribute to rebate and vault managers.
    function distributeFees() external;
}

File 38 of 58 : Wooracle_BSC.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './libraries/InitializableOwnable.sol';
import './interfaces/IWooracle.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';

/// @title Wooracle implementation in BSC
/// @notice Will be maintained and updated periodically by Woo.network in multichains.
contract Wooracle is InitializableOwnable, IWooracle {
    /* ----- State variables ----- */

    // 128 + 64 + 64 = 256 bits (slot size)
    struct TokenInfo {
        uint128 price; // 18 - base_decimal + quote_decimal
        uint64 coeff; // 18. coeff <= 1e18    (2^64 = 1.84e19)
        uint64 spread; // 18. spread <= 2e18   (2^64 = 1.84e19)
    }

    mapping(address => TokenInfo) public infos;

    address public override quoteToken;
    uint256 public override timestamp;

    uint256 public staleDuration;

    constructor() public {
        initOwner(msg.sender);
        staleDuration = uint256(300);
    }

    /* ----- External Functions ----- */

    /// @dev Set the quote token address.
    /// @param newQuoteToken token address
    function setQuoteToken(address newQuoteToken) external onlyOwner {
        quoteToken = newQuoteToken;
    }

    /// @dev Set the staleDuration.
    /// @param newStaleDuration the new stale duration
    function setStaleDuration(uint256 newStaleDuration) external onlyOwner {
        staleDuration = newStaleDuration;
    }

    /// @dev Update the base token prices.
    /// @param base the baseToken address
    /// @param newPrice the new prices for the base token
    function postPrice(address base, uint128 newPrice) external onlyOwner {
        infos[base].price = newPrice;
        timestamp = block.timestamp;
    }

    /// @dev batch update baseTokens prices
    /// @param bases list of baseToken address
    /// @param newPrices the updated prices list
    function postPriceList(address[] calldata bases, uint128[] calldata newPrices) external onlyOwner {
        uint256 length = bases.length;
        require(length == newPrices.length, 'Wooracle: length_INVALID');

        for (uint256 i = 0; i < length; i++) {
            infos[bases[i]].price = newPrices[i];
        }

        timestamp = block.timestamp;
    }

    /// @dev update the spreads info.
    /// @param base baseToken address
    /// @param newSpread the new spreads
    function postSpread(address base, uint64 newSpread) external onlyOwner {
        infos[base].spread = newSpread;
        timestamp = block.timestamp;
    }

    /// @dev batch update the spreads info.
    /// @param bases list of baseToken address
    /// @param newSpreads list of spreads info
    function postSpreadList(address[] calldata bases, uint64[] calldata newSpreads) external onlyOwner {
        uint256 length = bases.length;
        require(length == newSpreads.length, 'Wooracle: length_INVALID');

        for (uint256 i = 0; i < length; i++) {
            infos[bases[i]].spread = newSpreads[i];
        }

        timestamp = block.timestamp;
    }

    /// @dev update the state of the given base token.
    /// @param base baseToken address
    /// @param newPrice the new prices
    /// @param newSpread the new spreads
    /// @param newCoeff the new slippage coefficent
    function postState(
        address base,
        uint128 newPrice,
        uint64 newSpread,
        uint64 newCoeff
    ) external onlyOwner {
        _setState(base, newPrice, newSpread, newCoeff);
        timestamp = block.timestamp;
    }

    /// @dev batch update the prices, spreads and slipagge coeffs info.
    /// @param bases list of baseToken address
    /// @param newPrices the prices list
    /// @param newSpreads the spreads list
    /// @param newCoeffs the slippage coefficent list
    function postStateList(
        address[] calldata bases,
        uint128[] calldata newPrices,
        uint64[] calldata newSpreads,
        uint64[] calldata newCoeffs
    ) external onlyOwner {
        uint256 length = bases.length;
        require(
            length == newPrices.length && length == newSpreads.length && length == newCoeffs.length,
            'Wooracle: length_INVALID'
        );

        for (uint256 i = 0; i < length; i++) {
            _setState(bases[i], newPrices[i], newSpreads[i], newCoeffs[i]);
        }
        timestamp = block.timestamp;
    }

    /// @inheritdoc IWooracle
    function price(address base) external view override returns (uint256 priceNow, bool feasible) {
        priceNow = uint256(infos[base].price);
        feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds);
    }

    function getPrice(address base) external view override returns (uint256) {
        return uint256(infos[base].price);
    }

    function getSpread(address base) external view override returns (uint256) {
        return uint256(infos[base].spread);
    }

    function getCoeff(address base) external view override returns (uint256) {
        return uint256(infos[base].coeff);
    }

    /// @inheritdoc IWooracle
    function state(address base)
        external
        view
        override
        returns (
            uint256 priceNow,
            uint256 spreadNow,
            uint256 coeffNow,
            bool feasible
        )
    {
        TokenInfo storage info = infos[base];
        priceNow = uint256(info.price);
        spreadNow = uint256(info.spread);
        coeffNow = uint256(info.coeff);
        feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds);
    }

    function isFeasible(address base) public view override returns (bool) {
        return infos[base].price != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds);
    }

    /* ----- Private Functions ----- */

    function _setState(
        address base,
        uint128 newPrice,
        uint64 newSpread,
        uint64 newCoeff
    ) private {
        TokenInfo storage info = infos[base];
        info.price = newPrice;
        info.spread = newSpread;
        info.coeff = newCoeff;
    }
}

File 39 of 58 : Wooracle_Avax.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './libraries/InitializableOwnable.sol';
import './interfaces/IWooracle.sol';

/// @title Wooracle implementation in Avalanche chain.
/// @notice Will be maintained and updated periodically by Woo.network in multichains.
contract Wooracle is InitializableOwnable, IWooracle {
    /* ----- State variables ----- */

    struct TokenInfo {
        uint256 price; // 18 - base_decimal + quote_decimal
        uint256 coeff; // 36 - quote
        uint256 spread; // 18
    }

    mapping(address => TokenInfo) public infos;

    address public override quoteToken;
    uint256 public override timestamp;

    uint256 public staleDuration;

    constructor() public {
        initOwner(msg.sender);
        staleDuration = uint256(300);
    }

    /* ----- External Functions ----- */

    /// @dev Set the quote token address.
    /// @param newQuoteToken token address
    function setQuoteToken(address newQuoteToken) external onlyOwner {
        quoteToken = newQuoteToken;
    }

    /// @dev Set the staleDuration.
    /// @param newStaleDuration the new stale duration
    function setStaleDuration(uint256 newStaleDuration) external onlyOwner {
        staleDuration = newStaleDuration;
    }

    /// @dev Update the base token prices.
    /// @param base the baseToken address
    /// @param newPrice the new prices for the base token
    function postPrice(address base, uint256 newPrice) external onlyOwner {
        infos[base].price = newPrice;
        timestamp = block.timestamp;
    }

    /// @dev batch update baseTokens prices
    /// @param bases list of baseToken address
    /// @param newPrices the updated prices list
    function postPriceList(address[] calldata bases, uint256[] calldata newPrices) external onlyOwner {
        uint256 length = bases.length;
        require(length == newPrices.length, 'Wooracle: length_INVALID');

        for (uint256 i = 0; i < length; i++) {
            infos[bases[i]].price = newPrices[i];
        }

        timestamp = block.timestamp;
    }

    /// @dev update the spreads info.
    /// @param base baseToken address
    /// @param newSpread the new spreads
    function postSpread(address base, uint256 newSpread) external onlyOwner {
        infos[base].spread = newSpread;
        timestamp = block.timestamp;
    }

    /// @dev batch update the spreads info.
    /// @param bases list of baseToken address
    /// @param newSpreads list of spreads info
    function postSpreadList(address[] calldata bases, uint256[] calldata newSpreads) external onlyOwner {
        uint256 length = bases.length;
        require(length == newSpreads.length, 'Wooracle: length_INVALID');

        for (uint256 i = 0; i < length; i++) {
            infos[bases[i]].spread = newSpreads[i];
        }

        timestamp = block.timestamp;
    }

    /// @dev update the state of the given base token.
    /// @param base baseToken address
    /// @param newPrice the new prices
    /// @param newSpread the new spreads
    /// @param newCoeff the new slippage coefficent
    function postState(
        address base,
        uint256 newPrice,
        uint256 newSpread,
        uint256 newCoeff
    ) external onlyOwner {
        _setState(base, newPrice, newSpread, newCoeff);
        timestamp = block.timestamp;
    }

    /// @dev batch update the prices, spreads and slipagge coeffs info.
    /// @param bases list of baseToken address
    /// @param newPrices the prices list
    /// @param newSpreads the spreads list
    /// @param newCoeffs the slippage coefficent list
    function postStateList(
        address[] calldata bases,
        uint256[] calldata newPrices,
        uint256[] calldata newSpreads,
        uint256[] calldata newCoeffs
    ) external onlyOwner {
        uint256 length = bases.length;
        require(
            length == newPrices.length && length == newSpreads.length && length == newCoeffs.length,
            'Wooracle: length_INVALID'
        );

        for (uint256 i = 0; i < length; i++) {
            _setState(bases[i], newPrices[i], newSpreads[i], newCoeffs[i]);
        }
        timestamp = block.timestamp;
    }

    /// @inheritdoc IWooracle
    function price(address base) external view override returns (uint256 priceNow, bool feasible) {
        priceNow = infos[base].price;
        feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds);
    }

    function getPrice(address base) external view override returns (uint256) {
        return infos[base].price;
    }

    function getSpread(address base) external view override returns (uint256) {
        return infos[base].spread;
    }

    function getCoeff(address base) external view override returns (uint256) {
        return infos[base].coeff;
    }

    /// @inheritdoc IWooracle
    function state(address base)
        external
        view
        override
        returns (
            uint256 priceNow,
            uint256 spreadNow,
            uint256 coeffNow,
            bool feasible
        )
    {
        TokenInfo storage info = infos[base];
        priceNow = info.price;
        spreadNow = info.spread;
        coeffNow = info.coeff;
        feasible = priceNow != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds);
    }

    function isFeasible(address base) public view override returns (bool) {
        return infos[base].price != 0 && block.timestamp <= (timestamp + staleDuration * 1 seconds);
    }

    /* ----- Private Functions ----- */

    function _setState(
        address base,
        uint256 newPrice,
        uint256 newSpread,
        uint256 newCoeff
    ) private {
        TokenInfo storage info = infos[base];
        info.price = newPrice;
        info.spread = newSpread;
        info.coeff = newCoeff;
    }
}

File 40 of 58 : WooGuardian.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*
░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝
*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './libraries/InitializableOwnable.sol';
import './libraries/DecimalMath.sol';
import './interfaces/IWooGuardian.sol';
import './interfaces/AggregatorV3Interface.sol';

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';

/// @title Woo guardian implementation.
contract WooGuardian is IWooGuardian, InitializableOwnable {
    using SafeMath for uint256;
    using DecimalMath for uint256;

    /* ----- Type declarations ----- */

    struct RefInfo {
        address chainlinkRefOracle; // chainlink oracle for price checking
        uint96 refPriceFixCoeff; // chainlink price fix coeff
    }

    /* ----- State variables ----- */

    mapping(address => RefInfo) public refInfo;
    uint256 public priceBound;

    constructor(uint256 newPriceBound) public {
        initOwner(msg.sender);
        require(newPriceBound <= 1e18, 'WooGuardian: priceBound out of range');
        priceBound = newPriceBound;
    }

    function checkSwapPrice(
        uint256 price,
        address fromToken,
        address toToken
    ) external view override {
        require(fromToken != address(0), 'WooGuardian: fromToken_ZERO_ADDR');
        require(toToken != address(0), 'WooGuardian: toToken_ZERO_ADDR');

        uint256 refPrice = _refPrice(fromToken, toToken);

        require(
            refPrice.mulFloor(1e18 - priceBound) <= price && price <= refPrice.mulCeil(1e18 + priceBound),
            'WooGuardian: PRICE_UNRELIABLE'
        );
    }

    function checkInputAmount(address token, uint256 inputAmount) external view override {
        // TODO: qinchao
    }

    function checkSwapAmount(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 toAmount
    ) external view override {
        require(fromToken != address(0), 'WooGuardian: fromToken_ZERO_ADDR');
        require(toToken != address(0), 'WooGuardian: toToken_ZERO_ADDR');

        uint256 refPrice = _refPrice(fromToken, toToken);
        uint256 refToAmount = fromAmount.mulFloor(refPrice);
        require(
            refToAmount.mulFloor(1e18 - priceBound) <= toAmount && toAmount <= refToAmount.mulCeil(1e18 + priceBound),
            'WooGuardian: TO_AMOUNT_UNRELIABLE'
        );
    }

    function setToken(address token, address chainlinkRefOracle) external onlyOwner {
        require(token != address(0), 'WooGuardian: token_ZERO_ADDR');
        RefInfo storage info = refInfo[token];
        info.chainlinkRefOracle = chainlinkRefOracle;
        info.refPriceFixCoeff = _refPriceFixCoeff(token, chainlinkRefOracle);
        emit ChainlinkRefOracleUpdated(token, chainlinkRefOracle);
    }

    function _refPriceFixCoeff(address token, address chainlink) private view returns (uint96) {
        if (chainlink == address(0)) {
            return 0;
        }

        // About decimals:
        // For a sell base trade, we have quoteSize = baseSize * price
        // For calculation convenience, the decimals of price is 18-base.decimals+quote.decimals
        // If we have price = basePrice / quotePrice, then decimals of tokenPrice should be 36-token.decimals()
        // We use chainlink oracle price as token reference price, which decimals is chainlinkPrice.decimals()
        // We should multiply it by 10e(36-(token.decimals+chainlinkPrice.decimals)), which is refPriceFixCoeff
        uint256 decimalsToFix = uint256(ERC20(token).decimals()).add(
            uint256(AggregatorV3Interface(chainlink).decimals())
        );
        uint256 refPriceFixCoeff = 10**(uint256(36).sub(decimalsToFix));
        require(refPriceFixCoeff <= type(uint96).max);
        return uint96(refPriceFixCoeff);
    }

    function _refPrice(address fromToken, address toToken) private view returns (uint256) {
        RefInfo storage baseInfo = refInfo[fromToken];
        RefInfo storage quoteInfo = refInfo[toToken];

        require(baseInfo.chainlinkRefOracle != address(0), 'WooGuardian: fromToken_RefOracle_INVALID');
        require(quoteInfo.chainlinkRefOracle != address(0), 'WooGuardian: toToken_RefOracle_INVALID');

        (, int256 rawBaseRefPrice, , , ) = AggregatorV3Interface(baseInfo.chainlinkRefOracle).latestRoundData();
        require(rawBaseRefPrice >= 0, 'WooGuardian: INVALID_CHAINLINK_PRICE');
        (, int256 rawQuoteRefPrice, , , ) = AggregatorV3Interface(quoteInfo.chainlinkRefOracle).latestRoundData();
        require(rawQuoteRefPrice >= 0, 'WooGuardian: INVALID_CHAINLINK_QUOTE_PRICE');
        uint256 baseRefPrice = uint256(rawBaseRefPrice).mul(uint256(baseInfo.refPriceFixCoeff));
        uint256 quoteRefPrice = uint256(rawQuoteRefPrice).mul(uint256(quoteInfo.refPriceFixCoeff));

        return baseRefPrice.divFloor(quoteRefPrice);
    }
}

File 41 of 58 : WooFeeManager.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.6.12;
pragma experimental ABIEncoderV2;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import './libraries/InitializableOwnable.sol';
import './libraries/DecimalMath.sol';
import './interfaces/IWooPP.sol';
import './interfaces/IWooRebateManager.sol';
import './interfaces/IWooFeeManager.sol';
import './interfaces/IWooVaultManager.sol';
import './interfaces/IWooAccessManager.sol';

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

/// @title Contract to collect transaction fee of Woo private pool.
contract WooFeeManager is InitializableOwnable, ReentrancyGuard, IWooFeeManager {
    /* ----- Type declarations ----- */

    using SafeMath for uint256;
    using DecimalMath for uint256;
    using SafeERC20 for IERC20;

    /* ----- State variables ----- */

    mapping(address => uint256) public override feeRate; // decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01%
    uint256 public vaultRewardRate; // decimal: 18; 1e16 = 1%, 1e15 = 0.1%, 1e14 = 0.01%

    uint256 public rebateAmount;

    address public immutable override quoteToken;
    IWooRebateManager public rebateManager;
    IWooVaultManager public vaultManager;
    IWooAccessManager public accessManager;

    address public treasury;

    /* ----- Modifiers ----- */

    modifier onlyAdmin() {
        require(msg.sender == _OWNER_ || accessManager.isFeeAdmin(msg.sender), 'WooFeeManager: NOT_ADMIN');
        _;
    }

    constructor(
        address newQuoteToken,
        address newRebateManager,
        address newVaultManager,
        address newAccessManager,
        address newTreasury
    ) public {
        require(newQuoteToken != address(0), 'WooFeeManager: quoteToken_ZERO_ADDR');
        initOwner(msg.sender);
        quoteToken = newQuoteToken;
        rebateManager = IWooRebateManager(newRebateManager);
        vaultManager = IWooVaultManager(newVaultManager);
        vaultRewardRate = 1e18;
        accessManager = IWooAccessManager(newAccessManager);
        treasury = newTreasury;
    }

    /* ----- Public Functions ----- */

    function collectFee(uint256 amount, address brokerAddr) external override nonReentrant {
        TransferHelper.safeTransferFrom(quoteToken, msg.sender, address(this), amount);
        uint256 rebateRate = rebateManager.rebateRate(brokerAddr);
        if (rebateRate > 0) {
            uint256 curRebateAmount = amount.mulFloor(rebateRate);
            rebateManager.addRebate(brokerAddr, curRebateAmount);
            rebateAmount = rebateAmount.add(curRebateAmount);
        }
    }

    /* ----- Admin Functions ----- */

    function distributeFees() external override nonReentrant onlyAdmin {
        uint256 balance = IERC20(quoteToken).balanceOf(address(this));
        require(balance > 0, 'WooFeeManager: balance_ZERO');

        // Step 1: distribute the rebate balance
        if (rebateAmount > 0) {
            TransferHelper.safeApprove(quoteToken, address(rebateManager), rebateAmount);
            TransferHelper.safeTransfer(quoteToken, address(rebateManager), rebateAmount);

            balance = balance.sub(rebateAmount);
            rebateAmount = 0;
        }

        // Step 2: distribute the vault balance
        uint256 vaultAmount = balance.mulFloor(vaultRewardRate);
        if (vaultAmount > 0) {
            TransferHelper.safeApprove(quoteToken, address(vaultManager), vaultAmount);
            TransferHelper.safeTransfer(quoteToken, address(vaultManager), vaultAmount);
            balance = balance.sub(vaultAmount);
        }

        // Step 3: balance left for treasury
        if (balance > 0) {
            TransferHelper.safeApprove(quoteToken, treasury, balance);
            TransferHelper.safeTransfer(quoteToken, treasury, balance);
        }
    }

    function setFeeRate(address token, uint256 newFeeRate) external override onlyAdmin {
        require(newFeeRate <= 1e16, 'WooFeeManager: FEE_RATE>1%');
        feeRate[token] = newFeeRate;
        emit FeeRateUpdated(token, newFeeRate);
    }

    function setRebateManager(address newRebateManager) external onlyAdmin {
        require(newRebateManager != address(0), 'WooFeeManager: rebateManager_ZERO_ADDR');
        rebateManager = IWooRebateManager(newRebateManager);
    }

    function setVaultManager(address newVaultManager) external onlyAdmin {
        require(newVaultManager != address(0), 'WooFeeManager: newVaultManager_ZERO_ADDR');
        vaultManager = IWooVaultManager(newVaultManager);
    }

    function setVaultRewardRate(uint256 newVaultRewardRate) external onlyAdmin {
        require(newVaultRewardRate <= 1e18, 'WooFeeManager: vaultRewardRate_INVALID');
        vaultRewardRate = newVaultRewardRate;
    }

    function setAccessManager(address newAccessManager) external onlyOwner {
        require(newAccessManager != address(0), 'WooFeeManager: newAccessManager_ZERO_ADDR');
        accessManager = IWooAccessManager(newAccessManager);
    }

    function setTreasury(address newTreasury) external onlyOwner {
        require(newTreasury != address(0), 'WooFeeManager: newTreasury_ZERO_ADDR');
        treasury = newTreasury;
    }

    function emergencyWithdraw(address token, address to) external onlyOwner {
        require(token != address(0), 'WooFeeManager: token_ZERO_ADDR');
        require(to != address(0), 'WooFeeManager: to_ZERO_ADDR');
        uint256 amount = IERC20(token).balanceOf(address(this));
        TransferHelper.safeTransfer(token, to, amount);
        emit Withdraw(token, to, amount);
    }
}

File 42 of 58 : WooAccessManager.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import './interfaces/IWooAccessManager.sol';

contract WooAccessManager is IWooAccessManager, Ownable, Pausable {
    /* ----- State variables ----- */

    mapping(address => bool) public override isFeeAdmin;
    mapping(address => bool) public override isVaultAdmin;
    mapping(address => bool) public override isRebateAdmin;
    mapping(address => bool) public override isZeroFeeVault;

    /* ----- Admin Functions ----- */

    /// @inheritdoc IWooAccessManager
    function setFeeAdmin(address feeAdmin, bool flag) external override onlyOwner whenNotPaused {
        require(feeAdmin != address(0), 'WooAccessManager: feeAdmin_ZERO_ADDR');
        isFeeAdmin[feeAdmin] = flag;
        emit FeeAdminUpdated(feeAdmin, flag);
    }

    /// @inheritdoc IWooAccessManager
    function batchSetFeeAdmin(address[] calldata feeAdmins, bool[] calldata flags)
        external
        override
        onlyOwner
        whenNotPaused
    {
        require(feeAdmins.length == flags.length, 'WooAccessManager: length_INVALID');

        for (uint256 i = 0; i < feeAdmins.length; i++) {
            require(feeAdmins[i] != address(0), 'WooAccessManager: feeAdmin_ZERO_ADDR');
            isFeeAdmin[feeAdmins[i]] = flags[i];
            emit FeeAdminUpdated(feeAdmins[i], flags[i]);
        }
    }

    /// @inheritdoc IWooAccessManager
    function setVaultAdmin(address vaultAdmin, bool flag) external override onlyOwner whenNotPaused {
        require(vaultAdmin != address(0), 'WooAccessManager: vaultAdmin_ZERO_ADDR');
        isVaultAdmin[vaultAdmin] = flag;
        emit VaultAdminUpdated(vaultAdmin, flag);
    }

    /// @inheritdoc IWooAccessManager
    function batchSetVaultAdmin(address[] calldata vaultAdmins, bool[] calldata flags)
        external
        override
        onlyOwner
        whenNotPaused
    {
        require(vaultAdmins.length == flags.length, 'WooAccessManager: length_INVALID');

        for (uint256 i = 0; i < vaultAdmins.length; i++) {
            require(vaultAdmins[i] != address(0), 'WooAccessManager: vaultAdmin_ZERO_ADDR');
            isVaultAdmin[vaultAdmins[i]] = flags[i];
            emit VaultAdminUpdated(vaultAdmins[i], flags[i]);
        }
    }

    /// @inheritdoc IWooAccessManager
    function setRebateAdmin(address rebateAdmin, bool flag) external override onlyOwner whenNotPaused {
        require(rebateAdmin != address(0), 'WooAccessManager: rebateAdmin_ZERO_ADDR');
        isRebateAdmin[rebateAdmin] = flag;
        emit RebateAdminUpdated(rebateAdmin, flag);
    }

    /// @inheritdoc IWooAccessManager
    function batchSetRebateAdmin(address[] calldata rebateAdmins, bool[] calldata flags)
        external
        override
        onlyOwner
        whenNotPaused
    {
        require(rebateAdmins.length == flags.length, 'WooAccessManager: length_INVALID');

        for (uint256 i = 0; i < rebateAdmins.length; i++) {
            require(rebateAdmins[i] != address(0), 'WooAccessManager: rebateAdmin_ZERO_ADDR');
            isRebateAdmin[rebateAdmins[i]] = flags[i];
            emit RebateAdminUpdated(rebateAdmins[i], flags[i]);
        }
    }

    /// @inheritdoc IWooAccessManager
    function setZeroFeeVault(address vault, bool flag) external override onlyOwner whenNotPaused {
        require(vault != address(0), 'WooAccessManager: vault_ZERO_ADDR');
        isZeroFeeVault[vault] = flag;
        emit ZeroFeeVaultUpdated(vault, flag);
    }

    /// @inheritdoc IWooAccessManager
    function batchSetZeroFeeVault(address[] calldata vaults, bool[] calldata flags)
        external
        override
        onlyOwner
        whenNotPaused
    {
        require(vaults.length == flags.length, 'WooAccessManager: length_INVALID');

        for (uint256 i = 0; i < vaults.length; i++) {
            require(vaults[i] != address(0), 'WooAccessManager: vault_ZERO_ADDR');
            isZeroFeeVault[vaults[i]] = flags[i];
            emit ZeroFeeVaultUpdated(vaults[i], flags[i]);
        }
    }

    /// @notice Pause the contract.
    function pause() external onlyOwner {
        super._pause();
    }

    /// @notice Restart the contract.
    function unpause() external onlyOwner {
        super._unpause();
    }
}

File 43 of 58 : TestErc20Token.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.6.12;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract TestToken is ERC20('TestToken', 'TT'), Ownable {
    using SafeMath for uint256;

    function mint(address _to, uint256 _amount) public onlyOwner {
        _mint(_to, _amount);
    }
}

File 44 of 58 : VaultErc20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../interfaces/IStrategy.sol';
import '../interfaces/IWETH.sol';
import '../interfaces/IWooAccessManager.sol';
import '../interfaces/IVault.sol';

/*

░██╗░░░░░░░██╗░█████╗░░█████╗░░░░░░░███████╗██╗
░██║░░██╗░░██║██╔══██╗██╔══██╗░░░░░░██╔════╝██║
░╚██╗████╗██╔╝██║░░██║██║░░██║█████╗█████╗░░██║
░░████╔═████║░██║░░██║██║░░██║╚════╝██╔══╝░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝╚█████╔╝░░░░░░██║░░░░░██║
░░░╚═╝░░░╚═╝░░░╚════╝░░╚════╝░░░░░░░╚═╝░░░░░╚═╝

*
* MIT License
* ===========
*
* Copyright (c) 2020 WooTrade
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

contract VaultErc20 is IVault, ERC20, Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    struct StratCandidate {
        address implementation;
        uint256 proposedTime;
    }

    /* ----- State Variables ----- */

    address public immutable override want;

    IWooAccessManager public immutable accessManager;

    IStrategy public strategy;
    StratCandidate public stratCandidate;

    mapping(address => uint256) public costSharePrice;

    event NewStratCandidate(address indexed implementation);
    event UpgradeStrat(address indexed implementation);

    constructor(address initWant, address initAccessManager)
        public
        ERC20(
            string(abi.encodePacked('WOOFi Earn ', ERC20(initWant).name())),
            string(abi.encodePacked('we', ERC20(initWant).symbol()))
        )
    {
        require(initWant != address(0), 'Vault: initWant_ZERO_ADDR');
        require(initAccessManager != address(0), 'Vault: initAccessManager_ZERO_ADDR');

        want = initWant;
        accessManager = IWooAccessManager(initAccessManager);
    }

    modifier onlyAdmin() {
        require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'Vault: NOT_ADMIN');
        _;
    }

    /* ----- External Functions ----- */

    function deposit(uint256 amount) public payable override nonReentrant {
        require(amount > 0, 'Vault: amount_CAN_NOT_BE_ZERO');

        // STEP 0: strategy's routing work before deposit.
        if (address(strategy) != address(0)) {
            require(!strategy.paused(), 'Vault: strat_paused');
            strategy.beforeDeposit();
        }

        // STEP 1: check the deposit amount
        uint256 balanceBefore = balance();
        TransferHelper.safeTransferFrom(want, msg.sender, address(this), amount);
        uint256 balanceAfter = balance();
        require(amount <= balanceAfter.sub(balanceBefore), 'Vault: amount_NOT_ENOUGH');

        // STEP 2: issues the shares and update the cost basis
        uint256 shares = totalSupply() == 0 ? amount : amount.mul(totalSupply()).div(balanceBefore);
        uint256 sharesBefore = balanceOf(msg.sender);
        uint256 costBefore = costSharePrice[msg.sender];
        uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares));
        costSharePrice[msg.sender] = costAfter;
        _mint(msg.sender, shares);

        // STEP 3
        earn();
    }

    function withdraw(uint256 shares) public override nonReentrant {
        require(shares > 0, 'Vault: shares_ZERO');
        require(shares <= balanceOf(msg.sender), 'Vault: shares_NOT_ENOUGH');

        // STEP 0: burn the user's shares to start the withdrawal process.
        uint256 withdrawAmount = shares.mul(balance()).div(totalSupply());
        _burn(msg.sender, shares);

        // STEP 1: withdraw the token from strategy if needed
        uint256 balanceBefore = IERC20(want).balanceOf(address(this));
        if (balanceBefore < withdrawAmount) {
            uint256 balanceToWithdraw = withdrawAmount.sub(balanceBefore);
            require(_isStratActive(), 'Vault: STRAT_INACTIVE');
            strategy.withdraw(balanceToWithdraw);
            uint256 balanceAfter = IERC20(want).balanceOf(address(this));
            require(balanceAfter.sub(balanceBefore) > 0, 'Vault: Strat_WITHDRAW_ERROR');
            if (withdrawAmount > balanceAfter) {
                // NOTE: Tiny diff is accepted due to the decimal precision.
                withdrawAmount = balanceAfter;
            }
        }

        // STEP 3
        TransferHelper.safeTransfer(want, msg.sender, withdrawAmount);
    }

    function earn() public override {
        if (_isStratActive()) {
            uint256 balanceAvail = available();
            if (balanceAvail > 0) {
                TransferHelper.safeTransfer(want, address(strategy), balanceAvail);
                strategy.deposit();
            }
        }
    }

    function available() public view override returns (uint256) {
        return IERC20(want).balanceOf(address(this));
    }

    function balance() public view override returns (uint256) {
        return address(strategy) != address(0) ? available().add(strategy.balanceOf()) : available();
    }

    function getPricePerFullShare() public view override returns (uint256) {
        return totalSupply() == 0 ? 1e18 : balance().mul(1e18).div(totalSupply());
    }

    function _isStratActive() internal view returns (bool) {
        return address(strategy) != address(0) && !strategy.paused();
    }

    /* ----- Admin Functions ----- */

    function setupStrat(address _strat) public onlyAdmin {
        require(_strat != address(0), 'Vault: STRAT_ZERO_ADDR');
        require(address(strategy) == address(0), 'Vault: STRAT_ALREADY_SET');
        require(address(this) == IStrategy(_strat).vault(), 'Vault: STRAT_VAULT_INVALID');
        require(want == IStrategy(_strat).want(), 'Vault: STRAT_WANT_INVALID');
        strategy = IStrategy(_strat);

        emit UpgradeStrat(_strat);
    }

    function proposeStrat(address _implementation) public onlyAdmin {
        require(address(this) == IStrategy(_implementation).vault(), 'Vault: STRAT_VAULT_INVALID');
        require(want == IStrategy(_implementation).want(), 'Vault: STRAT_WANT_INVALID');
        stratCandidate = StratCandidate({implementation: _implementation, proposedTime: block.timestamp});

        emit NewStratCandidate(_implementation);
    }

    function upgradeStrat() public onlyAdmin {
        require(stratCandidate.implementation != address(0), 'Vault: NO_CANDIDATE');
        require(stratCandidate.proposedTime.add(48 hours) < block.timestamp, 'Vault: TIME_INVALID');

        emit UpgradeStrat(stratCandidate.implementation);

        strategy.retireStrat();
        strategy = IStrategy(stratCandidate.implementation);
        stratCandidate.implementation = address(0);
        stratCandidate.proposedTime = 5000000000; // 100+ years to ensure proposedTime check

        earn();
    }

    function inCaseTokensGetStuck(address stuckToken) external onlyAdmin {
        // NOTE: vault never allowed to access users' `want` token
        require(stuckToken != want, 'Vault: stuckToken_NOT_WANT');
        require(stuckToken != address(0), 'Vault: stuckToken_ZERO_ADDR');
        uint256 amount = IERC20(stuckToken).balanceOf(address(this));
        if (amount > 0) {
            TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
        }
    }
}

File 45 of 58 : VaultAvax.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../interfaces/IStrategy.sol';
import '../interfaces/IWETH.sol';
import '../interfaces/IWooAccessManager.sol';
import '../interfaces/IVault.sol';

contract VaultAvax is IVault, ERC20, Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    struct StratCandidate {
        address implementation;
        uint256 proposedTime;
    }

    /* ----- State Variables ----- */

    address public immutable override want;

    IWooAccessManager public immutable accessManager;

    IStrategy public strategy;
    StratCandidate public stratCandidate;

    mapping(address => uint256) public costSharePrice;

    event NewStratCandidate(address indexed implementation);
    event UpgradeStrat(address indexed implementation);

    /* ----- Constant Variables ----- */

    // WAVAX: https://snowtrace.io/address/0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7
    address public constant wrappedEther = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7;

    constructor(address initWant, address initAccessManager)
        public
        ERC20(
            string(abi.encodePacked('WOOFi Earn ', ERC20(initWant).name())),
            string(abi.encodePacked('we', ERC20(initWant).symbol()))
        )
    {
        require(initWant != address(0), 'Vault: initWant_ZERO_ADDR');
        require(initAccessManager != address(0), 'Vault: initAccessManager_ZERO_ADDR');

        want = initWant;
        accessManager = IWooAccessManager(initAccessManager);
    }

    modifier onlyAdmin() {
        require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'Vault: NOT_ADMIN');
        _;
    }

    /* ----- External Functions ----- */

    function deposit(uint256 amount) public payable override nonReentrant {
        require(amount > 0, 'Vault: amount_CAN_NOT_BE_ZERO');

        if (want == wrappedEther) {
            require(msg.value == amount, 'Vault: msg.value_INSUFFICIENT');
        } else {
            require(msg.value == 0, 'Vault: msg.value_INVALID');
        }

        if (address(strategy) != address(0)) {
            require(!strategy.paused(), 'Vault: strat_paused');
            strategy.beforeDeposit();
        }

        uint256 balanceBefore = balance();
        if (want == wrappedEther) {
            IWETH(wrappedEther).deposit{value: msg.value}();
        } else {
            TransferHelper.safeTransferFrom(want, msg.sender, address(this), amount);
        }
        uint256 balanceAfter = balance();
        require(amount <= balanceAfter.sub(balanceBefore), 'Vault: amount_NOT_ENOUGH');

        uint256 shares = totalSupply() == 0 ? amount : amount.mul(totalSupply()).div(balanceBefore);
        uint256 sharesBefore = balanceOf(msg.sender);
        uint256 costBefore = costSharePrice[msg.sender];
        uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares));
        costSharePrice[msg.sender] = costAfter;

        _mint(msg.sender, shares);

        earn();
    }

    function withdraw(uint256 shares) public override nonReentrant {
        require(shares > 0, 'Vault: shares_ZERO');
        require(shares <= balanceOf(msg.sender), 'Vault: shares_NOT_ENOUGH');

        uint256 withdrawAmount = shares.mul(balance()).div(totalSupply());
        _burn(msg.sender, shares);

        uint256 balanceBefore = IERC20(want).balanceOf(address(this));
        if (balanceBefore < withdrawAmount) {
            uint256 balanceToWithdraw = withdrawAmount.sub(balanceBefore);
            require(_isStratActive(), 'Vault: STRAT_INACTIVE');
            strategy.withdraw(balanceToWithdraw);
            uint256 balanceAfter = IERC20(want).balanceOf(address(this));
            if (withdrawAmount > balanceAfter) {
                // NOTE: in case a small amount not counted in, due to the decimal precision.
                withdrawAmount = balanceAfter;
            }
        }

        if (want == wrappedEther) {
            IWETH(wrappedEther).withdraw(withdrawAmount);
            TransferHelper.safeTransferETH(msg.sender, withdrawAmount);
        } else {
            TransferHelper.safeTransfer(want, msg.sender, withdrawAmount);
        }
    }

    function earn() public override {
        if (_isStratActive()) {
            uint256 balanceAvail = available();
            TransferHelper.safeTransfer(want, address(strategy), balanceAvail);
            strategy.deposit();
        }
    }

    function available() public view override returns (uint256) {
        return IERC20(want).balanceOf(address(this));
    }

    function balance() public view override returns (uint256) {
        return address(strategy) != address(0) ? available().add(strategy.balanceOf()) : available();
    }

    function getPricePerFullShare() public view override returns (uint256) {
        return totalSupply() == 0 ? 1e18 : balance().mul(1e18).div(totalSupply());
    }

    function _isStratActive() internal view returns (bool) {
        return address(strategy) != address(0) && !strategy.paused();
    }

    /* ----- Admin Functions ----- */

    function setupStrat(address _strat) public onlyAdmin {
        require(_strat != address(0), 'Vault: STRAT_ZERO_ADDR');
        require(address(strategy) == address(0), 'Vault: STRAT_ALREADY_SET');
        require(address(this) == IStrategy(_strat).vault(), 'Vault: STRAT_VAULT_INVALID');
        require(want == IStrategy(_strat).want(), 'Vault: STRAT_WANT_INVALID');
        strategy = IStrategy(_strat);

        emit UpgradeStrat(_strat);
    }

    function proposeStrat(address _implementation) public onlyAdmin {
        require(address(this) == IStrategy(_implementation).vault(), 'Vault: STRAT_VAULT_INVALID');
        require(want == IStrategy(_implementation).want(), 'Vault: STRAT_WANT_INVALID');
        stratCandidate = StratCandidate({implementation: _implementation, proposedTime: block.timestamp});

        emit NewStratCandidate(_implementation);
    }

    function upgradeStrat() public onlyAdmin {
        require(stratCandidate.implementation != address(0), 'Vault: NO_CANDIDATE');
        require(stratCandidate.proposedTime.add(48 hours) < block.timestamp, 'Vault: TIME_INVALID');

        emit UpgradeStrat(stratCandidate.implementation);

        strategy.retireStrat();
        strategy = IStrategy(stratCandidate.implementation);
        stratCandidate.implementation = address(0);
        stratCandidate.proposedTime = 5000000000; // 100+ years to ensure proposedTime check

        earn();
    }

    function inCaseTokensGetStuck(address stuckToken) external onlyAdmin {
        require(stuckToken != address(0), 'Vault: stuckToken_ZERO_ADDR');
        require(stuckToken != want, 'Vault: stuckToken_NOT_WANT');

        uint256 amount = IERC20(stuckToken).balanceOf(address(this));
        if (amount > 0) {
            TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
        }
    }

    function inCaseNativeTokensGetStuck() external onlyAdmin {
        // NOTE: vault never needs native tokens to do the yield farming;
        // This native token balance indicates a user's incorrect transfer.
        if (address(this).balance > 0) {
            TransferHelper.safeTransferETH(msg.sender, address(this).balance);
        }
    }

    receive() external payable {}
}

File 46 of 58 : Vault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../interfaces/IStrategy.sol';
import '../interfaces/IWETH.sol';
import '../interfaces/IWooAccessManager.sol';
import '../interfaces/IVault.sol';

contract Vault is IVault, ERC20, Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    struct StratCandidate {
        address implementation;
        uint256 proposedTime;
    }

    /* ----- State Variables ----- */

    address public immutable override want;

    IWooAccessManager public immutable accessManager;

    IStrategy public strategy;
    StratCandidate public stratCandidate;

    mapping(address => uint256) public costSharePrice;

    event NewStratCandidate(address indexed implementation);
    event UpgradeStrat(address indexed implementation);

    /* ----- Constant Variables ----- */

    // WBNB: https://bscscan.com/token/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c
    address public constant wrappedEther = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;

    constructor(address initWant, address initAccessManager)
        public
        ERC20(
            string(abi.encodePacked('WOOFi Earn ', ERC20(initWant).name())),
            string(abi.encodePacked('we', ERC20(initWant).symbol()))
        )
    {
        require(initWant != address(0), 'Vault: initWant_ZERO_ADDR');
        require(initAccessManager != address(0), 'Vault: initAccessManager_ZERO_ADDR');

        want = initWant;
        accessManager = IWooAccessManager(initAccessManager);
    }

    modifier onlyAdmin() {
        require(owner() == _msgSender() || accessManager.isVaultAdmin(msg.sender), 'Vault: NOT_ADMIN');
        _;
    }

    /* ----- External Functions ----- */

    function deposit(uint256 amount) public payable override nonReentrant {
        require(amount > 0, 'Vault: amount_CAN_NOT_BE_ZERO');

        if (want == wrappedEther) {
            require(msg.value == amount, 'Vault: msg.value_INSUFFICIENT');
        } else {
            require(msg.value == 0, 'Vault: msg.value_INVALID');
        }

        if (address(strategy) != address(0)) {
            require(!strategy.paused(), 'Vault: strat_paused');
            strategy.beforeDeposit();
        }

        uint256 balanceBefore = balance();
        if (want == wrappedEther) {
            IWETH(wrappedEther).deposit{value: msg.value}();
        } else {
            TransferHelper.safeTransferFrom(want, msg.sender, address(this), amount);
        }
        uint256 balanceAfter = balance();
        require(amount <= balanceAfter.sub(balanceBefore), 'Vault: amount_NOT_ENOUGH');

        uint256 shares = totalSupply() == 0 ? amount : amount.mul(totalSupply()).div(balanceBefore);
        uint256 sharesBefore = balanceOf(msg.sender);
        uint256 costBefore = costSharePrice[msg.sender];
        uint256 costAfter = (sharesBefore.mul(costBefore).add(amount.mul(1e18))).div(sharesBefore.add(shares));
        costSharePrice[msg.sender] = costAfter;

        _mint(msg.sender, shares);

        earn();
    }

    function withdraw(uint256 shares) public override nonReentrant {
        require(shares > 0, 'Vault: shares_ZERO');
        require(shares <= balanceOf(msg.sender), 'Vault: shares_NOT_ENOUGH');

        uint256 withdrawAmount = shares.mul(balance()).div(totalSupply());
        _burn(msg.sender, shares);

        uint256 balanceBefore = IERC20(want).balanceOf(address(this));
        if (balanceBefore < withdrawAmount) {
            uint256 balanceToWithdraw = withdrawAmount.sub(balanceBefore);
            require(_isStratActive(), 'Vault: STRAT_INACTIVE');
            strategy.withdraw(balanceToWithdraw);
            uint256 balanceAfter = IERC20(want).balanceOf(address(this));
            if (withdrawAmount > balanceAfter) {
                // NOTE: in case a small amount not counted in, due to the decimal precision.
                withdrawAmount = balanceAfter;
            }
        }

        if (want == wrappedEther) {
            IWETH(wrappedEther).withdraw(withdrawAmount);
            TransferHelper.safeTransferETH(msg.sender, withdrawAmount);
        } else {
            TransferHelper.safeTransfer(want, msg.sender, withdrawAmount);
        }
    }

    function earn() public override {
        if (_isStratActive()) {
            uint256 balanceAvail = available();
            TransferHelper.safeTransfer(want, address(strategy), balanceAvail);
            strategy.deposit();
        }
    }

    function available() public view override returns (uint256) {
        return IERC20(want).balanceOf(address(this));
    }

    function balance() public view override returns (uint256) {
        return address(strategy) != address(0) ? available().add(strategy.balanceOf()) : available();
    }

    function getPricePerFullShare() public view override returns (uint256) {
        return totalSupply() == 0 ? 1e18 : balance().mul(1e18).div(totalSupply());
    }

    function _isStratActive() internal view returns (bool) {
        return address(strategy) != address(0) && !strategy.paused();
    }

    /* ----- Admin Functions ----- */

    function setupStrat(address _strat) public onlyAdmin {
        require(_strat != address(0), 'Vault: STRAT_ZERO_ADDR');
        require(address(strategy) == address(0), 'Vault: STRAT_ALREADY_SET');
        require(address(this) == IStrategy(_strat).vault(), 'Vault: STRAT_VAULT_INVALID');
        require(want == IStrategy(_strat).want(), 'Vault: STRAT_WANT_INVALID');
        strategy = IStrategy(_strat);

        emit UpgradeStrat(_strat);
    }

    function proposeStrat(address _implementation) public onlyAdmin {
        require(address(this) == IStrategy(_implementation).vault(), 'Vault: STRAT_VAULT_INVALID');
        require(want == IStrategy(_implementation).want(), 'Vault: STRAT_WANT_INVALID');
        stratCandidate = StratCandidate({implementation: _implementation, proposedTime: block.timestamp});

        emit NewStratCandidate(_implementation);
    }

    function upgradeStrat() public onlyAdmin {
        require(stratCandidate.implementation != address(0), 'Vault: NO_CANDIDATE');
        require(stratCandidate.proposedTime.add(48 hours) < block.timestamp, 'Vault: TIME_INVALID');

        emit UpgradeStrat(stratCandidate.implementation);

        strategy.retireStrat();
        strategy = IStrategy(stratCandidate.implementation);
        stratCandidate.implementation = address(0);
        stratCandidate.proposedTime = 5000000000; // 100+ years to ensure proposedTime check

        earn();
    }

    function inCaseTokensGetStuck(address stuckToken) external onlyAdmin {
        require(stuckToken != want, 'Vault: stuckToken_NOT_WANT');
        require(stuckToken != address(0), 'Vault: stuckToken_ZERO_ADDR');
        uint256 amount = IERC20(stuckToken).balanceOf(address(this));
        if (amount > 0) {
            TransferHelper.safeTransfer(stuckToken, msg.sender, amount);
        }
    }

    function inCaseNativeTokensGetStuck() external onlyAdmin {
        // NOTE: vault never needs native tokens to do the yield farming;
        // This native token balance indicates a user's incorrect transfer.
        if (address(this).balance > 0) {
            TransferHelper.safeTransferETH(msg.sender, address(this).balance);
        }
    }

    receive() external payable {}
}

File 47 of 58 : StrategyTraderJoeLP.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../../../interfaces/PancakeSwap/IMasterChef.sol';
import '../../../interfaces/TraderJoe/IUniswapPair.sol';
import '../../../interfaces/TraderJoe/IUniswapRouter.sol';
import '../../../interfaces/IWooAccessManager.sol';
import '../../../interfaces/IStrategy.sol';

import '../BaseStrategy.sol';

contract StrategyTraderJoeLP is BaseStrategy {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    /* ----- State Variables ----- */

    uint256 public immutable pid;

    address[] public rewardToLP0Route;
    address[] public rewardToLP1Route;

    address public lpToken0;
    address public lpToken1;

    address public constant reward = address(0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd);
    address public constant uniRouter = address(0x60aE616a2155Ee3d9A68541Ba4544862310933d4);
    address public constant masterChef = address(0xd6a4F121CA35509aF06A0Be99093d08462f53052);

    constructor(
        address initVault,
        address initAccessManager,
        uint256 initPid,
        address[] memory initRewardToLP0Route,
        address[] memory initRewardToLP1Route
    ) public BaseStrategy(initVault, initAccessManager) {
        pid = initPid;
        rewardToLP0Route = initRewardToLP0Route;
        rewardToLP1Route = initRewardToLP1Route;

        if (initRewardToLP0Route.length == 0) {
            lpToken0 = reward;
        } else {
            require(initRewardToLP0Route[0] == reward);
            lpToken0 = initRewardToLP0Route[initRewardToLP0Route.length - 1];
        }
        if (initRewardToLP1Route.length == 0) {
            lpToken1 = reward;
        } else {
            require(initRewardToLP1Route[0] == reward);
            lpToken1 = initRewardToLP1Route[initRewardToLP1Route.length - 1];
        }

        require(
            IUniswapV2Pair(want).token0() == lpToken0 || IUniswapV2Pair(want).token0() == lpToken1,
            'StrategyLP: LP_token0_INVALID'
        );
        require(
            IUniswapV2Pair(want).token1() == lpToken0 || IUniswapV2Pair(want).token1() == lpToken1,
            'StrategyLP: LP_token1_INVALID'
        );

        (address lpToken, , , ) = IMasterChef(masterChef).poolInfo(initPid);
        require(lpToken == want, 'StrategyLP: wrong_initPid');

        _giveAllowances();
    }

    /* ----- External Functions ----- */

    function withdraw(uint256 amount) external override nonReentrant {
        require(msg.sender == vault, 'StrategyLP: NOT_VAULT');

        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance < amount) {
            IMasterChef(masterChef).withdraw(pid, amount.sub(wantBalance));
            wantBalance = IERC20(want).balanceOf(address(this));
        }

        // just in case the decimal precision for the very left staking amount
        uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance;

        uint256 fee = chargeWithdrawalFee(withdrawAmount);
        if (withdrawAmount > fee) {
            TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee));
        }
    }

    function harvest() public override whenNotPaused {
        require(msg.sender == tx.origin || msg.sender == vault, 'StrategyLP: EOA_OR_VAULT');

        IMasterChef(masterChef).deposit(pid, 0);
        uint256 rewardAmount = IERC20(reward).balanceOf(address(this));
        if (rewardAmount > 0) {
            uint256 wantBefore = IERC20(want).balanceOf(address(this));
            _addLiquidity();
            uint256 wantAfter = IERC20(want).balanceOf(address(this));
            uint256 perfAmount = wantAfter.sub(wantBefore);
            chargePerformanceFee(perfAmount);
        }
        deposit();
    }

    function deposit() public override whenNotPaused nonReentrant {
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            IMasterChef(masterChef).deposit(pid, wantBalance);
        }
    }

    function balanceOfPool() public view override returns (uint256) {
        (uint256 amount, ) = IMasterChef(masterChef).userInfo(pid, address(this));
        return amount;
    }

    /* ----- Private Functions ----- */

    function _giveAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
        TransferHelper.safeApprove(want, masterChef, uint256(-1));

        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(reward, uniRouter, uint256(-1));

        TransferHelper.safeApprove(lpToken0, uniRouter, 0);
        TransferHelper.safeApprove(lpToken0, uniRouter, uint256(-1));

        TransferHelper.safeApprove(lpToken1, uniRouter, 0);
        TransferHelper.safeApprove(lpToken1, uniRouter, uint256(-1));
    }

    function _removeAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(lpToken0, uniRouter, 0);
        TransferHelper.safeApprove(lpToken1, uniRouter, 0);
    }

    function _addLiquidity() private {
        uint256 rewardHalf = IERC20(reward).balanceOf(address(this)).div(2);

        if (lpToken0 != reward) {
            IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP0Route, address(this), now);
        }

        if (lpToken1 != reward) {
            IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP1Route, address(this), now);
        }

        uint256 lp0Balance = IERC20(lpToken0).balanceOf(address(this));
        uint256 lp1Balance = IERC20(lpToken1).balanceOf(address(this));
        IUniswapRouter(uniRouter).addLiquidity(lpToken0, lpToken1, lp0Balance, lp1Balance, 0, 0, address(this), now);
    }

    function retireStrat() external override {
        require(msg.sender == vault, '!vault');
        IMasterChef(masterChef).emergencyWithdraw(pid);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }

    function emergencyExit() external override onlyAdmin {
        IMasterChef(masterChef).emergencyWithdraw(pid);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }
}

File 48 of 58 : IUniswapPair.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IUniswapV2Pair {
    function factory() external view returns (address);

    function token0() external view returns (address);

    function token1() external view returns (address);

    function burn(address to) external returns (uint256 amount0, uint256 amount1);

    function getReserves()
        external
        view
        returns (
            uint112 reserve0,
            uint112 reserve1,
            uint32 blockTimestampLast
        );
}

File 49 of 58 : IUniswapRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IUniswapRouter {
    function factory() external pure returns (address);

    function WBNB() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );

    function addLiquidityBNB(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountBNB,
            uint256 liquidity
        );

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityBNB(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountBNB);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityBNBWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountBNB);

    function removeLiquidityBNBSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountBNB);

    function removeLiquidityBNBWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountBNB);

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactBNBForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    function swapExactTokensForBNBSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactBNBForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapTokensForExactBNB(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForBNB(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapBNBForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) external pure returns (uint256 amountB);

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountOut);

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountIn);

    function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts);

    function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts);
}

File 50 of 58 : StrategyTraderJoeDualLP.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../../../interfaces/PancakeSwap/IMasterChef.sol';
import '../../../interfaces/TraderJoe/IUniswapPair.sol';
import '../../../interfaces/TraderJoe/IUniswapRouter.sol';
import '../../../interfaces/IWooAccessManager.sol';
import '../../../interfaces/IStrategy.sol';

import '../BaseStrategy.sol';

contract StrategyTraderJoeDualLP is BaseStrategy {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    /* ----- State Variables ----- */

    uint256 public immutable pid;

    address[] public rewardToLP0Route;
    address[] public rewardToLP1Route;
    address[] public secondRewardToLP0Route;
    address[] public secondRewardToLP1Route;

    address public lpToken0;
    address public lpToken1;

    address public constant reward = address(0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd); // JOE
    address public constant secondReward = address(0x8729438EB15e2C8B576fCc6AeCdA6A148776C0F5); // QI
    address public constant uniRouter = address(0x60aE616a2155Ee3d9A68541Ba4544862310933d4);
    address public constant masterChef = address(0x188bED1968b795d5c9022F6a0bb5931Ac4c18F00);

    constructor(
        address initVault,
        address initAccessManager,
        uint256 initPid,
        address[] memory initRewardToLP0Route,
        address[] memory initRewardToLP1Route,
        address[] memory initSecondRewardToLP0Route,
        address[] memory initSecondRewardToLP1Route
    ) public BaseStrategy(initVault, initAccessManager) {
        pid = initPid;
        rewardToLP0Route = initRewardToLP0Route;
        rewardToLP1Route = initRewardToLP1Route;
        secondRewardToLP0Route = initSecondRewardToLP0Route;
        secondRewardToLP1Route = initSecondRewardToLP1Route;

        if (initRewardToLP0Route.length == 0) {
            lpToken0 = reward;
        } else {
            require(initRewardToLP0Route[0] == reward);
            lpToken0 = initRewardToLP0Route[initRewardToLP0Route.length - 1];
        }
        if (initRewardToLP1Route.length == 0) {
            lpToken1 = reward;
        } else {
            require(initRewardToLP1Route[0] == reward);
            lpToken1 = initRewardToLP1Route[initRewardToLP1Route.length - 1];
        }
        require(initSecondRewardToLP0Route[0] == secondReward);
        require(initSecondRewardToLP1Route[0] == secondReward);

        require(
            IUniswapV2Pair(want).token0() == lpToken0 || IUniswapV2Pair(want).token0() == lpToken1,
            'StrategyLP: LP_token0_INVALID'
        );
        require(
            IUniswapV2Pair(want).token1() == lpToken0 || IUniswapV2Pair(want).token1() == lpToken1,
            'StrategyLP: LP_token1_INVALID'
        );

        (address lpToken, , , ) = IMasterChef(masterChef).poolInfo(initPid);
        require(lpToken == want, 'StrategyLP: wrong_initPid');

        _giveAllowances();
    }

    /* ----- External Functions ----- */

    function withdraw(uint256 amount) external override nonReentrant {
        require(msg.sender == vault, 'StrategyLP: NOT_VAULT');

        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance < amount) {
            IMasterChef(masterChef).withdraw(pid, amount.sub(wantBalance));
            wantBalance = IERC20(want).balanceOf(address(this));
        }

        // just in case the decimal precision for the very left staking amount
        uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance;

        uint256 fee = chargeWithdrawalFee(withdrawAmount);
        if (withdrawAmount > fee) {
            TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee));
        }
    }

    function harvest() public override whenNotPaused {
        require(msg.sender == tx.origin || msg.sender == vault, 'StrategyLP: EOA_OR_VAULT');

        IMasterChef(masterChef).deposit(pid, 0);
        uint256 rewardAmount = IERC20(reward).balanceOf(address(this));
        uint256 secondRewardAmount = IERC20(secondReward).balanceOf(address(this));
        if (rewardAmount > 0 || secondRewardAmount > 0) {
            uint256 wantBefore = IERC20(want).balanceOf(address(this));
            _addLiquidity();
            uint256 wantAfter = IERC20(want).balanceOf(address(this));
            uint256 perfAmount = wantAfter.sub(wantBefore);
            chargePerformanceFee(perfAmount);
        }
        deposit();
    }

    function deposit() public override whenNotPaused nonReentrant {
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            IMasterChef(masterChef).deposit(pid, wantBalance);
        }
    }

    function balanceOfPool() public view override returns (uint256) {
        (uint256 amount, ) = IMasterChef(masterChef).userInfo(pid, address(this));
        return amount;
    }

    /* ----- Private Functions ----- */

    function _giveAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
        TransferHelper.safeApprove(want, masterChef, uint256(-1));

        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(reward, uniRouter, uint256(-1));

        TransferHelper.safeApprove(secondReward, uniRouter, 0);
        TransferHelper.safeApprove(secondReward, uniRouter, uint256(-1));

        TransferHelper.safeApprove(lpToken0, uniRouter, 0);
        TransferHelper.safeApprove(lpToken0, uniRouter, uint256(-1));

        TransferHelper.safeApprove(lpToken1, uniRouter, 0);
        TransferHelper.safeApprove(lpToken1, uniRouter, uint256(-1));
    }

    function _removeAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(secondReward, uniRouter, 0);
        TransferHelper.safeApprove(lpToken0, uniRouter, 0);
        TransferHelper.safeApprove(lpToken1, uniRouter, 0);
    }

    function _addLiquidity() private {
        uint256 rewardHalf = IERC20(reward).balanceOf(address(this)).div(2);
        uint256 secondRewardHalf = IERC20(secondReward).balanceOf(address(this)).div(2);

        if (lpToken0 != reward) {
            IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP0Route, address(this), now);
        }

        if (lpToken1 != reward) {
            IUniswapRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP1Route, address(this), now);
        }

        if (lpToken0 != secondReward) {
            IUniswapRouter(uniRouter).swapExactTokensForTokens(
                secondRewardHalf,
                0,
                secondRewardToLP0Route,
                address(this),
                now
            );
        }

        if (lpToken1 != secondReward) {
            IUniswapRouter(uniRouter).swapExactTokensForTokens(
                secondRewardHalf,
                0,
                secondRewardToLP1Route,
                address(this),
                now
            );
        }

        uint256 lp0Balance = IERC20(lpToken0).balanceOf(address(this));
        uint256 lp1Balance = IERC20(lpToken1).balanceOf(address(this));
        IUniswapRouter(uniRouter).addLiquidity(lpToken0, lpToken1, lp0Balance, lp1Balance, 0, 0, address(this), now);
    }

    function retireStrat() external override {
        require(msg.sender == vault, '!vault');
        IMasterChef(masterChef).emergencyWithdraw(pid);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }

    function emergencyExit() external override onlyAdmin {
        IMasterChef(masterChef).emergencyWithdraw(pid);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }
}

File 51 of 58 : StrategyLP.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../../../interfaces/PancakeSwap/IMasterChef.sol';
import '../../../interfaces/PancakeSwap/IPancakePair.sol';
import '../../../interfaces/PancakeSwap/IPancakeRouter.sol';
import '../../../interfaces/IWooAccessManager.sol';
import '../../../interfaces/IStrategy.sol';

import '../BaseStrategy.sol';

contract StrategyLP is BaseStrategy {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    /* ----- State Variables ----- */

    uint256 public immutable pid;

    address[] public rewardToLP0Route;
    address[] public rewardToLP1Route;

    address public lpToken0;
    address public lpToken1;

    address public constant reward = address(0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82);
    address public constant uniRouter = address(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    address public constant masterChef = address(0x73feaa1eE314F8c655E354234017bE2193C9E24E);

    constructor(
        address initVault,
        address initAccessManager,
        uint256 initPid,
        address[] memory initRewardToLP0Route,
        address[] memory initRewardToLP1Route
    ) public BaseStrategy(initVault, initAccessManager) {
        pid = initPid;
        rewardToLP0Route = initRewardToLP0Route;
        rewardToLP1Route = initRewardToLP1Route;

        if (initRewardToLP0Route.length == 0) {
            lpToken0 = reward;
        } else {
            require(initRewardToLP0Route[0] == reward);
            lpToken0 = initRewardToLP0Route[initRewardToLP0Route.length - 1];
        }
        if (initRewardToLP1Route.length == 0) {
            lpToken1 = reward;
        } else {
            require(initRewardToLP1Route[0] == reward);
            lpToken1 = initRewardToLP1Route[initRewardToLP1Route.length - 1];
        }

        require(
            IPancakePair(want).token0() == lpToken0 || IPancakePair(want).token0() == lpToken1,
            'StrategyLP: LP_token0_INVALID'
        );
        require(
            IPancakePair(want).token1() == lpToken0 || IPancakePair(want).token1() == lpToken1,
            'StrategyLP: LP_token1_INVALID'
        );

        (address lpToken, , , ) = IMasterChef(masterChef).poolInfo(initPid);
        require(lpToken == want, 'StrategyLP: wrong_initPid');

        _giveAllowances();
    }

    /* ----- External Functions ----- */

    function withdraw(uint256 amount) external override nonReentrant {
        require(msg.sender == vault, 'StrategyLP: NOT_VAULT');

        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance < amount) {
            IMasterChef(masterChef).withdraw(pid, amount.sub(wantBalance));
            wantBalance = IERC20(want).balanceOf(address(this));
        }

        // just in case the decimal precision for the very left staking amount
        uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance;

        uint256 fee = chargeWithdrawalFee(withdrawAmount);
        if (withdrawAmount > fee) {
            TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee));
        }
    }

    function harvest() public override whenNotPaused {
        require(msg.sender == tx.origin || msg.sender == vault, 'StrategyLP: EOA_OR_VAULT');

        IMasterChef(masterChef).deposit(pid, 0);
        uint256 rewardAmount = IERC20(reward).balanceOf(address(this));
        if (rewardAmount > 0) {
            uint256 wantBefore = IERC20(want).balanceOf(address(this));
            _addLiquidity();
            uint256 wantAfter = IERC20(want).balanceOf(address(this));
            uint256 perfAmount = wantAfter.sub(wantBefore);
            chargePerformanceFee(perfAmount);
        }
        deposit();
    }

    function deposit() public override whenNotPaused nonReentrant {
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            IMasterChef(masterChef).deposit(pid, wantBalance);
        }
    }

    function balanceOfPool() public view override returns (uint256) {
        (uint256 amount, ) = IMasterChef(masterChef).userInfo(pid, address(this));
        return amount;
    }

    /* ----- Private Functions ----- */

    function _giveAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
        TransferHelper.safeApprove(want, masterChef, uint256(-1));

        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(reward, uniRouter, uint256(-1));

        TransferHelper.safeApprove(lpToken0, uniRouter, 0);
        TransferHelper.safeApprove(lpToken0, uniRouter, uint256(-1));

        TransferHelper.safeApprove(lpToken1, uniRouter, 0);
        TransferHelper.safeApprove(lpToken1, uniRouter, uint256(-1));
    }

    function _removeAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(lpToken0, uniRouter, 0);
        TransferHelper.safeApprove(lpToken1, uniRouter, 0);
    }

    function _addLiquidity() private {
        uint256 rewardHalf = IERC20(reward).balanceOf(address(this)).div(2);

        if (lpToken0 != reward) {
            IPancakeRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP0Route, address(this), now);
        }

        if (lpToken1 != reward) {
            IPancakeRouter(uniRouter).swapExactTokensForTokens(rewardHalf, 0, rewardToLP1Route, address(this), now);
        }

        uint256 lp0Balance = IERC20(lpToken0).balanceOf(address(this));
        uint256 lp1Balance = IERC20(lpToken1).balanceOf(address(this));
        IPancakeRouter(uniRouter).addLiquidity(lpToken0, lpToken1, lp0Balance, lp1Balance, 0, 0, address(this), now);
    }

    function retireStrat() external override {
        require(msg.sender == vault, '!vault');
        IMasterChef(masterChef).emergencyWithdraw(pid);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }

    function emergencyExit() external override onlyAdmin {
        IMasterChef(masterChef).emergencyWithdraw(pid);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }
}

File 52 of 58 : IPancakePair.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IPancakePair {
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Transfer(address indexed from, address indexed to, uint256 value);

    function name() external pure returns (string memory);

    function symbol() external pure returns (string memory);

    function decimals() external pure returns (uint8);

    function totalSupply() external view returns (uint256);

    function balanceOf(address owner) external view returns (uint256);

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 value) external returns (bool);

    function transfer(address to, uint256 value) external returns (bool);

    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function PERMIT_TYPEHASH() external pure returns (bytes32);

    function nonces(address owner) external view returns (uint256);

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
    event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint256);

    function factory() external view returns (address);

    function token0() external view returns (address);

    function token1() external view returns (address);

    function getReserves()
        external
        view
        returns (
            uint112 reserve0,
            uint112 reserve1,
            uint32 blockTimestampLast
        );

    function price0CumulativeLast() external view returns (uint256);

    function price1CumulativeLast() external view returns (uint256);

    function kLast() external view returns (uint256);

    function mint(address to) external returns (uint256 liquidity);

    function burn(address to) external returns (uint256 amount0, uint256 amount1);

    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to,
        bytes calldata data
    ) external;

    function skim(address to) external;

    function sync() external;

    function initialize(address, address) external;
}

File 53 of 58 : StrategyBankerJoe.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../../../interfaces/BankerJoe/IJoeRouter.sol';
import '../../../interfaces/BankerJoe/IVToken.sol';
import '../../../interfaces/BankerJoe/IComptroller.sol';
import '../../../interfaces/IWooAccessManager.sol';
import '../../../interfaces/IWETH.sol';
import '../BaseStrategy.sol';

contract StrategyBankerJoe is BaseStrategy {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    /* ----- State Variables ----- */

    address public iToken;
    address[] public rewardToWantRoute;
    uint256 public lastHarvest;
    uint256 public supplyBal;

    /* ----- Constant Variables ----- */

    address public constant wrappedEther = address(0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7); // WAVAX
    address public constant reward = address(0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd); // JOE
    address public constant uniRouter = address(0x60aE616a2155Ee3d9A68541Ba4544862310933d4); // JoeRouter
    address public constant comptroller = address(0xdc13687554205E5b89Ac783db14bb5bba4A1eDaC);

    /* ----- Events ----- */

    event StratHarvest(address indexed harvester, uint256 wantHarvested, uint256 tvl);
    event Deposit(uint256 tvl);
    event Withdraw(uint256 tvl);

    constructor(
        address initVault,
        address initAccessManager,
        address initIToken,
        address[] memory initRewardToWantRoute
    ) public BaseStrategy(initVault, initAccessManager) {
        iToken = initIToken;
        rewardToWantRoute = initRewardToWantRoute;

        _giveAllowances();
    }

    /* ----- External Functions ----- */

    function beforeDeposit() public override {
        super.beforeDeposit();
        updateSupplyBal();
    }

    function rewardToWant() external view returns (address[] memory) {
        return rewardToWantRoute;
    }

    /* ----- Public Functions ----- */

    function harvest() public override whenNotPaused {
        require(msg.sender == tx.origin || msg.sender == address(vault), 'StrategyBankerJoeNative: EOA_or_vault');

        // When pendingImplementation not zero address, means there is a new implement ready to replace.
        if (IComptroller(comptroller).pendingImplementation() == address(0)) {
            uint256 beforeBal = balanceOfWant();

            IComptroller(comptroller).claimReward(0, address(this));
            IComptroller(comptroller).claimReward(1, address(this));
            uint256 toWrap = address(this).balance;
            if (toWrap > 0) {
                IWETH(wrappedEther).deposit{value: toWrap}();
            }

            uint256 rewardBal = IERC20(reward).balanceOf(address(this));
            if (rewardBal > 0) {
                IJoeRouter(uniRouter).swapExactTokensForTokens(rewardBal, 0, rewardToWantRoute, address(this), now);
                uint256 wantHarvested = balanceOfWant().sub(beforeBal);
                uint256 fee = chargePerformanceFee(wantHarvested);
                deposit();

                lastHarvest = block.timestamp;
                emit StratHarvest(msg.sender, wantHarvested.sub(fee), balanceOf());
            }
        } else {
            _withdrawAll();
            pause();
        }
    }

    function deposit() public override whenNotPaused nonReentrant {
        uint256 wantBal = balanceOfWant();

        if (wantBal > 0) {
            IVToken(iToken).mint(wantBal);
            updateSupplyBal();
            emit Deposit(balanceOf());
        }
    }

    function withdraw(uint256 amount) public override nonReentrant {
        require(msg.sender == vault, 'StrategyBankerJoeNative: !vault');
        require(amount > 0, 'StrategyBankerJoeNative: !amount');

        uint256 wantBal = balanceOfWant();

        if (wantBal < amount) {
            IVToken(iToken).redeemUnderlying(amount.sub(wantBal));
            updateSupplyBal();
            uint256 newWantBal = IERC20(want).balanceOf(address(this));
            require(newWantBal > wantBal, 'StrategyBankerJoeNative: !newWantBal');
            wantBal = newWantBal;
        }

        uint256 withdrawAmt = amount < wantBal ? amount : wantBal;

        uint256 fee = chargeWithdrawalFee(withdrawAmt);
        if (withdrawAmt > fee) {
            TransferHelper.safeTransfer(want, vault, withdrawAmt.sub(fee));
        }
        emit Withdraw(balanceOf());
    }

    function updateSupplyBal() public {
        supplyBal = IVToken(iToken).balanceOfUnderlying(address(this));
    }

    function balanceOfPool() public view override returns (uint256) {
        return supplyBal;
    }

    /* ----- Internal Functions ----- */

    function _giveAllowances() internal override {
        TransferHelper.safeApprove(want, iToken, 0);
        TransferHelper.safeApprove(want, iToken, uint256(-1));
        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(reward, uniRouter, uint256(-1));
        TransferHelper.safeApprove(wrappedEther, uniRouter, 0);
        TransferHelper.safeApprove(wrappedEther, uniRouter, uint256(-1));
    }

    function _removeAllowances() internal override {
        TransferHelper.safeApprove(want, iToken, 0);
        TransferHelper.safeApprove(reward, uniRouter, 0);
        TransferHelper.safeApprove(wrappedEther, uniRouter, 0);
    }

    function _withdrawAll() internal {
        uint256 iTokenBal = IERC20(iToken).balanceOf(address(this));
        if (iTokenBal > 0) {
            IVToken(iToken).redeem(iTokenBal);
        }
        updateSupplyBal();
    }

    /* ----- Admin Functions ----- */

    function retireStrat() external override {
        require(msg.sender == vault, 'StrategyBankerJoeNative: !vault');
        _withdrawAll();
        uint256 wantBal = IERC20(want).balanceOf(address(this));
        if (wantBal > 0) {
            TransferHelper.safeTransfer(want, vault, wantBal);
        }
    }

    function emergencyExit() external override onlyAdmin {
        _withdrawAll();
        uint256 wantBal = IERC20(want).balanceOf(address(this));
        if (wantBal > 0) {
            TransferHelper.safeTransfer(want, vault, wantBal);
        }
    }

    receive() external payable {}
}

File 54 of 58 : IJoeRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IJoeRouter {
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );
}

File 55 of 58 : IVToken.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

interface IVToken is IERC20 {
    function underlying() external returns (address);

    function mint(uint256 mintAmount) external returns (uint256);

    function redeem(uint256 redeemTokens) external returns (uint256);

    function redeemUnderlying(uint256 redeemAmount) external returns (uint256);

    function borrow(uint256 borrowAmount) external returns (uint256);

    function repayBorrow(uint256 repayAmount) external returns (uint256);

    function balanceOfUnderlying(address owner) external returns (uint256);

    function borrowBalanceCurrent(address account) external returns (uint256);

    function comptroller() external returns (address);
}

File 56 of 58 : IComptroller.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IComptroller {
    function claimComp(address holder, address[] calldata _iTokens) external;

    function claimComp(address holder) external;

    function compAccrued(address holder) external view returns (uint256 comp);

    function enterMarkets(address[] memory _iTokens) external;

    function pendingComptrollerImplementation() external view returns (address implementation);

    function pendingImplementation() external view returns (address implementation);

    function claimReward(uint8 rewardType, address holder) external;
}

File 57 of 58 : StrategyCake.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';

import '../BaseStrategy.sol';

contract StrategyCake is BaseStrategy {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;

    /* ----- Constant Variables ----- */

    address public constant masterChef = address(0x73feaa1eE314F8c655E354234017bE2193C9E24E);

    constructor(address initVault, address initAccessManager) public BaseStrategy(initVault, initAccessManager) {
        _giveAllowances();
    }

    /* ----- External Functions ----- */

    function withdraw(uint256 amount) external override nonReentrant {
        require(msg.sender == address(vault), 'StrategyCake: NOT_VAULT');

        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance < amount) {
            IMasterChef(masterChef).leaveStaking(amount.sub(wantBalance));
            wantBalance = IERC20(want).balanceOf(address(this));
        }

        // just in case the decimal precision for the very left staking amount
        uint256 withdrawAmount = amount < wantBalance ? amount : wantBalance;

        uint256 fee = chargeWithdrawalFee(withdrawAmount);
        if (withdrawAmount > fee) {
            TransferHelper.safeTransfer(want, vault, withdrawAmount.sub(fee));
        }
    }

    function harvest() public override whenNotPaused {
        require(msg.sender == tx.origin || msg.sender == address(vault), 'StrategyCake: EOA_or_vault');

        uint256 balanceBefore = IERC20(want).balanceOf(address(this));
        IMasterChef(masterChef).leaveStaking(0);
        uint256 balanceAfter = IERC20(want).balanceOf(address(this));

        uint256 perfAmount = balanceAfter.sub(balanceBefore);
        chargePerformanceFee(perfAmount);
        deposit();
    }

    function deposit() public override whenNotPaused nonReentrant {
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            IMasterChef(masterChef).enterStaking(wantBalance);
        }
    }

    function balanceOfPool() public view override returns (uint256) {
        (uint256 amount, ) = IMasterChef(masterChef).userInfo(0, address(this));
        return amount;
    }

    function _giveAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
        TransferHelper.safeApprove(want, masterChef, uint256(-1));
    }

    function _removeAllowances() internal override {
        TransferHelper.safeApprove(want, masterChef, 0);
    }

    /* ----- Admin Functions ----- */

    function retireStrat() external override {
        require(msg.sender == vault, '!vault');
        IMasterChef(masterChef).emergencyWithdraw(0);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }

    function emergencyExit() external override onlyAdmin {
        IMasterChef(masterChef).emergencyWithdraw(0);
        uint256 wantBalance = IERC20(want).balanceOf(address(this));
        if (wantBalance > 0) {
            TransferHelper.safeTransfer(want, vault, wantBalance);
        }
    }
}

File 58 of 58 : DecimalMathTest.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.6.12;

import '../libraries/DecimalMath.sol';

contract DecimalMathTest {
    function mulFloor(uint256 target, uint256 d) external pure returns (uint256) {
        return DecimalMath.mulFloor(target, d);
    }

    function mulCeil(uint256 target, uint256 d) external pure returns (uint256) {
        return DecimalMath.mulCeil(target, d);
    }

    function divFloor(uint256 target, uint256 d) external pure returns (uint256) {
        return DecimalMath.divFloor(target, d);
    }

    function divCeil(uint256 target, uint256 d) external pure returns (uint256) {
        return DecimalMath.divCeil(target, d);
    }

    function reciprocalFloor(uint256 target) external pure returns (uint256) {
        return DecimalMath.reciprocalFloor(target);
    }

    function reciprocalCeil(uint256 target) external pure returns (uint256) {
        return DecimalMath.reciprocalCeil(target);
    }
}

Settings
{
  "evmVersion": "istanbul",
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"newPool","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newPool","type":"address"}],"name":"WooPoolChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IWooRouter.SwapType","name":"swapType","type":"uint8"},{"indexed":true,"internalType":"address","name":"fromToken","type":"address"},{"indexed":true,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"rebateTo","type":"address"}],"name":"WooRouterSwap","type":"event"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"approveTarget","type":"address"},{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"minToAmount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"externalSwap","outputs":[{"internalType":"uint256","name":"realToAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"approveTarget","type":"address"},{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"externalSwap","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"baseAmount","type":"uint256"}],"name":"querySellBase","outputs":[{"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"name":"querySellQuote","outputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"}],"name":"querySwap","outputs":[{"internalType":"uint256","name":"toAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"minQuoteAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"rebateTo","type":"address"}],"name":"sellBase","outputs":[{"internalType":"uint256","name":"realQuoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"},{"internalType":"uint256","name":"minBaseAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"rebateTo","type":"address"}],"name":"sellQuote","outputs":[{"internalType":"uint256","name":"realBaseAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPool","type":"address"}],"name":"setPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bool","name":"whitelisted","type":"bool"}],"name":"setWhitelisted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"minToAmount","type":"uint256"},{"internalType":"address payable","name":"to","type":"address"},{"internalType":"address","name":"rebateTo","type":"address"}],"name":"swap","outputs":[{"internalType":"uint256","name":"realToAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wooPool","outputs":[{"internalType":"contract IWooPP","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b5060405162002d9238038062002d928339810160408190526200003491620002d8565b600062000040620000e5565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350600180556001600160a01b038216620000c05760405162461bcd60e51b8152600401620000b790620003cd565b60405180910390fd5b6001600160601b0319606083901b16608052620000dd81620000e9565b505062000454565b3390565b600260015414156200010f5760405162461bcd60e51b8152600401620000b79062000396565b60026001556200011e620000e5565b6001600160a01b03166200013162000299565b6001600160a01b0316146200015a5760405162461bcd60e51b8152600401620000b79062000361565b6001600160a01b038116620001835760405162461bcd60e51b8152600401620000b7906200032a565b600280546001600160a01b0319166001600160a01b03838116919091179182905560408051630217a4b760e41b81529051929091169163217a4b7091600480820192602092909190829003018186803b158015620001e057600080fd5b505afa158015620001f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200021b9190620002a8565b600480546001600160a01b0319166001600160a01b03928316179081905516620002595760405162461bcd60e51b8152600401620000b79062000404565b7f4577a21bd8e55848c574b7582f8e6cc6a7cf1c1958f36a9751eab6329d656b1e816040516200028a919062000316565b60405180910390a15060018055565b6000546001600160a01b031690565b600060208284031215620002ba578081fd5b81516001600160a01b0381168114620002d1578182fd5b9392505050565b60008060408385031215620002eb578081fd5b8251620002f8816200043b565b60208401519092506200030b816200043b565b809150509250929050565b6001600160a01b0391909116815260200190565b6020808252601c908201527f576f6f526f757465723a206e6577506f6f6c5f414444525f5a45524f00000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526019908201527f576f6f526f757465723a20776574685f5a45524f5f4144445200000000000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a2071756f7465546f6b656e5f414444525f5a45524f00604082015260600190565b6001600160a01b03811681146200045157600080fd5b50565b60805160601c6128f36200049f6000398061011d52806107cb5280610b6c5280610c8f5280610cbe5280610d0852806110a3528061113f528061118d5280611c1752506128f36000f3fe60806040526004361061010d5760003560e01c80637dc2038211610095578063ad5c464811610064578063ad5c4648146102ef578063e94803f414610304578063ea71ee8414610324578063f2fde38b14610337578063f3287c2f1461035757610160565b80637dc20382146102925780638da5cb5b146102a55780639281aa0b146102ba578063a7394603146102da57610160565b806366410a21116100dc57806366410a21146101fd5780636846fb501461021d578063715018a61461023d57806378e3214f1461025257806379a048761461027257610160565b8063199b83fa14610165578063217a4b701461018e5780633af32abf146101b05780634437152a146101dd57610160565b3661016057336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148061015857503360009081526003602052604090205460ff165b61015e57fe5b005b600080fd5b610178610173366004611efe565b610377565b6040516101859190612864565b60405180910390f35b34801561019a57600080fd5b506101a36105b9565b60405161018591906121a0565b3480156101bc57600080fd5b506101d06101cb366004611e1e565b6105c8565b6040516101859190612223565b3480156101e957600080fd5b5061015e6101f8366004611e1e565b6105dd565b34801561020957600080fd5b5061017861021836600461209a565b610777565b34801561022957600080fd5b506101786102383660046120c5565b610875565b34801561024957600080fd5b5061015e6109ef565b34801561025e57600080fd5b5061015e61026d36600461209a565b610a78565b34801561027e57600080fd5b5061017861028d36600461209a565b610b18565b6101786102a0366004611ff3565b610bbf565b3480156102b157600080fd5b506101a3610fc7565b3480156102c657600080fd5b5061015e6102d5366004612062565b610fd6565b3480156102e657600080fd5b506101a3611092565b3480156102fb57600080fd5b506101a36110a1565b34801561031057600080fd5b5061017861031f366004611fb3565b6110c5565b61015e610332366004611e56565b6113a6565b34801561034357600080fd5b5061015e610352366004611e1e565b6113c3565b34801561036357600080fd5b506101786103723660046120c5565b611483565b6000600260015414156103a55760405162461bcd60e51b815260040161039c906127f6565b60405180910390fd5b60026001556001600160a01b038a166103d05760405162461bcd60e51b815260040161039c90612566565b6001600160a01b0389166103f65760405162461bcd60e51b815260040161039c9061229f565b6001600160a01b03881661041c5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0387166104425760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b0384166104685760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b038a1660009081526003602052604090205460ff166104a05760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03891660009081526003602052604090205460ff166104d85760405162461bcd60e51b815260040161039c9061242f565b60006104e488306115f9565b90506104f48b8b8b8a88886116b0565b600061050089306115f9565b9050808211156105225760405162461bcd60e51b815260040161039c906126cd565b61052c818361182f565b92508683101561054e5760405162461bcd60e51b815260040161039c90612524565b610559898785611857565b856001600160a01b0316896001600160a01b03168b6001600160a01b031660008051602061289e83398151915260018c8833600060405161059e95949392919061222e565b60405180910390a45050600180559998505050505050505050565b6004546001600160a01b031681565b60036020526000908152604090205460ff1681565b600260015414156106005760405162461bcd60e51b815260040161039c906127f6565b600260015561060d6118a1565b6001600160a01b031661061e610fc7565b6001600160a01b0316146106445760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b03811661066a5760405162461bcd60e51b815260040161039c9061231c565b600280546001600160a01b0319166001600160a01b03838116919091179182905560408051630217a4b760e41b81529051929091169163217a4b7091600480820192602092909190829003018186803b1580156106c657600080fd5b505afa1580156106da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fe9190611e3a565b600480546001600160a01b0319166001600160a01b039283161790819055166107395760405162461bcd60e51b815260040161039c9061282d565b7f4577a21bd8e55848c574b7582f8e6cc6a7cf1c1958f36a9751eab6329d656b1e8160405161076891906121a0565b60405180910390a15060018055565b60006001600160a01b03831661079f5760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146107c957826107eb565b7f00000000000000000000000000000000000000000000000000000000000000005b6002546040516366410a2160e01b81529194506001600160a01b0316906366410a219061081e90869086906004016121d8565b60206040518083038186803b15801561083657600080fd5b505afa15801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086e919061213f565b9392505050565b60006002600154141561089a5760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166108c55760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166108eb5760405162461bcd60e51b815260040161039c906125a8565b6108f7863330886118a5565b60025461090f9087906001600160a01b031687611996565b6002546040516306846fb560e41b81526001600160a01b0390911690636846fb509061094790899089908990899089906004016121f1565b602060405180830381600087803b15801561096157600080fd5b505af1158015610975573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610999919061213f565b6004546040519192506001600160a01b0380861692918116919089169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60405180910390a46001805595945050505050565b6109f76118a1565b6001600160a01b0316610a08610fc7565b6001600160a01b031614610a2e5760405162461bcd60e51b815260040161039c9061264d565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60026001541415610a9b5760405162461bcd60e51b815260040161039c906127f6565b6002600155610aa86118a1565b6001600160a01b0316610ab9610fc7565b6001600160a01b031614610adf5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b038216610b055760405162461bcd60e51b815260040161039c90612268565b610b10823383611a84565b505060018055565b60006001600160a01b038316610b405760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610b6a5782610b8c565b7f00000000000000000000000000000000000000000000000000000000000000005b600254604051633cd0243b60e11b81529194506001600160a01b0316906379a048769061081e90869086906004016121d8565b600060026001541415610be45760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b038716610c0f5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b038616610c355760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b038316610c5b5760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b0387811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9081149188161481610c8d5788610caf565b7f00000000000000000000000000000000000000000000000000000000000000005b985080610cbc5787610cde565b7f00000000000000000000000000000000000000000000000000000000000000005b97508115610d7f5734871115610d065760405162461bcd60e51b815260040161039c90612616565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d6157600080fd5b505af1158015610d75573d6000803e3d6000fd5b5050505050610d8b565b610d8b8933308a6118a5565b600254610da3908a906001600160a01b031689611996565b6004546001600160a01b038a811691161415610dce57610dc7818989898989611b6b565b9250610f2b565b6004546001600160a01b0389811691161415610e6e576002546040516306846fb560e41b81526001600160a01b0390911690636846fb5090610e1c908c908b908b908b908b906004016121f1565b602060405180830381600087803b158015610e3657600080fd5b505af1158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc7919061213f565b6002546040516306846fb560e41b81526000916001600160a01b031690636846fb5090610ea7908d908c90869030908c906004016121f1565b602060405180830381600087803b158015610ec157600080fd5b505af1158015610ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef9919061213f565b600454600254919250610f19916001600160a01b03918216911683611996565b610f27828a838a8a8a611b6b565b9350505b846001600160a01b031681610f405788610f56565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031683610f6a578a610f80565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660008051602061289e83398151915260008b88338b604051610faf95949392919061222e565b60405180910390a45050600180559695505050505050565b6000546001600160a01b031690565b60026001541415610ff95760405162461bcd60e51b815260040161039c906127f6565b60026001556110066118a1565b6001600160a01b0316611017610fc7565b6001600160a01b03161461103d5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0382166110635760405162461bcd60e51b815260040161039c90612788565b6001600160a01b03919091166000908152600360205260409020805460ff191691151591909117905560018055565b6002546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000081565b60006001600160a01b0384166110ed5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0383166111135760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461113d578361115f565b7f00000000000000000000000000000000000000000000000000000000000000005b93506001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461118b57826111ad565b7f00000000000000000000000000000000000000000000000000000000000000005b6004549093506001600160a01b038581169116141561124f576002546040516366410a2160e01b81526001600160a01b03909116906366410a21906111f890869086906004016121d8565b60206040518083038186803b15801561121057600080fd5b505afa158015611224573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611248919061213f565b905061086e565b6004546001600160a01b038481169116141561129757600254604051633cd0243b60e11b81526001600160a01b03909116906379a04876906111f890879086906004016121d8565b600254604051633cd0243b60e11b81526000916001600160a01b0316906379a04876906112ca90889087906004016121d8565b60206040518083038186803b1580156112e257600080fd5b505afa1580156112f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131a919061213f565b6002546040516366410a2160e01b81529192506001600160a01b0316906366410a219061134d90879085906004016121d8565b60206040518083038186803b15801561136557600080fd5b505afa158015611379573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139d919061213f565b95945050505050565b6113b888888888886000898989610377565b505050505050505050565b6113cb6118a1565b6001600160a01b03166113dc610fc7565b6001600160a01b0316146114025760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0381166114285760405162461bcd60e51b815260040161039c906122d6565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000600260015414156114a85760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166114d35760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166114f95760405162461bcd60e51b815260040161039c906125a8565b600454611511906001600160a01b03163330886118a5565b60045460025461152e916001600160a01b03908116911687611996565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f9061156690899089908990899089906004016121f1565b602060405180830381600087803b15801561158057600080fd5b505af1158015611594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b8919061213f565b6004546040519192506001600160a01b0380861692898216929091169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146116a0576040516370a0823160e01b81526001600160a01b038416906370a082319061164b9085906004016121a0565b60206040518083038186803b15801561166357600080fd5b505afa158015611677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169b919061213f565b61086e565b506001600160a01b031631919050565b6001600160a01b03861660009081526003602052604090205460ff166116e85760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03851660009081526003602052604090205460ff166117205760405162461bcd60e51b815260040161039c9061242f565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461176057611750843330866118a5565b61175b848785611996565b611780565b348311156117805760405162461bcd60e51b815260040161039c90612616565b60006001600160a01b0380871690861673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146117b15760006117b3565b845b84846040516117c3929190612157565b60006040518083038185875af1925050503d8060008114611800576040519150601f19603f3d011682016040523d82523d6000602084013e611805565b606091505b50509050806118265760405162461bcd60e51b815260040161039c90612353565b50505050505050565b6000828211156118515760405162461bcd60e51b815260040161039c90612471565b50900390565b801561189c576001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156118915761188c8282611d4a565b61189c565b61189c838383611a84565b505050565b3390565b60006060856001600160a01b03166323b872dd8686866040516024016118cd939291906121b4565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119069190612167565b6000604051808303816000865af19150503d8060008114611943576040519150601f19603f3d011682016040523d82523d6000602084013e611948565b606091505b50915091508180156119725750805115806119725750808060200190518101906119729190612123565b61198e5760405162461bcd60e51b815260040161039c9061238a565b505050505050565b60006060846001600160a01b031663095ea7b385856040516024016119bc9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119f59190612167565b6000604051808303816000865af19150503d8060008114611a32576040519150601f19603f3d011682016040523d82523d6000602084013e611a37565b606091505b5091509150818015611a61575080511580611a61575080806020019051810190611a619190612123565b611a7d5760405162461bcd60e51b815260040161039c90612682565b5050505050565b60006060846001600160a01b031663a9059cbb8585604051602401611aaa9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051611ae39190612167565b6000604051808303816000865af19150503d8060008114611b20576040519150601f19603f3d011682016040523d82523d6000602084013e611b25565b606091505b5091509150818015611b4f575080511580611b4f575080806020019051810190611b4f9190612123565b611a7d5760405162461bcd60e51b815260040161039c9061273b565b60008615611cb35760025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611bab90899089908990309089906004016121f1565b602060405180830381600087803b158015611bc557600080fd5b505af1158015611bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfd919061213f565b604051632e1a7d4d60e01b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90611c4c908490600401612864565b600060405180830381600087803b158015611c6657600080fd5b505af1158015611c7a573d6000803e3d6000fd5b505050506001600160a01b038316611ca45760405162461bcd60e51b815260040161039c90612704565b611cae8382611d4a565b611d40565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611ceb90899089908990899089906004016121f1565b602060405180830381600087803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3d919061213f565b90505b9695505050505050565b604080516000808252602082019092526001600160a01b038416908390604051611d749190612167565b60006040518083038185875af1925050503d8060008114611db1576040519150601f19603f3d011682016040523d82523d6000602084013e611db6565b606091505b505090508061189c5760405162461bcd60e51b815260040161039c906123db565b60008083601f840112611de8578182fd5b50813567ffffffffffffffff811115611dff578182fd5b602083019150836020828501011115611e1757600080fd5b9250929050565b600060208284031215611e2f578081fd5b813561086e8161287a565b600060208284031215611e4b578081fd5b815161086e8161287a565b60008060008060008060008060e0898b031215611e71578384fd5b8835611e7c8161287a565b97506020890135611e8c8161287a565b96506040890135611e9c8161287a565b95506060890135611eac8161287a565b94506080890135935060a0890135611ec38161287a565b925060c089013567ffffffffffffffff811115611ede578283fd5b611eea8b828c01611dd7565b999c989b5096995094979396929594505050565b60008060008060008060008060006101008a8c031215611f1c578081fd5b8935611f278161287a565b985060208a0135611f378161287a565b975060408a0135611f478161287a565b965060608a0135611f578161287a565b955060808a0135945060a08a0135935060c08a0135611f758161287a565b925060e08a013567ffffffffffffffff811115611f90578182fd5b611f9c8c828d01611dd7565b915080935050809150509295985092959850929598565b600080600060608486031215611fc7578283fd5b8335611fd28161287a565b92506020840135611fe28161287a565b929592945050506040919091013590565b60008060008060008060c0878903121561200b578182fd5b86356120168161287a565b955060208701356120268161287a565b9450604087013593506060870135925060808701356120448161287a565b915060a08701356120548161287a565b809150509295509295509295565b60008060408385031215612074578182fd5b823561207f8161287a565b9150602083013561208f8161288f565b809150509250929050565b600080604083850312156120ac578182fd5b82356120b78161287a565b946020939093013593505050565b600080600080600060a086880312156120dc578081fd5b85356120e78161287a565b9450602086013593506040860135925060608601356121058161287a565b915060808601356121158161287a565b809150509295509295909350565b600060208284031215612134578081fd5b815161086e8161288f565b600060208284031215612150578081fd5b5051919050565b6000828483379101908152919050565b60008251815b81811015612187576020818601810151858301520161216d565b818111156121955782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0395861681526020810194909452604084019290925283166060830152909116608082015260a00190565b901515815260200190565b60a0810161223b8761286d565b958152602081019490945260408401929092526001600160a01b0390811660608401521660809091015290565b6020808252601a908201527f576f6f526f757465723a20746f6b656e5f414444525f5a45524f000000000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a20737761705461726765745f414444525f5a45524f00604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601c908201527f576f6f526f757465723a206e6577506f6f6c5f414444525f5a45524f00000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a2046414c4c4241434b5f535741505f4641494c454400604082015260600190565b60208082526031908201527f5472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472604082015270185b9cd9995c919c9bdb4819985a5b1959607a1b606082015260800190565b60208082526034908201527f5472616e7366657248656c7065723a3a736166655472616e736665724554483a60408201527308115512081d1c985b9cd9995c8819985a5b195960621b606082015260800190565b60208082526022908201527f576f6f526f757465723a20535741505f5441524745545f4e4f545f414c4c4f57604082015261115160f21b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b6020808252601c908201527f576f6f526f757465723a20746f546f6b656e5f414444525f5a45524f00000000604082015260600190565b60208082526025908201527f576f6f526f757465723a20415050524f56455f5441524745545f4e4f545f414c6040820152641313d5d15160da1b606082015260800190565b60208082526022908201527f576f6f526f757465723a207265616c546f416d6f756e745f4e4f545f454e4f5560408201526108e960f31b606082015260800190565b60208082526022908201527f576f6f526f757465723a20617070726f76655461726765745f414444525f5a45604082015261524f60f01b606082015260800190565b60208082526017908201527f576f6f526f757465723a20746f5f414444525f5a45524f000000000000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2062617365546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601d908201527f576f6f526f757465723a2066726f6d416d6f756e745f494e56414c4944000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252602b908201527f5472616e7366657248656c7065723a3a73616665417070726f76653a2061707060408201526a1c9bdd994819985a5b195960aa1b606082015260800190565b60208082526018908201527f576f6f526f757465723a2062616c616e63655f4552524f520000000000000000604082015260600190565b60208082526017908201527f576f6f526f757465723a20746f5f5a45524f5f41444452000000000000000000604082015260600190565b6020808252602d908201527f5472616e7366657248656c7065723a3a736166655472616e736665723a20747260408201526c185b9cd9995c8819985a5b1959609a1b606082015260800190565b6020808252601b908201527f576f6f526f757465723a207461726765745f414444525f5a45524f0000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2066726f6d546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252601f908201527f576f6f526f757465723a2071756f7465546f6b656e5f414444525f5a45524f00604082015260600190565b90815260200190565b6002811061287757fe5b50565b6001600160a01b038116811461287757600080fd5b801515811461287757600080fdfe27c98e911efdd224f4002f6cd831c3ad0d2759ee176f9ee8466d95826af22a1ca2646970667358221220cbe5c78b9ca4c75261104c135b8f3a9e2c582b1bc27767cb32dc41fd9a4e37c864736f6c634300060c0033000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000f8ce0d043891b62c55380fb1efbfb4f186153d96

Deployed Bytecode

0x60806040526004361061010d5760003560e01c80637dc2038211610095578063ad5c464811610064578063ad5c4648146102ef578063e94803f414610304578063ea71ee8414610324578063f2fde38b14610337578063f3287c2f1461035757610160565b80637dc20382146102925780638da5cb5b146102a55780639281aa0b146102ba578063a7394603146102da57610160565b806366410a21116100dc57806366410a21146101fd5780636846fb501461021d578063715018a61461023d57806378e3214f1461025257806379a048761461027257610160565b8063199b83fa14610165578063217a4b701461018e5780633af32abf146101b05780634437152a146101dd57610160565b3661016057336001600160a01b037f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c716148061015857503360009081526003602052604090205460ff165b61015e57fe5b005b600080fd5b610178610173366004611efe565b610377565b6040516101859190612864565b60405180910390f35b34801561019a57600080fd5b506101a36105b9565b60405161018591906121a0565b3480156101bc57600080fd5b506101d06101cb366004611e1e565b6105c8565b6040516101859190612223565b3480156101e957600080fd5b5061015e6101f8366004611e1e565b6105dd565b34801561020957600080fd5b5061017861021836600461209a565b610777565b34801561022957600080fd5b506101786102383660046120c5565b610875565b34801561024957600080fd5b5061015e6109ef565b34801561025e57600080fd5b5061015e61026d36600461209a565b610a78565b34801561027e57600080fd5b5061017861028d36600461209a565b610b18565b6101786102a0366004611ff3565b610bbf565b3480156102b157600080fd5b506101a3610fc7565b3480156102c657600080fd5b5061015e6102d5366004612062565b610fd6565b3480156102e657600080fd5b506101a3611092565b3480156102fb57600080fd5b506101a36110a1565b34801561031057600080fd5b5061017861031f366004611fb3565b6110c5565b61015e610332366004611e56565b6113a6565b34801561034357600080fd5b5061015e610352366004611e1e565b6113c3565b34801561036357600080fd5b506101786103723660046120c5565b611483565b6000600260015414156103a55760405162461bcd60e51b815260040161039c906127f6565b60405180910390fd5b60026001556001600160a01b038a166103d05760405162461bcd60e51b815260040161039c90612566565b6001600160a01b0389166103f65760405162461bcd60e51b815260040161039c9061229f565b6001600160a01b03881661041c5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0387166104425760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b0384166104685760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b038a1660009081526003602052604090205460ff166104a05760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03891660009081526003602052604090205460ff166104d85760405162461bcd60e51b815260040161039c9061242f565b60006104e488306115f9565b90506104f48b8b8b8a88886116b0565b600061050089306115f9565b9050808211156105225760405162461bcd60e51b815260040161039c906126cd565b61052c818361182f565b92508683101561054e5760405162461bcd60e51b815260040161039c90612524565b610559898785611857565b856001600160a01b0316896001600160a01b03168b6001600160a01b031660008051602061289e83398151915260018c8833600060405161059e95949392919061222e565b60405180910390a45050600180559998505050505050505050565b6004546001600160a01b031681565b60036020526000908152604090205460ff1681565b600260015414156106005760405162461bcd60e51b815260040161039c906127f6565b600260015561060d6118a1565b6001600160a01b031661061e610fc7565b6001600160a01b0316146106445760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b03811661066a5760405162461bcd60e51b815260040161039c9061231c565b600280546001600160a01b0319166001600160a01b03838116919091179182905560408051630217a4b760e41b81529051929091169163217a4b7091600480820192602092909190829003018186803b1580156106c657600080fd5b505afa1580156106da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fe9190611e3a565b600480546001600160a01b0319166001600160a01b039283161790819055166107395760405162461bcd60e51b815260040161039c9061282d565b7f4577a21bd8e55848c574b7582f8e6cc6a7cf1c1958f36a9751eab6329d656b1e8160405161076891906121a0565b60405180910390a15060018055565b60006001600160a01b03831661079f5760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146107c957826107eb565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b6002546040516366410a2160e01b81529194506001600160a01b0316906366410a219061081e90869086906004016121d8565b60206040518083038186803b15801561083657600080fd5b505afa15801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061086e919061213f565b9392505050565b60006002600154141561089a5760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166108c55760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166108eb5760405162461bcd60e51b815260040161039c906125a8565b6108f7863330886118a5565b60025461090f9087906001600160a01b031687611996565b6002546040516306846fb560e41b81526001600160a01b0390911690636846fb509061094790899089908990899089906004016121f1565b602060405180830381600087803b15801561096157600080fd5b505af1158015610975573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610999919061213f565b6004546040519192506001600160a01b0380861692918116919089169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60405180910390a46001805595945050505050565b6109f76118a1565b6001600160a01b0316610a08610fc7565b6001600160a01b031614610a2e5760405162461bcd60e51b815260040161039c9061264d565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60026001541415610a9b5760405162461bcd60e51b815260040161039c906127f6565b6002600155610aa86118a1565b6001600160a01b0316610ab9610fc7565b6001600160a01b031614610adf5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b038216610b055760405162461bcd60e51b815260040161039c90612268565b610b10823383611a84565b505060018055565b60006001600160a01b038316610b405760405162461bcd60e51b815260040161039c906125df565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610b6a5782610b8c565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b600254604051633cd0243b60e11b81529194506001600160a01b0316906379a048769061081e90869086906004016121d8565b600060026001541415610be45760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b038716610c0f5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b038616610c355760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b038316610c5b5760405162461bcd60e51b815260040161039c906125a8565b6001600160a01b0387811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9081149188161481610c8d5788610caf565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b985080610cbc5787610cde565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b97508115610d7f5734871115610d065760405162461bcd60e51b815260040161039c90612616565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c76001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d6157600080fd5b505af1158015610d75573d6000803e3d6000fd5b5050505050610d8b565b610d8b8933308a6118a5565b600254610da3908a906001600160a01b031689611996565b6004546001600160a01b038a811691161415610dce57610dc7818989898989611b6b565b9250610f2b565b6004546001600160a01b0389811691161415610e6e576002546040516306846fb560e41b81526001600160a01b0390911690636846fb5090610e1c908c908b908b908b908b906004016121f1565b602060405180830381600087803b158015610e3657600080fd5b505af1158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc7919061213f565b6002546040516306846fb560e41b81526000916001600160a01b031690636846fb5090610ea7908d908c90869030908c906004016121f1565b602060405180830381600087803b158015610ec157600080fd5b505af1158015610ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef9919061213f565b600454600254919250610f19916001600160a01b03918216911683611996565b610f27828a838a8a8a611b6b565b9350505b846001600160a01b031681610f405788610f56565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031683610f6a578a610f80565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660008051602061289e83398151915260008b88338b604051610faf95949392919061222e565b60405180910390a45050600180559695505050505050565b6000546001600160a01b031690565b60026001541415610ff95760405162461bcd60e51b815260040161039c906127f6565b60026001556110066118a1565b6001600160a01b0316611017610fc7565b6001600160a01b03161461103d5760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0382166110635760405162461bcd60e51b815260040161039c90612788565b6001600160a01b03919091166000908152600360205260409020805460ff191691151591909117905560018055565b6002546001600160a01b031681565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c781565b60006001600160a01b0384166110ed5760405162461bcd60e51b815260040161039c906127bf565b6001600160a01b0383166111135760405162461bcd60e51b815260040161039c906124a8565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461113d578361115f565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b93506001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461118b57826111ad565b7f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c75b6004549093506001600160a01b038581169116141561124f576002546040516366410a2160e01b81526001600160a01b03909116906366410a21906111f890869086906004016121d8565b60206040518083038186803b15801561121057600080fd5b505afa158015611224573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611248919061213f565b905061086e565b6004546001600160a01b038481169116141561129757600254604051633cd0243b60e11b81526001600160a01b03909116906379a04876906111f890879086906004016121d8565b600254604051633cd0243b60e11b81526000916001600160a01b0316906379a04876906112ca90889087906004016121d8565b60206040518083038186803b1580156112e257600080fd5b505afa1580156112f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131a919061213f565b6002546040516366410a2160e01b81529192506001600160a01b0316906366410a219061134d90879085906004016121d8565b60206040518083038186803b15801561136557600080fd5b505afa158015611379573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139d919061213f565b95945050505050565b6113b888888888886000898989610377565b505050505050505050565b6113cb6118a1565b6001600160a01b03166113dc610fc7565b6001600160a01b0316146114025760405162461bcd60e51b815260040161039c9061264d565b6001600160a01b0381166114285760405162461bcd60e51b815260040161039c906122d6565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000600260015414156114a85760405162461bcd60e51b815260040161039c906127f6565b60026001556001600160a01b0386166114d35760405162461bcd60e51b815260040161039c906125df565b6001600160a01b0383166114f95760405162461bcd60e51b815260040161039c906125a8565b600454611511906001600160a01b03163330886118a5565b60045460025461152e916001600160a01b03908116911687611996565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f9061156690899089908990899089906004016121f1565b602060405180830381600087803b15801561158057600080fd5b505af1158015611594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b8919061213f565b6004546040519192506001600160a01b0380861692898216929091169060008051602061289e833981519152906109da906000908b90889033908b9061222e565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146116a0576040516370a0823160e01b81526001600160a01b038416906370a082319061164b9085906004016121a0565b60206040518083038186803b15801561166357600080fd5b505afa158015611677573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061169b919061213f565b61086e565b506001600160a01b031631919050565b6001600160a01b03861660009081526003602052604090205460ff166116e85760405162461bcd60e51b815260040161039c906124df565b6001600160a01b03851660009081526003602052604090205460ff166117205760405162461bcd60e51b815260040161039c9061242f565b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461176057611750843330866118a5565b61175b848785611996565b611780565b348311156117805760405162461bcd60e51b815260040161039c90612616565b60006001600160a01b0380871690861673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146117b15760006117b3565b845b84846040516117c3929190612157565b60006040518083038185875af1925050503d8060008114611800576040519150601f19603f3d011682016040523d82523d6000602084013e611805565b606091505b50509050806118265760405162461bcd60e51b815260040161039c90612353565b50505050505050565b6000828211156118515760405162461bcd60e51b815260040161039c90612471565b50900390565b801561189c576001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156118915761188c8282611d4a565b61189c565b61189c838383611a84565b505050565b3390565b60006060856001600160a01b03166323b872dd8686866040516024016118cd939291906121b4565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119069190612167565b6000604051808303816000865af19150503d8060008114611943576040519150601f19603f3d011682016040523d82523d6000602084013e611948565b606091505b50915091508180156119725750805115806119725750808060200190518101906119729190612123565b61198e5760405162461bcd60e51b815260040161039c9061238a565b505050505050565b60006060846001600160a01b031663095ea7b385856040516024016119bc9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516119f59190612167565b6000604051808303816000865af19150503d8060008114611a32576040519150601f19603f3d011682016040523d82523d6000602084013e611a37565b606091505b5091509150818015611a61575080511580611a61575080806020019051810190611a619190612123565b611a7d5760405162461bcd60e51b815260040161039c90612682565b5050505050565b60006060846001600160a01b031663a9059cbb8585604051602401611aaa9291906121d8565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051611ae39190612167565b6000604051808303816000865af19150503d8060008114611b20576040519150601f19603f3d011682016040523d82523d6000602084013e611b25565b606091505b5091509150818015611b4f575080511580611b4f575080806020019051810190611b4f9190612123565b611a7d5760405162461bcd60e51b815260040161039c9061273b565b60008615611cb35760025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611bab90899089908990309089906004016121f1565b602060405180830381600087803b158015611bc557600080fd5b505af1158015611bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfd919061213f565b604051632e1a7d4d60e01b81529091506001600160a01b037f000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c71690632e1a7d4d90611c4c908490600401612864565b600060405180830381600087803b158015611c6657600080fd5b505af1158015611c7a573d6000803e3d6000fd5b505050506001600160a01b038316611ca45760405162461bcd60e51b815260040161039c90612704565b611cae8382611d4a565b611d40565b60025460405163f3287c2f60e01b81526001600160a01b039091169063f3287c2f90611ceb90899089908990899089906004016121f1565b602060405180830381600087803b158015611d0557600080fd5b505af1158015611d19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d3d919061213f565b90505b9695505050505050565b604080516000808252602082019092526001600160a01b038416908390604051611d749190612167565b60006040518083038185875af1925050503d8060008114611db1576040519150601f19603f3d011682016040523d82523d6000602084013e611db6565b606091505b505090508061189c5760405162461bcd60e51b815260040161039c906123db565b60008083601f840112611de8578182fd5b50813567ffffffffffffffff811115611dff578182fd5b602083019150836020828501011115611e1757600080fd5b9250929050565b600060208284031215611e2f578081fd5b813561086e8161287a565b600060208284031215611e4b578081fd5b815161086e8161287a565b60008060008060008060008060e0898b031215611e71578384fd5b8835611e7c8161287a565b97506020890135611e8c8161287a565b96506040890135611e9c8161287a565b95506060890135611eac8161287a565b94506080890135935060a0890135611ec38161287a565b925060c089013567ffffffffffffffff811115611ede578283fd5b611eea8b828c01611dd7565b999c989b5096995094979396929594505050565b60008060008060008060008060006101008a8c031215611f1c578081fd5b8935611f278161287a565b985060208a0135611f378161287a565b975060408a0135611f478161287a565b965060608a0135611f578161287a565b955060808a0135945060a08a0135935060c08a0135611f758161287a565b925060e08a013567ffffffffffffffff811115611f90578182fd5b611f9c8c828d01611dd7565b915080935050809150509295985092959850929598565b600080600060608486031215611fc7578283fd5b8335611fd28161287a565b92506020840135611fe28161287a565b929592945050506040919091013590565b60008060008060008060c0878903121561200b578182fd5b86356120168161287a565b955060208701356120268161287a565b9450604087013593506060870135925060808701356120448161287a565b915060a08701356120548161287a565b809150509295509295509295565b60008060408385031215612074578182fd5b823561207f8161287a565b9150602083013561208f8161288f565b809150509250929050565b600080604083850312156120ac578182fd5b82356120b78161287a565b946020939093013593505050565b600080600080600060a086880312156120dc578081fd5b85356120e78161287a565b9450602086013593506040860135925060608601356121058161287a565b915060808601356121158161287a565b809150509295509295909350565b600060208284031215612134578081fd5b815161086e8161288f565b600060208284031215612150578081fd5b5051919050565b6000828483379101908152919050565b60008251815b81811015612187576020818601810151858301520161216d565b818111156121955782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0395861681526020810194909452604084019290925283166060830152909116608082015260a00190565b901515815260200190565b60a0810161223b8761286d565b958152602081019490945260408401929092526001600160a01b0390811660608401521660809091015290565b6020808252601a908201527f576f6f526f757465723a20746f6b656e5f414444525f5a45524f000000000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a20737761705461726765745f414444525f5a45524f00604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601c908201527f576f6f526f757465723a206e6577506f6f6c5f414444525f5a45524f00000000604082015260600190565b6020808252601f908201527f576f6f526f757465723a2046414c4c4241434b5f535741505f4641494c454400604082015260600190565b60208082526031908201527f5472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472604082015270185b9cd9995c919c9bdb4819985a5b1959607a1b606082015260800190565b60208082526034908201527f5472616e7366657248656c7065723a3a736166655472616e736665724554483a60408201527308115512081d1c985b9cd9995c8819985a5b195960621b606082015260800190565b60208082526022908201527f576f6f526f757465723a20535741505f5441524745545f4e4f545f414c4c4f57604082015261115160f21b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b6020808252601c908201527f576f6f526f757465723a20746f546f6b656e5f414444525f5a45524f00000000604082015260600190565b60208082526025908201527f576f6f526f757465723a20415050524f56455f5441524745545f4e4f545f414c6040820152641313d5d15160da1b606082015260800190565b60208082526022908201527f576f6f526f757465723a207265616c546f416d6f756e745f4e4f545f454e4f5560408201526108e960f31b606082015260800190565b60208082526022908201527f576f6f526f757465723a20617070726f76655461726765745f414444525f5a45604082015261524f60f01b606082015260800190565b60208082526017908201527f576f6f526f757465723a20746f5f414444525f5a45524f000000000000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2062617365546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601d908201527f576f6f526f757465723a2066726f6d416d6f756e745f494e56414c4944000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252602b908201527f5472616e7366657248656c7065723a3a73616665417070726f76653a2061707060408201526a1c9bdd994819985a5b195960aa1b606082015260800190565b60208082526018908201527f576f6f526f757465723a2062616c616e63655f4552524f520000000000000000604082015260600190565b60208082526017908201527f576f6f526f757465723a20746f5f5a45524f5f41444452000000000000000000604082015260600190565b6020808252602d908201527f5472616e7366657248656c7065723a3a736166655472616e736665723a20747260408201526c185b9cd9995c8819985a5b1959609a1b606082015260800190565b6020808252601b908201527f576f6f526f757465723a207461726765745f414444525f5a45524f0000000000604082015260600190565b6020808252601e908201527f576f6f526f757465723a2066726f6d546f6b656e5f414444525f5a45524f0000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252601f908201527f576f6f526f757465723a2071756f7465546f6b656e5f414444525f5a45524f00604082015260600190565b90815260200190565b6002811061287757fe5b50565b6001600160a01b038116811461287757600080fd5b801515811461287757600080fdfe27c98e911efdd224f4002f6cd831c3ad0d2759ee176f9ee8466d95826af22a1ca2646970667358221220cbe5c78b9ca4c75261104c135b8f3a9e2c582b1bc27767cb32dc41fd9a4e37c864736f6c634300060c0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7000000000000000000000000f8ce0d043891b62c55380fb1efbfb4f186153d96

-----Decoded View---------------
Arg [0] : weth (address): 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7
Arg [1] : newPool (address): 0xF8cE0D043891b62c55380fB1EFBfB4F186153D96

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000b31f66aa3c1e785363f0875a1b74e27b85fd66c7
Arg [1] : 000000000000000000000000f8ce0d043891b62c55380fb1efbfb4f186153d96


Block Transaction Gas Used Reward
view all blocks ##produced##

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.