// File: /Users/akshaycm/Documents/relay3r.network/contracts/interfaces/Keep3r/IKeep3rV1Mini.sol



//SPDX-License-Identifier: MIT

pragma solidity ^0.6.12;
interface IKeep3rV1Mini {
    function isKeeper(address) external returns (bool);
    function worked(address keeper) external;
    function totalBonded() external view returns (uint);
    function bonds(address keeper, address credit) external view returns (uint);
    function votes(address keeper) external view returns (uint);
    function isMinKeeper(address keeper, uint minBond, uint earned, uint age) external returns (bool);
    function addCreditETH(address job) external payable;
    function credits(address job, address credit) external view returns (uint);
    function receipt(address credit, address keeper, uint amount) external;
    function ETH() external view returns (address);
    function receiptETH(address keeper, uint amount) external;
}
// File: /Users/akshaycm/Documents/relay3r.network/contracts/jobs/LidoRelayer.sol



//SPDX-License-Identifier: MIT
// Based on https://github.com/banteg/lido-keep3r/blob/master/contracts/LidoJob.vy, rewritten in solidity
pragma solidity >=0.6.12;
import '@openzeppelin/contracts/access/Ownable.sol';
import "@openzeppelin/contracts/math/Math.sol";
import "../interfaces/Keep3r/IKeep3rV1Mini.sol";
interface ILido {
    function getBufferedEther() external view returns (uint256);
    function depositBufferedEther(uint256 max_deposits) external returns (uint256);
    function isStopped() external view returns (bool);
}

contract LidoRelayer is Ownable {
    IKeep3rV1Mini RLR = IKeep3rV1Mini(0x5b3F693EfD5710106eb2Eac839368364aCB5a70f);
    ILido Lido = ILido(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);

    uint256 public constant DEPOSIT_SIZE = 32 * 1e18;
    uint256 public constant MIN_DEPOSITS = 16;
    uint256 public constant MAX_DEPOSITS = 60;
    uint256 public paused_until = 0;

    modifier upkeep() {
        require(RLR.isKeeper(msg.sender), "!relayer");
        _;
        RLR.worked(msg.sender);
    }

    function available_deposits() internal view returns (uint256) {
        if(Lido.isStopped()) return 0;
        if (paused_until > block.timestamp) return 0;
        return Math.min(Lido.getBufferedEther() / DEPOSIT_SIZE, MAX_DEPOSITS);
    }

    function workable() external view returns (bool) {
        return available_deposits() >= MIN_DEPOSITS;
    }

    function work() public upkeep {
        uint256 deposits = available_deposits();
        require (deposits >= MIN_DEPOSITS,"!workable");
        uint256 buffered = Lido.getBufferedEther();
        Lido.depositBufferedEther(deposits);
        // pause for a day if there is a key shortage
        uint256 deposited = buffered - Lido.getBufferedEther();
        if (deposited < deposits * DEPOSIT_SIZE)
            paused_until = block.timestamp + 86400;
    }

    //Use this to depricate this job to move rlr to another job later
    function destructJob() public onlyOwner {
        //Get the credits for this job first
        uint256 currRLRCreds = RLR.credits(address(this),address(RLR));
        //Send out RLR Credits if any
        if(currRLRCreds > 0) {
            //Invoke receipt to send all the credits of job to owner
            RLR.receipt(address(RLR),owner(),currRLRCreds);
        }
        //Finally self destruct the contract after sending the credits
        selfdestruct(payable(owner()));
    }
}

// File: @openzeppelin/contracts/GSN/Context.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

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

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

// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "../GSN/Context.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
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(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

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

// File: @openzeppelin/contracts/math/Math.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.6.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);
    }
}