// File: contracts/dependencies/chainlink/AggregatorV3Interface.sol



// SPDX-License-Identifier: MIT
// Chainlink Contracts v0.4.0 (v0.8/interfaces/AggregatorV3Interface.sol)
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
    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
        );
}

// File: contracts/dependencies/openzeppelin/utils/structs/EnumerableSet.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// File: contracts/migration/MigrationAddressResolver.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import "../protocol/mixins/MixinAddressResolver.sol";

/**
 * @notice Implements migration module to build caches of contract address from `AddressResolver.sol`
 * in the contract that is inherited `MixinAddressResolver.sol`.
 *
 * This contract is used only in the following cases.
 * - The case of the initial deployment of the contract.
 * - The case when some contract needs to deploy a new proxy contract.
 */
contract MigrationAddressResolver {
    function buildCaches(address[] calldata _addresses) external {
        for (uint256 i; i < _addresses.length; i++) {
            MixinAddressResolver destination = MixinAddressResolver(_addresses[i]);
            if (!destination.isResolverCached()) {
                destination.buildCache();
            }
        }
    }
}

// File: contracts/protocol/interfaces/IAddressResolver.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

interface IAddressResolver {
    error UnmatchedInputs();

    event AddressImported(bytes32 name, address destination);

    function getAddress(bytes32 name, string calldata reason) external view returns (address);

    function getAddress(bytes32 name) external view returns (address);

    function getAddresses() external view returns (address[] memory);

    function getNames() external view returns (bytes32[] memory);
}

// File: contracts/protocol/interfaces/IBeaconProxyController.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

interface IBeaconProxyController {
    error NoBeaconProxyContract();
    error InvalidProxyContract();

    event BeaconProxyUpdated(
        bytes32 indexed id,
        address indexed proxyAddress,
        address indexed newImplementationAddress,
        address oldImplementationAddress
    );

    function getBeaconProxyAddress(bytes32 beaconName) external view returns (address);

    function setFutureValueVaultImpl(address newImpl) external;

    function setLendingMarketImpl(address newImpl) external;

    function deployFutureValueVault() external returns (address futureValueVault);

    function deployLendingMarket(
        bytes32 ccy,
        uint256 orderFeeRate,
        uint256 cbLimitRange
    ) external returns (address market);
}

// File: contracts/protocol/interfaces/ICurrencyController.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {PriceFeed} from "../storages/CurrencyControllerStorage.sol";

/**
 * @dev Currency Controller contract is responsible for managing supported
 * currencies in Secured Finance Protocol
 *
 * Contract links new currencies to ETH Chainlink price feeds, without existing price feed
 * contract owner is not able to add a new currency into the protocol
 */
interface ICurrencyController {
    error InvalidCurrency();
    error InvalidHaircut();
    error InvalidPriceFeed();
    error InvalidDecimals(address priceFeed, uint8 decimals);
    error InvalidPriceFeedInputs();
    error StalePriceFeed(
        address priceFeed,
        uint256 heartbeat,
        uint256 updatedAt,
        uint256 blockTimestamp
    );

    event CurrencyAdded(bytes32 indexed ccy, uint256 haircut);
    event CurrencyRemoved(bytes32 indexed ccy);

    event HaircutUpdated(bytes32 indexed ccy, uint256 haircut);

    event PriceFeedUpdated(bytes32 ccy, uint256 decimals, address[] indexed priceFeeds);

    function convert(
        bytes32 _fromCcy,
        bytes32 _toCcy,
        uint256 _amount
    ) external view returns (uint256 amount);

    function convert(
        bytes32 _fromCcy,
        bytes32 _toCcy,
        uint256[] calldata _amounts
    ) external view returns (uint256[] memory amounts);

    function convertToBaseCurrency(
        bytes32 _ccy,
        uint256 _amount
    ) external view returns (uint256 amount);

    function convertToBaseCurrency(
        bytes32 _ccy,
        int256 _amount
    ) external view returns (int256 amount);

    function convertToBaseCurrency(
        bytes32 _ccy,
        uint256[] calldata _amounts
    ) external view returns (uint256[] memory amounts);

