ETH Price: $2,254.21 (+6.44%)

Transaction Decoder

Block:
21860253 at Feb-16-2025 04:48:47 PM +UTC
Transaction Fee:
0.0001538848321425 ETH $0.35
Gas Used:
193,860 Gas / 0.793793625 Gwei

Emitted Events:

270 RewardToken.Transfer( from=ExclusiveRewardPool, to=[Receiver] AutoStake, value=58433078313788254803967 )
271 ExclusiveRewardPool.Withdrawn( user=[Receiver] AutoStake, amount=58433078313788254803967 )
272 RewardToken.Transfer( from=ExclusiveRewardPool, to=[Receiver] AutoStake, value=34712299999999796392 )
273 ExclusiveRewardPool.RewardPaid( user=[Receiver] AutoStake, reward=34712299999999796392 )
274 RewardToken.Transfer( from=[Receiver] AutoStake, to=[Sender] 0x06218f8455f822e969bce846caf151e8a2a22dea, value=1127109510837073362 )
275 AutoStake.Withdrawn( user=[Sender] 0x06218f8455f822e969bce846caf151e8a2a22dea, total=1127109510837073362 )
276 RewardToken.Approval( owner=[Receiver] AutoStake, spender=ExclusiveRewardPool, value=0 )
277 RewardToken.Approval( owner=[Receiver] AutoStake, spender=ExclusiveRewardPool, value=58466663504277417526997 )
278 RewardToken.Transfer( from=[Receiver] AutoStake, to=ExclusiveRewardPool, value=58466663504277417526997 )
279 RewardToken.Approval( owner=[Receiver] AutoStake, spender=ExclusiveRewardPool, value=0 )
280 ExclusiveRewardPool.Staked( user=[Receiver] AutoStake, amount=58466663504277417526997 )

Account State Difference:

  Address   Before After State Difference Code
0x06218F84...8a2A22DEA
0.095168120511012872 Eth
Nonce: 403
0.095014235678870372 Eth
Nonce: 404
0.0001538848321425
0x25550Ccc...f9e00Fc50
(Harvest finance: PS)
(Titan Builder)
15.210758756687914296 Eth15.210758929695557256 Eth0.00000017300764296
0x8f5adC58...55999436C
0xa0246c90...88619A14D

Execution Trace

