From afc424cbe293201f4665e6c05e11c81359b7aa71 Mon Sep 17 00:00:00 2001 From: 0xnavigator <0xnavigator@proton.me> Date: Tue, 2 May 2023 12:10:12 +0200 Subject: [PATCH] Audit H2+L3: Incorrect value in _safeTokenClaim() --- src/Exit10.sol | 39 ++++++++------------------------ test/Exit10.bootstrapClaim.t.sol | 2 -- test/Exit10.exitClaim.t.sol | 8 ------- test/Exit10.stoClaim.t.sol | 7 ------ test/Exit10.t.sol | 4 ---- 5 files changed, 9 insertions(+), 51 deletions(-) diff --git a/src/Exit10.sol b/src/Exit10.sol index d78cc50..f76b273 100644 --- a/src/Exit10.sol +++ b/src/Exit10.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; import { IERC20, SafeERC20 } from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; +import { Math } from '@openzeppelin/contracts/utils/math/Math.sol'; import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; import { INPM } from './interfaces/INonfungiblePositionManager.sol'; import { IUniswapV3Pool } from './interfaces/IUniswapV3Pool.sol'; @@ -13,6 +14,7 @@ import { STOToken } from './STOToken.sol'; contract Exit10 is UniswapBase { using SafeERC20 for IERC20; + using Math for uint256; struct DeployParams { address NFT; @@ -54,15 +56,12 @@ contract Exit10 is UniswapBase { // EXIT TOKEN uint256 public exitTokenSupplyFinal; uint256 public exitTokenRewardsFinal; - uint256 public exitTokenRewardsClaimed; // BOOT TOKEN uint256 public bootstrapRewardsPlusRefund; - uint256 public bootstrapRewardsPlusRefundClaimed; // STO TOKEN uint256 public teamPlusBackersRewards; - uint256 public teamPlusBackersRewardsClaimed; bool public isBootstrapCapReached; bool public inExitMode; @@ -354,15 +353,7 @@ contract Exit10 is UniswapBase { function bootstrapClaim() external returns (uint256 claim) { uint256 bootBalance = IERC20(BOOT).balanceOf(msg.sender); - claim = _safeTokenClaim( - BOOT, - bootBalance / TOKEN_MULTIPLIER, - bootstrapBucketFinal, - bootstrapRewardsPlusRefund, - bootstrapRewardsPlusRefundClaimed - ); - - bootstrapRewardsPlusRefundClaimed += claim; + claim = _safeTokenClaim(BOOT, bootBalance / TOKEN_MULTIPLIER, bootstrapBucketFinal, bootstrapRewardsPlusRefund); _safeTransferToken(TOKEN_OUT, msg.sender, claim); @@ -371,15 +362,7 @@ contract Exit10 is UniswapBase { function stoClaim() external returns (uint256 claim) { uint256 stoBalance = IERC20(STO).balanceOf(msg.sender); - claim = _safeTokenClaim( - STO, - stoBalance, - STOToken(address(STO)).MAX_SUPPLY(), - teamPlusBackersRewards, - teamPlusBackersRewardsClaimed - ); - - teamPlusBackersRewardsClaimed += claim; + claim = _safeTokenClaim(STO, stoBalance, STOToken(address(STO)).MAX_SUPPLY(), teamPlusBackersRewards); _safeTransferToken(TOKEN_OUT, msg.sender, claim); @@ -388,9 +371,7 @@ contract Exit10 is UniswapBase { function exitClaim() external returns (uint256 claim) { uint256 exitBalance = IERC20(EXIT).balanceOf(msg.sender); - claim = _safeTokenClaim(EXIT, exitBalance, exitTokenSupplyFinal, exitTokenRewardsFinal, exitTokenRewardsClaimed); - - exitTokenRewardsClaimed += claim; + claim = _safeTokenClaim(EXIT, exitBalance, exitTokenSupplyFinal, exitTokenRewardsFinal); _safeTransferToken(TOKEN_OUT, msg.sender, claim); @@ -498,16 +479,14 @@ contract Exit10 is UniswapBase { function _safeTokenClaim( BaseToken _token, uint256 _amount, - uint256 _supply, - uint256 _externalSum, - uint256 _claimed - ) internal returns (uint256 _claim) { + uint256 _finalTotalSupply, + uint256 _rewardsFinalTotalSupply + ) internal returns (uint256 _claimableRewards) { _requireExitMode(); require(_amount != 0, 'EXIT10: Amount must be != 0'); _token.burn(msg.sender, IERC20(_token).balanceOf(msg.sender)); - _claim = (_amount * _externalSum) / _supply; - _claim = (_claimed + _claim <= _supply) ? _claim : _supply - _claimed; + _claimableRewards = _amount.mulDiv(_rewardsFinalTotalSupply, _finalTotalSupply, Math.Rounding.Down); } function _depositTokens(uint256 _amount0, uint256 _amount1) internal { diff --git a/test/Exit10.bootstrapClaim.t.sol b/test/Exit10.bootstrapClaim.t.sol index 45d3408..960cdbe 100644 --- a/test/Exit10.bootstrapClaim.t.sol +++ b/test/Exit10.bootstrapClaim.t.sol @@ -16,7 +16,6 @@ contract Exit10_bootstrapClaimTest is ABaseExit10Test { uint256 usdcBalanceBefore = _balance0(); uint256 bootBalance = _balance(boot); - uint256 bootstrapRewardsPlusRefundClaimedBefore = exit10.bootstrapRewardsPlusRefundClaimed(); exit10.bootstrapClaim(); @@ -25,7 +24,6 @@ contract Exit10_bootstrapClaimTest is ABaseExit10Test { assertEq(_balance(boot), 0, 'BOOT tokens burned'); assertGt(claimableAmount, 0, 'Check claimable > 0'); assertEq(_balance0(), usdcBalanceBefore + claimableAmount, 'USDC balance increased'); - assertGt(exit10.bootstrapRewardsPlusRefundClaimed(), bootstrapRewardsPlusRefundClaimedBefore, 'Claimed increased'); } function test_bootstrapClaim_RevertIf_NotExited() public { diff --git a/test/Exit10.exitClaim.t.sol b/test/Exit10.exitClaim.t.sol index bd342a3..a1db807 100644 --- a/test/Exit10.exitClaim.t.sol +++ b/test/Exit10.exitClaim.t.sol @@ -25,8 +25,6 @@ contract Exit10_exitClaimTest is ABaseExit10Test { vm.prank(alice); exit10.exitClaim(); - uint256 exitTokenRewardsClaimedBefore = exit10.exitTokenRewardsClaimed(); - // action! exit10.exitClaim(); @@ -36,12 +34,6 @@ contract Exit10_exitClaimTest is ABaseExit10Test { initialBalanceUSDC + (exit10.exitTokenRewardsFinal() * exitTokenShare) / precision, 'Check USD balance' ); - - assertEq( - exit10.exitTokenRewardsClaimed(), - exitTokenRewardsClaimedBefore + (exit10.exitTokenRewardsFinal() * exitTokenShare) / precision, - 'Remaining reward decreased' - ); } function test_exitClaim_RevertIf_NotExited() public { diff --git a/test/Exit10.stoClaim.t.sol b/test/Exit10.stoClaim.t.sol index 44dff3c..970de4c 100644 --- a/test/Exit10.stoClaim.t.sol +++ b/test/Exit10.stoClaim.t.sol @@ -27,7 +27,6 @@ contract Exit10_stoClaimTest is ABaseExit10Test { uint256 initialBalanceUSDC = _balance0(); uint256 precision = 1e18; uint256 stoTokenShare = (_balance(sto) * precision) / stoSupply; - uint256 teamPlusBackersRewardsClaimedBefore = exit10.teamPlusBackersRewardsClaimed(); exit10.stoClaim(); @@ -37,12 +36,6 @@ contract Exit10_stoClaimTest is ABaseExit10Test { initialBalanceUSDC + (exit10.teamPlusBackersRewards() * stoTokenShare) / precision, 'USD balance increased' ); - - assertEq( - exit10.teamPlusBackersRewardsClaimed(), - teamPlusBackersRewardsClaimedBefore + (exit10.teamPlusBackersRewards() * stoTokenShare) / precision, - 'Remaing reward increased' - ); } function test_stoClaim_RevertIf_NotExited() public { diff --git a/test/Exit10.t.sol b/test/Exit10.t.sol index b00d985..a3de5fe 100644 --- a/test/Exit10.t.sol +++ b/test/Exit10.t.sol @@ -15,12 +15,8 @@ contract Exit10Test is ABaseExit10Test { _checkBuckets(0, 0, 0, 0); assertEq(exit10.exitTokenSupplyFinal(), 0, 'setup exitTokenSupplyFinal'); assertEq(exit10.exitTokenRewardsFinal(), 0, 'setup exitTokenRewardsFinal'); - assertEq(exit10.exitTokenRewardsClaimed(), 0, 'setup exitTokenRewardsClaimed'); assertEq(exit10.bootstrapRewardsPlusRefund(), 0, 'setup bootstrapRewardsPlusRefund'); - assertEq(exit10.bootstrapRewardsPlusRefundClaimed(), 0, 'setup bootstrapRewardsPlusRefundClaimed'); assertEq(exit10.teamPlusBackersRewards(), 0, 'setup teamPlusBackersRewards'); - assertEq(exit10.teamPlusBackersRewardsClaimed(), 0, 'setup teamPlusBackersRewardsClaimed'); - assertEq(exit10.teamPlusBackersRewardsClaimed(), 0, 'setup teamPlusBackersRewardsClaimed'); assertTrue(!exit10.inExitMode(), 'Check inExitMode');