// File: contracts/Diamond.sol



// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {LibDiamond} from "contracts/libraries/LibDiamond.sol";

import {IDiamondLoupe} from "contracts/interfaces/IDiamondLoupe.sol";
import {IDiamondCut} from "contracts/interfaces/IDiamondCut.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {AppStorage} from "contracts/libraries/AppStorage.sol";
import {Errors} from "contracts/libraries/Errors.sol";
import {Constants} from "contracts/libraries/Constants.sol";

// See https://github.com/mudgen/diamond-2-hardhat/blob/main/contracts/Diamond.sol
// All code taken from diamond implementation, other than init code

contract Diamond {
    AppStorage internal s;

    constructor(address _contractOwner, address _diamondCutFacet, address _baseOracle)
        payable
    {
        require(_contractOwner != address(0), "Diamond: owner can't be address(0)");
        LibDiamond.setContractOwner(_contractOwner);
        s.admin = _contractOwner;

        // Add the diamondCut external function from the diamondCutFacet
        IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
        bytes4[] memory functionSelectors = new bytes4[](1);
        functionSelectors[0] = IDiamondCut.diamondCut.selector;
        cut[0] = IDiamondCut.FacetCut({
            facetAddress: _diamondCutFacet,
            action: IDiamondCut.FacetCutAction.Add,
            functionSelectors: functionSelectors
        });
        LibDiamond.diamondCut(cut, address(0), "");

        LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
        // adding ERC165 data
        ds.supportedInterfaces[type(IERC165).interfaceId] = true;
        ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
        ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
        ds.supportedInterfaces[type(IERC721).interfaceId] = true;

        // @dev init code
        // reentrantStatus needs to be initialized or else the first time nonreentrant is called reentrancy is possible
        s.reentrantStatus = Constants.NOT_ENTERED;
        // initialize to set slot
        s.tokenIdCounter = s.flaggerIdCounter = Constants.HEAD;
        s.name = "DITTO_NFT";
        s.symbol = "DNFT";
        require(_baseOracle != address(0), "Base oracle can't be address(0)");
        s.baseOracle = _baseOracle;
    }

    // Find facet for function that is called and execute the
    // function if a facet is found and return any value.
    fallback() external payable {
        LibDiamond.DiamondStorage storage ds;
        bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
        // get diamond storage
        assembly {
            ds.slot := position
        }

        // get facet from function selector
        address facet = address(bytes20(ds.facets[msg.sig]));
        if (facet == address(0)) revert Errors.FunctionNotFound(msg.sig);

        // Execute external function from facet using delegatecall and return any value.
        assembly {
            // copy function selector and any arguments
            calldatacopy(0, 0, calldatasize())
            // execute function call using the facet
            let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
            // get any return value
            returndatacopy(0, 0, returndatasize())
            // return any return value or error back to the caller
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    receive() external payable {
        revert("Diamond: Does not accept ether");
    }
}

// File: node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// File: contracts/libraries/LibDiamond.sol



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

/**
 * \
 * Author: Nick Mudge
 *
 * Implementation of Diamond facet.
 * Uses the diamond-2 version 1.3.4 implementation:
 * https://github.com/mudgen/diamond-2
 *
 * This is gas optimized by reducing storage reads and storage writes.
 * This code is as complex as it is to reduce gas costs.
 * /*****************************************************************************
 */

import {IDiamondCut} from "contracts/interfaces/IDiamondCut.sol";

/* solhint-disable */
library LibDiamond {
    bytes32 constant DIAMOND_STORAGE_POSITION =
        keccak256("diamond.standard.diamond.storage");

    struct DiamondStorage {
        // maps function selectors to the facets that execute the functions.
        // and maps the selectors to their position in the selectorSlots array.
        // func selector => address facet, selector position
        mapping(bytes4 => bytes32) facets;
        // array of slots of function selectors.
        // each slot holds 8 function selectors.
        mapping(uint256 => bytes32) selectorSlots;
        // owner of the contract
        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
        // The number of function selectors in selectorSlots
        uint16 selectorCount;
        // owner of the contract
        address contractOwner;
    }

    function diamondStorage() internal pure returns (DiamondStorage storage ds) {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
    }

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function setContractOwner(address _newOwner) internal {
        DiamondStorage storage ds = diamondStorage();
        address previousOwner = ds.contractOwner;
        ds.contractOwner = _newOwner;
        emit OwnershipTransferred(previousOwner, _newOwner);
    }

    function contractOwner() internal view returns (address contractOwner_) {
        contractOwner_ = diamondStorage().contractOwner;
    }

    function enforceIsContractOwner() internal view {
        require(
            msg.sender == diamondStorage().contractOwner,
            "LibDiamond: Must be contract owner"
        );
    }

    event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);

    bytes32 constant CLEAR_ADDRESS_MASK = bytes32(uint256(0xffffffffffffffffffffffff));
    bytes32 constant CLEAR_SELECTOR_MASK = bytes32(uint256(0xffffffff << 224));

    // Internal function version of diamondCut
    // This code is almost the same as the external diamondCut,
    // except it is using 'Facet[] memory _diamondCut' instead of
    // 'Facet[] calldata _diamondCut'.
    // The code is duplicated to prevent copying calldata to memory which
    // causes an error for a two dimensional array.
    function diamondCut(
        IDiamondCut.FacetCut[] memory _diamondCut,
        address _init,
        bytes memory _calldata
    ) internal {
        DiamondStorage storage ds = diamondStorage();
        uint256 originalSelectorCount = ds.selectorCount;
        uint256 selectorCount = originalSelectorCount;
        bytes32 selectorSlot;
        // Check if last selector slot is not full
        if (selectorCount % 8 > 0) {
            // get last selectorSlot
            selectorSlot = ds.selectorSlots[selectorCount / 8];
        }
        // loop through diamond cut
        for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
            (selectorCount, selectorSlot) = addReplaceRemoveFacetSelectors(
                selectorCount,
                selectorSlot,
                _diamondCut[facetIndex].facetAddress,
                _diamondCut[facetIndex].action,
                _diamondCut[facetIndex].functionSelectors
            );
        }
        if (selectorCount != originalSelectorCount) {
            ds.selectorCount = uint16(selectorCount);
        }
        // If last selector slot is not full
        if (selectorCount % 8 > 0) {
            ds.selectorSlots[selectorCount / 8] = selectorSlot;
        }
        emit DiamondCut(_diamondCut, _init, _calldata);
        initializeDiamondCut(_init, _calldata);
    }

    function addReplaceRemoveFacetSelectors(
        uint256 _selectorCount,
        bytes32 _selectorSlot,
        address _newFacetAddress,
        IDiamondCut.FacetCutAction _action,
        bytes4[] memory _selectors
    ) internal returns (uint256, bytes32) {
        DiamondStorage storage ds = diamondStorage();
        require(_selectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
        if (_action == IDiamondCut.FacetCutAction.Add) {
            require(
                _newFacetAddress != address(0),
                "LibDiamondCut: Add facet can't be address(0)"
            );
            enforceHasContractCode(
                _newFacetAddress, "LibDiamondCut: Add facet has no code"
            );
            for (
                uint256 selectorIndex; selectorIndex < _selectors.length; selectorIndex++
            ) {
                bytes4 selector = _selectors[selectorIndex];
                bytes32 oldFacet = ds.facets[selector];
                require(
                    address(bytes20(oldFacet)) == address(0),
                    "LibDiamondCut: Can't add function that already exists"
                );
                // add facet for selector
                ds.facets[selector] = bytes20(_newFacetAddress) | bytes32(_selectorCount);
                uint256 selectorInSlotPosition = (_selectorCount % 8) * 32;
                // clear selector position in slot and add selector
                _selectorSlot = (
                    _selectorSlot & ~(CLEAR_SELECTOR_MASK >> selectorInSlotPosition)
                ) | (bytes32(selector) >> selectorInSlotPosition);
                // if slot is full then write it to storage
                if (selectorInSlotPosition == 224) {
                    ds.selectorSlots[_selectorCount / 8] = _selectorSlot;
                    _selectorSlot = 0;
                }
                _selectorCount++;
            }
        } else if (_action == IDiamondCut.FacetCutAction.Replace) {
            require(
                _newFacetAddress != address(0),
                "LibDiamondCut: Replace facet can't be address(0)"
            );
            enforceHasContractCode(
                _newFacetAddress, "LibDiamondCut: Replace facet has no code"
            );
            for (
                uint256 selectorIndex; selectorIndex < _selectors.length; selectorIndex++
            ) {
                bytes4 selector = _selectors[selectorIndex];
                bytes32 oldFacet = ds.facets[selector];
                address oldFacetAddress = address(bytes20(oldFacet));
                // only useful if immutable functions exist
                require(
                    oldFacetAddress != address(this),
                    "LibDiamondCut: Can't replace immutable function"
                );
                require(
                    oldFacetAddress != _newFacetAddress,
                    "LibDiamondCut: Can't replace function with same function"
                );
                require(
                    oldFacetAddress != address(0),
                    "LibDiamondCut: Can't replace function that doesn't exist"
                );
                // replace old facet address
                ds.facets[selector] =
                    (oldFacet & CLEAR_ADDRESS_MASK) | bytes20(_newFacetAddress);
            }
        } else if (_action == IDiamondCut.FacetCutAction.Remove) {
            require(
                _newFacetAddress == address(0),
                "LibDiamondCut: Remove facet address must be address(0)"
            );
            uint256 selectorSlotCount = _selectorCount / 8;
            uint256 selectorInSlotIndex = (_selectorCount % 8) - 1;
            for (
                uint256 selectorIndex; selectorIndex < _selectors.length; selectorIndex++
            ) {
                if (_selectorSlot == 0) {
                    // get last selectorSlot
                    selectorSlotCount--;
                    _selectorSlot = ds.selectorSlots[selectorSlotCount];
                    selectorInSlotIndex = 7;
                }
                bytes4 lastSelector;
                uint256 oldSelectorsSlotCount;
                uint256 oldSelectorInSlotPosition;
                // adding a block here prevents stack too deep error
                {
                    bytes4 selector = _selectors[selectorIndex];
                    bytes32 oldFacet = ds.facets[selector];
                    require(
                        address(bytes20(oldFacet)) != address(0),
                        "LibDiamondCut: Can't remove function that doesn't exist"
                    );
                    // only useful if immutable functions exist
                    require(
                        address(bytes20(oldFacet)) != address(this),
                        "LibDiamondCut: Can't remove immutable function"
                    );
                    // replace selector with last selector in ds.facets
                    // gets the last selector
                    lastSelector = bytes4(_selectorSlot << (selectorInSlotIndex * 32));
                    if (lastSelector != selector) {
                        // update last selector slot position info
                        ds.facets[lastSelector] = (oldFacet & CLEAR_ADDRESS_MASK)
                            | bytes20(ds.facets[lastSelector]);
                    }
                    delete ds.facets[selector];
                    uint256 oldSelectorCount = uint16(uint256(oldFacet));
                    oldSelectorsSlotCount = oldSelectorCount / 8;
                    oldSelectorInSlotPosition = (oldSelectorCount % 8) * 32;
                }
                if (oldSelectorsSlotCount != selectorSlotCount) {
                    bytes32 oldSelectorSlot = ds.selectorSlots[oldSelectorsSlotCount];
                    // clears the selector being deleted and puts the last selector in its place.
                    oldSelectorSlot = (
                        oldSelectorSlot
                            & ~(CLEAR_SELECTOR_MASK >> oldSelectorInSlotPosition)
                    ) | (bytes32(lastSelector) >> oldSelectorInSlotPosition);
                    // update storage with the modified slot
                    ds.selectorSlots[oldSelectorsSlotCount] = oldSelectorSlot;
                } else {
                    // clears the selector being deleted and puts the last selector in its place.
                    _selectorSlot = (
                        _selectorSlot
                            & ~(CLEAR_SELECTOR_MASK >> oldSelectorInSlotPosition)
                    ) | (bytes32(lastSelector) >> oldSelectorInSlotPosition);
                }
                if (selectorInSlotIndex == 0) {
                    delete ds.selectorSlots[selectorSlotCount];
                    _selectorSlot = 0;
                }
                selectorInSlotIndex--;
            }
            _selectorCount = selectorSlotCount * 8 + selectorInSlotIndex + 1;
        } else {
            revert("LibDiamondCut: Incorrect FacetCutAction");
        }
        return (_selectorCount, _selectorSlot);
    }

    function initializeDiamondCut(address _init, bytes memory _calldata) internal {
        if (_init == address(0)) {
            require(
                _calldata.length == 0,
                "LibDiamondCut: _init is address(0) but_calldata is not empty"
            );
        } else {
            require(
                _calldata.length > 0,
                "LibDiamondCut: _calldata is empty but _init is not address(0)"
            );
            if (_init != address(this)) {
                enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
            }
            (bool success, bytes memory error) = _init.delegatecall(_calldata);
            if (success == false) {
                if (error.length > 0) {
                    // bubble up the error
                    revert(string(error));
                } else {
                    revert("LibDiamondCut: _init function reverted");
                }
            }
        }
    }

    function enforceHasContractCode(address _contract, string memory _errorMessage)
        internal
        view
    {
        uint256 contractSize;
        assembly {
            contractSize := extcodesize(_contract)
        }
        require(contractSize > 0, _errorMessage);
    }
}

// File: contracts/interfaces/IDiamondLoupe.sol



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

/*
 * Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
 * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
 */

// A loupe is a small magnifying glass used to look at diamonds.
// These functions look at diamonds
interface IDiamondLoupe {
    /// These functions are expected to be called frequently
    /// by tools.

    struct Facet {
        address facetAddress;
        bytes4[] functionSelectors;
    }

    /// @notice Gets all facet addresses and their four byte function selectors.
    /// @return facets_ Facet
    function facets() external view returns (Facet[] memory facets_);

    /// @notice Gets all the function selectors supported by a specific facet.
    /// @param _facet The facet address.
    /// @return facetFunctionSelectors_
    function facetFunctionSelectors(address _facet)
        external
        view
        returns (bytes4[] memory facetFunctionSelectors_);

    /// @notice Get all the facet addresses used by a diamond.
    /// @return facetAddresses_
    function facetAddresses() external view returns (address[] memory facetAddresses_);

    /// @notice Gets the facet that supports the given selector.
    /// @dev If facet is not found return address(0).
    /// @param _functionSelector The function selector.
    /// @return facetAddress_ The facet address.
    function facetAddress(bytes4 _functionSelector)
        external
        view
        returns (address facetAddress_);
}

// File: contracts/interfaces/IDiamondCut.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
/*
 * Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
 */

interface IDiamondCut {
    enum FacetCutAction {
        Add,
        Replace,
        Remove
    }

    struct FacetCut {
        address facetAddress;
        FacetCutAction action;
        bytes4[] functionSelectors;
    }

    /// @notice Add/replace/remove any number of functions and optionally execute
    ///         a function with delegatecall
    /// @param _diamondCut Contains the facet addresses and function selectors
    /// @param _init The address of the contract or facet to execute _calldata
    /// @param _calldata A function call, including function selector and arguments
    ///                  _calldata is executed with delegatecall on _init
    function diamondCut(
        FacetCut[] calldata _diamondCut,
        address _init,
        bytes calldata _calldata
    ) external;

    event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

// File: node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: contracts/libraries/AppStorage.sol



// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

import {STypes, F, SR} from "contracts/libraries/DataTypes.sol";
import {LibDiamond} from "contracts/libraries/LibDiamond.sol";
import {Errors} from "contracts/libraries/Errors.sol";
import {Constants} from "contracts/libraries/Constants.sol";

// import {console} from "contracts/libraries/console.sol";

struct AppStorage {
    address admin;
    address ownerCandidate;
    address baseOracle;
    uint24 flaggerIdCounter;
    uint40 tokenIdCounter; //NFT - As of 2023, Ethereum had ~2B total tx. Uint40 max value is 1T, which is more than enough for NFTs
    uint8 reentrantStatus;
    // DETH
    mapping(address deth => uint256 vault) dethVault;
    // Bridge
    mapping(address bridge => STypes.Bridge) bridge;
    // Vault
    mapping(uint256 vault => STypes.Vault) vault;
    mapping(uint256 vault => address[]) vaultBridges;
    mapping(uint256 vault => mapping(address account => STypes.VaultUser)) vaultUser;
    // Assets
    mapping(address asset => STypes.Asset) asset;
    mapping(address asset => mapping(address account => STypes.AssetUser)) assetUser;
    // Assets - Orderbook
    mapping(address asset => mapping(uint16 id => STypes.Order)) bids;
    mapping(address asset => mapping(uint16 id => STypes.Order)) asks;
    mapping(address asset => mapping(uint16 id => STypes.Order)) shorts;
    mapping(
        address asset
            => mapping(address account => mapping(uint8 id => STypes.ShortRecord))
        ) shortRecords;
    mapping(uint24 flaggerId => address flagger) flagMapping;
    // ERC721
    mapping(uint256 tokenId => STypes.NFT) nftMapping;
    mapping(uint256 tokenId => address) getApproved;
    mapping(address owner => mapping(address operator => bool)) isApprovedForAll;
    // ERC721 - Assets
    address[] assets;
    mapping(uint256 assetId => address) assetMapping;
    // ERC721 - METADATA STORAGE/LOGIC
    string name;
    string symbol;
}

function appStorage() pure returns (AppStorage storage s) {
    // solhint-disable-next-line no-inline-assembly
    assembly {
        s.slot := 0
    }
}

contract Modifiers {
    AppStorage internal s;

    modifier onlyDAO() {
        LibDiamond.enforceIsContractOwner();
        _;
    }

    modifier onlyAdminOrDAO() {
        if (msg.sender != LibDiamond.contractOwner() && msg.sender != s.admin) {
            revert Errors.NotOwnerOrAdmin();
        }
        _;
    }

    modifier onlyDiamond() {
        if (msg.sender != address(this)) revert Errors.NotDiamond();
        _;
    }

    modifier onlyValidAsset(address asset) {
        if (s.asset[asset].vault == 0) revert Errors.InvalidAsset();
        _;
    }

    modifier isNotFrozen(address asset) {
        if (s.asset[asset].frozen != F.Unfrozen) revert Errors.AssetIsFrozen();
        _;
    }

    modifier isPermanentlyFrozen(address asset) {
        if (s.asset[asset].frozen != F.Permanent) {
            revert Errors.AssetIsNotPermanentlyFrozen();
        }
        _;
    }

    function _onlyValidShortRecord(address asset, address shorter, uint8 id)
        internal
        view
    {
        uint8 maxId = s.assetUser[asset][shorter].shortRecordIdCounter;
        if (id >= maxId) revert Errors.InvalidShortId();
        if (id < Constants.SHORT_STARTING_ID) revert Errors.InvalidShortId();
        if (s.shortRecords[asset][shorter][id].status == SR.Closed) {
            revert Errors.InvalidShortId();
        }
    }

    modifier onlyValidShortRecord(address asset, address shorter, uint8 id) {
        _onlyValidShortRecord(asset, shorter, id);
        _;
    }

    modifier nonReentrant() {
        if (s.reentrantStatus == Constants.ENTERED) revert Errors.ReentrantCall();
        s.reentrantStatus = Constants.ENTERED;
        _;
        s.reentrantStatus = Constants.NOT_ENTERED;
    }

    modifier nonReentrantView() {
        if (s.reentrantStatus == Constants.ENTERED) revert Errors.ReentrantCallView();
        _;
    }

    modifier onlyValidBridge(address bridge) {
        if (s.bridge[bridge].vault == 0) revert Errors.InvalidBridge();
        _;
    }
}

// File: contracts/libraries/Errors.sol



// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

library Errors {
    error AlreadyMinted();
    error AssetIsFrozen();
    error AssetIsNotPermanentlyFrozen();
    error BadHintIdArray();
    error BadShortHint();
    error BridgeAlreadyCreated();
    error CannotCancelMoreThan1000Orders();
    error CannotExitPartialFillSR();
    error CannotFlagSelf();
    error CannotLeaveDustAmount();
    error CannotLiquidateSelf();
    error CannotMintAnymoreNFTs();
    error CannotMintLastShortRecord();
    error CannotSocializeDebt();
    error CannotTransferFlaggableShort();
    error CannotTransferFlaggedShort();
    error CollateralHigherThanMax();
    error CRLowerThanMin();
    error DifferentVaults();
    error ExitShortPriceTooLow();
    error FirstShortDeleted();
    error FirstShortMustBeNFT();
    error FunctionNotFound(bytes4 _functionSelector);
    error AlreadyLiquidatable();
    error InvalidAmount();
    error InvalidAsset();
    error InvalidBridge();
    error InvalidBuyback();
    error InvalidFlaggerHint();
    error InvalidInitialCR();
    error InvalidMsgValue();
    error InvalidPrice();
    error InvalidShortId();
    error InvalidTithe();
    error InvalidTokenId();
    error InvalidTwapPrice();
    error InvalidTWAPSecondsAgo();
    error InvalidVault();
    error InvalidDeth();
    error InsufficientWalletBalance();
    error InsufficientCollateral();
    error InsufficientERCEscrowed();
    error InsufficientETHEscrowed();
    error InsufficientEthInLiquidityPool();
    error InsufficientNumberOfShorts();
    error IsNotNFT();
    error LiquidationAlreadyFlagged();
    error LiquidationIneligibleWindow();
    error SecondaryLiquidationNoValidShorts();
    error MarketAlreadyCreated();
    error NoDittoReward();
    error NoSells();
    error NoShares();
    error NotActiveOrder();
    error NotBridgeForBaseCollateral();
    error NotDiamond();
    error NotLastOrder();
    error NotMinted();
    error NotOwner();
    error NotOwnerOrAdmin();
    error NotOwnerCandidate();
    error NoYield();
    error OrderIdCountTooLow();
    error OrderUnderMinimumSize();
    error OriginalShortRecordCancelled();
    error ParameterIsZero();
    error PostExitCRLtPreExitCR();
    error PriceOrAmountIs0();
    error ReceiverExceededShortRecordLimit();
    error ReentrantCall();
    error ReentrantCallView();
    error ShortNotFlagged();
    error ShortRecordIdOverflow();
    error ShortRecordIdsNotSorted();
    error SufficientCollateral();
    error TooManyHints();
    error UnderMinimum();
    error UnderMinimumDeposit();
    error VaultAlreadyCreated();

    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);
    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);
    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);
    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);
    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);
}