    function convertFromBaseCurrency(
        bytes32 _ccy,
        uint256 _amountETH
    ) external view returns (uint256 amount);

    function convertFromBaseCurrency(
        bytes32 _ccy,
        uint256[] calldata _amounts
    ) external view returns (uint256[] memory amounts);

    function getDecimals(bytes32) external view returns (uint8);

    function getCurrencies() external view returns (bytes32[] memory);

    function getHaircut(bytes32 _ccy) external view returns (uint256);

    function getPriceFeed(bytes32 _ccy) external view returns (PriceFeed memory);

    function getLastPrice(bytes32 _ccy) external view returns (int256 price);

    function getAggregatedLastPrice(bytes32 _ccy) external view returns (int256);

    function currencyExists(bytes32 _ccy) external view returns (bool);

    function updatePriceFeed(
        bytes32 _ccy,
        uint8 _decimals,
        address[] calldata _priceFeeds,
        uint256[] calldata _heartbeats
    ) external;

    function addCurrency(
        bytes32 _ccy,
        uint8 _decimals,
        uint256 _haircut,
        address[] calldata _priceFeeds,
        uint256[] calldata _heartbeats
    ) external;

    function updateHaircut(bytes32 _ccy, uint256 _haircut) external;

    function removeCurrency(bytes32 _ccy) external;
}

// File: contracts/protocol/interfaces/IGenesisValueVault.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {AutoRollLog} from "../storages/GenesisValueVaultStorage.sol";

interface IGenesisValueVault {
    error NoCompoundFactorExists(uint256 maturity);
    error CompoundFactorIsZero();
    error ResidualAmountIsNotZero();
    error UnitPriceIsZero();
    error InvalidMaturity();
    error InvalidAmount();
    error InvalidOrderFeeRate();
    error CurrencyAlreadyInitialized();
    error InitialCompoundFactorAlreadyFinalized();
    error AutoRollLogAlreadyUpdated(uint256 currentMaturity, uint256 nextMaturity);

    event Transfer(bytes32 indexed ccy, address indexed from, address indexed to, int256 value);
    event AutoRollExecuted(
        bytes32 indexed ccy,
        uint256 lendingCompoundFactor,
        uint256 borrowingCompoundFactor,
        uint256 unitPrice,
        uint256 currentMaturity,
        uint256 previousMaturity
    );

    function isInitialized(bytes32 ccy) external view returns (bool);

    function decimals(bytes32 ccy) external view returns (uint8);

    function getTotalLendingSupply(bytes32 ccy) external view returns (uint256);

    function getTotalBorrowingSupply(bytes32 ccy) external view returns (uint256);

    function getBalance(bytes32 ccy, address user) external view returns (int256);

    function getBalanceInFutureValue(bytes32 ccy, address user) external view returns (int256);

    function getMaturityGenesisValue(bytes32 ccy, uint256 maturity) external view returns (int256);

    function getCurrentMaturity(bytes32 ccy) external view returns (uint256);

    function getLendingCompoundFactor(bytes32 ccy) external view returns (uint256);

    function getBorrowingCompoundFactor(bytes32 ccy) external view returns (uint256);

    function getAutoRollLog(
        bytes32 ccy,
        uint256 maturity
    ) external view returns (AutoRollLog memory);

    function getLatestAutoRollLog(bytes32 ccy) external view returns (AutoRollLog memory);

    function calculateFVFromFV(
        bytes32 ccy,
        uint256 basisMaturity,
        uint256 destinationMaturity,
        int256 futureValue
    ) external view returns (int256);

    function calculateGVFromFV(
        bytes32 ccy,
        uint256 basisMaturity,
        int256 futureValue
    ) external view returns (int256);

    function calculateFVFromGV(
        bytes32 ccy,
        uint256 basisMaturity,
        int256 genesisValue
    ) external view returns (int256);

    function getBalanceFluctuationByAutoRolls(
        bytes32 ccy,
        address user,
        uint256 maturity
    ) external view returns (int256 fluctuation);

    function calculateBalanceFluctuationByAutoRolls(
        bytes32 ccy,
        int256 balance,
        uint256 fromMaturity,
        uint256 toMaturity
    ) external view returns (int256 fluctuation);

