The _baseMint
function implements a convertToAssets
function which converts the amount of shares
being minted into the vault's equivalent underlying asset.
function _baseMint(uint256 shares, address controller) internal returns (uint256 assets) {
_isWhitelisted();
_onlyOwnerOrOperator(controller);
CreditStrategy(creditStrategy).accrueFees(); // accrue fees before minting new shares
assets = convertToAssets(shares);
uint256 maxShares = maxMint(controller);
if (shares > maxShares) {
revert ERC4626ExceededMaxMint(controller, shares, maxShares);
}
claimableDepositRequest[REQUEST_ID][controller] -= assets;
totalPendingAssets -= assets;
}
The conversion in convertToAssets
rounds down, resulting in an amount of assets
that's smaller than the amount that it should be for the amount of shares
that the user is able to mint.
This allows the user to bypass the if (shares > maxShares)
check which restricts the minting cap.
The user is also subsequently able to mint more more shares of the vault than they should for the amount of underlying asset that they deposited, allowing them to steal value from the vault.
The following unit test was generated from the Echidna job that broke the property_mint_more_than_max_reverts
property, which was traced to the issue identified above.
function test_property_mint_more_than_max_reverts_4() public {
target_deployNewVaultAndStrategies(10,1,0,0,false);
lendingVault_requestDeposit_clamped(15824861138544471049183522,5126449573802124063641740424571421993873283090572071904972);
lendingVault_processDepositRequest_clamped(1064585729394055104088963594211526076999680138,5182720750843422510890579234475641313971507048);
lendingVault_deposit_for_receiver_clamped(1518015215476408580341739907892527095673747330795017148,185568137856059954905389924900423478233619527327573568);
vm.roll(block.number + 1);
vm.warp(block.timestamp + 32);
property_mint_more_than_max_reverts(1);
}