// File: contracts/libraries/Constants.sol



// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

library Constants {
    // @dev mark start of orders mapping
    uint8 internal constant HEAD = 1;
    // @dev only used as an alias since it's the same id
    uint8 internal constant TAIL = 1;
    // for all order types, starting point of orders
    uint8 internal constant STARTING_ID = 100;
    uint8 internal constant SHORT_MAX_ID = 254; // max uint8
    uint8 internal constant SHORT_STARTING_ID = 2;

    uint256 internal constant DUST_FACTOR = 0.5 ether;
    uint256 internal constant MIN_DURATION = 14 days;
    uint256 internal constant CRATIO_MAX = 15 ether;
    uint256 internal constant YIELD_DELAY_SECONDS = 60; // just need enough to prevent flash loan
    uint256 internal constant BRIDGE_YIELD_UPDATE_THRESHOLD = 1000 ether;
    uint256 internal constant BRIDGE_YIELD_PERCENT_THRESHOLD = 0.01 ether; // 1%

    // Bridge
    // @dev Matching RocketPool min deposit for now, Lido is 100 wei
    uint88 internal constant MIN_DEPOSIT = 0.01 ether;

    // re-entrancy
    uint8 internal constant NOT_ENTERED = 1;
    uint8 internal constant ENTERED = 2;
    uint256 internal constant ONE_DECIMAL_PLACES = 10;
    uint256 internal constant TWO_DECIMAL_PLACES = 100;
    uint256 internal constant THREE_DECIMAL_PLACES = 1000;
    uint256 internal constant FOUR_DECIMAL_PLACES = 10000;
    uint256 internal constant FIVE_DECIMAL_PLACES = 100000;
    uint256 internal constant SIX_DECIMAL_PLACES = 1000000;

    // set this to a datetime closer to deployment
    // @dev changing this will likely break the end to end fork test
    uint256 internal constant STARTING_TIME = 1660353637;

    int256 internal constant PREV = -1;
    int256 internal constant EXACT = 0;
    int256 internal constant NEXT = 1;

    bool internal constant MARKET_ORDER = true;
    bool internal constant LIMIT_ORDER = false;

    // Oracle
    // Base Oracle needs to be adjust 10**10 to have full 18 precision
    int256 internal constant BASE_ORACLE_DECIMALS = 10 ** 10;

    // Mainnet TWAP
    address internal constant USDC_WETH =
        address(0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
    address internal constant USDC = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    address internal constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    uint128 internal constant UNISWAP_WETH_BASE_AMT = 1 ether;
    uint256 internal constant DECIMAL_USDC = 10 ** 6; //USDC's ERC contract sets to 6 decimals
}

library Vault {
    // ONE is the default vault
    uint256 internal constant ONE = 1;
}

// File: contracts/libraries/DataTypes.sol



// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.21;

// import {console} from "contracts/libraries/console.sol";

//@dev leave room for others frozen types
//@dev Asset frozen status
enum F {
    Unfrozen,
    Permanent
}

// @dev if this is changed, modify orderTypetoString in libraries/console.sol
// @dev Order types
enum O {
    Uninitialized,
    LimitBid,
    LimitAsk,
    MarketBid,
    MarketAsk,
    LimitShort,
    Cancelled,
    Matched
}

// @dev ShortRecord status
enum SR {
    PartialFill,
    FullyFilled,
    Closed
}

// @dev oracle frequency
enum OF {
    OneHour,
    FifteenMinutes
}

// 2**n-1 with 18 decimals (prices, amount)
// uint64 = 18.45
// uint72 = 4.722k
// uint80 = 1.2m
// uint88 = 300m
// uint96 = 79B
// uint104 = 1.2t

// DataTypes used in storage
library STypes {
    // 2 slots
    struct Order {
        // SLOT 1: 88 + 80 + 16 + 16 + 16 + 8 + 8 + 16 + 8 = 256
        uint88 ercAmount; // max 300m erc
        uint80 price; // max 1.2m eth
        // max orders 65k, with id re-use
        uint16 prevId;
        uint16 id;
        uint16 nextId;
        O orderType;
        // @dev diff against contract creation timestamp to prevent overflow in 2106
        uint32 creationTime; // seconds
        // SLOT 2: 160 + 32 = 192 (64 unused)
        address addr; // 160
        O prevOrderType;
        // @dev storing as 170 with 2 decimals -> 1.70 ether
        uint16 initialCR; // @dev only used for LimitShort
        uint8 shortRecordId; // @dev only used for LimitShort
        uint64 filler;
    }

    // 2 slots
    // @dev dethYieldRate should match Vault
    struct ShortRecord {
        // SLOT 1: 88 + 88 + 80 = 256
        uint88 collateral; // price * ercAmount * initialCR
        uint88 ercDebt; // same as Order.ercAmount
        uint80 dethYieldRate;
        // SLOT 2: 64 + 40 + 32 + 24 + 8 + 8 + 8 + 8 = 184 (64 remaining)
        SR status;
        uint8 prevId;
        uint8 id;
        uint8 nextId;
        uint64 ercDebtRate; // socialized penalty rate
        uint32 updatedAt; // seconds
        uint32 flaggedAt; // seconds
        uint24 flaggerId;
        uint40 tokenId; // As of 2023, Ethereum had ~2B total tx. Uint40 max value is 1T, which is more than enough
    }

    struct NFT {
        // SLOT 1: 160 + 8 + 8 = 176 (80 unused)
        address owner;
        uint8 assetId;
        uint8 shortRecordId;
    }

    // uint8:  [0-255]
    // uint16: [0-65_535]
    // @dev see testMultiAssetSettings()
    struct Asset {
        // SLOT 1: 104 + 88 + 16 + 16 + 16 + 8 + 8 = 256 (0 unused)
        uint104 ercDebt; // max 20.2T
        uint88 dethCollateral;
        uint16 startingShortId;
        uint16 orderIdCounter; // max is uint16 but need to throw/handle that?
        uint16 initialCR; // 5 ether -> [1-10, 2 decimals]
        F frozen; // 0 or 1
        uint8 vault;
        // SLOT 2 (Liquidation Parameters)
        // 64 + 16*3 + 8*10 = 192 (64 unused)
        uint8 minBidEth; // 10 -> (1 * 10**18 / 10**2) = 0.1 ether
        uint8 minAskEth; // 10 -> (1 * 10**18 / 10**2) = 0.1 ether
        uint16 minShortErc; // 2000 -> (2000 * 10**18) -> 2000 ether
        uint8 minimumCR; // 1.1 ether -> [1-2, 2 decimals]
        uint8 tappFeePct; // 0.025 ether -> [0-2.5%, 3 decimals]
        uint8 callerFeePct; // 0.005 ether -> [0-2.5%, 3 decimals]
        uint8 forcedBidPriceBuffer; // 1.1 ether -> [1-2, 2 decimals]
        uint8 assetId;
        uint64 ercDebtRate; // max 18x, socialized penalty rate
        uint16 primaryLiquidationCR; // 1.5 ether -> [1-5, 2 decimals]
        uint16 secondaryLiquidationCR; // 1.4 ether -> [1-5, 2 decimals]
        uint8 resetLiquidationTime; // 12 hours -> [1-48 hours, 0 decimals]
        uint8 secondLiquidationTime; // 8 hours -> [1-48 hours, 0 decimals]
        uint8 firstLiquidationTime; // 6 hours -> [1-48 hours, 0 decimals]
        uint64 filler; // keep slots distinct
        // SLOT 3 (Chainlink)
        address oracle; // for non-usd asset
    }

    // 3 slots
    // @dev dethYieldRate should match ShortRecord
    struct Vault {
        // SLOT 1: 88 + 88 + 80 = 256 (0 unused)
        uint88 dethCollateral; // max 309m, 18 decimals
        uint88 dethTotal; // max 309m, 18 decimals
        uint80 dethYieldRate; // onlyUp
        // SLOT 2: 88 + 16 + 16 = 136 (128 unused)
        // tracked for shorter ditto rewards
        uint88 dethCollateralReward; // onlyUp
        uint16 dethTithePercent; // [0-100, 2 decimals]
        uint16 dittoShorterRate; // per unit of dethCollateral
        uint128 filler2;
        // SLOT 3: 128 + 96 + 16 + 16 = 256
        uint128 dittoMatchedShares;
        uint96 dittoMatchedReward; // max 79B, 18 decimals
        uint16 dittoMatchedRate;
        uint16 dittoMatchedTime; // last claim (in days) from STARTING_TIME
    }

    // 1 slots
    struct AssetUser {
        // SLOT 1: 104 + 24 + 32 + 8 = 168 (88 unused)
        uint104 ercEscrowed;
        uint24 g_flaggerId;
        uint32 g_flaggedAt; // represents the most recent flag - in hours
        uint8 shortRecordIdCounter;
        uint96 filler;
    }

    // 1 slots
    struct VaultUser {
        // SLOT 1: 88 + 88 + 80 = 256 (0 unused)
        uint88 ethEscrowed;
        uint88 dittoMatchedShares;
        uint80 dittoReward; // max 1.2m, 18 decimals
    }

    struct Bridge {
        // SLOT 1: 16 + 8 + 8 = 32 (224 unused)
        uint8 vault;
        uint16 withdrawalFee;
        uint8 unstakeFee;
    }
}

// @dev DataTypes only used in memory
library MTypes {
    struct OrderHint {
        uint16 hintId;
        uint256 creationTime;
    }

    struct BatchLiquidation {
        address shorter;
        uint8 shortId;
    }

    struct Match {
        uint88 fillEth;
        uint88 fillErc;
        uint88 colUsed;
        uint88 dittoMatchedShares;
        // Below used only for bids
        uint88 shortFillEth; // Includes colUsed + fillEth from shorts
        uint96 askFillErc; // Subset of fillErc
        bool ratesQueried; // Save gas when matching shorts
        uint80 dethYieldRate;
        uint64 ercDebtRate;
    }

    struct ExitShort {
        address asset;
        uint256 ercDebt;
        uint88 collateral;
        uint88 ethFilled;
        uint88 ercAmountLeft;
        uint88 ercFilled;
        uint256 beforeExitCR;
    }

    struct CombineShorts {
        bool shortFlagExists;
        uint32 shortUpdatedAt;
    }

    struct PrimaryLiquidation {
        address asset;
        uint256 vault;
        STypes.ShortRecord short;
        address shorter;
        uint256 cRatio;
        uint80 oraclePrice;
        uint256 forcedBidPriceBuffer;
        uint256 ethDebt;
        uint88 ethFilled;
        uint88 ercDebtMatched;
        bool loseCollateral;
        uint256 tappFeePct;
        uint256 callerFeePct;
        uint88 gasFee;
        uint88 totalFee; // gasFee + tappFee + callerFee
        uint256 minimumCR;
    }

    struct SecondaryLiquidation {
        address asset;
        uint256 vault;
        STypes.ShortRecord short;
        address shorter;
        uint256 cRatio;
        uint256 minimumCR;
        uint88 liquidatorCollateral;
        uint256 oraclePrice;
    }

    struct BidMatchAlgo {
        uint16 askId;
        uint16 shortHintId;
        uint16 shortId;
        uint16 prevShortId;
        uint16 firstShortIdBelowOracle;
        uint16 matchedAskId;
        uint16 matchedShortId;
        bool isMovingBack;
        bool isMovingFwd;
        uint256 oraclePrice;
        uint16 dustAskId;
        uint16 dustShortId;
    }

    struct CreateVaultParams {
        uint16 dethTithePercent;
        uint16 dittoMatchedRate;
        uint16 dittoShorterRate;
    }

    struct CreateLimitShortParam {
        address asset;
        uint256 eth;
        uint256 minShortErc;
        uint256 minAskEth;
        uint16 startingId;
        uint256 oraclePrice;
    }
}