    function initializeCurrencySetting(
        bytes32 ccy,
        uint8 decimals,
        uint256 compoundFactor,
        uint256 maturity
    ) external;

    function updateInitialCompoundFactor(bytes32 ccy, uint256 unitPrice) external;

    function executeAutoRoll(
        bytes32 ccy,
        uint256 maturity,
        uint256 nextMaturity,
        uint256 unitPrice,
        uint256 orderFeeRate
    ) external;

    function updateGenesisValueWithFutureValue(
        bytes32 ccy,
        address user,
        uint256 basisMaturity,
        int256 fvAmount
    ) external;

    function updateGenesisValueWithResidualAmount(
        bytes32 ccy,
        address user,
        uint256 basisMaturity
    ) external;

    function transferFrom(bytes32 ccy, address sender, address receiver, int256 amount) external;

    function cleanUpBalance(bytes32 ccy, address user, uint256 maturity) external;

    function executeForcedReset(bytes32 _ccy, address _user) external;

    function executeForcedReset(
        bytes32 _ccy,
        uint256 _maturity,
        address _user,
        int256 _amountInFV
    ) external returns (int256 removedAmount, int256 balance);
}

// File: contracts/protocol/interfaces/ILendingMarketController.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {ProtocolTypes} from "../types/ProtocolTypes.sol";
import {TerminationCurrencyCache} from "../storages/LendingMarketControllerStorage.sol";

interface ILendingMarketController {
    error InvalidMaturity();
    error InvalidCurrency();
    error AlreadyTerminated();
    error NotTerminated();
    error AlreadyInitialized();

    struct AdditionalFunds {
        bytes32 ccy;
        uint256 workingLendOrdersAmount;
        uint256 claimableAmount;
        uint256 workingBorrowOrdersAmount;
        uint256 debtAmount;
        uint256 lentAmount;
        uint256 borrowedAmount;
    }

    struct CalculatedTotalFunds {
        uint256 plusDepositAmountInAdditionalFundsCcy;
        uint256 minusDepositAmountInAdditionalFundsCcy;
        uint256 workingLendOrdersAmount;
        uint256 claimableAmount;
        uint256 collateralAmount;
        uint256 lentAmount;
        uint256 workingBorrowOrdersAmount;
        uint256 debtAmount;
        uint256 borrowedAmount;
    }

    struct CalculatedFunds {
        uint256 workingLendOrdersAmount;
        uint256 claimableAmount;
        uint256 collateralAmount;
        uint256 lentAmount;
        uint256 workingBorrowOrdersAmount;
        uint256 debtAmount;
        uint256 borrowedAmount;
    }

    struct GetOrderEstimationParams {
        bytes32 ccy;
        uint256 maturity;
        address user;
        ProtocolTypes.Side side;
        uint256 amount;
        uint256 unitPrice;
        uint256 additionalDepositAmount;
        bool ignoreBorrowedAmount;
    }

    function isTerminated() external view returns (bool);

    function isRedemptionRequired(address _user) external view returns (bool);

    function getMarketBasePeriod() external view returns (uint256);

    function getTerminationDate() external view returns (uint256);

    function getTerminationCurrencyCache(
        bytes32 _ccy
    ) external view returns (TerminationCurrencyCache memory);

    function getTerminationCollateralRatio(bytes32 _ccy) external view returns (uint256);

    function getMinDebtUnitPrice(bytes32 _ccy) external view returns (uint256);

    function getCurrentMinDebtUnitPrice(
        bytes32 _ccy,
        uint256 _maturity
    ) external view returns (uint256);

    function getGenesisDate(bytes32 ccy) external view returns (uint256);

    function getLendingMarket(bytes32 ccy) external view returns (address);

    function getFutureValueVault(bytes32 ccy) external view returns (address);

    function getOrderBookId(bytes32 _ccy, uint256 _maturity) external view returns (uint8);

    function getPendingOrderAmount(bytes32 _ccy, uint256 _maturity) external view returns (uint256);

