CollateralizationOracle - Fei in excluded deposits contributes to userCirculatingFei
`CollateralizationOracle.pcvStats` iterates over all deposits, queries the resistant balance and FEI for each deposit, and accumulates the total value of the resistant balances and the total resistant FEI. Any Guardian or Governor can exclude (and re-include) a deposit that has become problematic in some way, for example, because it is reporting wrong numbers. Finally, the `pcvStats` function computes the `userCirculatingFei` as the total FEI supply minus the accumulated resistant FEI balances; the idea here is to determine the amount of “free” FEI, or FEI that is not PCV. However, the FEI balances from excluded deposits contribute to the `userCirculatingFei`, although they are clearly not “free” FEI. That leads to a wrong `protocolEquity` and a skewed collateralization ratio and might therefore have a significant impact on the economics of the system.\\nIt should be noted that even the exclusion from the total PCV leads to a `protocolEquity` and a collateralization ratio that could be considered skewed (again, it might depend on the exact reasons for exclusion), but “adding” the missing FEI to the `userCirculatingFei` distorts these numbers even more.\\nIn the extreme scenario that all deposits have been excluded, the entire Fei supply is currently reported as `userCirculatingFei`.\\n```\\n/// @notice returns the Protocol-Controlled Value, User-circulating FEI, and\\n/// Protocol Equity.\\n/// @return protocolControlledValue : the total USD value of all assets held\\n/// by the protocol.\\n/// @return userCirculatingFei : the number of FEI not owned by the protocol.\\n/// @return protocolEquity : the difference between PCV and user circulating FEI.\\n/// If there are more circulating FEI than $ in the PCV, equity is 0.\\n/// @return validityStatus : the current oracle validity status (false if any\\n/// of the oracles for tokens held in the PCV are invalid, or if\\n/// this contract is paused).\\nfunction pcvStats() public override view returns (\\n  uint256 protocolControlledValue,\\n  uint256 userCirculatingFei,\\n  int256 protocolEquity,\\n  bool validityStatus\\n) {\\n    uint256 \\_protocolControlledFei = 0;\\n    validityStatus = !paused();\\n\\n    // For each token// rest of code\\n    for (uint256 i = 0; i < tokensInPcv.length(); i++) {\\n        address \\_token = tokensInPcv.at(i);\\n        uint256 \\_totalTokenBalance  = 0;\\n\\n        // For each deposit// rest of code\\n        for (uint256 j = 0; j < tokenToDeposits[\\_token].length(); j++) {\\n            address \\_deposit = tokenToDeposits[\\_token].at(j);\\n\\n            // ignore deposits that are excluded by the Guardian\\n            if (!excludedDeposits[\\_deposit]) {\\n                // read the deposit, and increment token balance/protocol fei\\n                (uint256 \\_depositBalance, uint256 \\_depositFei) = IPCVDepositBalances(\\_deposit).resistantBalanceAndFei();\\n                \\_totalTokenBalance += \\_depositBalance;\\n                \\_protocolControlledFei += \\_depositFei;\\n            }\\n        }\\n\\n        // If the protocol holds non-zero balance of tokens, fetch the oracle price to\\n        // increment PCV by \\_totalTokenBalance \\* oracle price USD.\\n        if (\\_totalTokenBalance != 0) {\\n            (Decimal.D256 memory \\_oraclePrice, bool \\_oracleValid) = IOracle(tokenToOracle[\\_token]).read();\\n            if (!\\_oracleValid) {\\n                validityStatus = false;\\n            }\\n            protocolControlledValue += \\_oraclePrice.mul(\\_totalTokenBalance).asUint256();\\n        }\\n    }\\n\\n    userCirculatingFei = fei().totalSupply() - \\_protocolControlledFei;\\n    protocolEquity = int256(protocolControlledValue) - int256(userCirculatingFei);\\n}\\n```\\n
It is unclear how to fix this. One might want to exclude the FEI in excluded deposits entirely from the calculation, but not knowing the amount was the reason to exclude the deposit in the first place.\\nOne option could be to let the entity that excludes a deposit specify substitute values that should be used instead of querying the numbers from the deposit. However, it is questionable whether this approach is practical if the numbers we'd like to see as substitute values change quickly or repeatedly over time. Ultimately, the querying function itself should be fixed. Moreover, as the substitute values can dramatically impact the system economics, we'd only like to trust the Governor with this and not give this permission to a Guardian. However, the original intention was to give a role with less trust than the Governor the possibility to react quickly to a deposit that reports wrong numbers; if the exclusion of deposits becomes the Governor's privilege, such a quick and lightweight intervention isn't possible anymore.\\nIndependently, we recommend taking proper care of the situation that all deposits - or just too many - have been excluded, for example, by setting the returned `validityStatus` to `false`, as in this case, there is not enough information to compute the collateralization ratio even as a crude approximation.
```\\n/// @notice returns the Protocol-Controlled Value, User-circulating FEI, and\\n/// Protocol Equity.\\n/// @return protocolControlledValue : the total USD value of all assets held\\n/// by the protocol.\\n/// @return userCirculatingFei : the number of FEI not owned by the protocol.\\n/// @return protocolEquity : the difference between PCV and user circulating FEI.\\n/// If there are more circulating FEI than $ in the PCV, equity is 0.\\n/// @return validityStatus : the current oracle validity status (false if any\\n/// of the oracles for tokens held in the PCV are invalid, or if\\n/// this contract is paused).\\nfunction pcvStats() public override view returns (\\n  uint256 protocolControlledValue,\\n  uint256 userCirculatingFei,\\n  int256 protocolEquity,\\n  bool validityStatus\\n) {\\n    uint256 \\_protocolControlledFei = 0;\\n    validityStatus = !paused();\\n\\n    // For each token// rest of code\\n    for (uint256 i = 0; i < tokensInPcv.length(); i++) {\\n        address \\_token = tokensInPcv.at(i);\\n        uint256 \\_totalTokenBalance  = 0;\\n\\n        // For each deposit// rest of code\\n        for (uint256 j = 0; j < tokenToDeposits[\\_token].length(); j++) {\\n            address \\_deposit = tokenToDeposits[\\_token].at(j);\\n\\n            // ignore deposits that are excluded by the Guardian\\n            if (!excludedDeposits[\\_deposit]) {\\n                // read the deposit, and increment token balance/protocol fei\\n                (uint256 \\_depositBalance, uint256 \\_depositFei) = IPCVDepositBalances(\\_deposit).resistantBalanceAndFei();\\n                \\_totalTokenBalance += \\_depositBalance;\\n                \\_protocolControlledFei += \\_depositFei;\\n            }\\n        }\\n\\n        // If the protocol holds non-zero balance of tokens, fetch the oracle price to\\n        // increment PCV by \\_totalTokenBalance \\* oracle price USD.\\n        if (\\_totalTokenBalance != 0) {\\n            (Decimal.D256 memory \\_oraclePrice, bool \\_oracleValid) = IOracle(tokenToOracle[\\_token]).read();\\n            if (!\\_oracleValid) {\\n                validityStatus = false;\\n            }\\n            protocolControlledValue += \\_oraclePrice.mul(\\_totalTokenBalance).asUint256();\\n        }\\n    }\\n\\n    userCirculatingFei = fei().totalSupply() - \\_protocolControlledFei;\\n    protocolEquity = int256(protocolControlledValue) - int256(userCirculatingFei);\\n}\\n```\\n