ETH Price: $1,947.41 (-1.56%)

Transaction Decoder

Block:
11206386 at Nov-06-2020 10:01:14 PM +UTC
Transaction Fee:
0.0014229124 ETH $2.77
Gas Used:
78,182 Gas / 18.2 Gwei

Emitted Events:

319 0x307e2752e8b8a9c29005001be66b1c012ca9cdb7.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000097b04c20ca1cf1e89249b7b7b7462eb382e943e7, 0x0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000007124565f8d7459f41 )
320 UniswapV2Pair.Transfer( from=[Receiver] 0x307e2752e8b8a9c29005001be66b1c012ca9cdb7, to=[Sender] 0x97b04c20ca1cf1e89249b7b7b7462eb382e943e7, value=131589106942799876193 )
321 0x307e2752e8b8a9c29005001be66b1c012ca9cdb7.0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364( 0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364, 0x00000000000000000000000097b04c20ca1cf1e89249b7b7b7462eb382e943e7, 000000000000000000000000000000000000000000000007222a6b9354799061 )

Account State Difference:

  Address   Before After State Difference Code
0x307E2752...12CA9CDB7
(Spark Pool)
5.613399331885049727 Eth5.614822244285049727 Eth0.0014229124
0x97B04C20...382E943e7
0.506794738526124655 Eth
Nonce: 449
0.505371826126124655 Eth
Nonce: 450
0.0014229124
0xA478c297...Ade33eB11

Execution Trace

