ETH Price: $2,666.67 (+7.68%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
_transfer Owners...176656802023-07-10 20:37:47681 days ago1689021467IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0007985727.84334688
Set Liquidation ...163502912023-01-06 21:00:47866 days ago1673038847IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0008612726.01962934
Set Liquidate Bo...163502842023-01-06 20:59:23866 days ago1673038763IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0007188524.81728624
Set Liquidate Bo...163502822023-01-06 20:58:59866 days ago1673038739IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0007169124.75026544
Set Liquidate Bo...163502812023-01-06 20:58:47866 days ago1673038727IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0007254925.04633129
Set Liquidate Bo...163502792023-01-06 20:58:23866 days ago1673038703IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0007326125.29234564
Set Deposit Unde...163502762023-01-06 20:57:47866 days ago1673038667IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0006719823.42564622
Set Deposit Unde...163502752023-01-06 20:57:35866 days ago1673038655IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0006844323.85961855
Set Deposit Unde...163502732023-01-06 20:57:11866 days ago1673038631IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0006931224.16241819
Set Deposit Unde...163502722023-01-06 20:56:59866 days ago1673038619IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0006726623.44920725
Set Deposit Coll...163502652023-01-06 20:55:35866 days ago1673038535IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0007779127.1267151
Set Collateral R...163502472023-01-06 20:51:59866 days ago1673038319IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0008184722.71025463
Set Collateral C...163502392023-01-06 20:50:23866 days ago1673038223IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0011271722.16923588
Set Debt Ceiling163501572023-01-06 20:33:47866 days ago1673037227IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0016235928.93434814
Set Debt Ceiling163501552023-01-06 20:33:23866 days ago1673037203IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0017323530.87252937
Set Debt Ceiling163501532023-01-06 20:32:59866 days ago1673037179IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0017008630.31142472
Set Debt Ceiling163501512023-01-06 20:32:35866 days ago1673037155IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0017493631.1756843
List Bond163501302023-01-06 20:28:23866 days ago1673036903IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.001346626.7013683
List Bond163501302023-01-06 20:28:23866 days ago1673036903IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.001346626.7013683
List Bond163501292023-01-06 20:28:11866 days ago1673036891IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.001353926.84614484
List Bond163501262023-01-06 20:27:35866 days ago1673036855IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0015558730.85088575
List Collateral163494792023-01-06 18:17:59867 days ago1673029079IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0051613351.67480718
_transfer Owners...162287202022-12-20 21:53:47883 days ago1671573227IN
0xEe46E4B6...f0bFcA8c3
0 ETH0.0006002520.92876893

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Fintroller

Compiler Version
v0.8.12+commit.f00d7308

Optimization Enabled:
Yes with 800 runs

Other Settings:
default evmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;

import "@prb/contracts/access/Ownable.sol";
import "@prb/contracts/token/erc20/IErc20.sol";

import "./IFintroller.sol";
import "../h-token/IHToken.sol";

/// @notice Fintroller
/// @author Hifi
contract Fintroller is
    IFintroller, // one dependency
    Ownable // one dependency
{
    /// PUBLIC STORAGE ///

    /// @inheritdoc IFintroller
    uint256 public override maxBonds;

    /// @inheritdoc IFintroller
    uint256 public override maxCollaterals;

    /// INTERNAL STORAGE ///

    /// @dev The threshold below which the collateral ratio cannot be set, equivalent to 100%.
    uint256 internal constant COLLATERAL_RATIO_LOWER_BOUND = 1.0e18;

    /// @dev The threshold above which the collateral ratio cannot be set, equivalent to 10,000%.
    uint256 internal constant COLLATERAL_RATIO_UPPER_BOUND = 1.0e20;

    /// @dev The default collateral ratio set when a new bond is listed, equivalent to 150%.
    uint256 internal constant DEFAULT_COLLATERAL_RATIO = 1.5e18;

    /// @dev The default liquidation incentive set when a new bond is listed, equivalent to 110%.
    uint256 internal constant DEFAULT_LIQUIDATION_INCENTIVE = 1.1e18;

    /// @dev The default maximum number of bond markets a single account can enter.
    uint256 internal constant DEFAULT_MAX_BONDS = 10;

    /// @dev The default maximum number of collaterals a single account can deposit.
    uint256 internal constant DEFAULT_MAX_COLLATERALS = 10;

    /// @dev The threshold below which the liquidation incentive cannot be set, equivalent to 100%.
    uint256 internal constant LIQUIDATION_INCENTIVE_LOWER_BOUND = 1.0e18;

    /// @dev The threshold above which the liquidation incentive cannot be set, equivalent to 150%.
    uint256 internal constant LIQUIDATION_INCENTIVE_UPPER_BOUND = 1.5e18;

    /// @notice Maps hTokens to Bond structs.
    mapping(IHToken => Bond) internal bonds;

    /// @notice Maps IErc20s to Collateral structs.
    mapping(IErc20 => Collateral) internal collaterals;

    /// CONSTRUCTOR ///

    constructor() Ownable() {
        // Set the max bonds limit.
        maxBonds = DEFAULT_MAX_BONDS;

        // Set the max collaterals limit
        maxCollaterals = DEFAULT_MAX_COLLATERALS;
    }

    /// PUBLIC CONSTANT FUNCTIONS ///

    /// @inheritdoc IFintroller
    function getBond(IHToken hToken) external view override returns (Bond memory) {
        return bonds[hToken];
    }

    /// @inheritdoc IFintroller
    function getBorrowAllowed(IHToken bond) external view override returns (bool) {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }
        return bonds[bond].isBorrowAllowed;
    }

    /// @inheritdoc IFintroller
    function getCollateral(IErc20 collateral) external view override returns (Collateral memory) {
        return collaterals[collateral];
    }

    /// @inheritdoc IFintroller
    function getCollateralCeiling(IErc20 collateral) external view override returns (uint256) {
        return collaterals[collateral].ceiling;
    }

    /// @inheritdoc IFintroller
    function getCollateralRatio(IErc20 collateral) external view override returns (uint256) {
        return collaterals[collateral].ratio;
    }

    /// @inheritdoc IFintroller
    function getDebtCeiling(IHToken bond) external view override returns (uint256) {
        return bonds[bond].debtCeiling;
    }

    /// @inheritdoc IFintroller
    function getDepositCollateralAllowed(IErc20 collateral) external view override returns (bool) {
        if (!collaterals[collateral].isListed) {
            revert Fintroller__CollateralNotListed(collateral);
        }
        return collaterals[collateral].isDepositCollateralAllowed;
    }

    /// @inheritdoc IFintroller
    function getDepositUnderlyingAllowed(IHToken bond) external view override returns (bool) {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }
        return bonds[bond].isDepositUnderlyingAllowed;
    }

    /// @inheritdoc IFintroller
    function getLiquidationIncentive(IErc20 collateral) external view override returns (uint256) {
        return collaterals[collateral].liquidationIncentive;
    }

    /// @inheritdoc IFintroller
    function getLiquidateBorrowAllowed(IHToken bond) external view override returns (bool) {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }
        return bonds[bond].isLiquidateBorrowAllowed;
    }

    /// @inheritdoc IFintroller
    function getRepayBorrowAllowed(IHToken bond) external view override returns (bool) {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }
        return bonds[bond].isRepayBorrowAllowed;
    }

    /// @inheritdoc IFintroller
    function isBondListed(IHToken bond) external view override returns (bool) {
        return bonds[bond].isListed;
    }

    /// @notice Checks if the collateral is listed.
    /// @param collateral The collateral to make the check against.
    /// @return bool true = listed, otherwise not.
    function isCollateralListed(IErc20 collateral) external view override returns (bool) {
        return collaterals[collateral].isListed;
    }

    /// PUBLIC NON-CONSTANT FUNCTIONS ///

    /// @inheritdoc IFintroller
    function listBond(IHToken bond) external override onlyOwner {
        bonds[bond] = Bond({
            debtCeiling: 0,
            isBorrowAllowed: true,
            isDepositUnderlyingAllowed: true,
            isLiquidateBorrowAllowed: true,
            isListed: true,
            isRepayBorrowAllowed: true
        });
        emit ListBond(owner, bond);
    }

    /// @inheritdoc IFintroller
    function listCollateral(IErc20 collateral) external override onlyOwner {
        // Checks: decimals are between the expected bounds.
        uint256 decimals = collateral.decimals();
        if (decimals == 0) {
            revert Fintroller__CollateralDecimalsZero();
        }
        if (decimals > 18) {
            revert Fintroller__CollateralDecimalsOverflow(decimals);
        }

        // Effects: update storage.
        collaterals[collateral] = Collateral({
            ceiling: 0,
            isDepositCollateralAllowed: true,
            isListed: true,
            liquidationIncentive: DEFAULT_LIQUIDATION_INCENTIVE,
            ratio: DEFAULT_COLLATERAL_RATIO
        });

        emit ListCollateral(owner, collateral);
    }

    /// @inheritdoc IFintroller
    function setBorrowAllowed(IHToken bond, bool state) external override onlyOwner {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }

        if (state && !bonds[bond].isLiquidateBorrowAllowed) {
            revert Fintroller__BondBorrowAllowedWithLiquidateBorrowDisallowed();
        }

        bonds[bond].isBorrowAllowed = state;
        emit SetBorrowAllowed(owner, bond, state);
    }

    /// @inheritdoc IFintroller
    function setCollateralCeiling(IHToken collateral, uint256 newCollateralCeiling) external override onlyOwner {
        // Checks: collateral is listed.
        if (!collaterals[collateral].isListed) {
            revert Fintroller__CollateralNotListed(collateral);
        }

        // Effects: update storage.
        uint256 oldCollateralCeiling = collaterals[collateral].ceiling;
        collaterals[collateral].ceiling = newCollateralCeiling;

        emit SetCollateralCeiling(owner, collateral, oldCollateralCeiling, newCollateralCeiling);
    }

    /// @inheritdoc IFintroller
    function setCollateralRatio(IErc20 collateral, uint256 newCollateralRatio) external override onlyOwner {
        // Checks: collateral is listed.
        if (!collaterals[collateral].isListed) {
            revert Fintroller__CollateralNotListed(collateral);
        }

        // Checks: new collateral ratio is within the accepted bounds.
        if (newCollateralRatio > COLLATERAL_RATIO_UPPER_BOUND) {
            revert Fintroller__CollateralRatioOverflow(newCollateralRatio);
        }
        if (newCollateralRatio < COLLATERAL_RATIO_LOWER_BOUND) {
            revert Fintroller__CollateralRatioUnderflow(newCollateralRatio);
        }
        if (newCollateralRatio < collaterals[collateral].liquidationIncentive) {
            revert Fintroller__CollateralRatioBelowLiquidationIncentive(newCollateralRatio);
        }

        // Effects: update storage.
        uint256 oldCollateralRatio = collaterals[collateral].ratio;
        collaterals[collateral].ratio = newCollateralRatio;

        emit SetCollateralRatio(owner, collateral, oldCollateralRatio, newCollateralRatio);
    }

    /// @inheritdoc IFintroller
    function setDebtCeiling(IHToken bond, uint256 newDebtCeiling) external override onlyOwner {
        // Checks: bond is listed.
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }

        // Checks: above total supply of hTokens.
        uint256 totalSupply = bond.totalSupply();
        if (newDebtCeiling < totalSupply) {
            revert Fintroller__DebtCeilingUnderflow(newDebtCeiling, totalSupply);
        }

        // Effects: update storage.
        uint256 oldDebtCeiling = bonds[bond].debtCeiling;
        bonds[bond].debtCeiling = newDebtCeiling;

        emit SetDebtCeiling(owner, bond, oldDebtCeiling, newDebtCeiling);
    }

    /// @inheritdoc IFintroller
    function setDepositCollateralAllowed(IErc20 collateral, bool state) external override onlyOwner {
        if (!collaterals[collateral].isListed) {
            revert Fintroller__CollateralNotListed(collateral);
        }
        collaterals[collateral].isDepositCollateralAllowed = state;
        emit SetDepositCollateralAllowed(owner, collateral, state);
    }

    /// @inheritdoc IFintroller
    function setDepositUnderlyingAllowed(IHToken bond, bool state) external override onlyOwner {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }
        bonds[bond].isDepositUnderlyingAllowed = state;
        emit SetDepositUnderlyingAllowed(owner, bond, state);
    }

    /// @inheritdoc IFintroller
    function setLiquidateBorrowAllowed(IHToken bond, bool state) external override onlyOwner {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }

        if (state && !bonds[bond].isRepayBorrowAllowed) {
            revert Fintroller__BondLiquidateBorrowAllowedWithRepayBorrowDisallowed();
        }
        if (!state && bonds[bond].isBorrowAllowed) {
            revert Fintroller__BondLiquidateBorrowDisallowedWithBorrowAllowed();
        }

        bonds[bond].isLiquidateBorrowAllowed = state;
        emit SetLiquidateBorrowAllowed(owner, bond, state);
    }

    /// @inheritdoc IFintroller
    function setLiquidationIncentive(IErc20 collateral, uint256 newLiquidationIncentive) external override onlyOwner {
        // Checks: collateral is listed.
        if (!collaterals[collateral].isListed) {
            revert Fintroller__CollateralNotListed(collateral);
        }

        // Checks: new collateral ratio is within the accepted bounds.
        if (newLiquidationIncentive > LIQUIDATION_INCENTIVE_UPPER_BOUND) {
            revert Fintroller__LiquidationIncentiveOverflow(newLiquidationIncentive);
        }
        if (newLiquidationIncentive < LIQUIDATION_INCENTIVE_LOWER_BOUND) {
            revert Fintroller__LiquidationIncentiveUnderflow(newLiquidationIncentive);
        }
        if (newLiquidationIncentive > collaterals[collateral].ratio) {
            revert Fintroller__LiquidationIncentiveAboveCollateralRatio(newLiquidationIncentive);
        }

        // Effects: update storage.
        uint256 oldLiquidationIncentive = collaterals[collateral].liquidationIncentive;
        collaterals[collateral].liquidationIncentive = newLiquidationIncentive;

        emit SetLiquidationIncentive(owner, collateral, oldLiquidationIncentive, newLiquidationIncentive);
    }

    /// @inheritdoc IFintroller
    function setMaxBonds(uint256 newMaxBonds) external override onlyOwner {
        uint256 oldMaxBonds = maxBonds;
        maxBonds = newMaxBonds;
        emit SetMaxBonds(owner, oldMaxBonds, newMaxBonds);
    }

    /// @inheritdoc IFintroller
    function setMaxCollaterals(uint256 newMaxCollaterals) external override onlyOwner {
        uint256 oldMaxCollaterals = maxCollaterals;
        maxCollaterals = newMaxCollaterals;
        emit SetMaxCollaterals(owner, oldMaxCollaterals, newMaxCollaterals);
    }

    /// @inheritdoc IFintroller
    function setRepayBorrowAllowed(IHToken bond, bool state) external override onlyOwner {
        if (!bonds[bond].isListed) {
            revert Fintroller__BondNotListed(bond);
        }

        if (!state && bonds[bond].isLiquidateBorrowAllowed) {
            revert Fintroller__BondRepayBorrowDisallowedWithLiquidateBorrowAllowed();
        }

        bonds[bond].isRepayBorrowAllowed = state;
        emit SetRepayBorrowAllowed(owner, bond, state);
    }
}

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