AutoStake.CALL( )
  • ExclusiveRewardPool.balanceOf( account=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50 ) => ( 58433078313788254803967 )
  • ExclusiveRewardPool.CALL( )
    • RewardToken.transfer( recipient=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50, amount=58433078313788254803967 ) => ( True )
    • RewardToken.transfer( recipient=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50, amount=34712299999999796392 ) => ( True )
    • RewardToken.balanceOf( account=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50 ) => ( 58467790613788254600359 )
    • RewardToken.transfer( recipient=0x06218F8455F822E969BCE846cAf151E8a2A22DEA, amount=1127109510837073362 ) => ( True )
    • RewardToken.balanceOf( account=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50 ) => ( 58466663504277417526997 )
    • RewardToken.balanceOf( account=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50 ) => ( 58466663504277417526997 )
    • RewardToken.approve( spender=0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C, amount=0 ) => ( True )
    • RewardToken.balanceOf( account=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50 ) => ( 58466663504277417526997 )
    • RewardToken.allowance( owner=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50, spender=0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C ) => ( 0 )
    • RewardToken.approve( spender=0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C, amount=58466663504277417526997 ) => ( True )
    • RewardToken.balanceOf( account=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50 ) => ( 58466663504277417526997 )
    • ExclusiveRewardPool.stake( amount=58466663504277417526997 )
      • RewardToken.transferFrom( sender=0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50, recipient=0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C, amount=58466663504277417526997 ) => ( True )
        File 1 of 3: AutoStake
        // 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/Governable.sol
        
        pragma solidity 0.5.16;
        
        
        contract Governable {
        
          Storage public store;
        
          constructor(address _store) public {
            require(_store != address(0), "new storage shouldn't be empty");
            store = Storage(_store);
          }
        
          modifier onlyGovernance() {
            require(store.isGovernance(msg.sender), "Not governance");
            _;
          }
        
          function setStorage(address _store) public onlyGovernance {
            require(_store != address(0), "new storage shouldn't be empty");
            store = Storage(_store);
          }
        
          function governance() public view returns (address) {
            return store.governance();
          }
        }
        
        // File: contracts/Controllable.sol
        
        pragma solidity 0.5.16;
        
        
        contract Controllable is Governable {
        
          constructor(address _storage) Governable(_storage) public {
          }
        
          modifier onlyController() {
            require(store.isController(msg.sender), "Not a controller");
            _;
          }
        
          modifier onlyControllerOrGovernance(){
            require((store.isController(msg.sender) || store.isGovernance(msg.sender)),
              "The caller must be controller or governance");
            _;
          }
        
          function controller() public view returns (address) {
            return store.controller();
          }
        }
        
        // 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/RewardPool.sol
        
        // https://etherscan.io/address/0xDCB6A51eA3CA5d3Fd898Fd6564757c7aAeC3ca92#code
        
        /**
         *Submitted for verification at Etherscan.io on 2020-04-22
        */
        
        /*
           ____            __   __        __   _
          / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
         _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ /
        /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\
             /___/
        
        * Synthetix: CurveRewards.sol
        *
        * Docs: https://docs.synthetix.io/
        *
        *
        * MIT License
        * ===========
        *
        * Copyright (c) 2020 Synthetix
        *
        * Permission is hereby granted, free of charge, to any person obtaining a copy
        * of this software and associated documentation files (the "Software"), to deal
        * in the Software without restriction, including without limitation the rights
        * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        * copies of the Software, and to permit persons to whom the Software is
        * furnished to do so, subject to the following conditions:
        *
        * The above copyright notice and this permission notice shall be included in all
        * copies or substantial portions of the Software.
        *
        * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        */
        
        // File: @openzeppelin/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/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/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 {
            // 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/ownership/Ownable.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        contract Ownable is Context {
            address private _owner;
        
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor () internal {
                _owner = _msgSender();
                emit OwnershipTransferred(address(0), _owner);
            }
        
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view returns (address) {
                return _owner;
            }
        
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                require(isOwner(), "Ownable: caller is not the owner");
                _;
            }
        
            /**
             * @dev Returns true if the caller is the current owner.
             */
            function isOwner() public view returns (bool) {
                return _msgSender() == _owner;
            }
        
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing any functionality that is only available to the owner.
             */
            function renounceOwnership() public onlyOwner {
                emit OwnershipTransferred(_owner, address(0));
                _owner = address(0);
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public onlyOwner {
                _transferOwnership(newOwner);
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             */
            function _transferOwnership(address newOwner) internal {
                require(newOwner != address(0), "Ownable: new owner is the zero address");
                emit OwnershipTransferred(_owner, newOwner);
                _owner = newOwner;
            }
        }
        
        // File: @openzeppelin/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/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.
             *
             * 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.
             *
             * 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.
             */
            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.
        
                // 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 != 0x0 && codehash != accountHash);
            }
        
            /**
             * @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/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: contracts/IRewardDistributionRecipient.sol
        
        pragma solidity ^0.5.0;
        
        
        
        contract IRewardDistributionRecipient is Ownable {
            address rewardDistribution;
        
            constructor(address _rewardDistribution) public {
                rewardDistribution = _rewardDistribution;
            }
        
            function notifyRewardAmount(uint256 reward) external;
        
            modifier onlyRewardDistribution() {
                require(_msgSender() == rewardDistribution, "Caller is not reward distribution");
                _;
            }
        
            function setRewardDistribution(address _rewardDistribution)
                external
                onlyOwner
            {
                rewardDistribution = _rewardDistribution;
            }
        }
        
        // File: contracts/CurveRewards.sol
        
        pragma solidity ^0.5.0;
        
        
        
        
        /*
        *   Changes made to the SynthetixReward contract
        *
        *   uni to lpToken, and make it as a parameter of the constructor instead of hardcoded.
        *
        *
        */
        
        contract LPTokenWrapper {
            using SafeMath for uint256;
            using SafeERC20 for IERC20;
        
            IERC20 public lpToken;
        
            uint256 private _totalSupply;
            mapping(address => uint256) private _balances;
        
            function totalSupply() public view returns (uint256) {
                return _totalSupply;
            }
        
            function balanceOf(address account) public view returns (uint256) {
                return _balances[account];
            }
        
            function stake(uint256 amount) public {
                _totalSupply = _totalSupply.add(amount);
                _balances[msg.sender] = _balances[msg.sender].add(amount);
                lpToken.safeTransferFrom(msg.sender, address(this), amount);
            }
        
            function withdraw(uint256 amount) public {
                _totalSupply = _totalSupply.sub(amount);
                _balances[msg.sender] = _balances[msg.sender].sub(amount);
                lpToken.safeTransfer(msg.sender, amount);
            }
        }
        
        /*
        *   [Hardwork]
        *   This pool doesn't mint.
        *   the rewards should be first transferred to this pool, then get "notified"
        *   by calling `notifyRewardAmount`
        */
        
        contract NoMintRewardPool is LPTokenWrapper, IRewardDistributionRecipient, Controllable {
        
            using Address for address;
        
            IERC20 public rewardToken;
            uint256 public duration; // making it not a constant is less gas efficient, but portable
        
            uint256 public periodFinish = 0;
            uint256 public rewardRate = 0;
            uint256 public lastUpdateTime;
            uint256 public rewardPerTokenStored;
            mapping(address => uint256) public userRewardPerTokenPaid;
            mapping(address => uint256) public rewards;
        
            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);
            event RewardDenied(address indexed user, uint256 reward);
        
            modifier updateReward(address account) {
                rewardPerTokenStored = rewardPerToken();
                lastUpdateTime = lastTimeRewardApplicable();
                if (account != address(0)) {
                    rewards[account] = earned(account);
                    userRewardPerTokenPaid[account] = rewardPerTokenStored;
                }
                _;
            }
        
            // [Hardwork] setting the reward, lpToken, duration, and rewardDistribution for each pool
            constructor(address _rewardToken,
                address _lpToken,
                uint256 _duration,
                address _rewardDistribution,
                address _storage) public
            IRewardDistributionRecipient(_rewardDistribution)
            Controllable(_storage) // only used for referencing the grey list
            {
                rewardToken = IERC20(_rewardToken);
                lpToken = IERC20(_lpToken);
                duration = _duration;
            }
        
            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
                    balanceOf(account)
                        .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                        .div(1e18)
                        .add(rewards[account]);
            }
        
            // stake visibility is public as overriding LPTokenWrapper's stake() function
            function stake(uint256 amount) public updateReward(msg.sender) {
                require(amount > 0, "Cannot stake 0");
                super.stake(amount);
                emit Staked(msg.sender, amount);
            }
        
            function withdraw(uint256 amount) public updateReward(msg.sender) {
                require(amount > 0, "Cannot withdraw 0");
                super.withdraw(amount);
                emit Withdrawn(msg.sender, amount);
            }
        
            function exit() external {
                withdraw(balanceOf(msg.sender));
                getReward();
            }
        
            /// A push mechanism for accounts that have not claimed their rewards for a long time.
            /// The implementation is semantically analogous to getReward(), but uses a push pattern
            /// instead of pull pattern.
            function pushReward(address recipient) public updateReward(recipient) onlyGovernance {
                uint256 reward = earned(recipient);
                if (reward > 0) {
                    rewards[recipient] = 0;
                    // If it is a normal user and not smart contract,
                    // then the requirement will pass
                    // If it is a smart contract, then
                    // make sure that it is not on our greyList.
                    if (!recipient.isContract() || !IController(controller()).greyList(recipient)) {
                        rewardToken.safeTransfer(recipient, reward);
                        emit RewardPaid(recipient, reward);
                    } else {
                        emit RewardDenied(recipient, reward);
                    }
                }
            }
        
            function getReward() public updateReward(msg.sender) {
                uint256 reward = earned(msg.sender);
                if (reward > 0) {
                    rewards[msg.sender] = 0;
                    // If it is a normal user and not smart contract,
                    // then the requirement will pass
                    // If it is a smart contract, then
                    // make sure that it is not on our greyList.
                    if (tx.origin == msg.sender || !IController(controller()).greyList(msg.sender)) {
                        rewardToken.safeTransfer(msg.sender, reward);
                        emit RewardPaid(msg.sender, reward);
                    } else {
                        emit RewardDenied(msg.sender, reward);
                    }
                }
            }
        
            function notifyRewardAmount(uint256 reward)
                external
                onlyRewardDistribution
                updateReward(address(0))
            {
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(duration);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(duration);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(duration);
                emit RewardAdded(reward);
            }
        }
        
        // File: contracts/AutoStake.sol
        
        pragma solidity 0.5.16;
        
        
        
        contract AutoStake is Controllable {
        
          using SafeERC20 for IERC20;
          using SafeMath for uint256;
        
          NoMintRewardPool public rewardPool;
          IERC20 public lpToken;
          uint256 public unit = 1e18;
          uint256 public valuePerShare = unit;
          uint256 public totalShares = 0;
          uint256 public totalValue = 0;
          mapping(address => uint256) public share;
        
          address public greylistEscrow;
          mapping (address => bool) smartContractStakers;
        
          event Staked(address indexed user, uint256 amount, uint256 sharesIssued, uint256 oldShareVaule, uint256 newShareValue, uint256 balanceOf);
          event StakingDenied(address indexed user, uint256 amount);
          event Withdrawn(address indexed user, uint256 total);
          event SmartContractDenied(address indexed greylistedAddress);
          event ForceGreylistExited(address indexed grelisted , uint256 amount);
        
          event SmartContractRecorded(address indexed smartContractAddress, address indexed smartContractInitiator);
        
          constructor(address _storage, address pool, address token, address _greylistEscrow) public
          Controllable(_storage)
          {
            rewardPool = NoMintRewardPool(pool);
            lpToken = IERC20(token);
            greylistEscrow = _greylistEscrow;
          }
        
          function setGreylistEscrow(address _greylistEscrow) external onlyGovernance {
            require(_greylistEscrow == address(0), "escrow cannot be empty address");
            greylistEscrow = _greylistEscrow;
          }
        
          function refreshAutoStake() external {
            exitRewardPool();
            updateValuePerShare();
            restakeIntoRewardPool();
          }
        
          function stake(uint256 amount) public {
            exitRewardPool();
            updateValuePerShare();
        
            if(tx.origin != msg.sender) {
              smartContractStakers[msg.sender] = true;
              emit SmartContractRecorded(msg.sender, tx.origin);
            }
        
            if(isGreylisted(msg.sender)){
              emit StakingDenied(msg.sender, amount);
            } else {
              // now we can issue shares
              lpToken.safeTransferFrom(msg.sender, address(this), amount);
              uint256 sharesToIssue = amount.mul(unit).div(valuePerShare);
              totalShares = totalShares.add(sharesToIssue);
              share[msg.sender] = share[msg.sender].add(sharesToIssue);
        
              uint256 oldValuePerShare = valuePerShare;
        
              // Rate needs to be updated here, otherwise the valuePerShare would be incorrect.
              updateValuePerShare();
        
              emit Staked(msg.sender, amount, sharesToIssue, oldValuePerShare, valuePerShare, balanceOf(msg.sender));
            }
        
            restakeIntoRewardPool();
          }
        
          function exit() public {
            exitRewardPool();
            updateValuePerShare();
        
            // If it is a normal user and not smart contract,
            // then the requirement will always pass
            // If it is a smart contract, then
            // make sure that it is not on our greyList.
            if (isGreylisted(msg.sender)) {
              // only Smart contracts can be denied
              emit SmartContractDenied(msg.sender);
            } else {
              // now we can transfer funds and burn shares
              uint256 toTransfer = balanceOf(msg.sender);
              lpToken.safeTransfer(msg.sender, toTransfer);
              totalShares = totalShares.sub(share[msg.sender]);
              share[msg.sender] = 0;
              emit Withdrawn(msg.sender, toTransfer);
              // Rate needs to be updated here, otherwise the valuePerShare would be incorrect.
              updateValuePerShare();
            }
        
            restakeIntoRewardPool();
          }
        
          function forceGreyListedExit(address greyListed) external onlyGovernance {
            require(isGreylisted(greyListed), "can only force exit a greylisted.");
            exitRewardPool();
            updateValuePerShare();
        
            uint256 toTransfer = balanceOf(greyListed);
            lpToken.safeTransfer(greylistEscrow, toTransfer);
            totalShares = totalShares.sub(share[greyListed]);
            share[greyListed] = 0;
            emit ForceGreylistExited(greyListed, toTransfer);
        
            updateValuePerShare();
            restakeIntoRewardPool();
          }
        
          function balanceOf(address who) public view returns(uint256) {
            return valuePerShare.mul(share[who]).div(unit);
          }
        
          function updateValuePerShare() internal {
            if (totalShares == 0) {
              totalValue = 0;
              valuePerShare = unit;
            } else {
              totalValue = lpToken.balanceOf(address(this));
              valuePerShare = totalValue.mul(unit).div(totalShares);
            }
          }
        
          function exitRewardPool() internal {
            if(rewardPool.balanceOf(address(this)) != 0){
              // exit and do accounting first
              rewardPool.exit();
            }
          }
        
          function restakeIntoRewardPool() internal {
            if(lpToken.balanceOf(address(this)) != 0){
              // stake back to the pool
              lpToken.safeApprove(address(rewardPool), 0);
              lpToken.safeApprove(address(rewardPool), lpToken.balanceOf(address(this)));
              rewardPool.stake(lpToken.balanceOf(address(this)));
            }
          }
        
          function isGreylisted(address _target) internal view returns (bool) {
            return (smartContractStakers[_target] && IController(controller()).greyList(_target));
          }
        }

        File 2 of 3: ExclusiveRewardPool
        // 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/Governable.sol
        
        pragma solidity 0.5.16;
        
        
        contract Governable {
        
          Storage public store;
        
          constructor(address _store) public {
            require(_store != address(0), "new storage shouldn't be empty");
            store = Storage(_store);
          }
        
          modifier onlyGovernance() {
            require(store.isGovernance(msg.sender), "Not governance");
            _;
          }
        
          function setStorage(address _store) public onlyGovernance {
            require(_store != address(0), "new storage shouldn't be empty");
            store = Storage(_store);
          }
        
          function governance() public view returns (address) {
            return store.governance();
          }
        }
        
        // File: contracts/Controllable.sol
        
        pragma solidity 0.5.16;
        
        
        contract Controllable is Governable {
        
          constructor(address _storage) Governable(_storage) public {
          }
        
          modifier onlyController() {
            require(store.isController(msg.sender), "Not a controller");
            _;
          }
        
          modifier onlyControllerOrGovernance(){
            require((store.isController(msg.sender) || store.isGovernance(msg.sender)),
              "The caller must be controller or governance");
            _;
          }
        
          function controller() public view returns (address) {
            return store.controller();
          }
        }
        
        // 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/ExclusiveRewardPool.sol
        
        // https://etherscan.io/address/0xDCB6A51eA3CA5d3Fd898Fd6564757c7aAeC3ca92#code
        
        /**
         *Submitted for verification at Etherscan.io on 2020-04-22
        */
        
        /*
           ____            __   __        __   _
          / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
         _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ /
        /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\
             /___/
        
        * Synthetix: CurveRewards.sol
        *
        * Docs: https://docs.synthetix.io/
        *
        *
        * MIT License
        * ===========
        *
        * Copyright (c) 2020 Synthetix
        *
        * Permission is hereby granted, free of charge, to any person obtaining a copy
        * of this software and associated documentation files (the "Software"), to deal
        * in the Software without restriction, including without limitation the rights
        * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        * copies of the Software, and to permit persons to whom the Software is
        * furnished to do so, subject to the following conditions:
        *
        * The above copyright notice and this permission notice shall be included in all
        * copies or substantial portions of the Software.
        *
        * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        */
        
        // File: @openzeppelin/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/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/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 {
            // 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/ownership/Ownable.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        contract Ownable is Context {
            address private _owner;
        
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor () internal {
                _owner = _msgSender();
                emit OwnershipTransferred(address(0), _owner);
            }
        
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view returns (address) {
                return _owner;
            }
        
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                require(isOwner(), "Ownable: caller is not the owner");
                _;
            }
        
            /**
             * @dev Returns true if the caller is the current owner.
             */
            function isOwner() public view returns (bool) {
                return _msgSender() == _owner;
            }
        
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing any functionality that is only available to the owner.
             */
            function renounceOwnership() public onlyOwner {
                emit OwnershipTransferred(_owner, address(0));
                _owner = address(0);
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public onlyOwner {
                _transferOwnership(newOwner);
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             */
            function _transferOwnership(address newOwner) internal {
                require(newOwner != address(0), "Ownable: new owner is the zero address");
                emit OwnershipTransferred(_owner, newOwner);
                _owner = newOwner;
            }
        }
        
        // File: @openzeppelin/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/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.
             *
             * 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.
             *
             * 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.
             */
            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.
        
                // 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 != 0x0 && codehash != accountHash);
            }
        
            /**
             * @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/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: contracts/IRewardDistributionRecipient.sol
        
        pragma solidity ^0.5.0;
        
        
        
        contract IRewardDistributionRecipient is Ownable {
            address rewardDistribution;
        
            constructor(address _rewardDistribution) public {
                rewardDistribution = _rewardDistribution;
            }
        
            function notifyRewardAmount(uint256 reward) external;
        
            modifier onlyRewardDistribution() {
                require(_msgSender() == rewardDistribution, "Caller is not reward distribution");
                _;
            }
        
            function setRewardDistribution(address _rewardDistribution)
                external
                onlyOwner
            {
                rewardDistribution = _rewardDistribution;
            }
        }
        
        // File: contracts/CurveRewards.sol
        
        pragma solidity ^0.5.0;
        
        
        
        
        /*
        *   Changes made to the SynthetixReward contract
        *
        *   uni to lpToken, and make it as a parameter of the constructor instead of hardcoded.
        *
        *
        */
        
        contract LPTokenWrapper {
            using SafeMath for uint256;
            using SafeERC20 for IERC20;
        
            IERC20 public lpToken;
        
            uint256 private _totalSupply;
            mapping(address => uint256) private _balances;
        
            function totalSupply() public view returns (uint256) {
                return _totalSupply;
            }
        
            function balanceOf(address account) public view returns (uint256) {
                return _balances[account];
            }
        
            function stake(uint256 amount) public {
                _totalSupply = _totalSupply.add(amount);
                _balances[msg.sender] = _balances[msg.sender].add(amount);
                lpToken.safeTransferFrom(msg.sender, address(this), amount);
            }
        
            function withdraw(uint256 amount) public {
                _totalSupply = _totalSupply.sub(amount);
                _balances[msg.sender] = _balances[msg.sender].sub(amount);
                lpToken.safeTransfer(msg.sender, amount);
            }
        }
        
        /*
        *   [Hardwork]
        *   This pool doesn't mint.
        *   the rewards should be first transferred to this pool, then get "notified"
        *   by calling `notifyRewardAmount`
        */
        
        contract ExclusiveRewardPool is LPTokenWrapper, IRewardDistributionRecipient, Controllable {
        
            using Address for address;
        
            IERC20 public rewardToken;
            uint256 public duration; // making it not a constant is less gas efficient, but portable
        
            uint256 public periodFinish = 0;
            uint256 public rewardRate = 0;
            uint256 public lastUpdateTime;
            uint256 public rewardPerTokenStored;
            mapping(address => uint256) public userRewardPerTokenPaid;
            mapping(address => uint256) public rewards;
        
            address public exclusiveAddress; // this would be the address of the AutoStake contract
        
            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);
            event RewardDenied(address indexed user, uint256 reward);
        
            modifier updateReward(address account) {
                rewardPerTokenStored = rewardPerToken();
                lastUpdateTime = lastTimeRewardApplicable();
                if (account != address(0)) {
                    rewards[account] = earned(account);
                    userRewardPerTokenPaid[account] = rewardPerTokenStored;
                }
                _;
            }
        
            // [Hardwork] setting the reward, lpToken, duration, and rewardDistribution for each pool
            constructor(address _rewardToken,
                address _lpToken,
                uint256 _duration,
                address _rewardDistribution,
                address _storage) public
            IRewardDistributionRecipient(_rewardDistribution)
            Controllable(_storage) // only used for referencing the grey list
            {
                rewardToken = IERC20(_rewardToken);
                lpToken = IERC20(_lpToken);
                duration = _duration;
            }
        
            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
                    balanceOf(account)
                        .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
                        .div(1e18)
                        .add(rewards[account]);
            }
        
            // only owner can initialize the exclusive address in the beginning.
            // the steps to link this to Autostake are:
            // (1) Deploy this reward pool
            // (2) Deploy Autostake contract and linking this pool in its constructor
            // (3) Invoke initExclusive in this reward pool to the Autostake contract
            function initExclusive(address _exclusive) onlyOwner external {
              require(exclusiveAddress == address(0), "exclusiveAddress has already been set");
              exclusiveAddress = _exclusive;
            }
        
            // stake visibility is public as overriding LPTokenWrapper's stake() function
            function stake(uint256 amount) public updateReward(msg.sender) {
                require(msg.sender == exclusiveAddress, "Must be the exclusiveAddress to stake");
                require(amount > 0, "Cannot stake 0");
                super.stake(amount);
                emit Staked(msg.sender, amount);
            }
        
            function withdraw(uint256 amount) public updateReward(msg.sender) {
                require(amount > 0, "Cannot withdraw 0");
                super.withdraw(amount);
                emit Withdrawn(msg.sender, amount);
            }
        
            function exit() external {
                withdraw(balanceOf(msg.sender));
                getReward();
            }
        
            /// A push mechanism for accounts that have not claimed their rewards for a long time.
            /// The implementation is semantically analogous to getReward(), but uses a push pattern
            /// instead of pull pattern.
            function pushReward(address recipient) public updateReward(recipient) onlyGovernance {
                uint256 reward = earned(recipient);
                if (reward > 0) {
                    rewards[recipient] = 0;
                    rewardToken.safeTransfer(recipient, reward);
                    emit RewardPaid(recipient, reward);
                }
            }
        
            function getReward() public updateReward(msg.sender) {
                uint256 reward = earned(msg.sender);
                if (reward > 0) {
                    rewards[msg.sender] = 0;
                    rewardToken.safeTransfer(msg.sender, reward);
                    emit RewardPaid(msg.sender, reward);
                }
            }
        
            function notifyRewardAmount(uint256 reward)
                external
                onlyRewardDistribution
                updateReward(address(0))
            {
                // overflow fix according to https://sips.synthetix.io/sips/sip-77
                require(reward < uint(-1) / 1e18, "the notified reward cannot invoke multiplication overflow");
                
                if (block.timestamp >= periodFinish) {
                    rewardRate = reward.div(duration);
                } else {
                    uint256 remaining = periodFinish.sub(block.timestamp);
                    uint256 leftover = remaining.mul(rewardRate);
                    rewardRate = reward.add(leftover).div(duration);
                }
                lastUpdateTime = block.timestamp;
                periodFinish = block.timestamp.add(duration);
                emit RewardAdded(reward);
            }
        }

        File 3 of 3: RewardToken
        // File: @openzeppelin/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/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 {
            // 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/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/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 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"));
            }
        }
        
        // File: @openzeppelin/contracts/access/Roles.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * @title Roles
         * @dev Library for managing addresses assigned to a Role.
         */
        library Roles {
            struct Role {
                mapping (address => bool) bearer;
            }
        
            /**
             * @dev Give an account access to this role.
             */
            function add(Role storage role, address account) internal {
                require(!has(role, account), "Roles: account already has role");
                role.bearer[account] = true;
            }
        
            /**
             * @dev Remove an account's access to this role.
             */
            function remove(Role storage role, address account) internal {
                require(has(role, account), "Roles: account does not have role");
                role.bearer[account] = false;
            }
        
            /**
             * @dev Check if an account has this role.
             * @return bool
             */
            function has(Role storage role, address account) internal view returns (bool) {
                require(account != address(0), "Roles: account is the zero address");
                return role.bearer[account];
            }
        }
        
        // File: @openzeppelin/contracts/access/roles/MinterRole.sol
        
        pragma solidity ^0.5.0;
        
        
        
        contract MinterRole is Context {
            using Roles for Roles.Role;
        
            event MinterAdded(address indexed account);
            event MinterRemoved(address indexed account);
        
            Roles.Role private _minters;
        
            constructor () internal {
                _addMinter(_msgSender());
            }
        
            modifier onlyMinter() {
                require(isMinter(_msgSender()), "MinterRole: caller does not have the Minter role");
                _;
            }
        
            function isMinter(address account) public view returns (bool) {
                return _minters.has(account);
            }
        
            function addMinter(address account) public onlyMinter {
                _addMinter(account);
            }
        
            function renounceMinter() public {
                _removeMinter(_msgSender());
            }
        
            function _addMinter(address account) internal {
                _minters.add(account);
                emit MinterAdded(account);
            }
        
            function _removeMinter(address account) internal {
                _minters.remove(account);
                emit MinterRemoved(account);
            }
        }
        
        // File: @openzeppelin/contracts/token/ERC20/ERC20Mintable.sol
        
        pragma solidity ^0.5.0;
        
        
        
        /**
         * @dev Extension of {ERC20} that adds a set of accounts with the {MinterRole},
         * which have permission to mint (create) new tokens as they see fit.
         *
         * At construction, the deployer of the contract is the only minter.
         */
        contract ERC20Mintable is ERC20, MinterRole {
            /**
             * @dev See {ERC20-_mint}.
             *
             * Requirements:
             *
             * - the caller must have the {MinterRole}.
             */
            function mint(address account, uint256 amount) public onlyMinter returns (bool) {
                _mint(account, amount);
                return true;
            }
        }
        
        // File: @openzeppelin/contracts/token/ERC20/ERC20Capped.sol
        
        pragma solidity ^0.5.0;
        
        
        /**
         * @dev Extension of {ERC20Mintable} that adds a cap to the supply of tokens.
         */
        contract ERC20Capped is ERC20Mintable {
            uint256 private _cap;
        
            /**
             * @dev Sets the value of the `cap`. This value is immutable, it can only be
             * set once during construction.
             */
            constructor (uint256 cap) public {
                require(cap > 0, "ERC20Capped: cap is 0");
                _cap = cap;
            }
        
            /**
             * @dev Returns the cap on the token's total supply.
             */
            function cap() public view returns (uint256) {
                return _cap;
            }
        
            /**
             * @dev See {ERC20Mintable-mint}.
             *
             * Requirements:
             *
             * - `value` must not cause the total supply to go over the cap.
             */
            function _mint(address account, uint256 value) internal {
                require(totalSupply().add(value) <= _cap, "ERC20Capped: cap exceeded");
                super._mint(account, value);
            }
        }
        
        // File: @openzeppelin/contracts/token/ERC20/ERC20Detailed.sol
        
        pragma solidity ^0.5.0;
        
        
        /**
         * @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: 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;
            }
        }
        
        // File: @openzeppelin/contracts/ownership/Ownable.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        contract Ownable is Context {
            address private _owner;
        
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            constructor () internal {
                address msgSender = _msgSender();
                _owner = msgSender;
                emit OwnershipTransferred(address(0), msgSender);
            }
        
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view returns (address) {
                return _owner;
            }
        
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                require(isOwner(), "Ownable: caller is not the owner");
                _;
            }
        
            /**
             * @dev Returns true if the caller is the current owner.
             */
            function isOwner() public view returns (bool) {
                return _msgSender() == _owner;
            }
        
            /**
             * @dev Leaves the contract without owner. It will not be possible to call
             * `onlyOwner` functions anymore. Can only be called by the current owner.
             *
             * NOTE: Renouncing ownership will leave the contract without an owner,
             * thereby removing any functionality that is only available to the owner.
             */
            function renounceOwnership() public onlyOwner {
                emit OwnershipTransferred(_owner, address(0));
                _owner = address(0);
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             * Can only be called by the current owner.
             */
            function transferOwnership(address newOwner) public onlyOwner {
                _transferOwnership(newOwner);
            }
        
            /**
             * @dev Transfers ownership of the contract to a new account (`newOwner`).
             */
            function _transferOwnership(address newOwner) internal {
                require(newOwner != address(0), "Ownable: new owner is the zero address");
                emit OwnershipTransferred(_owner, newOwner);
                _owner = newOwner;
            }
        }
        
        // 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/Governable.sol
        
        pragma solidity 0.5.16;
        
        
        contract Governable {
        
          Storage public store;
        
          constructor(address _store) public {
            require(_store != address(0), "new storage shouldn't be empty");
            store = Storage(_store);
          }
        
          modifier onlyGovernance() {
            require(store.isGovernance(msg.sender), "Not governance");
            _;
          }
        
          function setStorage(address _store) public onlyGovernance {
            require(_store != address(0), "new storage shouldn't be empty");
            store = Storage(_store);
          }
        
          function governance() public view returns (address) {
            return store.governance();
          }
        }
        
        // File: contracts/RewardToken.sol
        
        pragma solidity 0.5.16;
        
        
        
        
        
        
        
        contract RewardToken is ERC20, ERC20Detailed, ERC20Capped, Governable {
        
          uint256 public constant HARD_CAP = 5 * (10 ** 6) * (10 ** 18);
        
          constructor(address _storage) public
          ERC20Detailed("FARM Reward Token", "FARM", 18)
          ERC20Capped(HARD_CAP)
          Governable(_storage) {
            // msg.sender should not be a minter
            renounceMinter();
            // governance will become the only minter
            _addMinter(governance());
          }
        
          /**
          * Overrides adding new minters so that only governance can authorized them.
          */
          function addMinter(address _minter) public onlyGovernance {
            super.addMinter(_minter);
          }
        }