    function getOrderEstimation(
        GetOrderEstimationParams calldata params
    )
        external
        view
        returns (
            uint256 lastUnitPrice,
            uint256 filledAmount,
            uint256 filledAmountInFV,
            uint256 orderFeeInFV,
            uint256 placedAmount,
            uint256 coverage,
            bool isInsufficientDepositAmount
        );

    function getMaturities(bytes32 ccy) external view returns (uint256[] memory);

    function getOrderBookIds(bytes32 ccy) external view returns (uint8[] memory);

    function getUsedCurrencies(address user) external view returns (bytes32[] memory);

    function getTotalPresentValue(bytes32 ccy, address user) external view returns (int256);

    function getTotalPresentValueInBaseCurrency(
        address user
    ) external view returns (int256 totalPresentValue);

    function getGenesisValue(bytes32 ccy, address user) external view returns (int256 genesisValue);

    function getPosition(
        bytes32 _ccy,
        uint256 _maturity,
        address _user
    ) external view returns (int256 presentValue, int256 futureValue);

    function calculateFunds(
        bytes32 ccy,
        address user,
        uint256 liquidationThresholdRate
    ) external view returns (CalculatedFunds memory funds);

    function calculateTotalFundsInBaseCurrency(
        address user,
        AdditionalFunds calldata _additionalFunds,
        uint256 liquidationThresholdRate
    ) external view returns (CalculatedTotalFunds memory calculatedFunds);

    function isInitializedLendingMarket(bytes32 ccy) external view returns (bool);

    function initializeLendingMarket(
        bytes32 ccy,
        uint256 genesisDate,
        uint256 compoundFactor,
        uint256 orderFeeRate,
        uint256 circuitBreakerLimitRange,
        uint256 minDebtUnitPrice
    ) external;

    function createOrderBook(bytes32 ccy, uint256 openingDate, uint256 preOpeningDate) external;

    function executeOrder(
        bytes32 ccy,
        uint256 maturity,
        ProtocolTypes.Side side,
        uint256 amount,
        uint256 unitPrice
    ) external returns (bool);

    function depositAndExecuteOrder(
        bytes32 ccy,
        uint256 maturity,
        ProtocolTypes.Side side,
        uint256 amount,
        uint256 unitPrice
    ) external payable returns (bool);

    function executePreOrder(
        bytes32 ccy,
        uint256 maturity,
        ProtocolTypes.Side side,
        uint256 amount,
        uint256 unitPrice
    ) external returns (bool);

    function depositAndExecutesPreOrder(
        bytes32 ccy,
        uint256 maturity,
        ProtocolTypes.Side side,
        uint256 amount,
        uint256 unitPrice
    ) external payable returns (bool);

    function unwindPosition(bytes32 ccy, uint256 maturity) external returns (bool);

    function executeItayoseCall(bytes32 ccy, uint256 maturity) external returns (bool);

    function executeRedemption(bytes32 _ccy, uint256 _maturity) external returns (bool);

    function executeRepayment(bytes32 _ccy, uint256 _maturity) external returns (bool);

    function executeEmergencySettlement() external returns (bool);

    function executeLiquidationCall(
        bytes32 collateralCcy,
        bytes32 debtCcy,
        uint256 debtMaturity,
        address user
    ) external returns (bool);

    function executeForcedRepayment(
        bytes32 _collateralCcy,
        bytes32 _debtCcy,
        uint256 _debtMaturity,
        address _user
    ) external returns (bool);

    function cancelOrder(bytes32 ccy, uint256 maturity, uint48 orderId) external returns (bool);

    function rotateOrderBooks(bytes32 ccy) external;

    function executeEmergencyTermination() external;

    function pauseLendingMarket(bytes32 ccy) external returns (bool);

    function unpauseLendingMarket(bytes32 ccy) external returns (bool);

    function cleanUpAllFunds(address user) external returns (bool);

    function cleanUpFunds(bytes32 ccy, address user) external returns (uint256 activeOrderCount);

    function updateMinDebtUnitPrice(bytes32 _ccy, uint256 _minDebtUnitPrice) external;
}

// File: contracts/protocol/interfaces/IReserveFund.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

interface IReserveFund {
    event Pause(address account);
    event Unpause(address account);

