// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @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].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌
// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
//
// Trust math, not hardware.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.5;
import "./interfaces/IAssetPool.sol";
import "./interfaces/IAssetPoolUpgrade.sol";
import "./RewardsPool.sol";
import "./UnderwriterToken.sol";
import "./GovernanceUtils.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/// @title Asset Pool
/// @notice Asset pool is a component of a Coverage Pool. Asset Pool
/// accepts a single ERC20 token as collateral, and returns an
/// underwriter token. For example, an asset pool might accept deposits
/// in KEEP in return for covKEEP underwriter tokens. Underwriter tokens
/// represent an ownership share in the underlying collateral of the
/// Asset Pool.
contract AssetPool is Ownable, IAssetPool {
using SafeERC20 for IERC20;
using SafeERC20 for UnderwriterToken;
IERC20 public immutable collateralToken;
UnderwriterToken public immutable underwriterToken;
RewardsPool public immutable rewardsPool;
IAssetPoolUpgrade public newAssetPool;
/// @notice The time it takes the underwriter to withdraw their collateral
/// and rewards from the pool. This is the time that needs to pass
/// between initiating and completing the withdrawal. During that
/// time, underwriter is still earning rewards and their share of
/// the pool is still a subject of a possible coverage claim.
uint256 public withdrawalDelay = 21 days;
uint256 public newWithdrawalDelay;
uint256 public withdrawalDelayChangeInitiated;
/// @notice The time the underwriter has after the withdrawal delay passed
/// to complete the withdrawal. During that time, underwriter is
/// still earning rewards and their share of the pool is still
/// a subject of a possible coverage claim.
/// After the withdrawal timeout elapses, tokens stay in the pool
/// and the underwriter has to initiate the withdrawal again and
/// wait for the full withdrawal delay to complete the withdrawal.
uint256 public withdrawalTimeout = 2 days;
uint256 public newWithdrawalTimeout;
uint256 public withdrawalTimeoutChangeInitiated;
mapping(address => uint256) public withdrawalInitiatedTimestamp;
mapping(address => uint256) public pendingWithdrawal;
event Deposited(
address indexed underwriter,
uint256 amount,
uint256 covAmount
);
event CoverageClaimed(
address indexed recipient,
uint256 amount,
uint256 timestamp
);
event WithdrawalInitiated(
address indexed underwriter,
uint256 covAmount,
uint256 timestamp
);
event WithdrawalCompleted(
address indexed underwriter,
uint256 amount,
uint256 covAmount,
uint256 timestamp
);
event ApprovedAssetPoolUpgrade(address newAssetPool);
event CancelledAssetPoolUpgrade(address cancelledAssetPool);
event AssetPoolUpgraded(
address indexed underwriter,
uint256 collateralAmount,
uint256 covAmount,
uint256 timestamp
);
event WithdrawalDelayUpdateStarted(
uint256 withdrawalDelay,
uint256 timestamp
);
event WithdrawalDelayUpdated(uint256 withdrawalDelay);
event WithdrawalTimeoutUpdateStarted(
uint256 withdrawalTimeout,
uint256 timestamp
);
event WithdrawalTimeoutUpdated(uint256 withdrawalTimeout);
/// @notice Reverts if the withdrawal governance delay has not passed yet or
/// if the change was not yet initiated.
/// @param changeInitiatedTimestamp The timestamp at which the change has
/// been initiated
modifier onlyAfterWithdrawalGovernanceDelay(
uint256 changeInitiatedTimestamp
) {
require(changeInitiatedTimestamp > 0, "Change not initiated");
require(
/* solhint-disable-next-line not-rely-on-time */
block.timestamp - changeInitiatedTimestamp >=
withdrawalGovernanceDelay(),
"Governance delay has not elapsed"
);
_;
}
constructor(
IERC20 _collateralToken,
UnderwriterToken _underwriterToken,
address rewardsManager
) {
collateralToken = _collateralToken;
underwriterToken = _underwriterToken;
rewardsPool = new RewardsPool(
_collateralToken,
address(this),
rewardsManager
);
}
/// @notice Accepts the given amount of collateral token as a deposit and
/// mints underwriter tokens representing pool's ownership.
/// Optional data in extraData may include a minimal amount of
/// underwriter tokens expected to be minted for a depositor. There
/// are cases when an amount of minted tokens matters for a
/// depositor, as tokens might be used in third party exchanges.
/// @dev This function is a shortcut for approve + deposit.
function receiveApproval(
address from,
uint256 amount,
address token,
bytes calldata extraData
) external {
require(msg.sender == token, "Only token caller allowed");
require(
token == address(collateralToken),
"Unsupported collateral token"
);
uint256 toMint = _calculateTokensToMint(amount);
if (extraData.length != 0) {
require(extraData.length == 32, "Unexpected data length");
uint256 minAmountToMint = abi.decode(extraData, (uint256));
require(
minAmountToMint <= toMint,
"Amount to mint is smaller than the required minimum"
);
}
_deposit(from, amount, toMint);
}
/// @notice Accepts the given amount of collateral token as a deposit and
/// mints underwriter tokens representing pool's ownership.
/// @dev Before calling this function, collateral token needs to have the
/// required amount accepted to transfer to the asset pool.
/// @param amountToDeposit Collateral tokens amount that a user deposits to
/// the asset pool
/// @return The amount of minted underwriter tokens
function deposit(uint256 amountToDeposit)
external
override
returns (uint256)
{
uint256 toMint = _calculateTokensToMint(amountToDeposit);
_deposit(msg.sender, amountToDeposit, toMint);
return toMint;
}
/// @notice Accepts the given amount of collateral token as a deposit and
/// mints at least a minAmountToMint underwriter tokens representing
/// pool's ownership.
/// @dev Before calling this function, collateral token needs to have the
/// required amount accepted to transfer to the asset pool.
/// @param amountToDeposit Collateral tokens amount that a user deposits to
/// the asset pool
/// @param minAmountToMint Underwriter minimal tokens amount that a user
/// expects to receive in exchange for the deposited
/// collateral tokens
/// @return The amount of minted underwriter tokens
function depositWithMin(uint256 amountToDeposit, uint256 minAmountToMint)
external
override
returns (uint256)
{
uint256 toMint = _calculateTokensToMint(amountToDeposit);
require(
minAmountToMint <= toMint,
"Amount to mint is smaller than the required minimum"
);
_deposit(msg.sender, amountToDeposit, toMint);
return toMint;
}
/// @notice Initiates the withdrawal of collateral and rewards from the
/// pool. Must be followed with completeWithdrawal call after the
/// withdrawal delay passes. Accepts the amount of underwriter
/// tokens representing the share of the pool that should be
/// withdrawn. Can be called multiple times increasing the pool share
/// to withdraw and resetting the withdrawal initiated timestamp for
/// each call. Can be called with 0 covAmount to reset the
/// withdrawal initiated timestamp if the underwriter has a pending
/// withdrawal. In practice 0 covAmount should be used only to
/// initiate the withdrawal again in case one did not complete the
/// withdrawal before the withdrawal timeout elapsed.
/// @dev Before calling this function, underwriter token needs to have the
/// required amount accepted to transfer to the asset pool.
function initiateWithdrawal(uint256 covAmount) external override {
uint256 pending = pendingWithdrawal[msg.sender];
require(
covAmount > 0 || pending > 0,
"Underwriter token amount must be greater than 0"
);
pending += covAmount;
pendingWithdrawal[msg.sender] = pending;
/* solhint-disable not-rely-on-time */
withdrawalInitiatedTimestamp[msg.sender] = block.timestamp;
emit WithdrawalInitiated(msg.sender, pending, block.timestamp);
/* solhint-enable not-rely-on-time */
if (covAmount > 0) {
underwriterToken.safeTransferFrom(
msg.sender,
address(this),
covAmount
);
}
}
/// @notice Completes the previously initiated withdrawal for the
/// underwriter. Anyone can complete the withdrawal for the
/// underwriter. The withdrawal has to be completed before the
/// withdrawal timeout elapses. Otherwise, the withdrawal has to
/// be initiated again and the underwriter has to wait for the
/// entire withdrawal delay again before being able to complete
/// the withdrawal.
/// @return The amount of collateral withdrawn
function completeWithdrawal(address underwriter)
external
override
returns (uint256)
{
/* solhint-disable not-rely-on-time */
uint256 initiatedAt = withdrawalInitiatedTimestamp[underwriter];
require(initiatedAt > 0, "No withdrawal initiated for the underwriter");
uint256 withdrawalDelayEndTimestamp = initiatedAt + withdrawalDelay;
require(
withdrawalDelayEndTimestamp < block.timestamp,
"Withdrawal delay has not elapsed"
);
require(
withdrawalDelayEndTimestamp + withdrawalTimeout >= block.timestamp,
"Withdrawal timeout elapsed"
);
uint256 covAmount = pendingWithdrawal[underwriter];
uint256 covSupply = underwriterToken.totalSupply();
delete withdrawalInitiatedTimestamp[underwriter];
delete pendingWithdrawal[underwriter];
// slither-disable-next-line reentrancy-events
rewardsPool.withdraw();
uint256 collateralBalance = collateralToken.balanceOf(address(this));
uint256 amountToWithdraw = (covAmount * collateralBalance) / covSupply;
emit WithdrawalCompleted(
underwriter,
amountToWithdraw,
covAmount,
block.timestamp
);
collateralToken.safeTransfer(underwriter, amountToWithdraw);
/* solhint-enable not-rely-on-time */
underwriterToken.burn(covAmount);
return amountToWithdraw;
}
/// @notice Transfers collateral tokens to a new Asset Pool which previously
/// was approved by the governance. Upgrade does not have to obey
/// withdrawal delay.
/// Old underwriter tokens are burned in favor of new tokens minted
/// in a new Asset Pool. New tokens are sent directly to the
/// underwriter from a new Asset Pool.
/// @param covAmount Amount of underwriter tokens used to calculate collateral
/// tokens which are transferred to a new asset pool
/// @param _newAssetPool New Asset Pool address to check validity with the one
/// that was approved by the governance
function upgradeToNewAssetPool(uint256 covAmount, address _newAssetPool)
external
{
/* solhint-disable not-rely-on-time */
require(
address(newAssetPool) != address(0),
"New asset pool must be assigned"
);
require(
address(newAssetPool) == _newAssetPool,
"Addresses of a new asset pool must match"
);
require(
covAmount > 0,
"Underwriter token amount must be greater than 0"
);
uint256 covSupply = underwriterToken.totalSupply();
// slither-disable-next-line reentrancy-events
rewardsPool.withdraw();
uint256 collateralBalance = collateralToken.balanceOf(address(this));
uint256 collateralToTransfer = (covAmount * collateralBalance) /
covSupply;
collateralToken.safeApprove(
address(newAssetPool),
collateralToTransfer
);
// old underwriter tokens are burned in favor of new minted in a new
// asset pool
underwriterToken.burnFrom(msg.sender, covAmount);
// collateralToTransfer will be sent to a new AssetPool and new
// underwriter tokens will be minted and transferred back to the underwriter
newAssetPool.depositFor(msg.sender, collateralToTransfer);
emit AssetPoolUpgraded(
msg.sender,
collateralToTransfer,
covAmount,
block.timestamp
);
}
/// @notice Allows governance to set a new asset pool so the underwriters
/// can move their collateral tokens to a new asset pool without
/// having to wait for the withdrawal delay.
function approveNewAssetPoolUpgrade(IAssetPoolUpgrade _newAssetPool)
external
onlyOwner
{
require(
address(_newAssetPool) != address(0),
"New asset pool can't be zero address"
);
newAssetPool = _newAssetPool;
emit ApprovedAssetPoolUpgrade(address(_newAssetPool));
}
/// @notice Allows governance to cancel already approved new asset pool
/// in case of some misconfiguration.
function cancelNewAssetPoolUpgrade() external onlyOwner {
emit CancelledAssetPoolUpgrade(address(newAssetPool));
newAssetPool = IAssetPoolUpgrade(address(0));
}
/// @notice Allows the coverage pool to demand coverage from the asset hold
/// by this pool and send it to the provided recipient address.
function claim(address recipient, uint256 amount) external onlyOwner {
emit CoverageClaimed(recipient, amount, block.timestamp);
rewardsPool.withdraw();
collateralToken.safeTransfer(recipient, amount);
}
/// @notice Lets the contract owner to begin an update of withdrawal delay
/// parameter value. Withdrawal delay is the time it takes the
/// underwriter to withdraw their collateral and rewards from the
/// pool. This is the time that needs to pass between initiating and
/// completing the withdrawal. The change needs to be finalized with
/// a call to finalizeWithdrawalDelayUpdate after the required
/// governance delay passes. It is up to the contract owner to
/// decide what the withdrawal delay value should be but it should
/// be long enough so that the possibility of having free-riding
/// underwriters escaping from a potential coverage claim by
/// withdrawing their positions from the pool is negligible.
/// @param _newWithdrawalDelay The new value of withdrawal delay
function beginWithdrawalDelayUpdate(uint256 _newWithdrawalDelay)
external
onlyOwner
{
newWithdrawalDelay = _newWithdrawalDelay;
withdrawalDelayChangeInitiated = block.timestamp;
emit WithdrawalDelayUpdateStarted(_newWithdrawalDelay, block.timestamp);
}
/// @notice Lets the contract owner to finalize an update of withdrawal
/// delay parameter value. This call has to be preceded with
/// a call to beginWithdrawalDelayUpdate and the governance delay
/// has to pass.
function finalizeWithdrawalDelayUpdate()
external
onlyOwner
onlyAfterWithdrawalGovernanceDelay(withdrawalDelayChangeInitiated)
{
withdrawalDelay = newWithdrawalDelay;
emit WithdrawalDelayUpdated(withdrawalDelay);
newWithdrawalDelay = 0;
withdrawalDelayChangeInitiated = 0;
}
/// @notice Lets the contract owner to begin an update of withdrawal timeout
/// parameter value. The withdrawal timeout is the time the
/// underwriter has - after the withdrawal delay passed - to
/// complete the withdrawal. The change needs to be finalized with
/// a call to finalizeWithdrawalTimeoutUpdate after the required
/// governance delay passes. It is up to the contract owner to
/// decide what the withdrawal timeout value should be but it should
/// be short enough so that the time of free-riding by being able to
/// immediately escape from the claim is minimal and long enough so
/// that honest underwriters have a possibility to finalize the
/// withdrawal. It is all about the right proportions with
/// a relation to withdrawal delay value.
/// @param _newWithdrawalTimeout The new value of the withdrawal timeout
function beginWithdrawalTimeoutUpdate(uint256 _newWithdrawalTimeout)
external
onlyOwner
{
newWithdrawalTimeout = _newWithdrawalTimeout;
withdrawalTimeoutChangeInitiated = block.timestamp;
emit WithdrawalTimeoutUpdateStarted(
_newWithdrawalTimeout,
block.timestamp
);
}
/// @notice Lets the contract owner to finalize an update of withdrawal
/// timeout parameter value. This call has to be preceded with
/// a call to beginWithdrawalTimeoutUpdate and the governance delay
/// has to pass.
function finalizeWithdrawalTimeoutUpdate()
external
onlyOwner
onlyAfterWithdrawalGovernanceDelay(withdrawalTimeoutChangeInitiated)
{
withdrawalTimeout = newWithdrawalTimeout;
emit WithdrawalTimeoutUpdated(withdrawalTimeout);
newWithdrawalTimeout = 0;
withdrawalTimeoutChangeInitiated = 0;
}
/// @notice Grants pool shares by minting a given amount of the underwriter
/// tokens for the recipient address. In result, the recipient
/// obtains part of the pool ownership without depositing any
/// collateral tokens. Shares are usually granted for notifiers
/// reporting about various contract state changes.
/// @dev Can be called only by the contract owner.
/// @param recipient Address of the underwriter tokens recipient
/// @param covAmount Amount of the underwriter tokens which should be minted
function grantShares(address recipient, uint256 covAmount)
external
onlyOwner
{
rewardsPool.withdraw();
underwriterToken.mint(recipient, covAmount);
}
/// @notice Returns the remaining time that has to pass before the contract
/// owner will be able to finalize withdrawal delay update.
/// Bear in mind the contract owner may decide to wait longer and
/// this value is just an absolute minimum.
/// @return The time left until withdrawal delay update can be finalized
function getRemainingWithdrawalDelayUpdateTime()
external
view
returns (uint256)
{
return
GovernanceUtils.getRemainingChangeTime(
withdrawalDelayChangeInitiated,
withdrawalGovernanceDelay()
);
}
/// @notice Returns the remaining time that has to pass before the contract
/// owner will be able to finalize withdrawal timeout update.
/// Bear in mind the contract owner may decide to wait longer and
/// this value is just an absolute minimum.
/// @return The time left until withdrawal timeout update can be finalized
function getRemainingWithdrawalTimeoutUpdateTime()
external
view
returns (uint256)
{
return
GovernanceUtils.getRemainingChangeTime(
withdrawalTimeoutChangeInitiated,
withdrawalGovernanceDelay()
);
}
/// @notice Returns the current collateral token balance of the asset pool
/// plus the reward amount (in collateral token) earned by the asset
/// pool and not yet withdrawn to the asset pool.
/// @return The total value of asset pool in collateral token.
function totalValue() external view returns (uint256) {
return collateralToken.balanceOf(address(this)) + rewardsPool.earned();
}
/// @notice The time it takes to initiate and complete the withdrawal from
/// the pool plus 2 days to make a decision. This governance delay
/// should be used for all changes directly affecting underwriter
/// positions. This time is a minimum and the governance may choose
/// to wait longer before finalizing the update.
/// @return The withdrawal governance delay in seconds
function withdrawalGovernanceDelay() public view returns (uint256) {
return withdrawalDelay + withdrawalTimeout + 2 days;
}
/// @dev Calculates underwriter tokens to mint.
function _calculateTokensToMint(uint256 amountToDeposit)
internal
returns (uint256)
{
rewardsPool.withdraw();
uint256 covSupply = underwriterToken.totalSupply();
uint256 collateralBalance = collateralToken.balanceOf(address(this));
if (covSupply == 0) {
return amountToDeposit;
}
return (amountToDeposit * covSupply) / collateralBalance;
}
function _deposit(
address depositor,
uint256 amountToDeposit,
uint256 amountToMint
) internal {
require(depositor != address(this), "Self-deposit not allowed");
require(
amountToMint > 0,
"Minted tokens amount must be greater than 0"
);
emit Deposited(depositor, amountToDeposit, amountToMint);
underwriterToken.mint(depositor, amountToMint);
collateralToken.safeTransferFrom(
depositor,
address(this),
amountToDeposit
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./IERC20WithPermit.sol";
import "./IReceiveApproval.sol";
/// @title ERC20WithPermit
/// @notice Burnable ERC20 token with EIP2612 permit functionality. User can
/// authorize a transfer of their token with a signature conforming
/// EIP712 standard instead of an on-chain transaction from their
/// address. Anyone can submit this signature on the user's behalf by
/// calling the permit function, as specified in EIP2612 standard,
/// paying gas fees, and possibly performing other actions in the same
/// transaction.
contract ERC20WithPermit is IERC20WithPermit, Ownable {
/// @notice The amount of tokens owned by the given account.
mapping(address => uint256) public override balanceOf;
/// @notice The remaining number of tokens that spender will be
/// allowed to spend on behalf of owner through `transferFrom` and
/// `burnFrom`. This is zero by default.
mapping(address => mapping(address => uint256)) public override allowance;
/// @notice Returns the current nonce for EIP2612 permission for the
/// provided token owner for a replay protection. Used to construct
/// EIP2612 signature provided to `permit` function.
mapping(address => uint256) public override nonces;
uint256 public immutable cachedChainId;
bytes32 public immutable cachedDomainSeparator;
/// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
/// signature provided to `permit` function.
bytes32 public constant override PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
/// @notice The amount of tokens in existence.
uint256 public override totalSupply;
/// @notice The name of the token.
string public override name;
/// @notice The symbol of the token.
string public override symbol;
/// @notice The decimals places of the token.
uint8 public constant override decimals = 18;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
cachedChainId = block.chainid;
cachedDomainSeparator = buildDomainSeparator();
}
/// @notice Moves `amount` tokens from the caller's account to `recipient`.
/// @return True if the operation succeeded, reverts otherwise.
/// @dev Requirements:
/// - `recipient` cannot be the zero address,
/// - the caller must have a balance of at least `amount`.
function transfer(address recipient, uint256 amount)
external
override
returns (bool)
{
_transfer(msg.sender, recipient, amount);
return true;
}
/// @notice Moves `amount` tokens from `sender` to `recipient` using the
/// allowance mechanism. `amount` is then deducted from the caller's
/// allowance unless the allowance was made for `type(uint256).max`.
/// @return True if the operation succeeded, reverts otherwise.
/// @dev 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
) external override returns (bool) {
uint256 currentAllowance = allowance[sender][msg.sender];
if (currentAllowance != type(uint256).max) {
require(
currentAllowance >= amount,
"Transfer amount exceeds allowance"
);
_approve(sender, msg.sender, currentAllowance - amount);
}
_transfer(sender, recipient, amount);
return true;
}
/// @notice EIP2612 approval made with secp256k1 signature.
/// Users can authorize a transfer of their tokens with a signature
/// conforming EIP712 standard, rather than an on-chain transaction
/// from their address. Anyone can submit this signature on the
/// user's behalf by calling the permit function, paying gas fees,
/// and possibly performing other actions in the same transaction.
/// @dev The deadline argument can be set to `type(uint256).max to create
/// permits that effectively never expire. If the `amount` is set
/// to `type(uint256).max` then `transferFrom` and `burnFrom` will
/// not reduce an allowance.
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override {
/* solhint-disable-next-line not-rely-on-time */
require(deadline >= block.timestamp, "Permission expired");
// Validate `s` and `v` values for a malleability concern described in EIP2.
// Only signatures with `s` value in the lower half of the secp256k1
// curve's order and `v` value of 27 or 28 are considered valid.
require(
uint256(s) <=
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
"Invalid signature 's' value"
);
require(v == 27 || v == 28, "Invalid signature 'v' value");
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
amount,
nonces[owner]++,
deadline
)
)
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(
recoveredAddress != address(0) && recoveredAddress == owner,
"Invalid signature"
);
_approve(owner, spender, amount);
}
/// @notice Creates `amount` tokens and assigns them to `account`,
/// increasing the total supply.
/// @dev Requirements:
/// - `recipient` cannot be the zero address.
function mint(address recipient, uint256 amount) external onlyOwner {
require(recipient != address(0), "Mint to the zero address");
beforeTokenTransfer(address(0), recipient, amount);
totalSupply += amount;
balanceOf[recipient] += amount;
emit Transfer(address(0), recipient, amount);
}
/// @notice Destroys `amount` tokens from the caller.
/// @dev Requirements:
/// - the caller must have a balance of at least `amount`.
function burn(uint256 amount) external override {
_burn(msg.sender, amount);
}
/// @notice Destroys `amount` of tokens from `account` using the allowance
/// mechanism. `amount` is then deducted from the caller's allowance
/// unless the allowance was made for `type(uint256).max`.
/// @dev Requirements:
/// - `account` must have a balance of at least `amount`,
/// - the caller must have allowance for `account`'s tokens of at least
/// `amount`.
function burnFrom(address account, uint256 amount) external override {
uint256 currentAllowance = allowance[account][msg.sender];
if (currentAllowance != type(uint256).max) {
require(
currentAllowance >= amount,
"Burn amount exceeds allowance"
);
_approve(account, msg.sender, currentAllowance - amount);
}
_burn(account, amount);
}
/// @notice Calls `receiveApproval` function on spender previously approving
/// the spender to withdraw from the caller multiple times, up to
/// the `amount` amount. If this function is called again, it
/// overwrites the current allowance with `amount`. Reverts if the
/// approval reverted or if `receiveApproval` call on the spender
/// reverted.
/// @return True if both approval and `receiveApproval` calls succeeded.
/// @dev If the `amount` is set to `type(uint256).max` then
/// `transferFrom` and `burnFrom` will not reduce an allowance.
function approveAndCall(
address spender,
uint256 amount,
bytes memory extraData
) external override returns (bool) {
if (approve(spender, amount)) {
IReceiveApproval(spender).receiveApproval(
msg.sender,
amount,
address(this),
extraData
);
return true;
}
return false;
}
/// @notice Sets `amount` as the allowance of `spender` over the caller's
/// tokens.
/// @return True if the operation succeeded.
/// @dev If the `amount` is set to `type(uint256).max` then
/// `transferFrom` and `burnFrom` will not reduce an allowance.
/// 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
function approve(address spender, uint256 amount)
public
override
returns (bool)
{
_approve(msg.sender, spender, amount);
return true;
}
/// @notice Returns hash of EIP712 Domain struct with the token name as
/// a signing domain and token contract as a verifying contract.
/// Used to construct EIP2612 signature provided to `permit`
/// function.
/* solhint-disable-next-line func-name-mixedcase */
function DOMAIN_SEPARATOR() public view override returns (bytes32) {
// As explained in EIP-2612, if the DOMAIN_SEPARATOR contains the
// chainId and is defined at contract deployment instead of
// reconstructed for every signature, there is a risk of possible replay
// attacks between chains in the event of a future chain split.
// To address this issue, we check the cached chain ID against the
// current one and in case they are different, we build domain separator
// from scratch.
if (block.chainid == cachedChainId) {
return cachedDomainSeparator;
} else {
return buildDomainSeparator();
}
}
/// @dev Hook that is called before any transfer of tokens. This includes
/// minting and burning.
///
/// Calling conditions:
/// - when `from` and `to` are both non-zero, `amount` of `from`'s tokens
/// will be to transferred to `to`.
/// - when `from` is zero, `amount` tokens will be minted for `to`.
/// - when `to` is zero, `amount` of ``from``'s tokens will be burned.
/// - `from` and `to` are never both zero.
// slither-disable-next-line dead-code
function beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
function _burn(address account, uint256 amount) internal {
uint256 currentBalance = balanceOf[account];
require(currentBalance >= amount, "Burn amount exceeds balance");
beforeTokenTransfer(account, address(0), amount);
balanceOf[account] = currentBalance - amount;
totalSupply -= amount;
emit Transfer(account, address(0), amount);
}
function _transfer(
address sender,
address recipient,
uint256 amount
) private {
require(sender != address(0), "Transfer from the zero address");
require(recipient != address(0), "Transfer to the zero address");
require(recipient != address(this), "Transfer to the token address");
beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = balanceOf[sender];
require(senderBalance >= amount, "Transfer amount exceeds balance");
balanceOf[sender] = senderBalance - amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
function _approve(
address owner,
address spender,
uint256 amount
) private {
require(owner != address(0), "Approve from the zero address");
require(spender != address(0), "Approve to the zero address");
allowance[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function buildDomainSeparator() private view returns (bytes32) {
return
keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256(bytes("1")),
block.chainid,
address(this)
)
);
}
}
// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌
// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
//
// Trust math, not hardware.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.5;
library GovernanceUtils {
/// @notice Gets the time remaining until the governable parameter update
/// can be committed.
/// @param changeTimestamp Timestamp indicating the beginning of the change.
/// @param delay Governance delay.
/// @return Remaining time in seconds.
function getRemainingChangeTime(uint256 changeTimestamp, uint256 delay)
internal
view
returns (uint256)
{
require(changeTimestamp > 0, "Change not initiated");
/* solhint-disable-next-line not-rely-on-time */
uint256 elapsed = block.timestamp - changeTimestamp;
if (elapsed >= delay) {
return 0;
} else {
return delay - elapsed;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice An interface that should be implemented by tokens supporting
/// `approveAndCall`/`receiveApproval` pattern.
interface IApproveAndCall {
/// @notice Executes `receiveApproval` function on spender as specified in
/// `IReceiveApproval` interface. Approves spender to withdraw from
/// the caller multiple times, up to the `amount`. If this
/// function is called again, it overwrites the current allowance
/// with `amount`. Reverts if the approval reverted or if
/// `receiveApproval` call on the spender reverted.
function approveAndCall(
address spender,
uint256 amount,
bytes memory extraData
) external returns (bool);
}
// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌
// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
//
// Trust math, not hardware.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.5;
/// @title Asset Pool interface
/// @notice Asset Pool accepts a single ERC20 token as collateral, and returns
/// an underwriter token. For example, an asset pool might accept deposits
/// in KEEP in return for covKEEP underwriter tokens. Underwriter tokens
/// represent an ownership share in the underlying collateral of the
/// Asset Pool.
interface IAssetPool {
/// @notice Accepts the given amount of collateral token as a deposit and
/// mints underwriter tokens representing pool's ownership.
/// @dev Before calling this function, collateral token needs to have the
/// required amount accepted to transfer to the asset pool.
/// @return The amount of minted underwriter tokens
function deposit(uint256 amount) external returns (uint256);
/// @notice Accepts the given amount of collateral token as a deposit and
/// mints at least a minAmountToMint underwriter tokens representing
/// pool's ownership.
/// @dev Before calling this function, collateral token needs to have the
/// required amount accepted to transfer to the asset pool.
/// @return The amount of minted underwriter tokens
function depositWithMin(uint256 amountToDeposit, uint256 minAmountToMint)
external
returns (uint256);
/// @notice Initiates the withdrawal of collateral and rewards from the pool.
/// @dev Before calling this function, underwriter token needs to have the
/// required amount accepted to transfer to the asset pool.
function initiateWithdrawal(uint256 covAmount) external;
/// @notice Completes the previously initiated withdrawal for the
/// underwriter.
/// @return The amount of collateral withdrawn
function completeWithdrawal(address underwriter) external returns (uint256);
}
// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌
// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
//
// Trust math, not hardware.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.5;
/// @title Asset Pool upgrade interface
/// @notice Interface that has to be implemented by an Asset Pool accepting
/// upgrades from another asset pool.
interface IAssetPoolUpgrade {
/// @notice Accepts the given underwriter with collateral tokens amount as a
/// deposit. In exchange new underwriter tokens will be calculated,
/// minted and then transferred back to the underwriter.
function depositFor(address underwriter, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IApproveAndCall.sol";
/// @title IERC20WithPermit
/// @notice Burnable ERC20 token with EIP2612 permit functionality. User can
/// authorize a transfer of their token with a signature conforming
/// EIP712 standard instead of an on-chain transaction from their
/// address. Anyone can submit this signature on the user's behalf by
/// calling the permit function, as specified in EIP2612 standard,
/// paying gas fees, and possibly performing other actions in the same
/// transaction.
interface IERC20WithPermit is IERC20, IERC20Metadata, IApproveAndCall {
/// @notice EIP2612 approval made with secp256k1 signature.
/// Users can authorize a transfer of their tokens with a signature
/// conforming EIP712 standard, rather than an on-chain transaction
/// from their address. Anyone can submit this signature on the
/// user's behalf by calling the permit function, paying gas fees,
/// and possibly performing other actions in the same transaction.
/// @dev The deadline argument can be set to `type(uint256).max to create
/// permits that effectively never expire.
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/// @notice Destroys `amount` tokens from the caller.
function burn(uint256 amount) external;
/// @notice Destroys `amount` of tokens from `account`, deducting the amount
/// from caller's allowance.
function burnFrom(address account, uint256 amount) external;
/// @notice Returns hash of EIP712 Domain struct with the token name as
/// a signing domain and token contract as a verifying contract.
/// Used to construct EIP2612 signature provided to `permit`
/// function.
/* solhint-disable-next-line func-name-mixedcase */
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice Returns the current nonce for EIP2612 permission for the
/// provided token owner for a replay protection. Used to construct
/// EIP2612 signature provided to `permit` function.
function nonces(address owner) external view returns (uint256);
/// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
/// signature provided to `permit` function.
/* solhint-disable-next-line func-name-mixedcase */
function PERMIT_TYPEHASH() external pure returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice An interface that should be implemented by contracts supporting
/// `approveAndCall`/`receiveApproval` pattern.
interface IReceiveApproval {
/// @notice Receives approval to spend tokens. Called as a result of
/// `approveAndCall` call on the token.
function receiveApproval(
address from,
uint256 amount,
address token,
bytes calldata extraData
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.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);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/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.
*/
abstract 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 () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual 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;
}
}
// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌
// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
//
// Trust math, not hardware.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.5;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/// @title Rewards Pool
/// @notice RewardsPool accepts a single reward token and releases it to the
/// AssetPool over time in one week reward intervals. The owner of this
/// contract is the reward distribution address funding it with reward
/// tokens.
contract RewardsPool is Ownable {
using SafeERC20 for IERC20;
uint256 public constant DURATION = 7 days;
IERC20 public immutable rewardToken;
address public immutable assetPool;
// timestamp of the current reward interval end or the timestamp of the
// last interval end in case a new reward interval has not been allocated
uint256 public intervalFinish = 0;
// rate per second with which reward tokens are unlocked
uint256 public rewardRate = 0;
// amount of rewards accumulated and not yet withdrawn from the previous
// reward interval(s)
uint256 public rewardAccumulated = 0;
// the last time information in this contract was updated
uint256 public lastUpdateTime = 0;
event RewardToppedUp(uint256 amount);
event RewardWithdrawn(uint256 amount);
constructor(
IERC20 _rewardToken,
address _assetPool,
address owner
) {
rewardToken = _rewardToken;
// slither-disable-next-line missing-zero-check
assetPool = _assetPool;
transferOwnership(owner);
}
/// @notice Transfers the provided reward amount into RewardsPool and
/// creates a new, one-week reward interval starting from now.
/// Reward tokens from the previous reward interval that unlocked
/// over the time will be available for withdrawal immediately.
/// Reward tokens from the previous interval that has not been yet
/// unlocked, are added to the new interval being created.
/// @dev This function can be called only by the owner given that it creates
/// a new interval with one week length, starting from now.
function topUpReward(uint256 reward) external onlyOwner {
rewardAccumulated = earned();
/* solhint-disable not-rely-on-time */
if (block.timestamp >= intervalFinish) {
// see https://github.com/crytic/slither/issues/844
// slither-disable-next-line divide-before-multiply
rewardRate = reward / DURATION;
} else {
uint256 remaining = intervalFinish - block.timestamp;
uint256 leftover = remaining * rewardRate;
rewardRate = (reward + leftover) / DURATION;
}
intervalFinish = block.timestamp + DURATION;
lastUpdateTime = block.timestamp;
/* solhint-enable avoid-low-level-calls */
emit RewardToppedUp(reward);
rewardToken.safeTransferFrom(msg.sender, address(this), reward);
}
/// @notice Withdraws all unlocked reward tokens to the AssetPool.
function withdraw() external {
uint256 amount = earned();
rewardAccumulated = 0;
lastUpdateTime = lastTimeRewardApplicable();
emit RewardWithdrawn(amount);
rewardToken.safeTransfer(assetPool, amount);
}
/// @notice Returns the amount of earned and not yet withdrawn reward
/// tokens.
function earned() public view returns (uint256) {
return
rewardAccumulated +
((lastTimeRewardApplicable() - lastUpdateTime) * rewardRate);
}
/// @notice Returns the timestamp at which a reward was last time applicable.
/// When reward interval is pending, returns current block's
/// timestamp. If the last reward interval ended and no other reward
/// interval had been allocated, returns the last reward interval's
/// end timestamp.
function lastTimeRewardApplicable() public view returns (uint256) {
return Math.min(block.timestamp, intervalFinish);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @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 IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
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));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
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) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "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");
}
}
}
// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌
// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
//
// Trust math, not hardware.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.5;
import "@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol";
/// @title UnderwriterToken
/// @notice Underwriter tokens represent an ownership share in the underlying
/// collateral of the asset-specific pool. Underwriter tokens are minted
/// when a user deposits ERC20 tokens into asset-specific pool and they
/// are burned when a user exits the position. Underwriter tokens
/// natively support meta transactions. Users can authorize a transfer
/// of their underwriter tokens with a signature conforming EIP712
/// standard instead of an on-chain transaction from their address.
/// Anyone can submit this signature on the user's behalf by calling the
/// permit function, as specified in EIP2612 standard, paying gas fees,
/// and possibly performing other actions in the same transaction.
contract UnderwriterToken is ERC20WithPermit {
constructor(string memory _name, string memory _symbol)
ERC20WithPermit(_name, _symbol)
{}
}
{
"compilationTarget": {
"contracts/AssetPool.sol": "AssetPool"
},
"evmVersion": "berlin",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"_collateralToken","type":"address"},{"internalType":"contract UnderwriterToken","name":"_underwriterToken","type":"address"},{"internalType":"address","name":"rewardsManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAssetPool","type":"address"}],"name":"ApprovedAssetPoolUpgrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"covAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"AssetPoolUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"cancelledAssetPool","type":"address"}],"name":"CancelledAssetPoolUpgrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"CoverageClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"covAmount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"covAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WithdrawalCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawalDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WithdrawalDelayUpdateStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawalDelay","type":"uint256"}],"name":"WithdrawalDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint256","name":"covAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WithdrawalInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawalTimeout","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WithdrawalTimeoutUpdateStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawalTimeout","type":"uint256"}],"name":"WithdrawalTimeoutUpdated","type":"event"},{"inputs":[{"internalType":"contract IAssetPoolUpgrade","name":"_newAssetPool","type":"address"}],"name":"approveNewAssetPoolUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newWithdrawalDelay","type":"uint256"}],"name":"beginWithdrawalDelayUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newWithdrawalTimeout","type":"uint256"}],"name":"beginWithdrawalTimeoutUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelNewAssetPoolUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateralToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underwriter","type":"address"}],"name":"completeWithdrawal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToDeposit","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToDeposit","type":"uint256"},{"internalType":"uint256","name":"minAmountToMint","type":"uint256"}],"name":"depositWithMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalizeWithdrawalDelayUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalizeWithdrawalTimeoutUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getRemainingWithdrawalDelayUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRemainingWithdrawalTimeoutUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"covAmount","type":"uint256"}],"name":"grantShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"covAmount","type":"uint256"}],"name":"initiateWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"newAssetPool","outputs":[{"internalType":"contract IAssetPoolUpgrade","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newWithdrawalDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newWithdrawalTimeout","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pendingWithdrawal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"receiveApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsPool","outputs":[{"internalType":"contract RewardsPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underwriterToken","outputs":[{"internalType":"contract UnderwriterToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"covAmount","type":"uint256"},{"internalType":"address","name":"_newAssetPool","type":"address"}],"name":"upgradeToNewAssetPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalDelayChangeInitiated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalGovernanceDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawalInitiatedTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalTimeout","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalTimeoutChangeInitiated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]