0x307e2752e8b8a9c29005001be66b1c012ca9cdb7.2e1a7d4d( )
  • Vault.withdraw( numberOfShares=130443779101480558401 )
    • 0x2cf4ceb36172fb2196a47490419d57584234cbd4.STATICCALL( )
      • UniswapV2Pair.balanceOf( 0x2CF4cEB36172Fb2196a47490419D57584234Cbd4 ) => ( 0 )
      • StakingRewards.balanceOf( account=0x2CF4cEB36172Fb2196a47490419D57584234Cbd4 ) => ( 550238002411739522018920 )
      • UniswapV2Pair.balanceOf( 0x307E2752e8b8a9C29005001Be66B1c012CA9CDB7 ) => ( 30492243595469846357230 )
      • UniswapV2Pair.balanceOf( 0x307E2752e8b8a9C29005001Be66B1c012CA9CDB7 ) => ( 30492243595469846357230 )
      • UniswapV2Pair.transfer( to=0x97B04C20cA1Cf1E89249B7b7B7462eb382E943e7, value=131589106942799876193 ) => ( True )
        File 1 of 3: UniswapV2Pair
        // File: contracts/interfaces/IUniswapV2Pair.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2Pair {
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint 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 (uint);
            function balanceOf(address owner) external view returns (uint);
            function allowance(address owner, address spender) external view returns (uint);
        
            function approve(address spender, uint value) external returns (bool);
            function transfer(address to, uint value) external returns (bool);
            function transferFrom(address from, address to, uint 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 (uint);
        
            function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
        
            event Mint(address indexed sender, uint amount0, uint amount1);
            event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
            event Swap(
                address indexed sender,
                uint amount0In,
                uint amount1In,
                uint amount0Out,
                uint amount1Out,
                address indexed to
            );
            event Sync(uint112 reserve0, uint112 reserve1);
        
            function MINIMUM_LIQUIDITY() external pure returns (uint);
            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 (uint);
            function price1CumulativeLast() external view returns (uint);
            function kLast() external view returns (uint);
        
            function mint(address to) external returns (uint liquidity);
            function burn(address to) external returns (uint amount0, uint amount1);
            function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
            function skim(address to) external;
            function sync() external;
        
            function initialize(address, address) external;
        }
        
        // File: contracts/interfaces/IUniswapV2ERC20.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2ERC20 {
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint 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 (uint);
            function balanceOf(address owner) external view returns (uint);
            function allowance(address owner, address spender) external view returns (uint);
        
            function approve(address spender, uint value) external returns (bool);
            function transfer(address to, uint value) external returns (bool);
            function transferFrom(address from, address to, uint 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 (uint);
        
            function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
        }
        
        // File: contracts/libraries/SafeMath.sol
        
        pragma solidity =0.5.16;
        
        // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
        
        library SafeMath {
            function add(uint x, uint y) internal pure returns (uint z) {
                require((z = x + y) >= x, 'ds-math-add-overflow');
            }
        
            function sub(uint x, uint y) internal pure returns (uint z) {
                require((z = x - y) <= x, 'ds-math-sub-underflow');
            }
        
            function mul(uint x, uint y) internal pure returns (uint z) {
                require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
            }
        }
        
        // File: contracts/UniswapV2ERC20.sol
        
        pragma solidity =0.5.16;
        
        
        
        contract UniswapV2ERC20 is IUniswapV2ERC20 {
            using SafeMath for uint;
        
            string public constant name = 'Uniswap V2';
            string public constant symbol = 'UNI-V2';
            uint8 public constant decimals = 18;
            uint  public totalSupply;
            mapping(address => uint) public balanceOf;
            mapping(address => mapping(address => uint)) public allowance;
        
            bytes32 public DOMAIN_SEPARATOR;
            // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
            bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
            mapping(address => uint) public nonces;
        
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint value);
        
            constructor() public {
                uint chainId;
                assembly {
                    chainId := chainid
                }
                DOMAIN_SEPARATOR = keccak256(
                    abi.encode(
                        keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                        keccak256(bytes(name)),
                        keccak256(bytes('1')),
                        chainId,
                        address(this)
                    )
                );
            }
        
            function _mint(address to, uint value) internal {
                totalSupply = totalSupply.add(value);
                balanceOf[to] = balanceOf[to].add(value);
                emit Transfer(address(0), to, value);
            }
        
            function _burn(address from, uint value) internal {
                balanceOf[from] = balanceOf[from].sub(value);
                totalSupply = totalSupply.sub(value);
                emit Transfer(from, address(0), value);
            }
        
            function _approve(address owner, address spender, uint value) private {
                allowance[owner][spender] = value;
                emit Approval(owner, spender, value);
            }
        
            function _transfer(address from, address to, uint value) private {
                balanceOf[from] = balanceOf[from].sub(value);
                balanceOf[to] = balanceOf[to].add(value);
                emit Transfer(from, to, value);
            }
        
            function approve(address spender, uint value) external returns (bool) {
                _approve(msg.sender, spender, value);
                return true;
            }
        
            function transfer(address to, uint value) external returns (bool) {
                _transfer(msg.sender, to, value);
                return true;
            }
        
            function transferFrom(address from, address to, uint value) external returns (bool) {
                if (allowance[from][msg.sender] != uint(-1)) {
                    allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                }
                _transfer(from, to, value);
                return true;
            }
        
            function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                bytes32 digest = keccak256(
                    abi.encodePacked(
                        '\x19\x01',
                        DOMAIN_SEPARATOR,
                        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                    )
                );
                address recoveredAddress = ecrecover(digest, v, r, s);
                require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                _approve(owner, spender, value);
            }
        }
        
        // File: contracts/libraries/Math.sol
        
        pragma solidity =0.5.16;
        
        // a library for performing various math operations
        
        library Math {
            function min(uint x, uint y) internal pure returns (uint z) {
                z = x < y ? x : y;
            }
        
            // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
            function sqrt(uint y) internal pure returns (uint z) {
                if (y > 3) {
                    z = y;
                    uint x = y / 2 + 1;
                    while (x < z) {
                        z = x;
                        x = (y / x + x) / 2;
                    }
                } else if (y != 0) {
                    z = 1;
                }
            }
        }
        
        // File: contracts/libraries/UQ112x112.sol
        
        pragma solidity =0.5.16;
        
        // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
        
        // range: [0, 2**112 - 1]
        // resolution: 1 / 2**112
        
        library UQ112x112 {
            uint224 constant Q112 = 2**112;
        
            // encode a uint112 as a UQ112x112
            function encode(uint112 y) internal pure returns (uint224 z) {
                z = uint224(y) * Q112; // never overflows
            }
        
            // divide a UQ112x112 by a uint112, returning a UQ112x112
            function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                z = x / uint224(y);
            }
        }
        
        // File: contracts/interfaces/IERC20.sol
        
        pragma solidity >=0.5.0;
        
        interface IERC20 {
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint value);
        
            function name() external view returns (string memory);
            function symbol() external view returns (string memory);
            function decimals() external view returns (uint8);
            function totalSupply() external view returns (uint);
            function balanceOf(address owner) external view returns (uint);
            function allowance(address owner, address spender) external view returns (uint);
        
            function approve(address spender, uint value) external returns (bool);
            function transfer(address to, uint value) external returns (bool);
            function transferFrom(address from, address to, uint value) external returns (bool);
        }
        
        // File: contracts/interfaces/IUniswapV2Factory.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2Factory {
            event PairCreated(address indexed token0, address indexed token1, address pair, uint);
        
            function feeTo() external view returns (address);
            function feeToSetter() external view returns (address);
        
            function getPair(address tokenA, address tokenB) external view returns (address pair);
            function allPairs(uint) external view returns (address pair);
            function allPairsLength() external view returns (uint);
        
            function createPair(address tokenA, address tokenB) external returns (address pair);
        
            function setFeeTo(address) external;
            function setFeeToSetter(address) external;
        }
        
        // File: contracts/interfaces/IUniswapV2Callee.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2Callee {
            function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
        }
        
        // File: contracts/UniswapV2Pair.sol
        
        pragma solidity =0.5.16;
        
        
        
        
        
        
        
        
        contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
            using SafeMath  for uint;
            using UQ112x112 for uint224;
        
            uint public constant MINIMUM_LIQUIDITY = 10**3;
            bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
        
            address public factory;
            address public token0;
            address public token1;
        
            uint112 private reserve0;           // uses single storage slot, accessible via getReserves
            uint112 private reserve1;           // uses single storage slot, accessible via getReserves
            uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
        
            uint public price0CumulativeLast;
            uint public price1CumulativeLast;
            uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
        
            uint private unlocked = 1;
            modifier lock() {
                require(unlocked == 1, 'UniswapV2: LOCKED');
                unlocked = 0;
                _;
                unlocked = 1;
            }
        
            function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                _reserve0 = reserve0;
                _reserve1 = reserve1;
                _blockTimestampLast = blockTimestampLast;
            }
        
            function _safeTransfer(address token, address to, uint value) private {
                (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
            }
        
            event Mint(address indexed sender, uint amount0, uint amount1);
            event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
            event Swap(
                address indexed sender,
                uint amount0In,
                uint amount1In,
                uint amount0Out,
                uint amount1Out,
                address indexed to
            );
            event Sync(uint112 reserve0, uint112 reserve1);
        
            constructor() public {
                factory = msg.sender;
            }
        
            // called once by the factory at time of deployment
            function initialize(address _token0, address _token1) external {
                require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                token0 = _token0;
                token1 = _token1;
            }
        
            // update reserves and, on the first call per block, price accumulators
            function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                    // * never overflows, and + overflow is desired
                    price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                    price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                }
                reserve0 = uint112(balance0);
                reserve1 = uint112(balance1);
                blockTimestampLast = blockTimestamp;
                emit Sync(reserve0, reserve1);
            }
        
            // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
            function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                address feeTo = IUniswapV2Factory(factory).feeTo();
                feeOn = feeTo != address(0);
                uint _kLast = kLast; // gas savings
                if (feeOn) {
                    if (_kLast != 0) {
                        uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                        uint rootKLast = Math.sqrt(_kLast);
                        if (rootK > rootKLast) {
                            uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                            uint denominator = rootK.mul(5).add(rootKLast);
                            uint liquidity = numerator / denominator;
                            if (liquidity > 0) _mint(feeTo, liquidity);
                        }
                    }
                } else if (_kLast != 0) {
                    kLast = 0;
                }
            }
        
            // this low-level function should be called from a contract which performs important safety checks
            function mint(address to) external lock returns (uint liquidity) {
                (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                uint balance0 = IERC20(token0).balanceOf(address(this));
                uint balance1 = IERC20(token1).balanceOf(address(this));
                uint amount0 = balance0.sub(_reserve0);
                uint amount1 = balance1.sub(_reserve1);
        
                bool feeOn = _mintFee(_reserve0, _reserve1);
                uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                if (_totalSupply == 0) {
                    liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                   _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                } else {
                    liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                }
                require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                _mint(to, liquidity);
        
                _update(balance0, balance1, _reserve0, _reserve1);
                if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                emit Mint(msg.sender, amount0, amount1);
            }
        
            // this low-level function should be called from a contract which performs important safety checks
            function burn(address to) external lock returns (uint amount0, uint amount1) {
                (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                address _token0 = token0;                                // gas savings
                address _token1 = token1;                                // gas savings
                uint balance0 = IERC20(_token0).balanceOf(address(this));
                uint balance1 = IERC20(_token1).balanceOf(address(this));
                uint liquidity = balanceOf[address(this)];
        
                bool feeOn = _mintFee(_reserve0, _reserve1);
                uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                _burn(address(this), liquidity);
                _safeTransfer(_token0, to, amount0);
                _safeTransfer(_token1, to, amount1);
                balance0 = IERC20(_token0).balanceOf(address(this));
                balance1 = IERC20(_token1).balanceOf(address(this));
        
                _update(balance0, balance1, _reserve0, _reserve1);
                if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                emit Burn(msg.sender, amount0, amount1, to);
            }
        
            // this low-level function should be called from a contract which performs important safety checks
            function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
        
                uint balance0;
                uint balance1;
                { // scope for _token{0,1}, avoids stack too deep errors
                address _token0 = token0;
                address _token1 = token1;
                require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                balance0 = IERC20(_token0).balanceOf(address(this));
                balance1 = IERC20(_token1).balanceOf(address(this));
                }
                uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                }
        
                _update(balance0, balance1, _reserve0, _reserve1);
                emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
            }
        
            // force balances to match reserves
            function skim(address to) external lock {
                address _token0 = token0; // gas savings
                address _token1 = token1; // gas savings
                _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
            }
        
            // force reserves to match balances
            function sync() external lock {
                _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
            }
        }

        File 2 of 3: Vault
        // File: @openzeppelin/contracts-ethereum-package/contracts/math/Math.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * @dev Standard math utilities missing in the Solidity language.
         */
        library Math {
            /**
             * @dev Returns the largest of two numbers.
             */
            function max(uint256 a, uint256 b) internal pure returns (uint256) {
                return a >= b ? a : b;
            }
        
            /**
             * @dev Returns the smallest of two numbers.
             */
            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                return a < b ? a : b;
            }
        
            /**
             * @dev Returns the average of two numbers. The result is rounded towards
             * zero.
             */
            function average(uint256 a, uint256 b) internal pure returns (uint256) {
                // (a + b) / 2 can overflow, so we distribute
                return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
            }
        }
        
        // File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
        
        pragma solidity ^0.5.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, 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) {
                return sub(a, b, "SafeMath: subtraction overflow");
            }
        
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             *
             * _Available since v2.4.0._
             */
            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b <= a, errorMessage);
                uint256 c = a - b;
        
                return c;
            }
        
            /**
             * @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) {
                // 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 0;
                }
        
                uint256 c = a * b;
                require(c / a == b, "SafeMath: multiplication overflow");
        
                return c;
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts 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) {
                return div(a, b, "SafeMath: division by zero");
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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.
             *
             * _Available since v2.4.0._
             */
            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                // Solidity only automatically asserts when dividing by 0
                require(b > 0, errorMessage);
                uint256 c = a / b;
                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        
                return c;
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts 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) {
                return mod(a, b, "SafeMath: modulo by zero");
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message 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.
             *
             * _Available since v2.4.0._
             */
            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b != 0, errorMessage);
                return a % b;
            }
        }
        
        // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
         * the optional functions; to access them see {ERC20Detailed}.
         */
        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: @openzeppelin/contracts-ethereum-package/contracts/utils/Address.sol
        
        pragma solidity ^0.5.5;
        
        /**
         * @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) {
                // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                // for accounts without code, i.e. `keccak256('')`
                bytes32 codehash;
                bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                // solhint-disable-next-line no-inline-assembly
                assembly { codehash := extcodehash(account) }
                return (codehash != accountHash && codehash != 0x0);
            }
        
            /**
             * @dev Converts an `address` into `address payable`. Note that this is
             * simply a type cast: the actual underlying value is not changed.
             *
             * _Available since v2.4.0._
             */
            function toPayable(address account) internal pure returns (address payable) {
                return address(uint160(account));
            }
        
            /**
             * @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].
             *
             * _Available since v2.4.0._
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                // solhint-disable-next-line avoid-call-value
                (bool success, ) = recipient.call.value(amount)("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        }
        
        // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/SafeERC20.sol
        
        pragma solidity ^0.5.0;
        
        
        
        
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using SafeMath for uint256;
            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));
            }
        
            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.
        
                // A Solidity high level call has three parts:
                //  1. The target address is checked to verify it contains contract code
                //  2. The call itself is made, and success asserted
                //  3. The return value is decoded, which in turn checks the size of the returned data.
                // solhint-disable-next-line max-line-length
                require(address(token).isContract(), "SafeERC20: call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = address(token).call(data);
                require(success, "SafeERC20: low-level call failed");
        
                if (returndata.length > 0) { // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        
        // File: @openzeppelin/upgrades/contracts/Initializable.sol
        
        pragma solidity >=0.4.24 <0.6.0;
        
        
        /**
         * @title Initializable
         *
         * @dev Helper contract to support initializer functions. To use it, replace
         * the constructor with a function that has the `initializer` modifier.
         * WARNING: Unlike constructors, initializer functions must be manually
         * invoked. This applies both to deploying an Initializable contract, as well
         * as extending an Initializable contract via inheritance.
         * WARNING: When used with inheritance, manual care must be taken to not invoke
         * a parent initializer twice, or ensure that all initializers are idempotent,
         * because this is not dealt with automatically as with constructors.
         */
        contract Initializable {
        
          /**
           * @dev Indicates that the contract has been initialized.
           */
          bool private initialized;
        
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private initializing;
        
          /**
           * @dev Modifier to use in the initializer function of a contract.
           */
          modifier initializer() {
            require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
        
            bool isTopLevelCall = !initializing;
            if (isTopLevelCall) {
              initializing = true;
              initialized = true;
            }
        
            _;
        
            if (isTopLevelCall) {
              initializing = false;
            }
          }
        
          /// @dev Returns true if and only if the function is running in the constructor
          function isConstructor() private view returns (bool) {
            // extcodesize checks the size of the code stored in an address, and
            // address returns the current address. Since the code is still not
            // deployed when running a constructor, any checks on its code size will
            // yield zero, making it an effective way to detect if a contract is
            // under construction or not.
            uint256 cs;
            assembly { cs := extcodesize(address) }
            return cs == 0;
          }
        
          // Reserved storage space to allow for layout changes in the future.
          uint256[50] private ______gap;
        }
        
        // File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
        
        pragma solidity ^0.5.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.
         */
        contract Context is Initializable {
            // Empty internal constructor, to prevent people from mistakenly deploying
            // an instance of this contract, which should be used via inheritance.
            constructor () internal { }
            // solhint-disable-previous-line no-empty-blocks
        
            function _msgSender() internal view returns (address payable) {
                return msg.sender;
            }
        
            function _msgData() internal view returns (bytes memory) {
                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                return msg.data;
            }
        }
        
        // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol
        
        pragma solidity ^0.5.0;
        
        
        
        
        
        /**
         * @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 {ERC20Mintable}.
         *
         * 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 Initializable, Context, IERC20 {
            using SafeMath for uint256;
        
            mapping (address => uint256) private _balances;
        
            mapping (address => mapping (address => uint256)) private _allowances;
        
            uint256 private _totalSupply;
        
            /**
             * @dev See {IERC20-totalSupply}.
             */
            function totalSupply() public view returns (uint256) {
                return _totalSupply;
            }
        
            /**
             * @dev See {IERC20-balanceOf}.
             */
            function balanceOf(address account) public view 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 returns (bool) {
                _transfer(_msgSender(), recipient, amount);
                return true;
            }
        
            /**
             * @dev See {IERC20-allowance}.
             */
            function allowance(address owner, address spender) public view returns (uint256) {
                return _allowances[owner][spender];
            }
        
            /**
             * @dev See {IERC20-approve}.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function approve(address spender, uint256 amount) public 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 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 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 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 {
                require(sender != address(0), "ERC20: transfer from the zero address");
                require(recipient != address(0), "ERC20: transfer to the zero address");
        
                _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 {
                require(account != address(0), "ERC20: mint to the zero address");
        
                _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 {
                require(account != address(0), "ERC20: burn from the zero address");
        
                _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 is 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 {
                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 Destroys `amount` tokens from `account`.`amount` is then deducted
             * from the caller's allowance.
             *
             * See {_burn} and {_approve}.
             */
            function _burnFrom(address account, uint256 amount) internal {
                _burn(account, amount);
                _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
            }
        
            uint256[50] private ______gap;
        }
        
        // File: @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol
        
        pragma solidity ^0.5.0;
        
        
        
        /**
         * @dev Optional functions from the ERC20 standard.
         */
        contract ERC20Detailed is Initializable, IERC20 {
            string private _name;
            string private _symbol;
            uint8 private _decimals;
        
            /**
             * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
             * these values are immutable: they can only be set once during
             * construction.
             */
            function initialize(string memory name, string memory symbol, uint8 decimals) public initializer {
                _name = name;
                _symbol = symbol;
                _decimals = decimals;
            }
        
            /**
             * @dev Returns the name of the token.
             */
            function name() public view returns (string memory) {
                return _name;
            }
        
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view 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.
             *
             * 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 returns (uint8) {
                return _decimals;
            }
        
            uint256[50] private ______gap;
        }
        
        // File: contracts/hardworkInterface/IStrategy.sol
        
        pragma solidity 0.5.16;
        
        interface IStrategy {
            
            function unsalvagableTokens(address tokens) external view returns (bool);
            
            function governance() external view returns (address);
            function controller() external view returns (address);
            function underlying() external view returns (address);
            function vault() external view returns (address);
        
            function withdrawAllToVault() external;
            function withdrawToVault(uint256 amount) external;
        
            function investedUnderlyingBalance() external view returns (uint256); // itsNotMuch()
        
            // should only be called by controller
            function salvage(address recipient, address token, uint256 amount) external;
        
            function doHardWork() external;
            function depositArbCheck() external view returns(bool);
        }
        
        // File: contracts/hardworkInterface/IController.sol
        
        pragma solidity 0.5.16;
        
        interface IController {
            // [Grey list]
            // An EOA can safely interact with the system no matter what.
            // If you're using Metamask, you're using an EOA.
            // Only smart contracts may be affected by this grey list.
            //
            // This contract will not be able to ban any EOA from the system
            // even if an EOA is being added to the greyList, he/she will still be able
            // to interact with the whole system as if nothing happened.
            // Only smart contracts will be affected by being added to the greyList.
            // This grey list is only used in Vault.sol, see the code there for reference
            function greyList(address _target) external view returns(bool);
        
            function addVaultAndStrategy(address _vault, address _strategy) external;
            function doHardWork(address _vault) external;
            function hasVault(address _vault) external returns(bool);
        
            function salvage(address _token, uint256 amount) external;
            function salvageStrategy(address _strategy, address _token, uint256 amount) external;
        
            function notifyFee(address _underlying, uint256 fee) external;
            function profitSharingNumerator() external view returns (uint256);
            function profitSharingDenominator() external view returns (uint256);
        }
        
        // File: contracts/hardworkInterface/IVault.sol
        
        pragma solidity 0.5.16;
        
        interface IVault {
        
            function underlyingBalanceInVault() external view returns (uint256);
            function underlyingBalanceWithInvestment() external view returns (uint256);
        
            function governance() external view returns (address);
            function controller() external view returns (address);
            function underlying() external view returns (address);
            function strategy() external view returns (address);
        
            function setStrategy(address _strategy) external;
            function setVaultFractionToInvest(uint256 numerator, uint256 denominator) external;
        
            function deposit(uint256 amountWei) external;
            function depositFor(uint256 amountWei, address holder) external;
        
            function withdrawAll() external;
            function withdraw(uint256 numberOfShares) external;
            function getPricePerFullShare() external view returns (uint256);
        
            function underlyingBalanceWithInvestmentForHolder(address holder) view external returns (uint256);
        
            // hard work should be callable only by the controller (by the hard worker) or by governance
            function doHardWork() external;
            function rebalance() external;
        }
        
        // File: contracts/hardworkInterface/IUpgradeSource.sol
        
        pragma solidity 0.5.16;
        
        interface IUpgradeSource {
          function shouldUpgrade() external view returns (bool, address);
          function finalizeUpgrade() external;
        }
        
        // File: contracts/Storage.sol
        
        pragma solidity 0.5.16;
        
        contract Storage {
        
          address public governance;
          address public controller;
        
          constructor() public {
            governance = msg.sender;
          }
        
          modifier onlyGovernance() {
            require(isGovernance(msg.sender), "Not governance");
            _;
          }
        
          function setGovernance(address _governance) public onlyGovernance {
            require(_governance != address(0), "new governance shouldn't be empty");
            governance = _governance;
          }
        
          function setController(address _controller) public onlyGovernance {
            require(_controller != address(0), "new controller shouldn't be empty");
            controller = _controller;
          }
        
          function isGovernance(address account) public view returns (bool) {
            return account == governance;
          }
        
          function isController(address account) public view returns (bool) {
            return account == controller;
          }
        }
        
        // File: contracts/GovernableInit.sol
        
        pragma solidity 0.5.16;
        
        
        
        // A clone of Governable supporting the Initializable interface and pattern
        contract GovernableInit is Initializable {
        
          bytes32 internal constant _STORAGE_SLOT = 0xa7ec62784904ff31cbcc32d09932a58e7f1e4476e1d041995b37c917990b16dc;
        
          modifier onlyGovernance() {
            require(Storage(_storage()).isGovernance(msg.sender), "Not governance");
            _;
          }
        
          constructor() public {
            assert(_STORAGE_SLOT == bytes32(uint256(keccak256("eip1967.governableInit.storage")) - 1));
          }
        
          function initialize(address _store) public initializer {
            _setStorage(_store);
          }
        
          function _setStorage(address newStorage) private {
            bytes32 slot = _STORAGE_SLOT;
            // solhint-disable-next-line no-inline-assembly
            assembly {
              sstore(slot, newStorage)
            }
          }
        
          function setStorage(address _store) public onlyGovernance {
            require(_store != address(0), "new storage shouldn't be empty");
            _setStorage(_store);
          }
        
          function _storage() internal view returns (address str) {
            bytes32 slot = _STORAGE_SLOT;
            // solhint-disable-next-line no-inline-assembly
            assembly {
              str := sload(slot)
            }
          }
        
          function governance() public view returns (address) {
            return Storage(_storage()).governance();
          }
        }
        
        // File: contracts/ControllableInit.sol
        
        pragma solidity 0.5.16;
        
        
        // A clone of Governable supporting the Initializable interface and pattern
        contract ControllableInit is GovernableInit {
        
          constructor() public {
          }
        
          function initialize(address _storage) public initializer {
            GovernableInit.initialize(_storage);
          }
        
          modifier onlyController() {
            require(Storage(_storage()).isController(msg.sender), "Not a controller");
            _;
          }
        
          modifier onlyControllerOrGovernance(){
            require((Storage(_storage()).isController(msg.sender) || Storage(_storage()).isGovernance(msg.sender)),
              "The caller must be controller or governance");
            _;
          }
        
          function controller() public view returns (address) {
            return Storage(_storage()).controller();
          }
        }
        
        // File: contracts/VaultStorage.sol
        
        pragma solidity 0.5.16;
        
        
        contract VaultStorage is Initializable {
        
          bytes32 internal constant _STRATEGY_SLOT = 0xf1a169aa0f736c2813818fdfbdc5755c31e0839c8f49831a16543496b28574ea;
          bytes32 internal constant _UNDERLYING_SLOT = 0x1994607607e11d53306ef62e45e3bd85762c58d9bf38b5578bc4a258a26a7371;
          bytes32 internal constant _UNDERLYING_UNIT_SLOT = 0xa66bc57d4b4eed7c7687876ca77997588987307cb13ecc23f5e52725192e5fff;
          bytes32 internal constant _VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT = 0x39122c9adfb653455d0c05043bd52fcfbc2be864e832efd3abc72ce5a3d7ed5a;
          bytes32 internal constant _VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT = 0x469a3bad2fab7b936c45eecd1f5da52af89cead3e2ed7f732b6f3fc92ed32308;
          bytes32 internal constant _NEXT_IMPLEMENTATION_SLOT = 0xb1acf527cd7cd1668b30e5a9a1c0d845714604de29ce560150922c9d8c0937df;
          bytes32 internal constant _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT = 0x3bc747f4b148b37be485de3223c90b4468252967d2ea7f9fcbd8b6e653f434c9;
          bytes32 internal constant _NEXT_IMPLEMENTATION_DELAY_SLOT = 0x82ddc3be3f0c1a6870327f78f4979a0b37b21b16736ef5be6a7a7a35e530bcf0;
          bytes32 internal constant _STRATEGY_TIME_LOCK_SLOT = 0x6d02338b2e4c913c0f7d380e2798409838a48a2c4d57d52742a808c82d713d8b;
          bytes32 internal constant _FUTURE_STRATEGY_SLOT = 0xb441b53a4e42c2ca9182bc7ede99bedba7a5d9360d9dfbd31fa8ee2dc8590610;
          bytes32 internal constant _STRATEGY_UPDATE_TIME_SLOT = 0x56e7c0e75875c6497f0de657009613a32558904b5c10771a825cc330feff7e72;
        
          constructor() public {
            assert(_STRATEGY_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.strategy")) - 1));
            assert(_UNDERLYING_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.underlying")) - 1));
            assert(_UNDERLYING_UNIT_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.underlyingUnit")) - 1));
            assert(_VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.vaultFractionToInvestNumerator")) - 1));
            assert(_VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.vaultFractionToInvestDenominator")) - 1));
            assert(_NEXT_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.nextImplementation")) - 1));
            assert(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.nextImplementationTimestamp")) - 1));
            assert(_NEXT_IMPLEMENTATION_DELAY_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.nextImplementationDelay")) - 1));
            assert(_STRATEGY_TIME_LOCK_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.strategyTimeLock")) - 1));
            assert(_FUTURE_STRATEGY_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.futureStrategy")) - 1));
            assert(_STRATEGY_UPDATE_TIME_SLOT == bytes32(uint256(keccak256("eip1967.vaultStorage.strategyUpdateTime")) - 1));
          }
        
          function initialize(
            address _underlying,
            uint256 _toInvestNumerator,
            uint256 _toInvestDenominator,
            uint256 _underlyingUnit,
            uint256 _implementationChangeDelay,
            uint256 _strategyChangeDelay
          ) public initializer {
            _setUnderlying(_underlying);
            _setVaultFractionToInvestNumerator(_toInvestNumerator);
            _setVaultFractionToInvestDenominator(_toInvestDenominator);
            _setUnderlyingUnit(_underlyingUnit);
            _setNextImplementationDelay(_implementationChangeDelay);
            _setStrategyTimeLock(_strategyChangeDelay);
            _setStrategyUpdateTime(0);
            _setFutureStrategy(address(0));
          }
        
          function _setStrategy(address _address) internal {
            setAddress(_STRATEGY_SLOT, _address);
          }
        
          function _strategy() internal view returns (address) {
            return getAddress(_STRATEGY_SLOT);
          }
        
          function _setUnderlying(address _address) internal {
            setAddress(_UNDERLYING_SLOT, _address);
          }
        
          function _underlying() internal view returns (address) {
            return getAddress(_UNDERLYING_SLOT);
          }
        
          function _setUnderlyingUnit(uint256 _value) internal {
            setUint256(_UNDERLYING_UNIT_SLOT, _value);
          }
        
          function _underlyingUnit() internal view returns (uint256) {
            return getUint256(_UNDERLYING_UNIT_SLOT);
          }
        
          function _setVaultFractionToInvestNumerator(uint256 _value) internal {
            setUint256(_VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT, _value);
          }
        
          function _vaultFractionToInvestNumerator() internal view returns (uint256) {
            return getUint256(_VAULT_FRACTION_TO_INVEST_NUMERATOR_SLOT);
          }
        
          function _setVaultFractionToInvestDenominator(uint256 _value) internal {
            setUint256(_VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT, _value);
          }
        
          function _vaultFractionToInvestDenominator() internal view returns (uint256) {
            return getUint256(_VAULT_FRACTION_TO_INVEST_DENOMINATOR_SLOT);
          }
        
          function _setNextImplementation(address _address) internal {
            setAddress(_NEXT_IMPLEMENTATION_SLOT, _address);
          }
        
          function _nextImplementation() internal view returns (address) {
            return getAddress(_NEXT_IMPLEMENTATION_SLOT);
          }
        
          function _setNextImplementationTimestamp(uint256 _value) internal {
            setUint256(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT, _value);
          }
        
          function _nextImplementationTimestamp() internal view returns (uint256) {
            return getUint256(_NEXT_IMPLEMENTATION_TIMESTAMP_SLOT);
          }
        
          function _setNextImplementationDelay(uint256 _value) internal {
            setUint256(_NEXT_IMPLEMENTATION_DELAY_SLOT, _value);
          }
        
          function _nextImplementationDelay() internal view returns (uint256) {
            return getUint256(_NEXT_IMPLEMENTATION_DELAY_SLOT);
          }
        
          function _setStrategyTimeLock(uint256 _value) internal {
            setUint256(_STRATEGY_TIME_LOCK_SLOT, _value);
          }
        
          function _strategyTimeLock() internal view returns (uint256) {
            return getUint256(_STRATEGY_TIME_LOCK_SLOT);
          }
        
          function _setFutureStrategy(address _value) internal {
            setAddress(_FUTURE_STRATEGY_SLOT, _value);
          }
        
          function _futureStrategy() internal view returns (address) {
            return getAddress(_FUTURE_STRATEGY_SLOT);
          }
        
          function _setStrategyUpdateTime(uint256 _value) internal {
            setUint256(_STRATEGY_UPDATE_TIME_SLOT, _value);
          }
        
          function _strategyUpdateTime() internal view returns (uint256) {
            return getUint256(_STRATEGY_UPDATE_TIME_SLOT);
          }
        
          function setAddress(bytes32 slot, address _address) private {
            // solhint-disable-next-line no-inline-assembly
            assembly {
              sstore(slot, _address)
            }
          }
        
          function setUint256(bytes32 slot, uint256 _value) private {
            // solhint-disable-next-line no-inline-assembly
            assembly {
              sstore(slot, _value)
            }
          }
        
          function getAddress(bytes32 slot) private view returns (address str) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
              str := sload(slot)
            }
          }
        
          function getUint256(bytes32 slot) private view returns (uint256 str) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
              str := sload(slot)
            }
          }
        
          uint256[50] private ______gap;
        }
        
        // File: contracts/Vault.sol
        
        pragma solidity 0.5.16;
        
        
        
        
        
        
        
        
        
        
        
        
        
        contract Vault is ERC20, ERC20Detailed, IVault, IUpgradeSource, ControllableInit, VaultStorage {
          using SafeERC20 for IERC20;
          using Address for address;
          using SafeMath for uint256;
        
          event Withdraw(address indexed beneficiary, uint256 amount);
          event Deposit(address indexed beneficiary, uint256 amount);
          event Invest(uint256 amount);
          event StrategyAnnounced(address newStrategy, uint256 time);
          event StrategyChanged(address newStrategy, address oldStrategy);
        
          constructor() public {
          }
        
          // the function is name differently to not cause inheritance clash in truffle and allows tests
          function initializeVault(address _storage,
            address _underlying,
            uint256 _toInvestNumerator,
            uint256 _toInvestDenominator
          ) public initializer {
            require(_toInvestNumerator <= _toInvestDenominator, "cannot invest more than 100%");
            require(_toInvestDenominator != 0, "cannot divide by 0");
        
            ERC20Detailed.initialize(
              string(abi.encodePacked("FARM_", ERC20Detailed(_underlying).symbol())),
              string(abi.encodePacked("f", ERC20Detailed(_underlying).symbol())),
              ERC20Detailed(_underlying).decimals()
            );
            ControllableInit.initialize(
              _storage
            );
        
            uint256 underlyingUnit = 10 ** uint256(ERC20Detailed(address(_underlying)).decimals());
            uint256 implementationDelay = 12 hours;
            uint256 strategyChangeDelay = 12 hours;
            VaultStorage.initialize(
              _underlying,
              _toInvestNumerator,
              _toInvestDenominator,
              underlyingUnit,
              implementationDelay,
              strategyChangeDelay
            );
          }
        
          function strategy() public view returns(address) {
            return _strategy();
          }
        
          function underlying() public view returns(address) {
            return _underlying();
          }
        
          function underlyingUnit() public view returns(uint256) {
            return _underlyingUnit();
          }
        
          function vaultFractionToInvestNumerator() public view returns(uint256) {
            return _vaultFractionToInvestNumerator();
          }
        
          function vaultFractionToInvestDenominator() public view returns(uint256) {
            return _vaultFractionToInvestDenominator();
          }
        
          function nextImplementation() public view returns(address) {
            return _nextImplementation();
          }
        
          function nextImplementationTimestamp() public view returns(uint256) {
            return _nextImplementationTimestamp();
          }
        
          function nextImplementationDelay() public view returns(uint256) {
            return _nextImplementationDelay();
          }
        
          modifier whenStrategyDefined() {
            require(address(strategy()) != address(0), "Strategy must be defined");
            _;
          }
        
          // Only smart contracts will be affected by this modifier
          modifier defense() {
            require(
              (msg.sender == tx.origin) ||                // If it is a normal user and not smart contract,
                                                          // then the requirement will pass
              !IController(controller()).greyList(msg.sender), // If it is a smart contract, then
              "This smart contract has been grey listed"  // make sure that it is not on our greyList.
            );
            _;
          }
        
          /**
          * Chooses the best strategy and re-invests. If the strategy did not change, it just calls
          * doHardWork on the current strategy. Call this through controller to claim hard rewards.
          */
          function doHardWork() whenStrategyDefined onlyControllerOrGovernance external {
            // ensure that new funds are invested too
            invest();
            IStrategy(strategy()).doHardWork();
          }
        
          /*
          * Returns the cash balance across all users in this contract.
          */
          function underlyingBalanceInVault() view public returns (uint256) {
            return IERC20(underlying()).balanceOf(address(this));
          }
        
          /* Returns the current underlying (e.g., DAI's) balance together with
           * the invested amount (if DAI is invested elsewhere by the strategy).
          */
          function underlyingBalanceWithInvestment() view public returns (uint256) {
            if (address(strategy()) == address(0)) {
              // initial state, when not set
              return underlyingBalanceInVault();
            }
            return underlyingBalanceInVault().add(IStrategy(strategy()).investedUnderlyingBalance());
          }
        
          function getPricePerFullShare() public view returns (uint256) {
            return totalSupply() == 0
                ? underlyingUnit()
                : underlyingUnit().mul(underlyingBalanceWithInvestment()).div(totalSupply());
          }
        
          /* get the user's share (in underlying)
          */
          function underlyingBalanceWithInvestmentForHolder(address holder) view external returns (uint256) {
            if (totalSupply() == 0) {
              return 0;
            }
            return underlyingBalanceWithInvestment()
                .mul(balanceOf(holder))
                .div(totalSupply());
          }
        
          function futureStrategy() public view returns (address) {
            return _futureStrategy();
          }
        
          function strategyUpdateTime() public view returns (uint256) {
            return _strategyUpdateTime();
          }
        
          function strategyTimeLock() public view returns (uint256) {
            return _strategyTimeLock();
          }
        
          function canUpdateStrategy(address _strategy) public view returns(bool) {
            return strategy() == address(0) // no strategy was set yet
              || (_strategy == futureStrategy()
                  && block.timestamp > strategyUpdateTime()
                  && strategyUpdateTime() > 0); // or the timelock has passed
          }
        
          /**
          * Indicates that the strategy update will happen in the future
          */
          function announceStrategyUpdate(address _strategy) public onlyControllerOrGovernance {
            // records a new timestamp
            uint256 when = block.timestamp.add(strategyTimeLock());
            _setStrategyUpdateTime(when);
            _setFutureStrategy(_strategy);
            emit StrategyAnnounced(_strategy, when);
          }
        
          /**
          * Finalizes (or cancels) the strategy update by resetting the data
          */
          function finalizeStrategyUpdate() public onlyControllerOrGovernance {
            _setStrategyUpdateTime(0);
            _setFutureStrategy(address(0));
          }
        
          function setStrategy(address _strategy) public onlyControllerOrGovernance {
            require(canUpdateStrategy(_strategy),
              "The strategy exists and switch timelock did not elapse yet");
            require(_strategy != address(0), "new _strategy cannot be empty");
            require(IStrategy(_strategy).underlying() == address(underlying()), "Vault underlying must match Strategy underlying");
            require(IStrategy(_strategy).vault() == address(this), "the strategy does not belong to this vault");
        
            emit StrategyChanged(_strategy, strategy());
            if (address(_strategy) != address(strategy())) {
              if (address(strategy()) != address(0)) { // if the original strategy (no underscore) is defined
                IERC20(underlying()).safeApprove(address(strategy()), 0);
                IStrategy(strategy()).withdrawAllToVault();
              }
              _setStrategy(_strategy);
              IERC20(underlying()).safeApprove(address(strategy()), 0);
              IERC20(underlying()).safeApprove(address(strategy()), uint256(~0));
            }
            finalizeStrategyUpdate();
          }
        
          function setVaultFractionToInvest(uint256 numerator, uint256 denominator) external onlyGovernance {
            require(denominator > 0, "denominator must be greater than 0");
            require(numerator <= denominator, "denominator must be greater than or equal to the numerator");
            _setVaultFractionToInvestNumerator(numerator);
            _setVaultFractionToInvestDenominator(denominator);
          }
        
          function rebalance() external onlyControllerOrGovernance {
            withdrawAll();
            invest();
          }
        
          function availableToInvestOut() public view returns (uint256) {
            uint256 wantInvestInTotal = underlyingBalanceWithInvestment()
                .mul(vaultFractionToInvestNumerator())
                .div(vaultFractionToInvestDenominator());
            uint256 alreadyInvested = IStrategy(strategy()).investedUnderlyingBalance();
            if (alreadyInvested >= wantInvestInTotal) {
              return 0;
            } else {
              uint256 remainingToInvest = wantInvestInTotal.sub(alreadyInvested);
              return remainingToInvest <= underlyingBalanceInVault()
                // TODO: we think that the "else" branch of the ternary operation is not
                // going to get hit
                ? remainingToInvest : underlyingBalanceInVault();
            }
          }
        
          function invest() internal whenStrategyDefined {
            uint256 availableAmount = availableToInvestOut();
            if (availableAmount > 0) {
              IERC20(underlying()).safeTransfer(address(strategy()), availableAmount);
              emit Invest(availableAmount);
            }
          }
        
          /*
          * Allows for depositing the underlying asset in exchange for shares.
          * Approval is assumed.
          */
          function deposit(uint256 amount) external defense {
            _deposit(amount, msg.sender, msg.sender);
          }
        
          /*
          * Allows for depositing the underlying asset in exchange for shares
          * assigned to the holder.
          * This facilitates depositing for someone else (using DepositHelper)
          */
          function depositFor(uint256 amount, address holder) public defense {
            _deposit(amount, msg.sender, holder);
          }
        
          function withdrawAll() public onlyControllerOrGovernance whenStrategyDefined {
            IStrategy(strategy()).withdrawAllToVault();
          }
        
          function withdraw(uint256 numberOfShares) external {
            require(totalSupply() > 0, "Vault has no shares");
            require(numberOfShares > 0, "numberOfShares must be greater than 0");
            uint256 totalSupply = totalSupply();
            _burn(msg.sender, numberOfShares);
        
            uint256 underlyingAmountToWithdraw = underlyingBalanceWithInvestment()
                .mul(numberOfShares)
                .div(totalSupply);
            if (underlyingAmountToWithdraw > underlyingBalanceInVault()) {
              // withdraw everything from the strategy to accurately check the share value
              if (numberOfShares == totalSupply) {
                IStrategy(strategy()).withdrawAllToVault();
              } else {
                uint256 missing = underlyingAmountToWithdraw.sub(underlyingBalanceInVault());
                IStrategy(strategy()).withdrawToVault(missing);
              }
              // recalculate to improve accuracy
              underlyingAmountToWithdraw = Math.min(underlyingBalanceWithInvestment()
                  .mul(numberOfShares)
                  .div(totalSupply), underlyingBalanceInVault());
            }
        
            IERC20(underlying()).safeTransfer(msg.sender, underlyingAmountToWithdraw);
        
            // update the withdrawal amount for the holder
            emit Withdraw(msg.sender, underlyingAmountToWithdraw);
          }
        
          function _deposit(uint256 amount, address sender, address beneficiary) internal {
            require(amount > 0, "Cannot deposit 0");
            require(beneficiary != address(0), "holder must be defined");
        
            if (address(strategy()) != address(0)) {
              require(IStrategy(strategy()).depositArbCheck(), "Too much arb");
            }
        
            uint256 toMint = totalSupply() == 0
                ? amount
                : amount.mul(totalSupply()).div(underlyingBalanceWithInvestment());
            _mint(beneficiary, toMint);
        
            IERC20(underlying()).safeTransferFrom(sender, address(this), amount);
        
            // update the contribution amount for the beneficiary
            emit Deposit(beneficiary, amount);
          }
        
          /**
          * Schedules an upgrade for this vault's proxy.
          */
          function scheduleUpgrade(address impl) public onlyGovernance {
            _setNextImplementation(impl);
            _setNextImplementationTimestamp(block.timestamp.add(nextImplementationDelay()));
          }
        
          function shouldUpgrade() external view returns (bool, address) {
            return (
              nextImplementationTimestamp() != 0
                && block.timestamp > nextImplementationTimestamp()
                && nextImplementation() != address(0),
              nextImplementation()
            );
          }
        
          function finalizeUpgrade() external onlyGovernance {
            _setNextImplementation(address(0));
            _setNextImplementationTimestamp(0);
          }
        }

        File 3 of 3: StakingRewards
        pragma solidity ^0.5.16;
        
        /**
         * @dev Standard math utilities missing in the Solidity language.
         */
        library Math {
            /**
             * @dev Returns the largest of two numbers.
             */
            function max(uint256 a, uint256 b) internal pure returns (uint256) {
                return a >= b ? a : b;
            }
        
            /**
             * @dev Returns the smallest of two numbers.
             */
            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                return a < b ? a : b;
            }
        
            /**
             * @dev Returns the average of two numbers. The result is rounded towards
             * zero.
             */
            function average(uint256 a, uint256 b) internal pure returns (uint256) {
                // (a + b) / 2 can overflow, so we distribute
                return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
            }
        }
        
        /**
         * @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, 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");
                uint256 c = a - b;
        
                return c;
            }
        
            /**
             * @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) {
                // 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-solidity/pull/522
                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. Reverts 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) {
                // Solidity only automatically asserts when dividing by 0
                require(b > 0, "SafeMath: division by zero");
                uint256 c = a / b;
                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        
                return c;
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts 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 Interface of the ERC20 standard as defined in the EIP. Does not include
         * the optional functions; to access them see `ERC20Detailed`.
         */
        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.
             *
             * > 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);
        }
        
        /**
         * @dev Optional functions from the ERC20 standard.
         */
        contract ERC20Detailed is IERC20 {
            string private _name;
            string private _symbol;
            uint8 private _decimals;
        
            /**
             * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
             * these values are immutable: they can only be set once during
             * construction.
             */
            constructor (string memory name, string memory symbol, uint8 decimals) public {
                _name = name;
                _symbol = symbol;
                _decimals = decimals;
            }
        
            /**
             * @dev Returns the name of the token.
             */
            function name() public view returns (string memory) {
                return _name;
            }
        
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view 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.
             *
             * > Note that 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 returns (uint8) {
                return _decimals;
            }
        }
        
        
        /**
         * @dev Collection of functions related to the address type,
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * This test is non-exhaustive, and there may be false-negatives: during the
             * execution of a contract's constructor, its address will be reported as
             * not containing a contract.
             *
             * > It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies in 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;
            }
        }
        
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using SafeMath for uint256;
            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));
            }
        
            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);
                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.
        
                // A Solidity high level call has three parts:
                //  1. The target address is checked to verify it contains contract code
                //  2. The call itself is made, and success asserted
                //  3. The return value is decoded, which in turn checks the size of the returned data.
                // solhint-disable-next-line max-line-length
                require(address(token).isContract(), "SafeERC20: call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = address(token).call(data);
                require(success, "SafeERC20: low-level call failed");
        
                if (returndata.length > 0) { // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        
        /**
         * @dev Contract module that helps prevent reentrant calls to a function.
         *
         * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier
         * available, which can be aplied 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.
         */
        contract ReentrancyGuard {
            /// @dev counter to allow mutex lock with only one SSTORE operation
            uint256 private _guardCounter;
        
            constructor () internal {
                // The counter starts at one to prevent changing it from zero to a non-zero
                // value, which is a more expensive operation.
                _guardCounter = 1;
            }
        
            /**
             * @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() {
                _guardCounter += 1;
                uint256 localCounter = _guardCounter;
                _;
                require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
            }
        }
        
        // Inheritancea
        interface IStakingRewards {
            // Views
            function lastTimeRewardApplicable() external view returns (uint256);
        
            function rewardPerToken() external view returns (uint256);
        
            function earned(address account) external view returns (uint256);
        
            function getRewardForDuration() external view returns (uint256);
        
            function totalSupply() external view returns (uint256);
        
            function balanceOf(address account) external view returns (uint256);
        
            // Mutative
        
            function stake(uint256 amount) external;
        
            function withdraw(uint256 amount) external;
        
            function getReward() external;
        
            function exit() external;
        }
        
        contract RewardsDistributionRecipient {
            address public rewardsDistribution;
        
            function notifyRewardAmount(uint256 reward) external;
        
            modifier onlyRewardsDistribution() {
                require(msg.sender == rewardsDistribution, "Caller is not RewardsDistribution contract");
                _;
            }
        }
        
        contract StakingRewards is IStakingRewards, RewardsDistributionRecipient, ReentrancyGuard {
            using SafeMath for uint256;
            using SafeERC20 for IERC20;
        
            /* ========== STATE VARIABLES ========== */
        
            IERC20 public rewardsToken;
            IERC20 public stakingToken;
            uint256 public periodFinish = 0;
            uint256 public rewardRate = 0;
            uint256 public rewardsDuration = 60 days;
            uint256 public lastUpdateTime;
            uint256 public rewardPerTokenStored;
        
            mapping(address => uint256) public userRewardPerTokenPaid;
            mapping(address => uint256) public rewards;
        
            uint256 private _totalSupply;
            mapping(address => uint256) private _balances;
        
            /* ========== CONSTRUCTOR ========== */
        
            constructor(
                address _rewardsDistribution,
                address _rewardsToken,
                address _stakingToken
            ) public {
                rewardsToken = IERC20(_rewardsToken);
                stakingToken = IERC20(_stakingToken);
                rewardsDistribution = _rewardsDistribution;
            }
        
            /* ========== VIEWS ========== */
        
            function totalSupply() external view returns (uint256) {
                return _totalSupply;
            }
        
            function balanceOf(address account) external view returns (uint256) {
                return _balances[account];
            }
        
            function lastTimeRewardApplicable() public view returns (uint256) {
                return Math.min(block.timestamp, periodFinish);
            }
        
            function rewardPerToken() public view returns (uint256) {
                if (_totalSupply == 0) {
                    return rewardPerTokenStored;
                }
                return
                    rewardPerTokenStored.add(
                        lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(_totalSupply)
                    );
            }
        
            function earned(address account) public view returns (uint256) {
                return _balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]);
            }
        
            function getRewardForDuration() external view returns (uint256) {
                return rewardRate.mul(rewardsDuration);
            }
        
            /* ========== MUTATIVE FUNCTIONS ========== */
        
            function stakeWithPermit(uint256 amount, uint deadline, uint8 v, bytes32 r, bytes32 s) external nonReentrant updateReward(msg.sender) {
                require(amount > 0, "Cannot stake 0");
                _totalSupply = _totalSupply.add(amount);
                _balances[msg.sender] = _balances[msg.sender].add(amount);
        
                // permit
                IUniswapV2ERC20(address(stakingToken)).permit(msg.sender, address(this), amount, deadline, v, r, s);
        
                stakingToken.safeTransferFrom(msg.sender, address(this), amount);
                emit Staked(msg.sender, amount);
            }
        
            function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {
                require(amount > 0, "Cannot stake 0");
                _totalSupply = _totalSupply.add(amount);
                _balances[msg.sender] = _balances[msg.sender].add(amount);
                stakingToken.safeTransferFrom(msg.sender, address(this), amount);
                emit Staked(msg.sender, amount);
            }
        
            function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) {
                require(amount > 0, "Cannot withdraw 0");
                _totalSupply = _totalSupply.sub(amount);
                _balances[msg.sender] = _balances[msg.sender].sub(amount);
                stakingToken.safeTransfer(msg.sender, amount);
                emit Withdrawn(msg.sender, amount);
            }
        
            function getReward() public nonReentrant updateReward(msg.sender) {
                uint256 reward = rewards[msg.sender];
                if (reward > 0) {
                    rewards[msg.sender] = 0;
                    rewardsToken.safeTransfer(msg.sender, reward);
                    emit RewardPaid(msg.sender, reward);
                }
            }
        
            function exit() external {
                withdraw(_balances[msg.sender]);
                getReward();
            }
        
            /* ========== RESTRICTED FUNCTIONS ========== */
        
            function notifyRewardAmount(uint256 reward) external onlyRewardsDistribution updateReward(address(0)) {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(rewardsDuration);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(rewardsDuration);
                }
        
                // Ensure the provided reward amount is not more than the balance in the contract.
                // This keeps the reward rate in the right range, preventing overflows due to
                // very high values of rewardRate in the earned and rewardsPerToken functions;
                // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
                uint balance = rewardsToken.balanceOf(address(this));
                require(rewardRate <= balance.div(rewardsDuration), "Provided reward too high");
        
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(rewardsDuration);
                emit RewardAdded(reward);
            }
        
            /* ========== MODIFIERS ========== */
        
            modifier updateReward(address account) {
                rewardPerTokenStored = rewardPerToken();
                lastUpdateTime = lastTimeRewardApplicable();
                if (account != address(0)) {
                    rewards[account] = earned(account);
                    userRewardPerTokenPaid[account] = rewardPerTokenStored;
                }
                _;
            }
        
            /* ========== EVENTS ========== */
        
            event RewardAdded(uint256 reward);
            event Staked(address indexed user, uint256 amount);
            event Withdrawn(address indexed user, uint256 amount);
            event RewardPaid(address indexed user, uint256 reward);
        }
        
        interface IUniswapV2ERC20 {
            function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
        }