    function isPaused() external view returns (bool);

    function pause() external;

    function unpause() external;

    function deposit(bytes32 ccy, uint256 amount) external payable;

    function withdraw(bytes32 ccy, uint256 amount) external;
}

// File: contracts/protocol/interfaces/ITokenVault.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {ProtocolTypes} from "../types/ProtocolTypes.sol";
import {ILendingMarketController} from "../interfaces/ILendingMarketController.sol";

interface ITokenVault {
    error UnregisteredCurrency();
    error InvalidCurrency();
    error InvalidToken();
    error InvalidAmount(bytes32 ccy, uint256 amount, uint256 msgValue);
    error AmountIsZero();
    error CallerNotBaseCurrency(address caller);
    error MarketTerminated();
    error RedemptionIsRequired();

    event Deposit(address indexed user, bytes32 ccy, uint256 amount);
    event Withdraw(address indexed user, bytes32 ccy, uint256 amount);
    event Transfer(bytes32 indexed ccy, address indexed from, address indexed to, uint256 amount);
    event CurrencyRegistered(bytes32 ccy, address tokenAddress, bool isCollateral);
    event CurrencyUpdated(bytes32 ccy, bool isCollateral);

    function isCovered(address user) external view returns (bool);

    function isCollateral(bytes32 ccy) external view returns (bool);

    function isCollateral(bytes32[] calldata ccys) external view returns (bool[] memory);

    function isRegisteredCurrency(bytes32 ccy) external view returns (bool);

    function getTokenAddress(bytes32 ccy) external view returns (address);

    function getCollateralCurrencies() external view returns (bytes32[] memory);

    function getWithdrawableCollateral(address user) external view returns (uint256);

    function getWithdrawableCollateral(bytes32 ccy, address user) external view returns (uint256);

    function getCoverage(address user) external view returns (uint256);

    function getTotalUnusedCollateralAmount(address user) external view returns (uint256);

    function getTotalCollateralAmount(address user) external view returns (uint256);

    function getCollateralAmount(address user, bytes32 ccy) external view returns (uint256);

    function getBorrowableAmount(address user, bytes32 ccy) external view returns (uint256);

    function getLiquidationAmount(
        address user,
        bytes32 liquidationCcy,
        uint256 liquidationAmountMaximum
    ) external view returns (uint256 liquidationAmount, uint256 protocolFee, uint256 liquidatorFee);

    function getTotalDepositAmount(bytes32 ccy) external view returns (uint256);

    function getDepositAmount(address user, bytes32 ccy) external view returns (uint256);

    function getUsedCurrencies(address user) external view returns (bytes32[] memory);

    function calculateCoverage(
        address user,
        ILendingMarketController.AdditionalFunds memory funds
    ) external view returns (uint256 coverage, bool isInsufficientDepositAmount);

    function calculateLiquidationFees(
        uint256 liquidationAmount
    ) external view returns (uint256 protocolFee, uint256 liquidatorFee);

    function registerCurrency(bytes32 ccy, address tokenAddress, bool isCollateral) external;

    function updateCurrency(bytes32 ccy, bool isCollateral) external;

    function deposit(bytes32 ccy, uint256 amount) external payable;

    function depositFrom(address user, bytes32 ccy, uint256 amount) external payable;

    function withdraw(bytes32 ccy, uint256 amount) external;

    function addDepositAmount(address user, bytes32 ccy, uint256 amount) external;

    function removeDepositAmount(address user, bytes32 ccy, uint256 amount) external;

    function executeForcedReset(address user, bytes32 ccy) external returns (uint256 removedAmount);

    function transferFrom(
        bytes32 ccy,
        address sender,
        address receiver,
        uint256 amount
    ) external returns (uint256 untransferredAmount);

    function pause() external;

    function unpause() external;
}