import "./IOwnable.sol";

/// @notice Emitted when the caller is not the owner.
error Ownable__NotOwner(address owner, address caller);

/// @notice Emitted when setting the owner to the zero address.
error Ownable__OwnerZeroAddress();

/// @title Ownable
/// @author Paul Razvan Berg
contract Ownable is IOwnable {
    /// PUBLIC STORAGE ///

    /// @inheritdoc IOwnable
    address public override owner;

    /// MODIFIERS ///

    /// @notice Throws if called by any account other than the owner.
    modifier onlyOwner() {
        if (owner != msg.sender) {
            revert Ownable__NotOwner(owner, msg.sender);
        }
        _;
    }

    /// CONSTRUCTOR ///

    /// @notice Initializes the contract setting the deployer as the initial owner.
    constructor() {
        address msgSender = msg.sender;
        owner = msgSender;
        emit TransferOwnership(address(0), msgSender);
    }

    /// PUBLIC NON-CONSTANT FUNCTIONS ///

    /// @inheritdoc IOwnable
    function _renounceOwnership() public virtual override onlyOwner {
        emit TransferOwnership(owner, address(0));
        owner = address(0);
    }

    /// @inheritdoc IOwnable
    function _transferOwnership(address newOwner) public virtual override onlyOwner {
        if (newOwner == address(0)) {
            revert Ownable__OwnerZeroAddress();
        }
        emit TransferOwnership(owner, newOwner);
        owner = newOwner;
    }
}

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

/// @title IErc20
/// @author Paul Razvan Berg
/// @notice Implementation for the Erc20 standard.
///
/// We have followed general OpenZeppelin guidelines: functions revert instead of returning
/// `false` on failure. This behavior is nonetheless conventional and does not conflict with
/// the with the expectations of Erc20 applications.
///
/// Additionally, an {Approval} event is emitted on calls to {transferFrom}. This allows
/// applications to reconstruct the allowance for all accounts just by listening to said
/// events. Other implementations of the Erc may not emit these events, as it isn't
/// required by the specification.
///
/// Finally, the non-standard {decreaseAllowance} and {increaseAllowance} functions have been
/// added to mitigate the well-known issues around setting allowances.
///
/// @dev Forked from OpenZeppelin
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC20/ERC20.sol
interface IErc20 {
    /// EVENTS ///

    /// @notice Emitted when an approval happens.
    /// @param owner The address of the owner of the tokens.
    /// @param spender The address of the spender.
    /// @param amount The maximum amount that can be spent.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @notice Emitted when a transfer happens.
    /// @param from The account sending the tokens.
    /// @param to The account receiving the tokens.
    /// @param amount The amount of tokens transferred.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// CONSTANT FUNCTIONS ///

    /// @notice Returns the remaining number of tokens that `spender` will be allowed to spend
    /// on behalf of `owner` through {transferFrom}. This is zero by default.
    ///
    /// @dev This value changes when {approve} or {transferFrom} are called.
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Returns the amount of tokens owned by `account`.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the number of decimals used to get its user representation.
    function decimals() external view returns (uint8);

    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token, usually a shorter version of the name.
    function symbol() external view returns (string memory);

    /// @notice Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256);

    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// @dev Emits an {Approval} event.
    ///
    /// 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
    ///
    /// Requirements:
    ///
    /// - `spender` cannot be the zero address.
    ///
    /// @return a boolean value indicating whether the operation succeeded.
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Atomically decreases the allowance granted to `spender` by the caller.
    ///
    /// @dev Emits an {Approval} event indicating the updated allowance.
    ///
    /// This is an alternative to {approve} that can be used as a mitigation for problems described
    /// in {Erc20Interface-approve}.
    ///
    /// Requirements:
    ///
    /// - `spender` cannot be the zero address.
    /// - `spender` must have allowance for the caller of at least `subtractedAmount`.
    function decreaseAllowance(address spender, uint256 subtractedAmount) external returns (bool);

    /// @notice Atomically increases the allowance granted to `spender` by the caller.
    ///
    /// @dev Emits an {Approval} event indicating the updated allowance.
    ///
    /// This is an alternative to {approve} that can be used as a mitigation for the problems described above.
    ///
    /// Requirements:
    ///
    /// - `spender` cannot be the zero address.
    function increaseAllowance(address spender, uint256 addedAmount) external returns (bool);

