pragma solidity ^0.8.13;





interface IERC4626 is IERC20 {
    // The address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
    function asset() external view returns(address assetTokenAddress);

    // Total amount of the underlying asset that is “managed” by Vault.
    function totalAssets() external view returns(uint256 totalManagedAssets);

    // The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met.
    function convertToShares(uint256 assets) external view returns(uint256 shares); 

    // The amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met.
    function convertToAssets(uint256 shares) external view returns(uint256 assets);
 
    // Maximum amount of the underlying asset that can be deposited into the Vault for the receiver, through a deposit call.
    function maxDeposit(address receiver) external view returns(uint256 maxAssets);

    // Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
    function previewDeposit(uint256 assets) external view returns(uint256 shares);

    // Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
    function deposit(uint256 assets, address receiver) external returns(uint256 shares);

    // Maximum amount of shares that can be minted from the Vault for the receiver, through a mint call.
    function maxMint(address receiver) external view returns(uint256 maxShares); 

    // Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
    function previewMint(uint256 shares) external view returns(uint256 assets);

    // Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
    function mint(uint256 shares, address receiver) external returns(uint256 assets);

    // Maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call.
    function maxWithdraw(address owner) external view returns(uint256 maxAssets);

    // Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
    function previewWithdraw(uint256 assets) external view returns(uint256 shares);

    // Burns shares from owner and sends exactly assets of underlying tokens to receiver.
    function withdraw(uint256 assets, address receiver, address owner) external returns(uint256 shares);

    // Maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a redeem call.
    function maxRedeem(address owner) external view returns(uint256 maxShares);

    // Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
    function previewRedeem(uint256 shares) external view returns(uint256 assets);

    // Burns exactly shares from owner and sends assets of underlying tokens to receiver.
    function redeem(uint256 shares, address receiver, address owner) external returns(uint256 assets);

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
    event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares);
}

interface IVeVault is IERC4626 {
    function asset() external view  returns (address assetTokenAddress);
    function totalAssets() external view  returns (uint256 totalManagedAssets);
    function totalSupply() external view  returns (uint256);
    function balanceOf(address account) external view  returns (uint256);
    function convertToShares(uint256 assets, uint256 lockTime) external view returns (uint256 shares);
    function convertToShares(uint256 assets)  external view returns (uint256 shares);
    function convertToAssets(uint256 shares, uint256 lockTime) external view returns (uint256 assets);
    function convertToAssets(uint256 shares)  external view returns (uint256 assets);
    function maxDeposit(address)  external pure returns (uint256 maxAssets);
    function previewDeposit(uint256 assets, uint256 lockTime) external view returns (uint256 shares);
    function previewDeposit(uint256 assets)  external view returns (uint256 shares);
    function maxMint(address)  external pure returns (uint256 maxShares);
    function previewMint(uint256 shares, uint256 lockTime) external view returns (uint256 assets);
    function previewMint(uint256 shares)  external view returns (uint256 assets);
    function maxWithdraw(address owner)  external view returns (uint256 maxAssets);
    function previewWithdraw(uint256 assets, uint256 lockTime) external view returns (uint256 shares);
    function previewWithdraw(uint256 assets)  external view returns (uint256 shares);
    function maxRedeem(address owner)  external view returns (uint256 maxShares);
    function previewRedeem(uint256 shares, uint256 lockTime) external view returns (uint256 assets);
    function previewRedeem(uint256 shares)  external view returns (uint256 assets);
    function allowance(address, address)  external view returns (uint256);
    function assetBalanceOf(address account) external view returns (uint256);
    function unlockDate(address account) external view returns (uint256);
    function gracePeriod() external view returns (uint256);
    function penaltyPercentage() external view returns (uint256);
    function minLockTime() external view returns (uint256);
    function maxLockTime() external view returns (uint256);
    function transfer(address, uint256) external  returns (bool);
    function approve(address, uint256) external  returns (bool);
    function transferFrom(address, address, uint256) external  returns (bool);
    function veMult(address owner) external view returns (uint256);
    function deposit(uint256 assets, address receiver, uint256 lockTime) external returns (uint256 shares);
    function deposit(uint256 assets, address receiver)  external returns (uint256 shares);
    function mint(uint256 shares, address receiver, uint256 lockTime) external returns (uint256 assets);
    function mint(uint256 shares, address receiver)  external returns (uint256 assets);
    function withdraw(uint256 assets, address receiver, address owner)  external returns (uint256 shares);
    function redeem(uint256 shares, address receiver, address owner)  external returns (uint256 assets);
    function exit() external returns (uint256 shares);
    function changeUnlockRule(bool flag) external;
    function changeGracePeriod(uint256 newGracePeriod) external;
    function changeEpoch(uint256 newEpoch) external;
    function changeMinPenalty(uint256 newMinPenalty) external;
    function changeMaxPenalty(uint256 newMaxPenalty) external;
    function changeWhitelistRecoverERC20(address tokenAddress, bool flag) external;
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external;
    function recoverERC721(address tokenAddress, uint256 tokenId) external;