// File: contracts/protocol/libraries/AddressResolverLib.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {Contracts} from "../libraries/Contracts.sol";
import {IBeaconProxyController} from "../interfaces/IBeaconProxyController.sol";
import {ICurrencyController} from "../interfaces/ICurrencyController.sol";
import {IGenesisValueVault} from "../interfaces/IGenesisValueVault.sol";
import {ILendingMarketController} from "../interfaces/ILendingMarketController.sol";
import {IReserveFund} from "../interfaces/IReserveFund.sol";
import {ITokenVault} from "../interfaces/ITokenVault.sol";
import {MixinAddressResolverStorage as Storage} from "../storages/mixins/MixinAddressResolverStorage.sol";

library AddressResolverLib {
    error MissingAddress(string name);

    function getAddress(bytes32 name) internal view returns (address) {
        address _foundAddress = Storage.slot().addressCache[name];
        if (_foundAddress == address(0)) revert MissingAddress(string(abi.encodePacked(name)));
        return _foundAddress;
    }

    function beaconProxyController() internal view returns (IBeaconProxyController) {
        return IBeaconProxyController(getAddress(Contracts.BEACON_PROXY_CONTROLLER));
    }

    function currencyController() internal view returns (ICurrencyController) {
        return ICurrencyController(getAddress(Contracts.CURRENCY_CONTROLLER));
    }

    function genesisValueVault() internal view returns (IGenesisValueVault) {
        return IGenesisValueVault(getAddress(Contracts.GENESIS_VALUE_VAULT));
    }

    function lendingMarketController() internal view returns (ILendingMarketController) {
        return ILendingMarketController(getAddress(Contracts.LENDING_MARKET_CONTROLLER));
    }

    function reserveFund() internal view returns (IReserveFund) {
        return IReserveFund(getAddress(Contracts.RESERVE_FUND));
    }

    function tokenVault() internal view returns (ITokenVault) {
        return ITokenVault(getAddress(Contracts.TOKEN_VAULT));
    }
}

// File: contracts/protocol/libraries/Contracts.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

library Contracts {
    bytes32 internal constant BEACON_PROXY_CONTROLLER = "BeaconProxyController";
    bytes32 internal constant CURRENCY_CONTROLLER = "CurrencyController";
    bytes32 internal constant GENESIS_VALUE_VAULT = "GenesisValueVault";
    bytes32 internal constant LENDING_MARKET_CONTROLLER = "LendingMarketController";
    bytes32 internal constant RESERVE_FUND = "ReserveFund";
    bytes32 internal constant TOKEN_VAULT = "TokenVault";
}

library BeaconContracts {
    bytes32 internal constant FUTURE_VALUE_VAULT = "FutureValueVault";
    bytes32 internal constant LENDING_MARKET = "LendingMarket";
}

// File: contracts/protocol/mixins/MixinAddressResolver.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {AddressResolverLib} from "../libraries/AddressResolverLib.sol";
import {IAddressResolver} from "../interfaces/IAddressResolver.sol";
import {IBeaconProxyController} from "../interfaces/IBeaconProxyController.sol";
import {ICurrencyController} from "../interfaces/ICurrencyController.sol";
import {IGenesisValueVault} from "../interfaces/IGenesisValueVault.sol";
import {ILendingMarketController} from "../interfaces/ILendingMarketController.sol";
import {IReserveFund} from "../interfaces/IReserveFund.sol";
import {ITokenVault} from "../interfaces/ITokenVault.sol";
import {MixinAddressResolverStorage as Storage} from "../storages/mixins/MixinAddressResolverStorage.sol";

