BriVault

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

Mixed use of `msg.sender` and the `receiver` argument in `deposit` function can break the functionality of the protocol and prevent the user from joining an event.

Root + Impact

Description

  • In the BriVault::deposit function, there is a receiver argument which is used for storing the staked amount in the stakedAsset mapping. Meanwhile the asset amount for deposit and fee is deducted from msg.sender's account and the share token is also minted to it.

  • If the user enters an address different than the one they are calling the function from (i.e. msg.sender), then, the required information will be splitted and partially stored for various accounts. This will prevent the user to call the BriVault::joinEvent function and participate in the event. They will not even be able to call the BriVault::cancelParticipation function and get a refund.

function deposit(uint256 assets, address receiver) public override returns (uint256) {
require(receiver != address(0));
if (block.timestamp >= eventStartDate) {
revert eventStarted();
}
uint256 fee = _getParticipationFee(assets);
// charge on a percentage basis points
if (minimumAmount + fee > assets) {
revert lowFeeAndAmount();
}
uint256 stakeAsset = assets - fee;
@> 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 participantShares;
}
function joinEvent(uint256 countryId) public {
@> if (stakedAsset[msg.sender] == 0) {
@> revert noDeposit();
}
// Ensure countryId is a valid index in the `teams` array
if (countryId >= teams.length) {
revert invalidCountry();
}
if (block.timestamp > eventStartDate) {
revert eventStarted();
}
userToCountry[msg.sender] = teams[countryId];
uint256 participantShares = balanceOf(msg.sender);
userSharesToCountry[msg.sender][countryId] = participantShares;
usersAddress.push(msg.sender);
numberOfParticipants++;
totalParticipantShares += participantShares;
emit joinedEvent(msg.sender, countryId);
}
function cancelParticipation() public {
if (block.timestamp >= eventStartDate){
revert eventStarted();
}
@> uint256 refundAmount = stakedAsset[msg.sender];
stakedAsset[msg.sender] = 0;
uint256 shares = balanceOf(msg.sender);
_burn(msg.sender, shares);
@> IERC20(asset()).safeTransfer(msg.sender, refundAmount);
}

Risk

Likelihood: Medium

  • It happens when the user mistakenly or unknowingly enters an address different from msg.sender for the receiver argument.

Impact: High

  • When it happens the user will not be able to particiapte in the event or refund.

  • The second impact is that the fund will remain in the vault, while its balance is supposed to be 0 after all claims.

Proof of Concept

Please copy/paste the following function into the test file, and run it.

function test_userCannotParticipateOrRefundWhenReceiverDifferentFromMsgSender()
public
{
// Arrange
address receiver = makeAddr("Another User");
vm.startPrank(user1);
mockToken.approve(address(briVault), 5 ether);
// Act
briVault.deposit(5 ether, receiver);
// Assert
vm.expectRevert(abi.encodeWithSignature("noDeposit()"));
briVault.joinEvent(20);
uint256 user1BalanceBefore = IERC20(address(mockToken)).balanceOf(
user1
);
briVault.cancelParticipation();
vm.stopPrank();
uint256 user1BalanceAfter = IERC20(address(mockToken)).balanceOf(user1);
assert(user1BalanceAfter == user1BalanceBefore);
}

Recommended Mitigation

To solve this issue we need to remove the receiver parameter from the function declaration and replace its instances in the body of the function with msg.sender.

- function deposit(uint256 assets, address receiver) public override returns (uint256) {
+ function deposit(uint256 assets) public override returns (uint256) {
- require(receiver != address(0));
+ require(msg.sender != address(0));
if (block.timestamp >= eventStartDate) {
revert eventStarted();
}
uint256 fee = _getParticipationFee(assets);
// charge on a percentage basis points
if (minimumAmount + fee > assets) {
revert lowFeeAndAmount();
}
uint256 stakeAsset = assets - fee;
- stakedAsset[receiver] = stakeAsset;
+ stakedAsset[msg.sender] = 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);
+ emit deposited (msg.sender, stakeAsset);
return participantShares;
}
Updates

Appeal created

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

Shares Minted to msg.sender Instead of Specified Receiver

Support

FAQs

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

Give us feedback!