    event PayPenalty(address indexed caller, address indexed owner, uint256 assets);
    event Burn(address indexed user, uint256 shares);
    event Mint(address indexed user, uint256 shares);
    event Recovered(address token, uint256 amount);
    event RecoveredNFT(address tokenAddress, uint256 tokenId);
    event ChangeWhitelistERC20(address indexed tokenAddress, bool whitelistState);
}

// https://docs.synthetix.io/contracts/source/contracts/Owned
abstract contract Owned {
    address public owner;
    address public nominatedOwner;

    constructor(address _owner) {
        require(_owner != address(0), "Owner address cannot be 0");
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    modifier onlyOwner {
        _onlyOwner();
        _;
    }

    function _onlyOwner() private view {
        require(msg.sender == owner, "Only the contract owner may perform this action");
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}

// https://docs.synthetix.io/contracts/source/contracts/Pausable
abstract contract Pausable is Owned {
    uint public lastPauseTime;
    bool public paused;

    constructor() {
        // This contract is abstract, and thus cannot be instantiated directly
        require(owner != address(0), "Owner must be set");
        // Paused will be false, and lastPauseTime will be 0 upon initialisation
    }

    /**
     * @notice Change the paused state of the contract
     * @dev Only the contract owner may call this.
     */
    function setPaused(bool _paused) external onlyOwner {
        // Ensure we're actually changing the state before we do anything
        if (_paused == paused) {
            return;
        }

        // Set our paused state.
        paused = _paused;

        // If applicable, set the last pause time.
        if (paused) {
            lastPauseTime = block.timestamp;
        }

        // Let everyone know that our pause state has changed.
        emit PauseChanged(paused);
    }

    event PauseChanged(bool isPaused);

    modifier notPaused {
        require(!paused, "This action cannot be performed while the contract is paused");
        _;
    }
}

// https://docs.synthetix.io/contracts/source/contracts/RewardsDistributionRecipient
abstract contract RewardsDistributionRecipient is Owned {
    address public rewardsDistribution;

    function notifyRewardAmount(uint256 reward) virtual external;

    modifier onlyRewardsDistribution() {
        require(msg.sender == rewardsDistribution, "Caller is not RewardsDistribution contract");
        _;
    }

    function setRewardsDistribution(address _rewardsDistribution) external onlyOwner {
        rewardsDistribution = _rewardsDistribution;
    }
}

error RewardTooHigh();
error RewardPeriodNotComplete(uint256 finish);
error NotWhitelisted();
error InsufficientBalance(uint256 available, uint256 required);

/** 
 * @title Implements a reward system which grant rewards based on veToken balance 
 * @author gcontarini jocorrei
 * @notice This implementation was inspired by the StakingReward contract from Synthetixio
 * @dev Implement a new constructor to deploy this contract 
 */
contract Rewards is RewardsDistributionRecipient, ReentrancyGuard, Pausable {
    using SafeERC20 for IERC20;

    struct Account {
        uint256 rewardPerTokenPaid;
        uint256 rewards;
        uint256 dueDate;
    }

    /* ========== STATE VARIABLES ========== */

    address public rewardsToken;
    address public vault;                    // address of the ve vault
    uint256 public periodFinish = 0;         // end of the rewardDuration period
    uint256 public rewardRate = 0;           // rewards per second distributed by the contract ==> rewardavailable / rewardDuration
    uint256 public rewardsDuration = 7 days; // the rewards inside the contract are gone be distributed during this period
    uint256 public lastUpdateTime;           // when the reward period started
    uint256 public rewardPerTokenStored;     // amounts of reward per staked token

    mapping(address => Account) public accounts;
    
    // Only allow recoverERC20 from this list
    mapping(address => bool) public whitelistRecoverERC20;

    /* ========== CONSTRUCTOR ========== */

    constructor(
        address _owner,
        address _vault,
        address _rewardsDistribution,
        address _rewardsToken
    ) Owned(_owner) {
        rewardsToken = _rewardsToken;
        rewardsDistribution = _rewardsDistribution;
        vault = _vault;
        lastUpdateTime = block.timestamp;
    }

    /* ========== VIEWS ========== */

    /**
     * @notice Get the vault address
     */
    function getVaultAddress() public view returns (address) {
        return vault;
    }

    /**
     * @notice Pick the correct date for applying the reward
     * Apply until the end of periodFinish or until
     * unlockDate for funds in the veVault
     * @return date which the reward is applicable for and address
     */
    function lastTimeRewardApplicable(address owner) public view returns (uint256) {
        if (owner != address(0) && accounts[owner].dueDate < periodFinish) {
            return block.timestamp < accounts[owner].dueDate ? block.timestamp : accounts[owner].dueDate;
        }
        return block.timestamp < periodFinish ? block.timestamp : periodFinish;
    }

    /**
     * @notice Calculate how much reward must be given for an user
     * per token in veVault. 
     * @dev If dueDate is less than the period finish,
     * a "negative" reward is applied to ensure that
     * rewards are applied only until this date.
     * @return amount of reward per token an addres is elegible to receive so far
     */
    function rewardPerToken(address owner) public view returns (uint256) {
        uint256 _totalSupply = IVeVault(vault).totalSupply();

        if (_totalSupply == 0) {
            return rewardPerTokenStored;
        }
        uint256 userLastTime = lastTimeRewardApplicable(owner);
        
        // Apply a negative reward per token when
        // due date is already over.
        if (userLastTime < lastUpdateTime) {
            return rewardPerTokenStored
                - ((lastUpdateTime - userLastTime)
                    * rewardRate
                    * 1e18
                    / _totalSupply
                );
        }
        return rewardPerTokenStored
                + ((userLastTime - lastUpdateTime)
                    * rewardRate
                    * 1e18
                    / _totalSupply
                );
    }
    
    /**
     * @notice Calculates how much rewards a staker earned 
     * until this moment.
     * @dev Only apply reward until period finish or unlock date.
     * @return amount of reward available to claim 
     */
    function earned(address owner) public view returns (uint256) {
        uint256 currentReward = rewardPerToken(owner);
        uint256 paidReward = accounts[owner].rewardPerTokenPaid;

        uint256 moreReward = 0;
        if (currentReward > paidReward) {
            moreReward = IVeVault(vault).balanceOf(owner)
                            * (currentReward - paidReward)
                            / 1e18;
        }
        return accounts[owner].rewards + moreReward;
    }

    /**
     * @notice Total rewards that will be paid during the distribution
     */
    function getRewardForDuration() external view returns (uint256) {
        return rewardRate * rewardsDuration;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    /**
     * @notice Notify the reward contract about a deposit in the
     * veVault contract. This is important to assure the
     * contract will account user's rewards.
     * @return account full information
     */
    function notifyDeposit() public updateReward(msg.sender) returns(Account memory) {
        emit NotifyDeposit(msg.sender, accounts[owner].rewardPerTokenPaid, accounts[owner].dueDate);
        return accounts[owner];
    }

    /**
     * @notice Claim rewards for user.
     * @dev In case of no rewards claimable
     * just update the user status and do nothing.
     */
    function getReward() public updateReward(msg.sender) {
        uint256 reward = accounts[msg.sender].rewards;
        if (reward <= 0) return;
        
        accounts[msg.sender].rewards = 0;
        IERC20(rewardsToken).safeTransfer(msg.sender, reward);
        emit RewardPaid(msg.sender, reward);
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    /**
     * @notice Set the contract to start distribuiting rewards
     * for ve holders.
     * @param reward: amount of tokens to be distributed
     */
    function notifyRewardAmount(uint256 reward)
            external
            override 
            onlyRewardsDistribution 
            updateReward(address(0)) {
        if (block.timestamp >= periodFinish) {
            rewardRate = reward / rewardsDuration;
        } else {
            uint256 remaining = periodFinish - block.timestamp;
            uint256 leftover = remaining * rewardRate;
            rewardRate = (reward + leftover) / rewardsDuration;
        }

        // Ensure the provided reward amount is not more than the balance in the contract.
        // This keeps the reward rate in the right range, preventing overflows due to
        // very high values of rewardRate in the earned and rewardsPerToken functions;
        // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
        uint balance = IERC20(rewardsToken).balanceOf(address(this));
        if (rewardRate > balance / rewardsDuration) revert RewardTooHigh();

        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp + rewardsDuration;
        emit RewardAdded(reward);
    }
    
    /**
     * @notice Allow owner to change reward duration
     * Only allow the change if period finish has already ended
     */
    function setRewardsDuration(uint256 _rewardsDuration) external onlyOwner {
        if (block.timestamp <= periodFinish) revert RewardPeriodNotComplete(periodFinish);

        rewardsDuration = _rewardsDuration;
        emit RewardsDurationUpdated(rewardsDuration);
    }

    /**
     * @notice Added to support to recover ERC20 token within a whitelist 
     */
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner {
        if (whitelistRecoverERC20[tokenAddress] == false) revert NotWhitelisted();
        
        uint balance = IERC20(tokenAddress).balanceOf(address(this));
        if (balance < tokenAmount) revert InsufficientBalance({
                available: balance,
                required: tokenAmount
        });
        
        IERC20(tokenAddress).safeTransfer(owner, tokenAmount);
        emit Recovered(tokenAddress, tokenAmount);
    }

    /**
     * @dev It's possible to owner whitelist the underlying token
     * and do some kind of rugpull. To prevent that, it'recommended
     * that owner is a multisig address. Also, it emits an event
     * of changes in the ERC20 whitelist as a safety check.
     * @notice Owner can whitelist an ERC20 to recover it afterwards.
     * Emits and event to notify all users about it 
     * @param flag: true to allow recover for the token
     */
    function changeWhitelistRecoverERC20(address tokenAddress, bool flag) external onlyOwner {
        whitelistRecoverERC20[tokenAddress] = flag;
        emit ChangeWhitelistERC20(tokenAddress, flag);
    }

    /**
     * @notice Added to support to recover ERC721 
     */
    function recoverERC721(address tokenAddress, uint256 tokenId) external onlyOwner {
        IERC721(tokenAddress).safeTransferFrom(address(this), owner, tokenId);
        emit RecoveredNFT(tokenAddress, tokenId);
    }

    /* ========== MODIFIERS ========== */

    /**
     * @dev Update user rewards accordlingly to
     * the current timestamp.
     */
    modifier updateReward(address owner) {
        rewardPerTokenStored = rewardPerToken(address(0));
        lastUpdateTime = lastTimeRewardApplicable(address(0));

        if (owner != address(0)) {
            if (accounts[owner].rewardPerTokenPaid == 0)
                accounts[owner].rewardPerTokenPaid = rewardPerTokenStored;
            accounts[owner].dueDate = IVeVault(vault).unlockDate(owner);
            accounts[owner].rewards = earned(owner);
            accounts[owner].rewardPerTokenPaid = rewardPerToken(address(0));
        }
        _;
    }

    /* ========== EVENTS ========== */

    event RewardAdded(uint256 reward);
    event RewardPaid(address indexed user, uint256 reward);
    event RewardsDurationUpdated(uint256 newDuration);
    event NotifyDeposit(address indexed user, uint256 rewardPerTokenPaid, uint256 dueDate);
    event Recovered(address token, uint256 amount);
    event RecoveredNFT(address tokenAddress, uint256 tokenId);
    event ChangeWhitelistERC20(address indexed tokenAddress, bool whitelistState);
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_rewardsDistribution","type":"address"},{"internalType":"address","name":"_rewardsToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"NotWhitelisted","type":"error"},{"inputs":[{"internalType":"uint256","name":"finish","type":"uint256"}],"name":"RewardPeriodNotComplete","type":"error"},{"inputs":[],"name":"RewardTooHigh","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"whitelistState","type":"bool"}],"name":"ChangeWhitelistERC20","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardPerTokenPaid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dueDate","type":"uint256"}],"name":"NotifyDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"RecoveredNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"RewardsDurationUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accounts","outputs":[{"internalType":"uint256","name":"rewardPerTokenPaid","type":"uint256"},{"internalType":"uint256","name":"rewards","type":"uint256"},{"internalType":"uint256","name":"dueDate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"bool","name":"flag","type":"bool"}],"name":"changeWhitelistRecoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPauseTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"notifyDeposit","outputs":[{"components":[{"internalType":"uint256","name":"rewardPerTokenPaid","type":"uint256"},{"internalType":"uint256","name":"rewards","type":"uint256"},{"internalType":"uint256","name":"dueDate","type":"uint256"}],"internalType":"struct Rewards.Account","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"recoverERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDistribution","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsDistribution","type":"address"}],"name":"setRewardsDistribution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistRecoverERC20","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]