contract MixinAddressResolver {
    event CacheUpdated(bytes32 name, address destination);

    error OnlyAcceptedContract(string name);
    error ResolverAlreadyRegistered();

    modifier onlyLendingMarketController() {
        if (msg.sender != address(AddressResolverLib.lendingMarketController()))
            revert OnlyAcceptedContract("LendingMarketController");
        _;
    }

    /**
     * @notice Returns the contract names used in this contract.
     * @dev The contract name list is in `./libraries/Contracts.sol`.
     */
    function requiredContracts() public pure virtual returns (bytes32[] memory contracts) {}

    function buildCache() public {
        // The resolver must call this function whenever it updates its state
        bytes32[] memory contractNames = requiredContracts();
        for (uint256 i; i < contractNames.length; i++) {
            bytes32 name = contractNames[i];
            // Note: can only be invoked once the resolver has all the targets needed added
            address destination = Storage.slot().resolver.getAddress(
                name,
                string(abi.encodePacked("Resolver missing target: ", name))
            );
            Storage.slot().addressCache[name] = destination;
            emit CacheUpdated(name, destination);
        }
    }

    function isResolverCached() external view returns (bool) {
        bytes32[] memory contractNames = requiredContracts();
        for (uint256 i; i < contractNames.length; i++) {
            bytes32 name = contractNames[i];
            // false if our cache is invalid or if the resolver doesn't have the required address
            if (
                Storage.slot().resolver.getAddress(name) != Storage.slot().addressCache[name] ||
                Storage.slot().addressCache[name] == address(0)
            ) {
                return false;
            }
        }

        return true;
    }

    /**
     * @dev Register the Address Resolver contract
     * @param _resolver The address of the Address Resolver contract
     */
    function registerAddressResolver(address _resolver) internal {
        if (address(Storage.slot().resolver) != address(0)) revert ResolverAlreadyRegistered();
        Storage.slot().resolver = IAddressResolver(_resolver);
    }

    function getAddress(bytes32 name) internal view returns (address) {
        return AddressResolverLib.getAddress(name);
    }

    function resolver() public view returns (IAddressResolver) {
        return Storage.slot().resolver;
    }

    function beaconProxyController() internal view returns (IBeaconProxyController) {
        return AddressResolverLib.beaconProxyController();
    }

    function currencyController() internal view returns (ICurrencyController) {
        return AddressResolverLib.currencyController();
    }

    function genesisValueVault() internal view returns (IGenesisValueVault) {
        return AddressResolverLib.genesisValueVault();
    }

    function reserveFund() internal view returns (IReserveFund) {
        return AddressResolverLib.reserveFund();
    }

    function lendingMarketController() internal view returns (ILendingMarketController) {
        return AddressResolverLib.lendingMarketController();
    }

    function tokenVault() internal view returns (ITokenVault) {
        return AddressResolverLib.tokenVault();
    }
}

// File: contracts/protocol/storages/CurrencyControllerStorage.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import "../../dependencies/chainlink/AggregatorV3Interface.sol";
import "../../dependencies/openzeppelin/utils/structs/EnumerableSet.sol";

struct PriceFeed {
    AggregatorV3Interface[] instances;
    uint256[] heartbeats;
}

library CurrencyControllerStorage {
    using EnumerableSet for EnumerableSet.Bytes32Set;

    bytes32 internal constant STORAGE_SLOT =
        bytes32(uint256(keccak256("sf.storage.currencyController")) - 1);

    struct Storage {
        // Protocol currencies
        EnumerableSet.Bytes32Set currencies;
        mapping(bytes32 ccy => uint256 haircut) haircuts;
        // Total cached decimals of the price feeds
        mapping(bytes32 ccy => uint8 decimals) decimalsCaches;
        mapping(bytes32 ccy => PriceFeed priceFeed) priceFeeds;
    }

    function slot() internal pure returns (Storage storage r) {
        bytes32 _slot = STORAGE_SLOT;
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := _slot
        }
    }
}

// File: contracts/protocol/storages/GenesisValueVaultStorage.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

struct MaturityUnitPrice {
    uint256 unitPrice;
    uint256 compoundFactor;
    uint256 next;
    uint256 prev;
}

struct AutoRollLog {
    uint256 unitPrice;
    uint256 lendingCompoundFactor;
    uint256 borrowingCompoundFactor;
    uint256 next;
    uint256 prev;
}

