Summary
In the event of market shutdown, users can call redeemErc(). The user gets less than expected amtZeth
(ethEscrowed) due to division before multiplication by the protocol.
Vulnerability Details
In the event of market shutdown, users can call redeemErc():
File: contracts/facets/MarketShutdownFacet.sol
57
58 * @notice Allows user to redeem erc from their wallet and/or escrow at the oracle price of market shutdown
59 * @dev Market must be permanently frozen, redemptions drawn from the combined collateral of all short records
60 *
61 * @param asset The market that will be impacted
62 */
63
64 function redeemErc(address asset, uint88 amtWallet, uint88 amtEscrow)
65 external
66 isPermanentlyFrozen(asset)
67 nonReentrant
68 {
69 if (amtWallet > 0) {
70 asset.burnMsgSenderDebt(amtWallet);
71 }
72
73 if (amtEscrow > 0) {
74 s.assetUser[asset][msg.sender].ercEscrowed -= amtEscrow;
75 }
76
77 uint88 amtErc = amtWallet + amtEscrow;
78 @> uint256 cRatio = _getAssetCollateralRatio(asset);
79
80 @> uint88 amtZeth = amtErc.mulU88(LibOracle.getPrice(asset)).mulU88(cRatio);
81 s.vaultUser[s.asset[asset].vault][msg.sender].ethEscrowed += amtZeth;
82 emit Events.RedeemErc(asset, msg.sender, amtWallet, amtEscrow);
83 }
Line 80 contains the calculation uint88 amtZeth = amtErc.mulU88(LibOracle.getPrice(asset)).mulU88(cRatio);
, where cRatio is calculated on Line 78 as uint256 cRatio = _getAssetCollateralRatio(asset)
calling the private function in the same file, _getAssetCollateralRatio():
File: contracts/facets/MarketShutdownFacet.sol
85
86 * @notice Computes the c-ratio of an asset class
87 *
88 * @param asset The market that will be impacted
89 *
90 * @return cRatio
91 */
92
93 function _getAssetCollateralRatio(address asset)
94 private
95 view
96 returns (uint256 cRatio)
97 {
98 STypes.Asset storage Asset = s.asset[asset];
99 @> return Asset.zethCollateral.div(LibOracle.getPrice(asset).mul(Asset.ercDebt));
100 }
Simplifying, the calculation for amtZeth
on Line 80 becomes:
amtZeth = amtErc * price * (cRatio) => amtErc * price * ( zethCollateral / (price * ercDebt) )
Division operation is done before multiplication. Correct form should be:
amtZeth = ( amtErc * price * zethCollateral ) / (price * ercDebt)
Impact
The user gets less than expected amtZeth
(ethEscrowed)
Tools Used
Manual inspection.
Recommendations
Perform multiplication before division as shown in the above calculation.