BriVault

First Flight #52
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Violation of the `ERC4626` standard, `ERC4626::mint` function can be called instead of `BriVault.deposit` to receive shares, denial of service as users cannot participate in the tournament.

Root + Impact

Description

  • The BriVault contract inherits from ERC4626 and overrides the deposit function to include custom logic that tracks staked assets and enables users to participate in a tournament by setting the stakedAsset mapping for the receiver.

  • However, the mint function from ERC4626 is not overridden, allowing users to call it directly to deposit assets and receive shares without triggering the custom logic, which results in the stakedAsset mapping not being updated and prevents those users from joining the tournament via joinEvent.

// Root cause in the codebase with @> marks to highlight the relevant section
@> function mint(uint256 shares, address receiver) public virtual returns (uint256) {
uint256 maxShares = maxMint(receiver);
if (shares > maxShares) {
revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
}
uint256 assets = previewMint(shares);
_deposit(_msgSender(), receiver, assets, shares);
return assets;
}

Risk

Likelihood: High

  • Users interact with the contract via standard ERC4626 interfaces or tools that default to mint for share-based deposits.

  • Developers or users familiar with ERC4626 may call mint directly assuming equivalence to deposit without reviewing overrides.

Impact: Medium

  • Users who deposit via mint cannot participate in the tournament, leading to denial of service and potential loss of rewards or opportunities.

  • Misleading ERC4626 compliance causes users to incur unnecessary gas costs for withdrawals and redeposits or miss time-sensitive events.

Proof of Concept

Add the following code snippet to the briVault.t.sol test file. This test verifies that user cannot join the tournament if the briVault::mint function is used to deposit.

function test_mintShares() public {
vm.startPrank(user1);
mockToken.approve(address(briVault), 5 ether);
briVault.mint(5 ether, user1);
uint256 userVaultBalance = briVault.balanceOf(user1);
uint256 stakedAmount = briVault.stakedAsset(user1);
console.log("userVaultBalance: ", userVaultBalance);
console.log("stakedAmount: ", stakedAmount);
vm.expectRevert(BriVault.noDeposit.selector);
briVault.joinEvent(10);
vm.stopPrank();
}

Recommended Mitigation

Recommended Mitigation: Override the ERC4626::mint to add custom logic similar to ERC4626::deposit.

+ function mint(uint256 shares, address receiver) public override returns (uint256) {
+ uint256 maxShares = maxMint(receiver);
+ if (shares > maxShares) {
+ revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
+ }
+
+ uint256 assets = previewMint(shares);
+
+ uint256 fee = _getParticipationFee(assets);
+
+ if (minimumAmount + fee > assets + fee) {
+ revert lowFeeAndAmount();
+ }
+
+ uint256 stakeAsset = assets;
+
+ stakedAsset[receiver] = stakeAsset;
+
+ uint256 participantShares = _convertToShares(stakeAsset);
+
+ IERC20(asset()).safeTransferFrom(msg.sender, participationFeeAddress, fee);
+
+ IERC20(asset()).safeTransferFrom(msg.sender, address(this), stakeAsset);
+
+ _mint(msg.sender, participantShares);
+
+ emit deposited(receiver, stakeAsset);
+
+ return assets + fee;
+ }
Updates

Appeal created

bube Lead Judge 19 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Unrestricted ERC4626 functions

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!