library GenesisValueVaultStorage {
    bytes32 internal constant STORAGE_SLOT =
        bytes32(uint256(keccak256("sf.storage.genesisValueVault")) - 1);

    struct Storage {
        mapping(bytes32 ccy => bool isInitialized) isInitialized;
        mapping(bytes32 ccy => uint256 compoundFactor) initialCompoundFactors;
        mapping(bytes32 ccy => uint256 compoundFactor) lendingCompoundFactors;
        mapping(bytes32 ccy => uint256 compoundFactor) borrowingCompoundFactors;
        mapping(bytes32 ccy => uint256 maturity) currentMaturity;
        mapping(bytes32 ccy => uint8 decimals) decimals;
        mapping(bytes32 ccy => mapping(address user => int256 balance)) balances;
        mapping(bytes32 ccy => uint256 amount) totalLendingSupplies;
        mapping(bytes32 ccy => uint256 amount) totalBorrowingSupplies;
        // Total amount supplied per maturity
        mapping(bytes32 ccy => mapping(uint256 maturity => int256 balance)) maturityBalances;
        mapping(bytes32 ccy => mapping(uint256 maturity => AutoRollLog log)) autoRollLogs;
        // Maturity when the user receives the balance on the target currency
        mapping(bytes32 ccy => mapping(address user => uint256 maturity)) userMaturities;
    }

    function slot() internal pure returns (Storage storage r) {
        bytes32 _slot = STORAGE_SLOT;
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := _slot
        }
    }
}

// File: contracts/protocol/storages/LendingMarketControllerStorage.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import "../../dependencies/openzeppelin/utils/structs/EnumerableSet.sol";

struct ObservationPeriodLog {
    uint256 totalAmount;
    uint256 totalFutureValue;
}

struct TerminationCurrencyCache {
    int256 price;
    uint8 decimals;
}

library LendingMarketControllerStorage {
    using EnumerableSet for EnumerableSet.Bytes32Set;
    using EnumerableSet for EnumerableSet.UintSet;

    bytes32 internal constant STORAGE_SLOT =
        bytes32(uint256(keccak256("sf.storage.lendingMarketController")) - 1);

    struct Storage {
        uint256 marketBasePeriod;
        uint256 terminationDate;
        mapping(bytes32 ccy => TerminationCurrencyCache currency) terminationCurrencyCaches;
        mapping(bytes32 ccy => uint256 ratio) terminationCollateralRatios;
        mapping(bytes32 ccy => uint8[] orderBookIds) orderBookIdLists;
        mapping(bytes32 ccy => address lendingMarket) lendingMarkets;
        mapping(bytes32 ccy => address futureValueVault) futureValueVaults;
        mapping(bytes32 ccy => uint256 unitPrice) minDebtUnitPrices;
        mapping(bytes32 ccy => uint256 genesisDate) genesisDates;
        mapping(bytes32 ccy => mapping(uint256 maturity => uint256 amount)) pendingOrderAmounts;
        // Order book id history to get order book id from maturity
        mapping(bytes32 ccy => mapping(uint256 maturity => uint8 orderBookIds)) maturityOrderBookIds;
        // List of maturity that the user has open orders or positions
        mapping(bytes32 ccy => mapping(address user => EnumerableSet.UintSet maturities)) usedMaturities;
        // Observation period logs that is used for auto-rolls
        mapping(bytes32 ccy => mapping(uint256 maturity => ObservationPeriodLog log)) observationPeriodLogs;
        // List of currency that the user has open orders or positions
        mapping(address user => EnumerableSet.Bytes32Set currency) usedCurrencies;
        mapping(address user => bool isRedeemed) isRedeemed;
    }

    function slot() internal pure returns (Storage storage r) {
        bytes32 _slot = STORAGE_SLOT;
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := _slot
        }
    }
}

// File: contracts/protocol/storages/mixins/MixinAddressResolverStorage.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {IAddressResolver} from "../../interfaces/IAddressResolver.sol";

library MixinAddressResolverStorage {
    bytes32 internal constant STORAGE_SLOT =
        bytes32(uint256(keccak256("sf.storage.mixinAddressResolver")) - 1);

    struct Storage {
        IAddressResolver resolver;
        mapping(bytes32 contractName => address contractAddress) addressCache;
    }

    function slot() internal pure returns (Storage storage r) {
        bytes32 _slot = STORAGE_SLOT;
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := _slot
        }
    }
}

// File: contracts/protocol/types/ProtocolTypes.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

/**
 * @dev ProtocolTypes is a base-level contract that holds common Secured Finance protocol types
 * @author Secured Finance
 */
library ProtocolTypes {
    enum Side {
        LEND,
        BORROW
    }
}