Tiny Licorice Loris
Medium
BridgeLogic.executeMintUnbacked()
fails to check if the reserve to be minted is frozen.
function executeMintUnbacked(//@audit-issue A frozen, A paused reserve can still be supplied to and used as collateral.
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(uint256 => address) storage reservesList,
DataTypes.UserConfigurationMap storage userConfig,
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external {
DataTypes.ReserveData storage reserve = reservesData[asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
reserve.updateState(reserveCache);
ValidationLogic.validateSupply(reserveCache, reserve, amount, onBehalfOf);
uint256 unbackedMintCap = reserveCache.reserveConfiguration.getUnbackedMintCap();
uint256 reserveDecimals = reserveCache.reserveConfiguration.getDecimals();
uint256 unbacked = reserve.unbacked += amount.toUint128();
require(
unbacked <= unbackedMintCap * (10 ** reserveDecimals),
Errors.UNBACKED_MINT_CAP_EXCEEDED
);
reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, 0, 0);
bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(//@audit-ok `executeMintUnbacked()` can fail to `setUsingAsCollateral()` for first-time supply user. (A malicious user can prevent this by transferring a very minuscule amount to `onBehalfOf`)
msg.sender,
onBehalfOf,
amount,
reserveCache.nextLiquidityIndex
);
if (isFirstSupply) {
if (
ValidationLogic.validateAutomaticUseAsCollateral(
reservesData,
reservesList,
userConfig,
reserveCache.reserveConfiguration,
reserveCache.aTokenAddress
)
) {
userConfig.setUsingAsCollateral(reserve.id, true);
emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
}
}
emit MintUnbacked(asset, msg.sender, onBehalfOf, amount, referralCode);
}
There's no check whatsoever in the logic of BridgeLogic.executeMintUnbacked()
to ascertain if the reserve to be minted is freezed by admins.
So users can still mint unbacked for frozen reserves and supply to them later via BridgeLogic.executeBackUnbacked()
This breaks the invariant where frozen reserves shouldn't allow new supplies
/**
* @notice Freeze or unfreeze a reserve. A frozen reserve doesn't allow any new supply, borrow
* or rate swap but allows repayments, liquidations, rate rebalances and withdrawals.
* @param asset The address of the underlying asset of the reserve
* @param freeze True if the reserve needs to be frozen, false otherwise
*/
function setReserveFreeze(address asset, bool freeze) external;
The logic of BridgeLogic.executeMintUnbacked()
fails to check if the asset reserve to be minted is frozen.
Can happen anytime, all the time
- Admins freezes an asset reserve maybe via
Poolconfigurator.setReserveFreeze()
- User doesn't know, he uses
BridgeLogic.executeMintUnbacked()
for that asset reserve and if all conditions are met it even sets the asset reserve as collateral for him - User later supplies to the reserve later via
BridgeLogic.executeBackUnbacked()
A asset reserve frozen by admins can still be used as collateral and supplied to by users.
This breaks the invariant where frozen reserves shouldn't allow new supplies
/**
* @notice Freeze or unfreeze a reserve. A frozen reserve doesn't allow any new supply, borrow
* or rate swap but allows repayments, liquidations, rate rebalances and withdrawals.
* @param asset The address of the underlying asset of the reserve
* @param freeze True if the reserve needs to be frozen, false otherwise
*/
function setReserveFreeze(address asset, bool freeze) external;
No response
check the flag of the asset reserve that BridgeLogic.executeMintUnbacked()
is to be used for and revert if it is frozen