creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/core/SortedTroves.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.19;\n\nimport \"contracts/interfaces/ITroveManager.sol\";\n\n/**\n    @title Prisma Sorted Troves\n    @notice Based on Liquity's `SortedTroves`:\n            https://github.com/liquity/dev/blob/main/packages/contracts/contracts/SortedTroves.sol\n\n            Originally derived from `SortedDoublyLinkedList`:\n            https://github.com/livepeer/protocol/blob/master/contracts/libraries/SortedDoublyLL.sol\n */\ncontract SortedTroves {\n    ITroveManager public troveManager;\n\n    Data public data;\n\n    // Information for a node in the list\n    struct Node {\n        bool exists;\n        address nextId; // Id of next node (smaller NICR) in the list\n        address prevId; // Id of previous node (larger NICR) in the list\n    }\n\n    // Information for the list\n    struct Data {\n        address head; // Head of the list. Also the node in the list with the largest NICR\n        address tail; // Tail of the list. Also the node in the list with the smallest NICR\n        uint256 size; // Current size of the list\n        mapping(address => Node) nodes; // Track the corresponding ids for each node in the list\n    }\n\n    event NodeAdded(address _id, uint256 _NICR);\n    event NodeRemoved(address _id);\n\n    function setAddresses(address _troveManagerAddress) external {\n        require(address(troveManager) == address(0), \"Already set\");\n        troveManager = ITroveManager(_troveManagerAddress);\n    }\n\n    /*\n     * @dev Add a node to the list\n     * @param _id Node's id\n     * @param _NICR Node's NICR\n     * @param _prevId Id of previous node for the insert position\n     * @param _nextId Id of next node for the insert position\n     */\n\n    function insert(address _id, uint256 _NICR, address _prevId, address _nextId) external {\n        ITroveManager troveManagerCached = troveManager;\n\n        _requireCallerIsTroveManager(troveManagerCached);\n\n        Node storage node = data.nodes[_id];\n        // List must not already contain node\n        require(!node.exists, \"SortedTroves: List already contains the node\");\n        // Node id must not be null\n        require(_id != address(0), \"SortedTroves: Id cannot be zero\");\n\n        _insert(node, troveManagerCached, _id, _NICR, _prevId, _nextId);\n    }\n\n    function _insert(\n        Node storage node,\n        ITroveManager _troveManager,\n        address _id,\n        uint256 _NICR,\n        address _prevId,\n        address _nextId\n    ) internal {\n        // NICR must be non-zero\n        require(_NICR > 0, \"SortedTroves: NICR must be positive\");\n\n        address prevId = _prevId;\n        address nextId = _nextId;\n\n        if (!_validInsertPosition(_troveManager, _NICR, prevId, nextId)) {\n            // Sender's hint was not a valid insert position\n            // Use sender's hint to find a valid insert position\n            (prevId, nextId) = _findInsertPosition(_troveManager, _NICR, prevId, nextId);\n        }\n\n        node.exists = true;\n\n        if (prevId == address(0) && nextId == address(0)) {\n            // Insert as head and tail\n            data.head = _id;\n            data.tail = _id;\n        } else if (prevId == address(0)) {\n            // Insert before `prevId` as the head\n            address head = data.head;\n            node.nextId = head;\n            data.nodes[head].prevId = _id;\n            data.head = _id;\n        } else if (nextId == address(0)) {\n            // Insert after `nextId` as the tail\n            address tail = data.tail;\n            node.prevId = tail;\n            data.nodes[tail].nextId = _id;\n            data.tail = _id;\n        } else {\n            // Insert at insert position between `prevId` and `nextId`\n            node.nextId = nextId;\n            node.prevId = prevId;\n            data.nodes[prevId].nextId = _id;\n            data.nodes[nextId].prevId = _id;\n        }\n\n        data.size = data.size + 1;\n        emit NodeAdded(_id, _NICR);\n    }\n\n    function remove(address _id) external {\n        _requireCallerIsTroveManager(troveManager);\n        _remove(data.nodes[_id], _id);\n    }\n\n    /*\n     * @dev Remove a node from the list\n     * @param _id Node's id\n     */\n    function _remove(Node storage node, address _id) internal {\n        // List must contain the node\n        require(node.exists, \"SortedTroves: List does not contain the id\");\n\n        if (data.size > 1) {\n            // List contains more than a single node\n            if (_id == data.head) {\n                // The removed node is the head\n                // Set head to next node\n                address head = node.nextId;\n                data.head = head;\n                // Set prev pointer of new head to null\n                data.nodes[head].prevId = address(0);\n            } else if (_id == data.tail) {\n                address tail = node.prevId;\n                // The removed node is the tail\n                // Set tail to previous node\n                data.tail = tail;\n                // Set next pointer of new tail to null\n                data.nodes[tail].nextId = address(0);\n            } else {\n                address prevId = node.prevId;\n                address nextId = node.nextId;\n                // The removed node is neither the head nor the tail\n                // Set next pointer of previous node to the next node\n                data.nodes[prevId].nextId = nextId;\n                // Set prev pointer of next node to the previous node\n                data.nodes[nextId].prevId = prevId;\n            }\n        } else {\n            // List contains a single node\n            // Set the head and tail to null\n            data.head = address(0);\n            data.tail = address(0);\n        }\n\n        delete data.nodes[_id];\n        data.size = data.size - 1;\n        emit NodeRemoved(_id);\n    }\n\n    /*\n     * @dev Re-insert the node at a new position, based on its new NICR\n     * @param _id Node's id\n     * @param _newNICR Node's new NICR\n     * @param _prevId Id of previous node for the new insert position\n     * @param _nextId Id of next node for the new insert position\n     */\n    function reInsert(address _id, uint256 _newNICR, address _prevId, address _nextId) external {\n        ITroveManager troveManagerCached = troveManager;\n\n        _requireCallerIsTroveManager(troveManagerCached);\n\n        Node storage node = data.nodes[_id];\n\n        // Remove node from the list\n        _remove(node, _id);\n\n        _insert(node, troveManagerCached, _id, _newNICR, _prevId, _nextId);\n    }\n\n    /*\n     * @dev Checks if the list contains a node\n     */\n    function contains(address _id) public view returns (bool) {\n        return data.nodes[_id].exists;\n    }\n\n    /*\n     * @dev Checks if the list is empty\n     */\n    function isEmpty() public view returns (bool) {\n        return data.size == 0;\n    }\n\n    /*\n     * @dev Returns the current size of the list\n     */\n    function getSize() external view returns (uint256) {\n        return data.size;\n    }\n\n    /*\n     * @dev Returns the first node in the list (node with the largest NICR)\n     */\n    function getFirst() external view returns (address) {\n        return data.head;\n    }\n\n    /*\n     * @dev Returns the last node in the list (node with the smallest NICR)\n     */\n    function getLast() external view returns (address) {\n        return data.tail;\n    }\n\n    /*\n     * @dev Returns the next node (with a smaller NICR) in the list for a given node\n     * @param _id Node's id\n     */\n    function getNext(address _id) external view returns (address) {\n        return data.nodes[_id].nextId;\n    }\n\n    /*\n     * @dev Returns the previous node (with a larger NICR) in the list for a given node\n     * @param _id Node's id\n     */\n    function getPrev(address _id) external view returns (address) {\n        return data.nodes[_id].prevId;\n    }\n\n    /*\n     * @dev Check if a pair of nodes is a valid insertion point for a new node with the given NICR\n     * @param _NICR Node's NICR\n     * @param _prevId Id of previous node for the insert position\n     * @param _nextId Id of next node for the insert position\n     */\n    function validInsertPosition(uint256 _NICR, address _prevId, address _nextId) external view returns (bool) {\n        return _validInsertPosition(troveManager, _NICR, _prevId, _nextId);\n    }\n\n    function _validInsertPosition(\n        ITroveManager _troveManager,\n        uint256 _NICR,\n        address _prevId,\n        address _nextId\n    ) internal view returns (bool) {\n        if (_prevId == address(0) && _nextId == address(0)) {\n            // `(null, null)` is a valid insert position if the list is empty\n            return isEmpty();\n        } else if (_prevId == address(0)) {\n            // `(null, _nextId)` is a valid insert position if `_nextId` is the head of the list\n            return data.head == _nextId && _NICR >= _troveManager.getNominalICR(_nextId);\n        } else if (_nextId == address(0)) {\n            // `(_prevId, null)` is a valid insert position if `_prevId` is the tail of the list\n            return data.tail == _prevId && _NICR <= _troveManager.getNominalICR(_prevId);\n        } else {\n            // `(_prevId, _nextId)` is a valid insert position if they are adjacent nodes and `_NICR` falls between the two nodes' NICRs\n            return\n                data.nodes[_prevId].nextId == _nextId &&\n                _troveManager.getNominalICR(_prevId) >= _NICR &&\n                _NICR >= _troveManager.getNominalICR(_nextId);\n        }\n    }\n\n    /*\n     * @dev Descend the list (larger NICRs to smaller NICRs) to find a valid insert position\n     * @param _troveManager TroveManager contract, passed in as param to save SLOAD’s\n     * @param _NICR Node's NICR\n     * @param _startId Id of node to start descending the list from\n     */\n    function _descendList(\n        ITroveManager _troveManager,\n        uint256 _NICR,\n        address _startId\n    ) internal view returns (address, address) {\n        // If `_startId` is the head, check if the insert position is before the head\n        if (data.head == _startId && _NICR >= _troveManager.getNominalICR(_startId)) {\n            return (address(0), _startId);\n        }\n\n        address prevId = _startId;\n        address nextId = data.nodes[prevId].nextId;\n\n        // Descend the list until we reach the end or until we find a valid insert position\n        while (prevId != address(0) && !_validInsertPosition(_troveManager, _NICR, prevId, nextId)) {\n            prevId = data.nodes[prevId].nextId;\n            nextId = data.nodes[prevId].nextId;\n        }\n\n        return (prevId, nextId);\n    }\n\n    /*\n     * @dev Ascend the list (smaller NICRs to larger NICRs) to find a valid insert position\n     * @param _troveManager TroveManager contract, passed in as param to save SLOAD’s\n     * @param _NICR Node's NICR\n     * @param _startId Id of node to start ascending the list from\n     */\n    function _ascendList(\n        ITroveManager _troveManager,\n        uint256 _NICR,\n        address _startId\n    ) internal view returns (address, address) {\n        // If `_startId` is the tail, check if the insert position is after the tail\n        if (data.tail == _startId && _NICR <= _troveManager.getNominalICR(_startId)) {\n            return (_startId, address(0));\n        }\n\n        address nextId = _startId;\n        address prevId = data.nodes[nextId].prevId;\n\n        // Ascend the list until we reach the end or until we find a valid insertion point\n        while (nextId != address(0) && !_validInsertPosition(_troveManager, _NICR, prevId, nextId)) {\n            nextId = data.nodes[nextId].prevId;\n            prevId = data.nodes[nextId].prevId;\n        }\n\n        return (prevId, nextId);\n    }\n\n    /*\n     * @dev Find the insert position for a new node with the given NICR\n     * @param _NICR Node's NICR\n     * @param _prevId Id of previous node for the insert position\n     * @param _nextId Id of next node for the insert position\n     */\n    function findInsertPosition(\n        uint256 _NICR,\n        address _prevId,\n        address _nextId\n    ) external view returns (address, address) {\n        return _findInsertPosition(troveManager, _NICR, _prevId, _nextId);\n    }\n\n    function _findInsertPosition(\n        ITroveManager _troveManager,\n        uint256 _NICR,\n        address _prevId,\n        address _nextId\n    ) internal view returns (address, address) {\n        address prevId = _prevId;\n        address nextId = _nextId;\n\n        if (prevId != address(0)) {\n            if (!contains(prevId) || _NICR > _troveManager.getNominalICR(prevId)) {\n                // `prevId` does not exist anymore or now has a smaller NICR than the given NICR\n                prevId = address(0);\n            }\n        }\n\n        if (nextId != address(0)) {\n            if (!contains(nextId) || _NICR < _troveManager.getNominalICR(nextId)) {\n                // `nextId` does not exist anymore or now has a larger NICR than the given NICR\n                nextId = address(0);\n            }\n        }\n\n        if (prevId == address(0) && nextId == address(0)) {\n            // No hint - descend list starting from head\n            return _descendList(_troveManager, _NICR, data.head);\n        } else if (prevId == address(0)) {\n            // No `prevId` for hint - ascend list starting from `nextId`\n            return _ascendList(_troveManager, _NICR, nextId);\n        } else if (nextId == address(0)) {\n            // No `nextId` for hint - descend list starting from `prevId`\n            return _descendList(_troveManager, _NICR, prevId);\n        } else {\n            // Descend list starting from `prevId`\n            return _descendList(_troveManager, _NICR, prevId);\n        }\n    }\n\n    function _requireCallerIsTroveManager(ITroveManager _troveManager) internal view {\n        require(msg.sender == address(_troveManager), \"SortedTroves: Caller is not the TroveManager\");\n    }\n}\n"
      },
      "contracts/interfaces/ITroveManager.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\ninterface ITroveManager {\n    event BaseRateUpdated(uint256 _baseRate);\n    event CollateralSent(address _to, uint256 _amount);\n    event LTermsUpdated(uint256 _L_collateral, uint256 _L_debt);\n    event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime);\n    event Redemption(\n        uint256 _attemptedDebtAmount,\n        uint256 _actualDebtAmount,\n        uint256 _collateralSent,\n        uint256 _collateralFee\n    );\n    event RewardClaimed(address indexed account, address indexed recipient, uint256 claimed);\n    event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot);\n    event TotalStakesUpdated(uint256 _newTotalStakes);\n    event TroveIndexUpdated(address _borrower, uint256 _newIndex);\n    event TroveSnapshotsUpdated(uint256 _L_collateral, uint256 _L_debt);\n    event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 _stake, uint8 _operation);\n\n    function addCollateralSurplus(address borrower, uint256 collSurplus) external;\n\n    function applyPendingRewards(address _borrower) external returns (uint256 coll, uint256 debt);\n\n    function claimCollateral(address _receiver) external;\n\n    function claimReward(address receiver) external returns (uint256);\n\n    function closeTrove(address _borrower, address _receiver, uint256 collAmount, uint256 debtAmount) external;\n\n    function closeTroveByLiquidation(address _borrower) external;\n\n    function collectInterests() external;\n\n    function decayBaseRateAndGetBorrowingFee(uint256 _debt) external returns (uint256);\n\n    function decreaseDebtAndSendCollateral(address account, uint256 debt, uint256 coll) external;\n\n    function fetchPrice() external returns (uint256);\n\n    function finalizeLiquidation(\n        address _liquidator,\n        uint256 _debt,\n        uint256 _coll,\n        uint256 _collSurplus,\n        uint256 _debtGasComp,\n        uint256 _collGasComp\n    ) external;\n\n    function getEntireSystemBalances() external returns (uint256, uint256, uint256);\n\n    function movePendingTroveRewardsToActiveBalances(uint256 _debt, uint256 _collateral) external;\n\n    function notifyRegisteredId(uint256[] calldata _assignedIds) external returns (bool);\n\n    function openTrove(\n        address _borrower,\n        uint256 _collateralAmount,\n        uint256 _compositeDebt,\n        uint256 NICR,\n        address _upperHint,\n        address _lowerHint,\n        bool _isRecoveryMode\n    ) external returns (uint256 stake, uint256 arrayIndex);\n\n    function redeemCollateral(\n        uint256 _debtAmount,\n        address _firstRedemptionHint,\n        address _upperPartialRedemptionHint,\n        address _lowerPartialRedemptionHint,\n        uint256 _partialRedemptionHintNICR,\n        uint256 _maxIterations,\n        uint256 _maxFeePercentage\n    ) external;\n\n    function setAddresses(address _priceFeedAddress, address _sortedTrovesAddress, address _collateralToken) external;\n\n    function setParameters(\n        uint256 _minuteDecayFactor,\n        uint256 _redemptionFeeFloor,\n        uint256 _maxRedemptionFee,\n        uint256 _borrowingFeeFloor,\n        uint256 _maxBorrowingFee,\n        uint256 _interestRateInBPS,\n        uint256 _maxSystemDebt,\n        uint256 _MCR\n    ) external;\n\n    function setPaused(bool _paused) external;\n\n    function setPriceFeed(address _priceFeedAddress) external;\n\n    function startSunset() external;\n\n    function updateBalances() external;\n\n    function updateTroveFromAdjustment(\n        bool _isRecoveryMode,\n        bool _isDebtIncrease,\n        uint256 _debtChange,\n        uint256 _netDebtChange,\n        bool _isCollIncrease,\n        uint256 _collChange,\n        address _upperHint,\n        address _lowerHint,\n        address _borrower,\n        address _receiver\n    ) external returns (uint256, uint256, uint256);\n\n    function vaultClaimReward(address claimant, address) external returns (uint256);\n\n    function BOOTSTRAP_PERIOD() external view returns (uint256);\n\n    function CCR() external view returns (uint256);\n\n    function DEBT_GAS_COMPENSATION() external view returns (uint256);\n\n    function DECIMAL_PRECISION() external view returns (uint256);\n\n    function L_collateral() external view returns (uint256);\n\n    function L_debt() external view returns (uint256);\n\n    function MAX_INTEREST_RATE_IN_BPS() external view returns (uint256);\n\n    function MCR() external view returns (uint256);\n\n    function PERCENT_DIVISOR() external view returns (uint256);\n\n    function PRISMA_CORE() external view returns (address);\n\n    function Troves(\n        address\n    )\n        external\n        view\n        returns (\n            uint256 debt,\n            uint256 coll,\n            uint256 stake,\n            uint8 status,\n            uint128 arrayIndex,\n            uint256 activeInterestIndex\n        );\n\n    function accountLatestMint(address) external view returns (uint32 amount, uint32 week, uint32 day);\n\n    function baseRate() external view returns (uint256);\n\n    function borrowerOperationsAddress() external view returns (address);\n\n    function borrowingFeeFloor() external view returns (uint256);\n\n    function claimableReward(address account) external view returns (uint256);\n\n    function collateralToken() external view returns (address);\n\n    function dailyMintReward(uint256) external view returns (uint256);\n\n    function debtToken() external view returns (address);\n\n    function defaultedCollateral() external view returns (uint256);\n\n    function defaultedDebt() external view returns (uint256);\n\n    function emissionId() external view returns (uint16 debt, uint16 minting);\n\n    function getBorrowingFee(uint256 _debt) external view returns (uint256);\n\n    function getBorrowingFeeWithDecay(uint256 _debt) external view returns (uint256);\n\n    function getBorrowingRate() external view returns (uint256);\n\n    function getBorrowingRateWithDecay() external view returns (uint256);\n\n    function getCurrentICR(address _borrower, uint256 _price) external view returns (uint256);\n\n    function getEntireDebtAndColl(\n        address _borrower\n    ) external view returns (uint256 debt, uint256 coll, uint256 pendingDebtReward, uint256 pendingCollateralReward);\n\n    function getEntireSystemColl() external view returns (uint256);\n\n    function getEntireSystemDebt() external view returns (uint256);\n\n    function getNominalICR(address _borrower) external view returns (uint256);\n\n    function getPendingCollAndDebtRewards(address _borrower) external view returns (uint256, uint256);\n\n    function getRedemptionFeeWithDecay(uint256 _collateralDrawn) external view returns (uint256);\n\n    function getRedemptionRate() external view returns (uint256);\n\n    function getRedemptionRateWithDecay() external view returns (uint256);\n\n    function getTotalActiveCollateral() external view returns (uint256);\n\n    function getTotalActiveDebt() external view returns (uint256);\n\n    function getTotalMints(uint256 week) external view returns (uint32[7] memory);\n\n    function getTroveCollAndDebt(address _borrower) external view returns (uint256 coll, uint256 debt);\n\n    function getTroveFromTroveOwnersArray(uint256 _index) external view returns (address);\n\n    function getTroveOwnersCount() external view returns (uint256);\n\n    function getTroveStake(address _borrower) external view returns (uint256);\n\n    function getTroveStatus(address _borrower) external view returns (uint256);\n\n    function getWeek() external view returns (uint256 week);\n\n    function getWeekAndDay() external view returns (uint256, uint256);\n\n    function guardian() external view returns (address);\n\n    function hasPendingRewards(address _borrower) external view returns (bool);\n\n    function interestPayable() external view returns (uint256);\n\n    function interestRate() external view returns (uint256);\n\n    function lastFeeOperationTime() external view returns (uint256);\n\n    function lastUpdate() external view returns (uint32);\n\n    function liquidationManager() external view returns (address);\n\n    function maxSystemDebt() external view returns (uint256);\n\n    function minuteDecayFactor() external view returns (uint256);\n\n    function owner() external view returns (address);\n\n    function paused() external view returns (bool);\n\n    function periodFinish() external view returns (uint32);\n\n    function priceFeed() external view returns (address);\n\n    function redemptionFeeFloor() external view returns (uint256);\n\n    function rewardIntegral() external view returns (uint256);\n\n    function rewardIntegralFor(address) external view returns (uint256);\n\n    function rewardRate() external view returns (uint128);\n\n    function rewardSnapshots(address) external view returns (uint256 collateral, uint256 debt);\n\n    function sortedTroves() external view returns (address);\n\n    function sunsetting() external view returns (bool);\n\n    function surplusBalances(address) external view returns (uint256);\n\n    function totalCollateralSnapshot() external view returns (uint256);\n\n    function totalStakes() external view returns (uint256);\n\n    function totalStakesSnapshot() external view returns (uint256);\n\n    function vault() external view returns (address);\n}\n"
      }
    },
    "settings": {
      "evmVersion": "paris",
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "libraries": {
        "SortedTroves.sol": {}
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      }
    }
  }}