// File: src/LiquidationsGraceSentinel.sol



// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;

import {ILiquidationsGraceSentinel} from './ILiquidationsGraceSentinel.sol';
import {Ownable} from './Ownable.sol';

/// @title LiquidationsGraceSentinel
/// @author BGD Labs
/// @notice Registry to allow a temporary stop liquidations on Aave, for users
///   to have enough time to protect their positions, after a pause
/// - Mirroring the pause limitations: if the asset paused is debt or collateral involved
///   in a liquidation, the liquidation is not possible
/// - Being an emergency mechanism, it is designed to be controlled by an entity like the Aave Guardian
contract LiquidationsGraceSentinel is Ownable, ILiquidationsGraceSentinel {
  mapping(address => uint40) public override gracePeriodUntil;

  /// @notice Function to set grace period to one or multiple Aave underlyings
  /// @dev To enable a grace period, a timestamp in the future should be set,
  ///      To disable a grace period, any timestamp in the past works, like 0
  /// @param assets Address of the underlying asset listed on Aave
  /// @param until Timestamp when the liquidations' grace period will end
  function setGracePeriods(
    address[] calldata assets,
    uint40[] calldata until
  ) external override onlyOwner {
    require(assets.length == until.length, 'INCONSISTENT_PARAMS_LENGTH');
    for (uint256 i = 0; i < assets.length; i++) {
      gracePeriodUntil[assets[i]] = until[i];
      emit GracePeriodSet(assets[i], until[i]);
    }
  }
}

// File: src/ILiquidationsGraceSentinel.sol



// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;

interface ILiquidationsGraceSentinel {
  /**
   * @dev Emitted when a new grace period is set
   * @param asset Address of the underlying asset listed on Aave
   * @param until Timestamp until the grace period will be activated
   **/
  event GracePeriodSet(address indexed asset, uint40 until);

  /**
   * @dev Returns until when a grace period is enabled
   * @param asset Address of the underlying asset listed on Aave
   **/
  function gracePeriodUntil(address asset) external view returns (uint40);

  /// @notice Function to set grace period to one or multiple Aave underlyings
  /// @dev To enable a grace period, a timestamp in the future should be set,
  ///      To disable a grace period, any timestamp in the past works, like 0
  /// @param assets Address of the underlying asset listed on Aave
  /// @param until Timestamp when the liquidations' grace period will end
  function setGracePeriods(address[] calldata assets, uint40[] calldata until) external;
}

// File: src/Ownable.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import './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: src/Context.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

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