    /// @notice Moves `amount` tokens from the caller's account to `recipient`.
    ///
    /// @dev Emits a {Transfer} event.
    ///
    /// Requirements:
    ///
    /// - `recipient` cannot be the zero address.
    /// - The caller must have a balance of at least `amount`.
    ///
    /// @return a boolean value indicating whether the operation succeeded.
    function transfer(address recipient, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount`
    /// `is then deducted from the caller's allowance.
    ///
    /// @dev Emits a {Transfer} event and an {Approval} event indicating the updated allowance. This is
    /// not required by the Erc. See the note at the beginning of {Erc20}.
    ///
    /// Requirements:
    ///
    /// - `sender` and `recipient` cannot be the zero address.
    /// - `sender` must have a balance of at least `amount`.
    /// - The caller must have approed `sender` to spent at least `amount` tokens.
    ///
    /// @return a boolean value indicating whether the operation succeeded.
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

import "@prb/contracts/token/erc20/IErc20.sol";
import "@prb/contracts/access/IOwnable.sol";

import "../h-token/IHToken.sol";

/// @notice IFintroller
/// @author Hifi
/// @notice Controls the financial permissions and risk parameters for the Hifi protocol.
interface IFintroller is IOwnable {
    /// CUSTOM ERRORS ///

    /// @notice Emitted when interacting with a bond that is not listed.
    error Fintroller__BondNotListed(IHToken bond);

    /// @notice Emitted when allowing borrow for a bond when liquidate borrow is disallowed.
    error Fintroller__BondBorrowAllowedWithLiquidateBorrowDisallowed();

    /// @notice Emitted when allowing liquidate borrow for a bond when repay borrow is disallowed.
    error Fintroller__BondLiquidateBorrowAllowedWithRepayBorrowDisallowed();

    /// @notice Emitted when disallowing liquidate borrow for a bond when borrow is allowed.
    error Fintroller__BondLiquidateBorrowDisallowedWithBorrowAllowed();

    /// @notice Emitted when disallowing repay borrow for a bond when liquidate borrow is allowed.
    error Fintroller__BondRepayBorrowDisallowedWithLiquidateBorrowAllowed();

    /// @notice Emitted when listing a collateral that has more than 18 decimals.
    error Fintroller__CollateralDecimalsOverflow(uint256 decimals);

    /// @notice Emitted when listing a collateral that has zero decimals.
    error Fintroller__CollateralDecimalsZero();

    /// @notice Emitted when interacting with a collateral that is not listed.
    error Fintroller__CollateralNotListed(IErc20 collateral);

    /// @notice Emitted when setting a new collateral ratio that is below the liquidation incentive
    error Fintroller__CollateralRatioBelowLiquidationIncentive(uint256 newCollateralRatio);

    /// @notice Emitted when setting a new collateral ratio that is above the upper bound.
    error Fintroller__CollateralRatioOverflow(uint256 newCollateralRatio);

    /// @notice Emitted when setting a new collateral ratio that is below the lower bound.
    error Fintroller__CollateralRatioUnderflow(uint256 newCollateralRatio);

    /// @notice Emitted when setting a new debt ceiling that is below the total supply of hTokens.
    error Fintroller__DebtCeilingUnderflow(uint256 newDebtCeiling, uint256 totalSupply);

    /// @notice Emitted when setting a new liquidation incentive that is higher than the collateral ratio.
    error Fintroller__LiquidationIncentiveAboveCollateralRatio(uint256 newLiquidationIncentive);

    /// @notice Emitted when setting a new liquidation incentive that is above the upper bound.
    error Fintroller__LiquidationIncentiveOverflow(uint256 newLiquidationIncentive);

    /// @notice Emitted when setting a new liquidation incentive that is below the lower bound.
    error Fintroller__LiquidationIncentiveUnderflow(uint256 newLiquidationIncentive);

    /// EVENTS ///

    /// @notice Emitted when a new bond is listed.
    /// @param owner The address of the contract owner.
    /// @param bond The newly listed bond.
    event ListBond(address indexed owner, IHToken indexed bond);

    /// @notice Emitted when a new collateral is listed.
    /// @param owner The address of the contract owner.
    /// @param collateral The newly listed collateral.
    event ListCollateral(address indexed owner, IErc20 indexed collateral);

    /// @notice Emitted when the borrow permission is updated.
    /// @param owner The address of the contract owner.
    /// @param bond The related HToken.
    /// @param state True if borrowing is allowed.
    event SetBorrowAllowed(address indexed owner, IHToken indexed bond, bool state);

    /// @notice Emitted when the collateral ceiling is updated.
    /// @param owner The address of the contract owner.
    /// @param collateral The related collateral.
    /// @param oldCollateralCeiling The old collateral ceiling.
    /// @param newCollateralCeiling The new collateral ceiling.
    event SetCollateralCeiling(
        address indexed owner,
        IErc20 indexed collateral,
        uint256 oldCollateralCeiling,
        uint256 newCollateralCeiling
    );

    /// @notice Emitted when the collateral ratio is updated.
    /// @param owner The address of the contract owner.
    /// @param collateral The related HToken.
    /// @param oldCollateralRatio The old collateral ratio.
    /// @param newCollateralRatio the new collateral ratio.
    event SetCollateralRatio(
        address indexed owner,
        IErc20 indexed collateral,
        uint256 oldCollateralRatio,
        uint256 newCollateralRatio
    );

    /// @notice Emitted when the debt ceiling for a bond is updated.
    /// @param owner The address of the contract owner.
    /// @param bond The related HToken.
    /// @param oldDebtCeiling The old debt ceiling.
    /// @param newDebtCeiling The new debt ceiling.
    event SetDebtCeiling(address indexed owner, IHToken indexed bond, uint256 oldDebtCeiling, uint256 newDebtCeiling);

    /// @notice Emitted when the deposit collateral permission is updated.
    /// @param owner The address of the contract owner.
    /// @param state True if depositing collateral is allowed.
    event SetDepositCollateralAllowed(address indexed owner, IErc20 indexed collateral, bool state);

    /// @notice Emitted when the deposit underlying permission is set.
    /// @param owner The address of the contract owner.
    /// @param bond The related HToken.
    /// @param state True if deposit underlying is allowed.
    event SetDepositUnderlyingAllowed(address indexed owner, IHToken indexed bond, bool state);

    /// @notice Emitted when the liquidate borrow permission is updated.
    /// @param owner The address of the contract owner.
    /// @param bond The related HToken.
    /// @param state True if liquidating borrow is allowed.
    event SetLiquidateBorrowAllowed(address indexed owner, IHToken indexed bond, bool state);

    /// @notice Emitted when the collateral liquidation incentive is set.
    /// @param owner The address of the contract owner.
    /// @param collateral The related collateral.
    /// @param oldLiquidationIncentive The old liquidation incentive.
    /// @param newLiquidationIncentive The new liquidation incentive.
    event SetLiquidationIncentive(
        address indexed owner,
        IErc20 collateral,
        uint256 oldLiquidationIncentive,
        uint256 newLiquidationIncentive
    );

    /// @notice Emitted when a new max bonds value is set.
    /// @param owner The address indexed owner.
    /// @param oldMaxBonds The address of the old max bonds value.
    /// @param newMaxBonds The address of the new max bonds value.
    event SetMaxBonds(address indexed owner, uint256 oldMaxBonds, uint256 newMaxBonds);

    /// @notice Emitted when a new max collaterals value is set.
    /// @param owner The address indexed owner.
    /// @param oldMaxCollaterals The address of the old max collaterals value.
    /// @param newMaxCollaterals The address of the new max collaterals value.
    event SetMaxCollaterals(address indexed owner, uint256 oldMaxCollaterals, uint256 newMaxCollaterals);

    /// @notice Emitted when the repay borrow permission is updated.
    /// @param owner The address of the contract owner.
    /// @param bond The related HToken.
    /// @param state True if repaying borrow is allowed.
    event SetRepayBorrowAllowed(address indexed owner, IHToken indexed bond, bool state);

    /// STRUCTS ///

    struct Bond {
        uint256 debtCeiling;
        bool isBorrowAllowed;
        bool isDepositUnderlyingAllowed;
        bool isLiquidateBorrowAllowed;
        bool isListed;
        bool isRepayBorrowAllowed;
    }

    struct Collateral {
        uint256 ceiling;
        uint256 ratio;
        uint256 liquidationIncentive;
        bool isDepositCollateralAllowed;
        bool isListed;
    }

    /// CONSTANT FUNCTIONS ///

    /// @notice Returns the Bond struct instance associated to the given address.
    /// @dev It is not an error to provide an invalid address.
    /// @param bond The address of the bond contract.
    /// @return The bond object.
    function getBond(IHToken bond) external view returns (Bond memory);

    /// @notice Checks if the account should be allowed to borrow hTokens.
    /// @dev The bond must be listed.
    /// @param bond The bond to make the check against.
    /// @return bool true = allowed, false = not allowed.
    function getBorrowAllowed(IHToken bond) external view returns (bool);

    /// @notice Returns the Collateral struct instance associated to the given address.
    /// @dev It is not an error to provide an invalid address.
    /// @param collateral The address of the collateral contract.
    /// @return The collateral object.
    function getCollateral(IErc20 collateral) external view returns (Collateral memory);

    /// @notice Returns the collateral ceiling.
    /// @dev It is not an error to provide an invalid address.
    /// @param collateral The address of the collateral contract.
    /// @return The collateral ceiling as a uint256, or zero if an invalid address was provided.
    function getCollateralCeiling(IErc20 collateral) external view returns (uint256);

    /// @notice Returns the collateral ratio.
    /// @dev It is not an error to provide an invalid address.
    /// @param collateral The address of the collateral contract.
    /// @return The collateral ratio, or zero if an invalid address was provided.
    function getCollateralRatio(IErc20 collateral) external view returns (uint256);

    /// @notice Returns the debt ceiling for the given bond.
    /// @dev It is not an error to provide an invalid address.
    /// @param bond The address of the bond contract.
    /// @return The debt ceiling as a uint256, or zero if an invalid address was provided.
    function getDebtCeiling(IHToken bond) external view returns (uint256);

    /// @notice Checks if collateral deposits are allowed.
    /// @dev The collateral must be listed.
    /// @param collateral The collateral to make the check against.
    /// @return bool true = allowed, false = not allowed.
    function getDepositCollateralAllowed(IErc20 collateral) external view returns (bool);

    /// @notice Checks if underlying deposits are allowed.
    /// @dev The bond must be listed.
    /// @param bond The bond to make the check against.
    /// @return bool true = allowed, false = not allowed.
    function getDepositUnderlyingAllowed(IHToken bond) external view returns (bool);

    /// @notice Returns the liquidation incentive of the given collateral.
    /// @dev It is not an error to provide an invalid address.
    /// @param collateral The address of the collateral contract.
    /// @return The liquidation incentive, or zero if an invalid address was provided.
    function getLiquidationIncentive(IErc20 collateral) external view returns (uint256);

    /// @notice Checks if the account should be allowed to liquidate hToken borrows.
    /// @dev The bond must be listed.
    /// @param bond The bond to make the check against.
    /// @return bool true = allowed, false = not allowed.
    function getLiquidateBorrowAllowed(IHToken bond) external view returns (bool);

    /// @notice Checks if the account should be allowed to repay borrows.
    /// @dev The bond must be listed.
    /// @param bond The bond to make the check against.
    /// @return bool true = allowed, false = not allowed.
    function getRepayBorrowAllowed(IHToken bond) external view returns (bool);

    /// @notice Checks if the bond is listed.
    /// @param bond The bond to make the check against.
    /// @return bool true = listed, otherwise not.
    function isBondListed(IHToken bond) external view returns (bool);

    /// @notice Checks if the collateral is listed.
    /// @param collateral The collateral to make the check against.
    /// @return bool true = listed, otherwise not.
    function isCollateralListed(IErc20 collateral) external view returns (bool);

    /// @notice Returns the maximum number of bond markets a single account can enter.
    function maxBonds() external view returns (uint256);

    /// @notice Returns the maximum number of Collaterals a single account can deposit.
    function maxCollaterals() external view returns (uint256);

    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Marks the bond as listed in this registry.
    ///
    /// @dev It is not an error to list a bond twice. Emits a {ListBond} event.
    ///
    /// Requirements:
    /// - The caller must be the owner.
    ///
    /// @param bond The hToken contract to list.
    function listBond(IHToken bond) external;

    /// @notice Marks the collateral as listed in this registry.
    ///
    /// @dev Emits a {ListCollateral} event. It is not an error to list a bond twice.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The collateral must have between 1 and 18 decimals.
    ///
    /// @param collateral The collateral contract to list.
    function listCollateral(IErc20 collateral) external;

    /// @notice Updates the state of the permission accessed by the hToken before a borrow.
    ///
    /// @dev Emits a {SetBorrowAllowed} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The bond must be listed.
    ///
    /// @param bond The bond to update the permission for.
    /// @param state The new state to put in storage.
    function setBorrowAllowed(IHToken bond, bool state) external;

    /// @notice Updates the collateral ceiling.
    ///
    /// @dev Emits a {SetCollateralCeiling} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The collateral must be listed.
    ///
    /// @param collateral The collateral to update the ceiling for.
    /// @param newCollateralCeiling The new collateral ceiling.
    function setCollateralCeiling(IHToken collateral, uint256 newCollateralCeiling) external;

    /// @notice Updates the collateral ratio.
    ///
    /// @dev Emits a {SetCollateralRatio} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The collateral must be listed.
    /// - The new collateral ratio cannot be higher than the maximum collateral ratio.
    /// - The new collateral ratio cannot be lower than the minimum collateral ratio.
    ///
    /// @param collateral The collateral to update the collateral ratio for.
    /// @param newCollateralRatio The new collateral ratio.
    function setCollateralRatio(IErc20 collateral, uint256 newCollateralRatio) external;

    /// @notice Updates the debt ceiling for the given bond.
    ///
    /// @dev Emits a {SetDebtCeiling} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The bond must be listed.
    /// - The debt ceiling cannot fall below the current total supply of hTokens.
    ///
    /// @param bond The bond to update the debt ceiling for.
    /// @param newDebtCeiling The new debt ceiling.
    function setDebtCeiling(IHToken bond, uint256 newDebtCeiling) external;

    /// @notice Updates the state of the permission accessed by the BalanceSheet before a collateral deposit.
    ///
    /// @dev Emits a {SetDepositCollateralAllowed} event.
    ///
    /// Requirements:
    /// - The caller must be the owner.
    ///
    /// @param collateral The collateral to update the permission for.
    /// @param state The new state to put in storage.
    function setDepositCollateralAllowed(IErc20 collateral, bool state) external;

    /// @notice Updates the state of the permission accessed by the hToken before an underlying deposit.
    ///
    /// @dev Emits a {SetDepositUnderlyingAllowed} event.
    ///
    /// Requirements:
    /// - The caller must be the owner.
    ///
    /// @param bond The bond to update the permission for.
    /// @param state The new state to put in storage.
    function setDepositUnderlyingAllowed(IHToken bond, bool state) external;

    /// @notice Updates the collateral liquidation incentive.
    ///
    /// @dev Emits a {SetLiquidationIncentive} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The collateral must be listed.
    /// - The new liquidation incentive cannot be higher than the maximum liquidation incentive.
    /// - The new liquidation incentive cannot be lower than the minimum liquidation incentive.
    ///
    /// @param collateral The collateral to update the liquidation incentive for.
    /// @param newLiquidationIncentive The new liquidation incentive.
    function setLiquidationIncentive(IErc20 collateral, uint256 newLiquidationIncentive) external;

    /// @notice Updates the state of the permission accessed by the hToken before a liquidate borrow.
    ///
    /// @dev Emits a {SetLiquidateBorrowAllowed} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The bond must be listed.
    ///
    /// @param bond The hToken contract to update the permission for.
    /// @param state The new state to put in storage.
    function setLiquidateBorrowAllowed(IHToken bond, bool state) external;

    /// @notice Sets max bonds value, which controls how many bond markets a single account can enter.
    ///
    /// @dev Emits a {SetMaxBonds} event.
    ///
    /// Requirements:
    /// - The caller must be the owner.
    ///
    /// @param newMaxBonds New max bonds value.
    function setMaxBonds(uint256 newMaxBonds) external;

    /// @notice Sets max collaterals value, which controls how many collaterals a single account can deposit.
    ///
    /// @dev Emits a {SetMaxCollaterals} event.
    ///
    /// Requirements:
    /// - The caller must be the owner.
    ///
    /// @param newMaxCollaterals New max collaterals value.
    function setMaxCollaterals(uint256 newMaxCollaterals) external;

    /// @notice Updates the state of the permission accessed by the hToken before a repay borrow.
    ///
    /// @dev Emits a {SetRepayBorrowAllowed} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The bond must be listed.
    ///
    /// @param bond The hToken contract to update the permission for.
    /// @param state The new state to put in storage.
    function setRepayBorrowAllowed(IHToken bond, bool state) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

import "@prb/contracts/access/IOwnable.sol";
import "@prb/contracts/token/erc20/IErc20.sol";
import "@prb/contracts/token/erc20/IErc20Permit.sol";
import "@prb/contracts/token/erc20/IErc20Recover.sol";

import "../balance-sheet/IBalanceSheetV2.sol";
import "../fintroller/IFintroller.sol";

/// @title IHToken
/// @author Hifi
/// @notice Zero-coupon bond that tracks an Erc20 underlying asset.
interface IHToken is
    IOwnable, // no dependency
    IErc20Permit, // one dependency
    IErc20Recover // one dependency
{
    /// CUSTOM ERRORS ///

    /// @notice Emitted when the bond matured.
    error HToken__BondMatured(uint256 now, uint256 maturity);

    /// @notice Emitted when the bond did not mature.
    error HToken__BondNotMatured(uint256 now, uint256 maturity);

    /// @notice Emitted when burning hTokens and the caller is not the BalanceSheet contract.
    error HToken__BurnNotAuthorized(address caller);

    /// @notice Emitted when underlying deposits are not allowed by the Fintroller contract.
    error HToken__DepositUnderlyingNotAllowed();

    /// @notice Emitted when depositing a zero amount of underlying.
    error HToken__DepositUnderlyingZero();

    /// @notice Emitted when the maturity is in the past.
    error HToken__MaturityPassed(uint256 now, uint256 maturity);

    /// @notice Emitted when minting hTokens and the caller is not the BalanceSheet contract.
    error HToken__MintNotAuthorized(address caller);

    /// @notice Emitted when redeeming more underlying that there is in the reserve.
    error HToken__RedeemInsufficientLiquidity(uint256 underlyingAmount, uint256 totalUnderlyingReserve);

    /// @notice Emitted when redeeming a zero amount of underlying.
    error HToken__RedeemZero();

    /// @notice Emitted when constructing the contract and the underlying has more than 18 decimals.
    error HToken__UnderlyingDecimalsOverflow(uint256 decimals);

    /// @notice Emitted when constructing the contract and the underlying has zero decimals.
    error HToken__UnderlyingDecimalsZero();

    /// @notice Emitted when withdrawing more underlying than there is available.
    error HToken__WithdrawUnderlyingUnderflow(address depositor, uint256 availableAmount, uint256 underlyingAmount);

    /// @notice Emitted when withdrawing a zero amount of underlying.
    error HToken__WithdrawUnderlyingZero();

    /// EVENTS ///

    /// @notice Emitted when tokens are burnt.
    /// @param holder The address of the holder.
    /// @param burnAmount The amount of burnt tokens.
    event Burn(address indexed holder, uint256 burnAmount);

    /// @notice Emitted when underlying is deposited in exchange for an equivalent amount of hTokens.
    /// @param depositor The address of the depositor.
    /// @param depositUnderlyingAmount The amount of deposited underlying.
    /// @param hTokenAmount The amount of minted hTokens.
    event DepositUnderlying(address indexed depositor, uint256 depositUnderlyingAmount, uint256 hTokenAmount);

    /// @notice Emitted when tokens are minted.
    /// @param beneficiary The address of the holder.
    /// @param mintAmount The amount of minted tokens.
    event Mint(address indexed beneficiary, uint256 mintAmount);

    /// @notice Emitted when underlying is redeemed.
    /// @param account The account redeeming the underlying.
    /// @param underlyingAmount The amount of redeemed underlying.
    /// @param hTokenAmount The amount of provided hTokens.
    event Redeem(address indexed account, uint256 underlyingAmount, uint256 hTokenAmount);

    /// @notice Emitted when the BalanceSheet is set.
    /// @param owner The address of the owner.
    /// @param oldBalanceSheet The address of the old BalanceSheet.
    /// @param newBalanceSheet The address of the new BalanceSheet.
    event SetBalanceSheet(address indexed owner, IBalanceSheetV2 oldBalanceSheet, IBalanceSheetV2 newBalanceSheet);

    /// @notice Emitted when a depositor withdraws previously deposited underlying.
    /// @param depositor The address of the depositor.
    /// @param underlyingAmount The amount of withdrawn underlying.
    /// @param hTokenAmount The amount of minted hTokens.
    event WithdrawUnderlying(address indexed depositor, uint256 underlyingAmount, uint256 hTokenAmount);

    /// PUBLIC CONSTANT FUNCTIONS ///

    /// @notice Returns the BalanceSheet contract this HToken is connected to.
    function balanceSheet() external view returns (IBalanceSheetV2);

    /// @notice Returns the balance of the given depositor.
    function getDepositorBalance(address depositor) external view returns (uint256 amount);

    /// @notice Returns the Fintroller contract this HToken is connected to.
    function fintroller() external view returns (IFintroller);

    /// @notice Checks if the bond matured.
    /// @return bool true = bond matured, otherwise it didn't.
    function isMatured() external view returns (bool);

    /// @notice Unix timestamp in seconds for when this HToken matures.
    function maturity() external view returns (uint256);

    /// @notice The amount of underlying redeemable after maturation.
    function totalUnderlyingReserve() external view returns (uint256);

    /// @notice The Erc20 underlying asset for this HToken.
    function underlying() external view returns (IErc20);

    /// @notice The ratio between normalized precision (1e18) and the underlying precision.
    function underlyingPrecisionScalar() external view returns (uint256);

    /// PUBLIC NON-CONSTANT FUNCTIONS ///

    /// @notice Destroys `burnAmount` tokens from `holder`, reducing the token supply.
    ///
    /// @dev Emits a {Burn} and a {Transfer} event.
    ///
    /// Requirements:
    /// - Can only be called by the BalanceSheet contract.
    ///
    /// @param holder The account whose hTokens to burn.
    /// @param burnAmount The amount of hTokens to burn.
    function burn(address holder, uint256 burnAmount) external;

    /// @notice Deposits underlying in exchange for an equivalent amount of hTokens.
    ///
    /// @dev Emits a {DepositUnderlying} event.
    ///
    /// Requirements:
    ///
    /// - The Fintroller must allow this action to be performed.
    /// - The underlying amount to deposit cannot be zero.
    /// - The caller must have allowed this contract to spend `underlyingAmount` tokens.
    ///
    /// @param underlyingAmount The amount of underlying to deposit.
    function depositUnderlying(uint256 underlyingAmount) external;

    /// @notice Prints new tokens into existence and assigns them to `beneficiary`, increasing the total supply.
    ///
    /// @dev Emits a {Mint} and a {Transfer} event.
    ///
    /// Requirements:
    /// - Can only be called by the BalanceSheet contract.
    ///
    /// @param beneficiary The account to mint the hTokens for.
    /// @param mintAmount The amount of hTokens to print into existence.
    function mint(address beneficiary, uint256 mintAmount) external;

    /// @notice Pays the token holder the face value after maturation.
    ///
    /// @dev Emits a {Redeem} event.
    ///
    /// Requirements:
    ///
    /// - Can only be called after maturation.
    /// - The amount of underlying to redeem cannot be zero.
    /// - There must be enough liquidity in the contract.
    ///
    /// @param underlyingAmount The amount of underlying to redeem.
    function redeem(uint256 underlyingAmount) external;

    /// @notice Updates the BalanceSheet contract this HToken is connected to.
    ///
    /// @dev Throws a {SetBalanceSheet} event.
    ///
    /// Requirements:
    /// - The caller must be the owner.
    ///
    /// @param newBalanceSheet The address of the new BalanceSheet contract.
    function _setBalanceSheet(IBalanceSheetV2 newBalanceSheet) external;

    /// @notice Withdraws underlying in exchange for hTokens.
    ///
    /// @dev Emits a {WithdrawUnderlying} event.
    ///
    /// Requirements:
    ///
    /// - The underlying amount to withdraw cannot be zero.
    /// - Can only be called before maturation.
    ///
    /// @param underlyingAmount The amount of underlying to withdraw.
    function withdrawUnderlying(uint256 underlyingAmount) external;
}

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

/// @title IOwnable
/// @author Paul Razvan Berg
/// @notice Contract module that 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 {transfer}.
///
/// 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.
///
/// @dev Forked from OpenZeppelin
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/access/Ownable.sol
interface IOwnable {
    /// EVENTS ///

    /// @notice Emitted when ownership is transferred.
    /// @param oldOwner The address of the old owner.
    /// @param newOwner The address of the new owner.
    event TransferOwnership(address indexed oldOwner, address indexed newOwner);

    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Leaves the contract without owner, so it will not be possible to call `onlyOwner`
    /// functions anymore.
    ///
    /// WARNING: Doing this will leave the contract without an owner, thereby removing any
    /// functionality that is only available to the owner.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    function _renounceOwnership() external;

    /// @notice Transfers the owner of the contract to a new account (`newOwner`). Can only be
    /// called by the current owner.
    /// @param newOwner The account of the new owner.
    function _transferOwnership(address newOwner) external;

    /// CONSTANT FUNCTIONS ///

    /// @notice The address of the owner account or contract.
    /// @return The address of the owner.
    function owner() external view returns (address);
}

// SPDX-License-Identifier: Unlicense
// solhint-disable func-name-mixedcase
pragma solidity >=0.8.4;

import "./IErc20.sol";

/// @title IErc20Permit
/// @author Paul Razvan Berg
/// @notice Extension of Erc20 that allows token holders to use their tokens without sending any
/// transactions by setting the allowance with a signature using the `permit` method, and then spend
/// them via `transferFrom`.
/// @dev See https://eips.ethereum.org/EIPS/eip-2612.
interface IErc20Permit is IErc20 {
    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Sets `value` as the allowance of `spender` over `owner`'s tokens, assuming the latter's
    /// signed approval.
    ///
    /// @dev Emits an {Approval} event.
    ///
    /// IMPORTANT: The same issues Erc20 `approve` has related to transaction
    /// ordering also apply here.
    ///
    /// Requirements:
    ///
    /// - `owner` cannot be the zero address.
    /// - `spender` cannot be the zero address.
    /// - `deadline` must be a timestamp in the future.
    /// - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` over the Eip712-formatted
    /// function arguments.
    /// - The signature must use `owner`'s current nonce.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /// CONSTANT FUNCTIONS ///

    /// @notice The Eip712 domain's keccak256 hash.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice Provides replay protection.
    function nonces(address account) external view returns (uint256);

    /// @notice keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    function PERMIT_TYPEHASH() external view returns (bytes32);

    /// @notice Eip712 version of this implementation.
    function version() external view returns (string memory);
}

// SPDX-License-Identifier: Unlicense
// solhint-disable var-name-mixedcase
pragma solidity >=0.8.4;

import "./IErc20.sol";
import "../../access/IOwnable.sol";

/// @title IErc20Recover
/// @author Paul Razvan Berg
/// @notice Contract that gives the owner the ability to recover the Erc20 tokens that were sent
/// (accidentally, or not) to the contract.
interface IErc20Recover is IOwnable {
    /// EVENTS ///

    /// @notice Emitted when tokens are recovered.
    /// @param owner The address of the owner recoverring the tokens.
    /// @param token The address of the recovered token.
    /// @param recoverAmount The amount of recovered tokens.
    event Recover(address indexed owner, IErc20 token, uint256 recoverAmount);

    /// @notice Emitted when tokens are set as non-recoverable.
    /// @param owner The address of the owner calling the function.
    /// @param nonRecoverableTokens An array of token addresses.
    event SetNonRecoverableTokens(address indexed owner, IErc20[] nonRecoverableTokens);

    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Recover Erc20 tokens sent to this contract (by accident or otherwise).
    /// @dev Emits a {RecoverToken} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The contract must be initialized.
    /// - The amount to recover cannot be zero.
    /// - The token to recover cannot be among the non-recoverable tokens.
    ///
    /// @param token The token to make the recover for.
    /// @param recoverAmount The uint256 amount to recover, specified in the token's decimal system.
    function _recover(IErc20 token, uint256 recoverAmount) external;

    /// @notice Sets the tokens that this contract cannot recover.
    ///
    /// @dev Emits a {SetNonRecoverableTokens} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The contract cannot be already initialized.
    ///
    /// @param tokens The array of tokens to set as non-recoverable.
    function _setNonRecoverableTokens(IErc20[] calldata tokens) external;

    /// CONSTANT FUNCTIONS ///

    /// @notice The tokens that can be recovered cannot be in this mapping.
    function nonRecoverableTokens(uint256 index) external view returns (IErc20);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

import "@prb/contracts/token/erc20/IErc20.sol";

import "../fintroller/IFintroller.sol";
import "../h-token/IHToken.sol";
import "../../access/IOwnableUpgradeable.sol";
import "../../oracles/IChainlinkOperator.sol";

/// @title IBalanceSheetV2
/// @author Hifi
/// @notice Manages the collaterals and the debts for all users.
interface IBalanceSheetV2 is IOwnableUpgradeable {
    /// CUSTOM ERRORS ///

    /// @notice Emitted when the bond matured.
    error BalanceSheet__BondMatured(IHToken bond);

    /// @notice Emitted when the account exceeds the maximum numbers of bonds permitted.
    error BalanceSheet__BorrowMaxBonds(IHToken bond, uint256 newBondListLength, uint256 maxBonds);

    /// @notice Emitted when the account exceeds the maximum numbers of collateral permitted.
    error BalanceSheet__DepositMaxCollaterals(
        IErc20 collateral,
        uint256 newCollateralListLength,
        uint256 maxCollaterals
    );

    /// @notice Emitted when borrows are not allowed by the Fintroller contract.
    error BalanceSheet__BorrowNotAllowed(IHToken bond);

    /// @notice Emitted when borrowing a zero amount of hTokens.
    error BalanceSheet__BorrowZero();

    /// @notice Emitted when the new collateral amount exceeds the collateral ceiling.
    error BalanceSheet__CollateralCeilingOverflow(uint256 newTotalSupply, uint256 debtCeiling);

    /// @notice Emitted when the new total amount of debt exceeds the debt ceiling.
    error BalanceSheet__DebtCeilingOverflow(uint256 newCollateralAmount, uint256 debtCeiling);

    /// @notice Emitted when collateral deposits are not allowed by the Fintroller contract.
    error BalanceSheet__DepositCollateralNotAllowed(IErc20 collateral);

    /// @notice Emitted when depositing a zero amount of collateral.
    error BalanceSheet__DepositCollateralZero();

    /// @notice Emitted when setting the Fintroller contract to the zero address.
    error BalanceSheet__FintrollerZeroAddress();

    /// @notice Emitted when there is not enough collateral to seize.
    error BalanceSheet__LiquidateBorrowInsufficientCollateral(
        address account,
        uint256 vaultCollateralAmount,
        uint256 seizableAmount
    );

    /// @notice Emitted when borrow liquidations are not allowed by the Fintroller contract.
    error BalanceSheet__LiquidateBorrowNotAllowed(IHToken bond);

    /// @notice Emitted when the borrower is liquidating themselves.
    error BalanceSheet__LiquidateBorrowSelf(address account);

    /// @notice Emitted when there is a liquidity shortfall.
    error BalanceSheet__LiquidityShortfall(address account, uint256 shortfallLiquidity);

    /// @notice Emitted when there is no liquidity shortfall.
    error BalanceSheet__NoLiquidityShortfall(address account);

    /// @notice Emitted when setting the oracle contract to the zero address.
    error BalanceSheet__OracleZeroAddress();

    /// @notice Emitted when the repayer does not have enough hTokens to repay the debt.
    error BalanceSheet__RepayBorrowInsufficientBalance(IHToken bond, uint256 repayAmount, uint256 hTokenBalance);

    /// @notice Emitted when repaying more debt than the borrower owes.
    error BalanceSheet__RepayBorrowInsufficientDebt(IHToken bond, uint256 repayAmount, uint256 debtAmount);

    /// @notice Emitted when borrow repays are not allowed by the Fintroller contract.
    error BalanceSheet__RepayBorrowNotAllowed(IHToken bond);

    /// @notice Emitted when repaying a borrow with a zero amount of hTokens.
    error BalanceSheet__RepayBorrowZero();

    /// @notice Emitted when withdrawing more collateral than there is in the vault.
    error BalanceSheet__WithdrawCollateralUnderflow(
        address account,
        uint256 vaultCollateralAmount,
        uint256 withdrawAmount
    );

    /// @notice Emitted when withdrawing a zero amount of collateral.
    error BalanceSheet__WithdrawCollateralZero();

    /// EVENTS ///

    /// @notice Emitted when a borrow is made.
    /// @param account The address of the borrower.
    /// @param bond The address of the bond contract.
    /// @param borrowAmount The amount of hTokens borrowed.
    event Borrow(address indexed account, IHToken indexed bond, uint256 borrowAmount);

    /// @notice Emitted when collateral is deposited.
    /// @param account The address of the borrower.
    /// @param collateral The related collateral.
    /// @param collateralAmount The amount of deposited collateral.
    event DepositCollateral(address indexed account, IErc20 indexed collateral, uint256 collateralAmount);

    /// @notice Emitted when a borrow is liquidated.
    /// @param liquidator The address of the liquidator.
    /// @param borrower The address of the borrower.
    /// @param bond The address of the bond contract.
    /// @param repayAmount The amount of repaid funds.
    /// @param collateral The address of the collateral contract.
    /// @param seizedCollateralAmount The amount of seized collateral.
    event LiquidateBorrow(
        address indexed liquidator,
        address indexed borrower,
        IHToken indexed bond,
        uint256 repayAmount,
        IErc20 collateral,
        uint256 seizedCollateralAmount
    );

    /// @notice Emitted when a borrow is repaid.
    /// @param payer The address of the payer.
    /// @param borrower The address of the borrower.
    /// @param bond The address of the bond contract.
    /// @param repayAmount The amount of repaid funds.
    /// @param newDebtAmount The amount of the new debt.
    event RepayBorrow(
        address indexed payer,
        address indexed borrower,
        IHToken indexed bond,
        uint256 repayAmount,
        uint256 newDebtAmount
    );

    /// @notice Emitted when a new Fintroller contract is set.
    /// @param owner The address of the owner.
    /// @param oldFintroller The address of the old Fintroller contract.
    /// @param newFintroller The address of the new Fintroller contract.
    event SetFintroller(address indexed owner, address oldFintroller, address newFintroller);

    /// @notice Emitted when a new oracle contract is set.
    /// @param owner The address of the owner.
    /// @param oldOracle The address of the old oracle contract.
    /// @param newOracle The address of the new oracle contract.
    event SetOracle(address indexed owner, address oldOracle, address newOracle);

    /// @notice Emitted when collateral is withdrawn.
    /// @param account The address of the borrower.
    /// @param collateral The related collateral.
    /// @param collateralAmount The amount of withdrawn collateral.
    event WithdrawCollateral(address indexed account, IErc20 indexed collateral, uint256 collateralAmount);

    /// CONSTANT FUNCTIONS ///

    /// @notice Returns the list of bond markets the given account entered.
    /// @dev It is not an error to provide an invalid address.
    /// @param account The borrower account to make the query against.
    function getBondList(address account) external view returns (IHToken[] memory);

    /// @notice Returns the amount of collateral deposited by the given account for the given collateral type.
    /// @dev It is not an error to provide an invalid address.
    /// @param account The borrower account to make the query against.
    /// @param collateral The collateral to make the query against.
    function getCollateralAmount(address account, IErc20 collateral) external view returns (uint256 collateralAmount);

    /// @notice Returns the list of collaterals the given account deposited.
    /// @dev It is not an error to provide an invalid address.
    /// @param account The borrower account to make the query against.
    function getCollateralList(address account) external view returns (IErc20[] memory);

    /// @notice Calculates the current account liquidity.
    /// @param account The account to make the query against.
    /// @return excessLiquidity account liquidity in excess of collateral requirements.
    /// @return shortfallLiquidity account shortfall below collateral requirements
    function getCurrentAccountLiquidity(address account)
        external
        view
        returns (uint256 excessLiquidity, uint256 shortfallLiquidity);

    /// @notice Returns the amount of debt accrued by the given account in the given bond market.
    /// @dev It is not an error to provide an invalid address.
    /// @param account The borrower account to make the query against.
    /// @param bond The bond to make the query against.
    function getDebtAmount(address account, IHToken bond) external view returns (uint256 debtAmount);

    /// @notice Calculates the account liquidity given a modified collateral, collateral amount, bond and debt amount,
    /// using the current prices provided by the oracle.
    ///
    /// @dev Works by summing up each collateral amount multiplied by the USD value of each unit and divided by its
    /// respective collateral ratio, then dividing the sum by the total amount of debt drawn by the user.
    ///
    /// Caveats:
    /// - This function expects that the "collateralList" and the "bondList" are each modified in advance to include
    /// the collateral and bond due to be modified.
    ///
    /// @param account The account to make the query against.
    /// @param collateralModify The collateral to make the check against.
    /// @param collateralAmountModify The hypothetical normalized amount of collateral.
    /// @param bondModify The bond to make the check against.
    /// @param debtAmountModify The hypothetical amount of debt.
    /// @return excessLiquidity hypothetical account liquidity in excess of collateral requirements.
    /// @return shortfallLiquidity hypothetical account shortfall below collateral requirements
    function getHypotheticalAccountLiquidity(
        address account,
        IErc20 collateralModify,
        uint256 collateralAmountModify,
        IHToken bondModify,
        uint256 debtAmountModify
    ) external view returns (uint256 excessLiquidity, uint256 shortfallLiquidity);

    /// @notice Calculates the amount of hTokens that should be repaid in order to seize a given amount of collateral.
    /// Note that this is for informational purposes only, it doesn't say anything about whether the user can be
    /// liquidated.
    /// @dev The formula used is:
    /// repayAmount = (seizableCollateralAmount * collateralPriceUsd) / (liquidationIncentive * underlyingPriceUsd)
    /// @param collateral The collateral to make the query against.
    /// @param seizableCollateralAmount The amount of collateral to seize.
    /// @param bond The bond to make the query against.
    /// @return repayAmount The amount of hTokens that should be repaid.
    function getRepayAmount(
        IErc20 collateral,
        uint256 seizableCollateralAmount,
        IHToken bond
    ) external view returns (uint256 repayAmount);

    /// @notice Calculates the amount of collateral that can be seized when liquidating a borrow. Note that this
    /// is for informational purposes only, it doesn't say anything about whether the user can be liquidated.
    /// @dev The formula used is:
    /// seizableCollateralAmount = repayAmount * liquidationIncentive * underlyingPriceUsd / collateralPriceUsd
    /// @param bond The bond to make the query against.
    /// @param repayAmount The amount of hTokens to repay.
    /// @param collateral The collateral to make the query against.
    /// @return seizableCollateralAmount The amount of seizable collateral.
    function getSeizableCollateralAmount(
        IHToken bond,
        uint256 repayAmount,
        IErc20 collateral
    ) external view returns (uint256 seizableCollateralAmount);

    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Increases the debt of the caller and mints new hTokens.
    ///
    /// @dev Emits a {Borrow} event.
    ///
    /// Requirements:
    ///
    /// - The Fintroller must allow this action to be performed.
    /// - The maturity of the bond must be in the future.
    /// - The amount to borrow cannot be zero.
    /// - The new length of the bond list must be below the max bonds limit.
    /// - The new total amount of debt cannot exceed the debt ceiling.
    /// - The caller must not end up having a shortfall of liquidity.
    ///
    /// @param bond The address of the bond contract.
    /// @param borrowAmount The amount of hTokens to borrow and print into existence.
    function borrow(IHToken bond, uint256 borrowAmount) external;

    /// @notice Deposits collateral in the caller's account.
    ///
    /// @dev Emits a {DepositCollateral} event.
    ///
    /// Requirements:
    ///
    /// - The Fintroller must allow this action to be performed.
    /// - The amount to deposit cannot be zero.
    /// - The caller must have allowed this contract to spend `collateralAmount` tokens.
    /// - The new collateral amount cannot exceed the collateral ceiling.
    ///
    /// @param collateral The address of the collateral contract.
    /// @param depositAmount The amount of collateral to deposit.
    function depositCollateral(IErc20 collateral, uint256 depositAmount) external;

    /// @notice Repays the debt of the borrower and rewards the caller with a surplus of collateral.
    ///
    /// @dev Emits a {LiquidateBorrow} event.
    ///
    /// Requirements:
    ///
    /// - All from "repayBorrow".
    /// - The caller cannot be the same with the borrower.
    /// - The Fintroller must allow this action to be performed.
    /// - The borrower must have a shortfall of liquidity if the bond didn't mature.
    /// - The amount of seized collateral cannot be more than what the borrower has in the vault.
    ///
    /// @param bond The address of the bond contract.
    /// @param borrower The account to liquidate.
    /// @param repayAmount The amount of hTokens to repay.
    /// @param collateral The address of the collateral contract.
    function liquidateBorrow(
        address borrower,
        IHToken bond,
        uint256 repayAmount,
        IErc20 collateral
    ) external;

    /// @notice Erases the borrower's debt and takes the hTokens out of circulation.
    ///
    /// @dev Emits a {RepayBorrow} event.
    ///
    /// Requirements:
    ///
    /// - The amount to repay cannot be zero.
    /// - The Fintroller must allow this action to be performed.
    /// - The caller must have at least `repayAmount` hTokens.
    /// - The caller must have at least `repayAmount` debt.
    ///
    /// @param bond The address of the bond contract.
    /// @param repayAmount The amount of hTokens to repay.
    function repayBorrow(IHToken bond, uint256 repayAmount) external;

    /// @notice Erases the borrower's debt and takes the hTokens out of circulation.
    ///
    /// @dev Emits a {RepayBorrow} event.
    ///
    /// Requirements:
    /// - Same as the `repayBorrow` function, but here `borrower` is the account that must have at least
    /// `repayAmount` hTokens to repay the borrow.
    ///
    /// @param borrower The borrower account for which to repay the borrow.
    /// @param bond The address of the bond contract
    /// @param repayAmount The amount of hTokens to repay.
    function repayBorrowBehalf(
        address borrower,
        IHToken bond,
        uint256 repayAmount
    ) external;

    /// @notice Updates the Fintroller contract this BalanceSheet is connected to.
    ///
    /// @dev Emits a {SetFintroller} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The new address cannot be the zero address.
    ///
    /// @param newFintroller The new Fintroller contract.
    function setFintroller(IFintroller newFintroller) external;

    /// @notice Updates the oracle contract.
    ///
    /// @dev Emits a {SetOracle} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The new address cannot be the zero address.
    ///
    /// @param newOracle The new oracle contract.
    function setOracle(IChainlinkOperator newOracle) external;

    /// @notice Withdraws a portion or all of the collateral.
    ///
    /// @dev Emits a {WithdrawCollateral} event.
    ///
    /// Requirements:
    ///
    /// - The amount to withdraw cannot be zero.
    /// - There must be enough collateral in the vault.
    /// - The caller's account cannot fall below the collateral ratio.
    ///
    /// @param collateral The address of the collateral contract.
    /// @param withdrawAmount The amount of collateral to withdraw.
    function withdrawCollateral(IErc20 collateral, uint256 withdrawAmount) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

/// @title IOwnableUpgradeable
/// @author Hifi
interface IOwnableUpgradeable {
    /// CUSTOM ERRORS ///

    /// @notice Emitted when the caller is not the owner.
    error OwnableUpgradeable__NotOwner(address owner, address caller);

    /// @notice Emitted when setting the owner to the zero address.
    error OwnableUpgradeable__OwnerZeroAddress();

    /// EVENTS ///

    /// @notice Emitted when ownership is transferred.
    /// @param oldOwner The address of the old owner.
    /// @param newOwner The address of the new owner.
    event TransferOwnership(address indexed oldOwner, address indexed newOwner);

    /// CONSTANT FUNCTIONS ///

    /// @notice The address of the owner account or contract.
    /// @return The address of the owner.
    function owner() external view returns (address);

    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Leaves the contract without an owner, so it will not be possible to call `onlyOwner`
    /// functions anymore.
    ///
    /// WARNING: Doing this will leave the contract without an owner, thereby removing any
    /// functionality that is only available to the owner.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    function _renounceOwnership() external;

    /// @notice Transfers the owner of the contract to a new account (`newOwner`). Can only be
    /// called by the current owner.
    /// @param newOwner The account of the new owner.
    function _transferOwnership(address newOwner) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

import "@prb/contracts/token/erc20/IErc20.sol";
import "@prb/contracts/access/IOwnable.sol";

import "../external/chainlink/IAggregatorV3.sol";

/// @title IChainlinkOperator
/// @author Hifi
/// @notice Aggregates the price feeds provided by Chainlink.
interface IChainlinkOperator {
    /// CUSTOM ERRORS ///

    /// @notice Emitted when the decimal precision of the feed is not the same as the expected number.
    error ChainlinkOperator__DecimalsMismatch(string symbol, uint256 decimals);

    /// @notice Emitted when trying to interact with a feed not set yet.
    error ChainlinkOperator__FeedNotSet(string symbol);

    /// @notice Emitted when the price returned by the oracle is less than or equal to zero.
    error ChainlinkOperator__PriceLessThanOrEqualToZero(string symbol);

    /// @notice Emitted when the latest price update timestamp returned by the oracle is too old.
    error ChainlinkOperator__PriceStale(string symbol);

    /// EVENTS ///

    /// @notice Emitted when a feed is deleted.
    /// @param asset The related asset.
    /// @param feed The related feed.
    event DeleteFeed(IErc20 indexed asset, IAggregatorV3 indexed feed);

    /// @notice Emitted when a feed is set.
    /// @param asset The related asset.
    /// @param feed The related feed.
    event SetFeed(IErc20 indexed asset, IAggregatorV3 indexed feed);

    /// @notice Emitted when the Chainlink price staleness threshold is set.
    /// @param oldPriceStalenessThreshold The old Chainlink price staleness threshold.
    /// @param newPriceStalenessThreshold The new Chainlink price staleness threshold.
    event SetPriceStalenessThreshold(uint256 oldPriceStalenessThreshold, uint256 newPriceStalenessThreshold);

    /// STRUCTS ///

    struct Feed {
        IErc20 asset;
        IAggregatorV3 id;
        bool isSet;
    }

    /// CONSTANT FUNCTIONS ///

    /// @notice Gets the official feed for a symbol.
    /// @param symbol The symbol to return the feed for.
    /// @return (address asset, address id, bool isSet).
    function getFeed(string memory symbol)
        external
        view
        returns (
            IErc20,
            IAggregatorV3,
            bool
        );

    /// @notice Gets the official price for a symbol and adjusts it have 18 decimals instead of the
    /// format used by Chainlink, which has 8 decimals.
    ///
    /// @dev Requirements:
    /// - The normalized price cannot overflow.
    ///
    /// @param symbol The Erc20 symbol of the token for which to query the price.
    /// @return The normalized price.
    function getNormalizedPrice(string memory symbol) external view returns (uint256);

    /// @notice Gets the official price for a symbol in the default format used by Chainlink, which
    /// has 8 decimals.
    ///
    /// @dev Requirements:
    ///
    /// - The feed must be set.
    /// - The price returned by the oracle cannot be zero.
    ///
    /// @param symbol The symbol to fetch the price for.
    /// @return The price denominated in USD, with 8 decimals.
    function getPrice(string memory symbol) external view returns (uint256);

    /// @notice Chainlink price precision for USD-quoted data.
    function pricePrecision() external view returns (uint256);

    /// @notice The ratio between normalized precision (1e18) and the Chainlink price precision (1e8).
    function pricePrecisionScalar() external view returns (uint256);

    /// @notice The Chainlink price staleness threshold.
    function priceStalenessThreshold() external view returns (uint256);

    /// NON-CONSTANT FUNCTIONS ///

    /// @notice Deletes a previously set Chainlink price feed.
    ///
    /// @dev Emits a {DeleteFeed} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The feed must be set already.
    ///
    /// @param symbol The Erc20 symbol of the asset to delete the feed for.
    function deleteFeed(string memory symbol) external;

    /// @notice Sets a Chainlink price feed.
    ///
    /// @dev It is not an error to set a feed twice. Emits a {SetFeed} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    /// - The number of decimals of the feed must be 8.
    ///
    /// @param asset The address of the Erc20 contract for which to get the price.
    /// @param feed The address of the Chainlink price feed contract.
    function setFeed(IErc20 asset, IAggregatorV3 feed) external;

    /// @notice Sets the Chainlink price staleness threshold.
    ///
    /// @dev Emits a {SetPriceStalenessThreshold} event.
    ///
    /// Requirements:
    ///
    /// - The caller must be the owner.
    ///
    /// @param newPriceStalenessThreshold The new Chainlink price staleness threshold.
    function setPriceStalenessThreshold(uint256 newPriceStalenessThreshold) external;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

/// @title IAggregatorV3
/// @author Hifi
/// @dev Forked from Chainlink
/// github.com/smartcontractkit/chainlink/blob/v1.2.0/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
interface IAggregatorV3 {
    function decimals() external view returns (uint8);

    function description() external view returns (string memory);

    function version() external view returns (uint256);

    /// getRoundData and latestRoundData should both raise "No data present" if they do not have
    /// data to report, instead of returning unset values which could be misinterpreted as
    /// actual reported values.
    function getRoundData(uint80 _roundId)
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );

    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );
}

Settings
{
  "metadata": {
    "bytecodeHash": "none"
  },
  "optimizer": {
    "enabled": true,
    "runs": 800
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Fintroller__BondBorrowAllowedWithLiquidateBorrowDisallowed","type":"error"},{"inputs":[],"name":"Fintroller__BondLiquidateBorrowAllowedWithRepayBorrowDisallowed","type":"error"},{"inputs":[],"name":"Fintroller__BondLiquidateBorrowDisallowedWithBorrowAllowed","type":"error"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"Fintroller__BondNotListed","type":"error"},{"inputs":[],"name":"Fintroller__BondRepayBorrowDisallowedWithLiquidateBorrowAllowed","type":"error"},{"inputs":[{"internalType":"uint256","name":"decimals","type":"uint256"}],"name":"Fintroller__CollateralDecimalsOverflow","type":"error"},{"inputs":[],"name":"Fintroller__CollateralDecimalsZero","type":"error"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"Fintroller__CollateralNotListed","type":"error"},{"inputs":[{"internalType":"uint256","name":"newCollateralRatio","type":"uint256"}],"name":"Fintroller__CollateralRatioBelowLiquidationIncentive","type":"error"},{"inputs":[{"internalType":"uint256","name":"newCollateralRatio","type":"uint256"}],"name":"Fintroller__CollateralRatioOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"newCollateralRatio","type":"uint256"}],"name":"Fintroller__CollateralRatioUnderflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"newDebtCeiling","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"name":"Fintroller__DebtCeilingUnderflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"newLiquidationIncentive","type":"uint256"}],"name":"Fintroller__LiquidationIncentiveAboveCollateralRatio","type":"error"},{"inputs":[{"internalType":"uint256","name":"newLiquidationIncentive","type":"uint256"}],"name":"Fintroller__LiquidationIncentiveOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"newLiquidationIncentive","type":"uint256"}],"name":"Fintroller__LiquidationIncentiveUnderflow","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"caller","type":"address"}],"name":"Ownable__NotOwner","type":"error"},{"inputs":[],"name":"Ownable__OwnerZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"ListBond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"ListCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IHToken","name":"bond","type":"address"},{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"SetBorrowAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IErc20","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldCollateralCeiling","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCollateralCeiling","type":"uint256"}],"name":"SetCollateralCeiling","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IErc20","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldCollateralRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCollateralRatio","type":"uint256"}],"name":"SetCollateralRatio","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IHToken","name":"bond","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldDebtCeiling","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDebtCeiling","type":"uint256"}],"name":"SetDebtCeiling","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IErc20","name":"collateral","type":"address"},{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"SetDepositCollateralAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IHToken","name":"bond","type":"address"},{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"SetDepositUnderlyingAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IHToken","name":"bond","type":"address"},{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"SetLiquidateBorrowAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"contract IErc20","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldLiquidationIncentive","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLiquidationIncentive","type":"uint256"}],"name":"SetLiquidationIncentive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldMaxBonds","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxBonds","type":"uint256"}],"name":"SetMaxBonds","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldMaxCollaterals","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxCollaterals","type":"uint256"}],"name":"SetMaxCollaterals","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"contract IHToken","name":"bond","type":"address"},{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"SetRepayBorrowAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"TransferOwnership","type":"event"},{"inputs":[],"name":"_renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"_transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"hToken","type":"address"}],"name":"getBond","outputs":[{"components":[{"internalType":"uint256","name":"debtCeiling","type":"uint256"},{"internalType":"bool","name":"isBorrowAllowed","type":"bool"},{"internalType":"bool","name":"isDepositUnderlyingAllowed","type":"bool"},{"internalType":"bool","name":"isLiquidateBorrowAllowed","type":"bool"},{"internalType":"bool","name":"isListed","type":"bool"},{"internalType":"bool","name":"isRepayBorrowAllowed","type":"bool"}],"internalType":"struct IFintroller.Bond","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"getBorrowAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"getCollateral","outputs":[{"components":[{"internalType":"uint256","name":"ceiling","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"uint256","name":"liquidationIncentive","type":"uint256"},{"internalType":"bool","name":"isDepositCollateralAllowed","type":"bool"},{"internalType":"bool","name":"isListed","type":"bool"}],"internalType":"struct IFintroller.Collateral","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"getCollateralCeiling","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"getCollateralRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"getDebtCeiling","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"getDepositCollateralAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"getDepositUnderlyingAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"getLiquidateBorrowAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"getLiquidationIncentive","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"getRepayBorrowAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"isBondListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"isCollateralListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"}],"name":"listBond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"}],"name":"listCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxBonds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxCollaterals","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":"contract IHToken","name":"bond","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setBorrowAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"collateral","type":"address"},{"internalType":"uint256","name":"newCollateralCeiling","type":"uint256"}],"name":"setCollateralCeiling","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"},{"internalType":"uint256","name":"newCollateralRatio","type":"uint256"}],"name":"setCollateralRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"},{"internalType":"uint256","name":"newDebtCeiling","type":"uint256"}],"name":"setDebtCeiling","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setDepositCollateralAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setDepositUnderlyingAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setLiquidateBorrowAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IErc20","name":"collateral","type":"address"},{"internalType":"uint256","name":"newLiquidationIncentive","type":"uint256"}],"name":"setLiquidationIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxBonds","type":"uint256"}],"name":"setMaxBonds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxCollaterals","type":"uint256"}],"name":"setMaxCollaterals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IHToken","name":"bond","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setRepayBorrowAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50600080546001600160a01b031916339081178255604051909182917f5c486528ec3e3f0ea91181cff8116f02bfa350e03b8b6f12e00765adbb5af85c908290a350600a6001819055600255611b4b8061006b6000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638da5cb5b1161010f578063ce8f6d3e116100a2578063e3ee6e4711610071578063e3ee6e4714610601578063e60f07731461060a578063eecb855d1461061d578063fd0ff4b91461063057600080fd5b8063ce8f6d3e1461059c578063d29d44ee146105af578063d59f3f53146105c2578063d686e9ee146105d557600080fd5b8063aeb4fcc1116100de578063aeb4fcc114610550578063b60b825714610563578063bb23ffec14610576578063c0273a211461058957600080fd5b80638da5cb5b146104905780639b56d6c9146104bb5780639bd8f6e8146105145780639d7385561461052757600080fd5b80633c7981091161018757806363efc2281161015657806363efc228146104215780637922911f1461045757806381a7bc971461046a5780638559d20d1461047d57600080fd5b80633c798109146103ca57806342a4e064146103f35780634b3f2889146104065780635054d1501461040e57600080fd5b806315a3ba43116101c357806315a3ba4314610330578063227661cb1461036a578063298f7b181461037d578063309071a7146103c157600080fd5b806302b5bda7146101ea57806305e18c9d146101ff5780630d8912f314610212575b600080fd5b6101fd6101f8366004611a64565b610643565b005b6101fd61020d366004611a99565b6107e3565b6102d1610220366004611ab2565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b0316600090815260036020908152604091829020825160c0810184528154815260019091015460ff808216151593830193909352610100810483161515938201939093526201000083048216151560608201526301000000830482161515608082015264010000000090920416151560a082015290565b6040516103279190600060c0820190508251825260208301511515602083015260408301511515604083015260608301511515606083015260808301511515608083015260a0830151151560a083015292915050565b60405180910390f35b61035c61033e366004611ab2565b6001600160a01b031660009081526004602052604090206001015490565b604051908152602001610327565b6101fd610378366004611a64565b610878565b6103b161038b366004611ab2565b6001600160a01b0316600090815260046020526040902060030154610100900460ff1690565b6040519015158152602001610327565b61035c60025481565b61035c6103d8366004611ab2565b6001600160a01b031660009081526003602052604090205490565b6101fd610401366004611a64565b6109b7565b6101fd610aae565b6103b161041c366004611ab2565b610b45565b6103b161042f366004611ab2565b6001600160a01b03166000908152600360205260409020600101546301000000900460ff1690565b6101fd610465366004611a64565b610bba565b6103b1610478366004611ab2565b610caa565b6101fd61048b366004611ab2565b610d1a565b6000546104a3906001600160a01b031681565b6040516001600160a01b039091168152602001610327565b6104ce6104c9366004611ab2565b610e4e565b6040516103279190600060a08201905082518252602083015160208301526040830151604083015260608301511515606083015260808301511515608083015292915050565b6101fd610522366004611ad6565b610ee7565b61035c610535366004611ab2565b6001600160a01b031660009081526004602052604090205490565b6101fd61055e366004611ad6565b611077565b6101fd610571366004611ad6565b611204565b6103b1610584366004611ab2565b6112fd565b6101fd610597366004611ab2565b611373565b6103b16105aa366004611ab2565b611524565b6101fd6105bd366004611ab2565b611592565b6101fd6105d0366004611a64565b611661565b61035c6105e3366004611ab2565b6001600160a01b031660009081526004602052604090206002015490565b61035c60015481565b6103b1610618366004611ab2565b6117ab565b6101fd61062b366004611a99565b611823565b6101fd61063e366004611ad6565b6118b0565b6000546001600160a01b031633146106885760005460405163cc6bdb1d60e01b81526001600160a01b0390911660048201523360248201526044015b60405180910390fd5b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166106d65760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b80801561070757506001600160a01b038216600090815260036020526040902060010154640100000000900460ff16155b15610725576040516313ad546f60e31b815260040160405180910390fd5b8015801561074e57506001600160a01b03821660009081526003602052604090206001015460ff165b1561076c57604051632ed3d4a760e21b815260040160405180910390fd5b6001600160a01b038083166000818152600360205260408082206001018054861515620100000262ff00001990911617905590549051919216907f71dc0d35e1b9ee171f1b8ac9511d05e460ed7416cc401fe4d33978c44f1ca35b906107d790851515815260200190565b60405180910390a35050565b6000546001600160a01b031633146108235760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b600180549082905560005460408051838152602081018590526001600160a01b03909216917ffdc6136113b0185cc0d7209bef28516dcabeae2ebd769f707fcf8710e565656791015b60405180910390a25050565b6000546001600160a01b031633146108b85760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166109065760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b80801561093557506001600160a01b03821660009081526003602052604090206001015462010000900460ff16155b1561095357604051630eae82a960e11b815260040160405180910390fd5b6001600160a01b03808316600081815260036020526040808220600101805486151560ff1990911617905590549051919216907fb415ca45b135e3d2eb232571276198ac50330743ec23e7745c78a5b78a0a1b51906107d790851515815260200190565b6000546001600160a01b031633146109f75760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff16610a455760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b6001600160a01b0380831660008181526003602052604080822060010180548615156101000261ff001990911617905590549051919216907fc55893173627fb718b33144ccc6045fe1b2be87d4f4ab1b4e0460abbfe617c5f906107d790851515815260200190565b6000546001600160a01b03163314610aee5760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b600080546040516001600160a01b03909116907f5c486528ec3e3f0ea91181cff8116f02bfa350e03b8b6f12e00765adbb5af85c908390a36000805473ffffffffffffffffffffffffffffffffffffffff19169055565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff16610b935760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b0316600090815260036020526040902060010154610100900460ff1690565b6000546001600160a01b03163314610bfa5760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff16610c4657604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b6001600160a01b03808316600081815260046020526040808220600301805486151560ff1990911617905590549051919216907f0f3b9071297b60393e9906170c1e2262c9f49e48683463c2268e6b2214a02c82906107d790851515815260200190565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff16610cf85760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b031660009081526003602052604090206001015460ff1690565b6000546001600160a01b03163314610d5a5760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6040805160c081018252600080825260016020808401828152848601838152606086018481526080870185815260a088018681526001600160a01b038b8116808a5260039097528a892099518a559451989096018054935192519151965161ffff1990941698151561ff00191698909817610100921515929092029190911763ffff00001916620100009115159190910263ff0000001916176301000000941515949094029390931764ff000000001916640100000000931515939093029290921790935581549351929316917fd81bca3d01ee48c675a3635409a0de9f165d21de38d1b30566de2b764b96cd129190a350565b610e846040518060a001604052806000815260200160008152602001600081526020016000151581526020016000151581525090565b506001600160a01b0316600090815260046020908152604091829020825160a0810184528154815260018201549281019290925260028101549282019290925260039091015460ff80821615156060840152610100909104161515608082015290565b6000546001600160a01b03163314610f275760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff16610f7357604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b6714d1120d7b160000811115610f9f5760405163ba4e26f960e01b81526004810182905260240161067f565b670de0b6b3a7640000811015610fcb57604051633c292c4d60e11b81526004810182905260240161067f565b6001600160a01b03821660009081526004602052604090206001015481111561100a57604051637ab9833b60e01b81526004810182905260240161067f565b6001600160a01b038281166000818152600460209081526040808320600201805490879055925481519485529184018390528301859052909216907f344a32babe164e447da4243dd7af4572ba3e0db01b3644c0fca84ecfb3e66c9f9060600160405180910390a2505050565b6000546001600160a01b031633146110b75760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166111055760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b6000826001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611145573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111699190611b02565b90508082101561119657604051635a6bd6d160e01b8152600481018390526024810182905260440161067f565b6001600160a01b0380841660008181526003602052604080822080549087905591549051919316907f722c25b91b159fac5bf2699329b2651dcbf7e2e9470fb0261243e801b9d271b3906111f69085908890918252602082015260400190565b60405180910390a350505050565b6000546001600160a01b031633146112445760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff1661129057604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b6001600160a01b0380831660008181526004602052604080822080549086905591549051919316907f1bb3ad18b0ca1c3aed435a2d1b4caf5ecce0ef121e229a73034dd22815703d4c906112f09085908790918252602082015260400190565b60405180910390a3505050565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff1661134b5760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b031660009081526003602052604090206001015462010000900460ff1690565b6000546001600160a01b031633146113b35760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114179190611b1b565b60ff1690508061143a576040516314a5ea3560e11b815260040160405180910390fd5b601281111561145f576040516315432fa560e21b81526004810182905260240161067f565b6040805160a08101825260008082526714d1120d7b1600006020808401918252670f43fc2c04ee0000848601908152600160608601818152608087018281526001600160a01b038b811680895260049096528988209851895595519288019290925591516002870155905160039095018054915161ffff1990921695151561ff00191695909517610100911515919091021790935581549351929316917f6232505455ee4aa22dab92c1da7c3890a53b8f120c0c4e759173b9e33446e2e09190a35050565b6001600160a01b038116600090815260046020526040812060030154610100900460ff1661157057604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b506001600160a01b031660009081526004602052604090206003015460ff1690565b6000546001600160a01b031633146115d25760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0381166115f957604051634208fc5d60e01b815260040160405180910390fd5b600080546040516001600160a01b03808516939216917f5c486528ec3e3f0ea91181cff8116f02bfa350e03b8b6f12e00765adbb5af85c91a36000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6000546001600160a01b031633146116a15760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166116ef5760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b8015801561171e57506001600160a01b03821660009081526003602052604090206001015462010000900460ff165b1561173c576040516335a8f47160e01b815260040160405180910390fd5b6001600160a01b0380831660008181526003602052604080822060010180548615156401000000000264ff000000001990911617905590549051919216907f10424d34f926d4dd41e2f901147ab7b042eac6bf6d1e86205bce22c3744cc426906107d790851515815260200190565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff166117f95760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b0316600090815260036020526040902060010154640100000000900460ff1690565b6000546001600160a01b031633146118635760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b600280549082905560005460408051838152602081018590526001600160a01b03909216917fc3fa55da3d00e91f363615e63306100388093bb3ba58877972aa9b84a7dc1959910161086c565b6000546001600160a01b031633146118f05760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff1661193c57604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b68056bc75e2d63100000811115611969576040516321acffb360e11b81526004810182905260240161067f565b670de0b6b3a764000081101561199557604051637444adfb60e11b81526004810182905260240161067f565b6001600160a01b0382166000908152600460205260409020600201548110156119d457604051633c674d3f60e11b81526004810182905260240161067f565b6001600160a01b0380831660008181526004602052604080822060010180549086905591549051919316907f3352e03805e9a22af9ab02ba260864857143dc41b53213d78e1451b98d472ee4906112f09085908790918252602082015260400190565b6001600160a01b0381168114611a4c57600080fd5b50565b80358015158114611a5f57600080fd5b919050565b60008060408385031215611a7757600080fd5b8235611a8281611a37565b9150611a9060208401611a4f565b90509250929050565b600060208284031215611aab57600080fd5b5035919050565b600060208284031215611ac457600080fd5b8135611acf81611a37565b9392505050565b60008060408385031215611ae957600080fd5b8235611af481611a37565b946020939093013593505050565b600060208284031215611b1457600080fd5b5051919050565b600060208284031215611b2d57600080fd5b815160ff81168114611acf57600080fdfea164736f6c634300080c000a

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101e55760003560e01c80638da5cb5b1161010f578063ce8f6d3e116100a2578063e3ee6e4711610071578063e3ee6e4714610601578063e60f07731461060a578063eecb855d1461061d578063fd0ff4b91461063057600080fd5b8063ce8f6d3e1461059c578063d29d44ee146105af578063d59f3f53146105c2578063d686e9ee146105d557600080fd5b8063aeb4fcc1116100de578063aeb4fcc114610550578063b60b825714610563578063bb23ffec14610576578063c0273a211461058957600080fd5b80638da5cb5b146104905780639b56d6c9146104bb5780639bd8f6e8146105145780639d7385561461052757600080fd5b80633c7981091161018757806363efc2281161015657806363efc228146104215780637922911f1461045757806381a7bc971461046a5780638559d20d1461047d57600080fd5b80633c798109146103ca57806342a4e064146103f35780634b3f2889146104065780635054d1501461040e57600080fd5b806315a3ba43116101c357806315a3ba4314610330578063227661cb1461036a578063298f7b181461037d578063309071a7146103c157600080fd5b806302b5bda7146101ea57806305e18c9d146101ff5780630d8912f314610212575b600080fd5b6101fd6101f8366004611a64565b610643565b005b6101fd61020d366004611a99565b6107e3565b6102d1610220366004611ab2565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b0316600090815260036020908152604091829020825160c0810184528154815260019091015460ff808216151593830193909352610100810483161515938201939093526201000083048216151560608201526301000000830482161515608082015264010000000090920416151560a082015290565b6040516103279190600060c0820190508251825260208301511515602083015260408301511515604083015260608301511515606083015260808301511515608083015260a0830151151560a083015292915050565b60405180910390f35b61035c61033e366004611ab2565b6001600160a01b031660009081526004602052604090206001015490565b604051908152602001610327565b6101fd610378366004611a64565b610878565b6103b161038b366004611ab2565b6001600160a01b0316600090815260046020526040902060030154610100900460ff1690565b6040519015158152602001610327565b61035c60025481565b61035c6103d8366004611ab2565b6001600160a01b031660009081526003602052604090205490565b6101fd610401366004611a64565b6109b7565b6101fd610aae565b6103b161041c366004611ab2565b610b45565b6103b161042f366004611ab2565b6001600160a01b03166000908152600360205260409020600101546301000000900460ff1690565b6101fd610465366004611a64565b610bba565b6103b1610478366004611ab2565b610caa565b6101fd61048b366004611ab2565b610d1a565b6000546104a3906001600160a01b031681565b6040516001600160a01b039091168152602001610327565b6104ce6104c9366004611ab2565b610e4e565b6040516103279190600060a08201905082518252602083015160208301526040830151604083015260608301511515606083015260808301511515608083015292915050565b6101fd610522366004611ad6565b610ee7565b61035c610535366004611ab2565b6001600160a01b031660009081526004602052604090205490565b6101fd61055e366004611ad6565b611077565b6101fd610571366004611ad6565b611204565b6103b1610584366004611ab2565b6112fd565b6101fd610597366004611ab2565b611373565b6103b16105aa366004611ab2565b611524565b6101fd6105bd366004611ab2565b611592565b6101fd6105d0366004611a64565b611661565b61035c6105e3366004611ab2565b6001600160a01b031660009081526004602052604090206002015490565b61035c60015481565b6103b1610618366004611ab2565b6117ab565b6101fd61062b366004611a99565b611823565b6101fd61063e366004611ad6565b6118b0565b6000546001600160a01b031633146106885760005460405163cc6bdb1d60e01b81526001600160a01b0390911660048201523360248201526044015b60405180910390fd5b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166106d65760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b80801561070757506001600160a01b038216600090815260036020526040902060010154640100000000900460ff16155b15610725576040516313ad546f60e31b815260040160405180910390fd5b8015801561074e57506001600160a01b03821660009081526003602052604090206001015460ff165b1561076c57604051632ed3d4a760e21b815260040160405180910390fd5b6001600160a01b038083166000818152600360205260408082206001018054861515620100000262ff00001990911617905590549051919216907f71dc0d35e1b9ee171f1b8ac9511d05e460ed7416cc401fe4d33978c44f1ca35b906107d790851515815260200190565b60405180910390a35050565b6000546001600160a01b031633146108235760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b600180549082905560005460408051838152602081018590526001600160a01b03909216917ffdc6136113b0185cc0d7209bef28516dcabeae2ebd769f707fcf8710e565656791015b60405180910390a25050565b6000546001600160a01b031633146108b85760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166109065760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b80801561093557506001600160a01b03821660009081526003602052604090206001015462010000900460ff16155b1561095357604051630eae82a960e11b815260040160405180910390fd5b6001600160a01b03808316600081815260036020526040808220600101805486151560ff1990911617905590549051919216907fb415ca45b135e3d2eb232571276198ac50330743ec23e7745c78a5b78a0a1b51906107d790851515815260200190565b6000546001600160a01b031633146109f75760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff16610a455760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b6001600160a01b0380831660008181526003602052604080822060010180548615156101000261ff001990911617905590549051919216907fc55893173627fb718b33144ccc6045fe1b2be87d4f4ab1b4e0460abbfe617c5f906107d790851515815260200190565b6000546001600160a01b03163314610aee5760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b600080546040516001600160a01b03909116907f5c486528ec3e3f0ea91181cff8116f02bfa350e03b8b6f12e00765adbb5af85c908390a36000805473ffffffffffffffffffffffffffffffffffffffff19169055565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff16610b935760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b0316600090815260036020526040902060010154610100900460ff1690565b6000546001600160a01b03163314610bfa5760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff16610c4657604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b6001600160a01b03808316600081815260046020526040808220600301805486151560ff1990911617905590549051919216907f0f3b9071297b60393e9906170c1e2262c9f49e48683463c2268e6b2214a02c82906107d790851515815260200190565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff16610cf85760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b031660009081526003602052604090206001015460ff1690565b6000546001600160a01b03163314610d5a5760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6040805160c081018252600080825260016020808401828152848601838152606086018481526080870185815260a088018681526001600160a01b038b8116808a5260039097528a892099518a559451989096018054935192519151965161ffff1990941698151561ff00191698909817610100921515929092029190911763ffff00001916620100009115159190910263ff0000001916176301000000941515949094029390931764ff000000001916640100000000931515939093029290921790935581549351929316917fd81bca3d01ee48c675a3635409a0de9f165d21de38d1b30566de2b764b96cd129190a350565b610e846040518060a001604052806000815260200160008152602001600081526020016000151581526020016000151581525090565b506001600160a01b0316600090815260046020908152604091829020825160a0810184528154815260018201549281019290925260028101549282019290925260039091015460ff80821615156060840152610100909104161515608082015290565b6000546001600160a01b03163314610f275760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff16610f7357604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b6714d1120d7b160000811115610f9f5760405163ba4e26f960e01b81526004810182905260240161067f565b670de0b6b3a7640000811015610fcb57604051633c292c4d60e11b81526004810182905260240161067f565b6001600160a01b03821660009081526004602052604090206001015481111561100a57604051637ab9833b60e01b81526004810182905260240161067f565b6001600160a01b038281166000818152600460209081526040808320600201805490879055925481519485529184018390528301859052909216907f344a32babe164e447da4243dd7af4572ba3e0db01b3644c0fca84ecfb3e66c9f9060600160405180910390a2505050565b6000546001600160a01b031633146110b75760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166111055760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b6000826001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611145573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111699190611b02565b90508082101561119657604051635a6bd6d160e01b8152600481018390526024810182905260440161067f565b6001600160a01b0380841660008181526003602052604080822080549087905591549051919316907f722c25b91b159fac5bf2699329b2651dcbf7e2e9470fb0261243e801b9d271b3906111f69085908890918252602082015260400190565b60405180910390a350505050565b6000546001600160a01b031633146112445760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff1661129057604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b6001600160a01b0380831660008181526004602052604080822080549086905591549051919316907f1bb3ad18b0ca1c3aed435a2d1b4caf5ecce0ef121e229a73034dd22815703d4c906112f09085908790918252602082015260400190565b60405180910390a3505050565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff1661134b5760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b031660009081526003602052604090206001015462010000900460ff1690565b6000546001600160a01b031633146113b35760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114179190611b1b565b60ff1690508061143a576040516314a5ea3560e11b815260040160405180910390fd5b601281111561145f576040516315432fa560e21b81526004810182905260240161067f565b6040805160a08101825260008082526714d1120d7b1600006020808401918252670f43fc2c04ee0000848601908152600160608601818152608087018281526001600160a01b038b811680895260049096528988209851895595519288019290925591516002870155905160039095018054915161ffff1990921695151561ff00191695909517610100911515919091021790935581549351929316917f6232505455ee4aa22dab92c1da7c3890a53b8f120c0c4e759173b9e33446e2e09190a35050565b6001600160a01b038116600090815260046020526040812060030154610100900460ff1661157057604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b506001600160a01b031660009081526004602052604090206003015460ff1690565b6000546001600160a01b031633146115d25760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0381166115f957604051634208fc5d60e01b815260040160405180910390fd5b600080546040516001600160a01b03808516939216917f5c486528ec3e3f0ea91181cff8116f02bfa350e03b8b6f12e00765adbb5af85c91a36000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6000546001600160a01b031633146116a15760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b0382166000908152600360205260409020600101546301000000900460ff166116ef5760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b8015801561171e57506001600160a01b03821660009081526003602052604090206001015462010000900460ff165b1561173c576040516335a8f47160e01b815260040160405180910390fd5b6001600160a01b0380831660008181526003602052604080822060010180548615156401000000000264ff000000001990911617905590549051919216907f10424d34f926d4dd41e2f901147ab7b042eac6bf6d1e86205bce22c3744cc426906107d790851515815260200190565b6001600160a01b0381166000908152600360205260408120600101546301000000900460ff166117f95760405163216a460f60e11b81526001600160a01b038316600482015260240161067f565b506001600160a01b0316600090815260036020526040902060010154640100000000900460ff1690565b6000546001600160a01b031633146118635760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b600280549082905560005460408051838152602081018590526001600160a01b03909216917fc3fa55da3d00e91f363615e63306100388093bb3ba58877972aa9b84a7dc1959910161086c565b6000546001600160a01b031633146118f05760005460405163cc6bdb1d60e01b81526001600160a01b03909116600482015233602482015260440161067f565b6001600160a01b038216600090815260046020526040902060030154610100900460ff1661193c57604051630141c9a760e01b81526001600160a01b038316600482015260240161067f565b68056bc75e2d63100000811115611969576040516321acffb360e11b81526004810182905260240161067f565b670de0b6b3a764000081101561199557604051637444adfb60e11b81526004810182905260240161067f565b6001600160a01b0382166000908152600460205260409020600201548110156119d457604051633c674d3f60e11b81526004810182905260240161067f565b6001600160a01b0380831660008181526004602052604080822060010180549086905591549051919316907f3352e03805e9a22af9ab02ba260864857143dc41b53213d78e1451b98d472ee4906112f09085908790918252602082015260400190565b6001600160a01b0381168114611a4c57600080fd5b50565b80358015158114611a5f57600080fd5b919050565b60008060408385031215611a7757600080fd5b8235611a8281611a37565b9150611a9060208401611a4f565b90509250929050565b600060208284031215611aab57600080fd5b5035919050565b600060208284031215611ac457600080fd5b8135611acf81611a37565b9392505050565b60008060408385031215611ae957600080fd5b8235611af481611a37565b946020939093013593505050565b600060208284031215611b1457600080fd5b5051919050565b600060208284031215611b2d57600080fd5b815160ff81168114611acf57600080fdfea164736f6c634300080c000a

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.