diff --git a/src/DerolasAuction.sol b/src/DerolasAuction.sol index a02bac2..abfef66 100644 --- a/src/DerolasAuction.sol +++ b/src/DerolasAuction.sol @@ -8,6 +8,8 @@ import {IBalancerVaultAdmin} from "./interfaces/IBalancerVaultAdmin.sol"; import {SafeTransferLib} from "./libraries/SafeTransferLib.sol"; +error NoAvailableIncentiveBalance(); + /// @dev Round point struct. /// @notice Stores information about a specific round struct RoundPoint { @@ -275,9 +277,11 @@ contract DerolasAuction { } // Calculate rewards - uint256 amount = (donation * roundPoints[claimRound].availableRewards) / totalRoundDonations; + uint256 claimedAmount = roundToClaimed[claimRound][account]; - return amount - claimedAmount; + if (claimedAmount > 0) return 0; // Already claimed + uint256 amount = (donation * roundPoints[claimRound].availableRewards) / totalRoundDonations; + return amount; } /// @dev Estimates ticket percentage. @@ -307,8 +311,10 @@ contract DerolasAuction { uint256 curRound = currentRound; require(msg.value >= roundPoints[curRound].minDonation, "Donation amount is less than the minimum donation"); + uint256 allocatedRewards = this.getAllocatedIncentives(); require( - IToken(incentiveTokenAddress).balanceOf(address(this)) >= roundPoints[curRound].availableRewards, + IToken(incentiveTokenAddress).balanceOf(address(this)) + >= roundPoints[curRound].availableRewards + allocatedRewards, "Not enough rewards to play the game" ); @@ -417,11 +423,31 @@ contract DerolasAuction { /// @dev withdraws the incentive token balance. /// @notice This function allows the owner to withdraw the incentive token balance from the contract. function withdrawIncentiveTokenBalance() external { + require(_locked == 1, "ReentrancyGuard"); + _locked = 2; + // check what is promised to participants in the current round and the claim round + uint256 allocatedRewards = this.getAllocatedIncentives(); + require(msg.sender == owner, "Unauthorized account"); - uint256 incentiveBalance = IToken(incentiveTokenAddress).balanceOf(address(this)); - require(incentiveBalance > 0, "No incentive token balance to withdraw"); + uint256 incentiveBalance = IToken(incentiveTokenAddress).balanceOf(address(this)) - allocatedRewards; + if (incentiveBalance == 0) revert NoAvailableIncentiveBalance(); SafeTransferLib.safeTransfer(incentiveTokenAddress, owner, incentiveBalance); emit TopUpIncentiveWithdrawn(incentiveBalance); + _locked = 1; + } + + /// @dev gets allocated Incentives + /// @return allocatedRewards Allocated rewards. + function getAllocatedIncentives() external view returns (uint256 allocatedRewards) { + uint256 curRound = currentRound; + uint256 claimRound = curRound - 1; + + if (roundPoints[claimRound].totalDonated > 0) { + allocatedRewards += roundPoints[claimRound].availableRewards; + } + if (roundPoints[curRound].totalDonated > 0) { + allocatedRewards += roundPoints[curRound].availableRewards; + } } /// @dev Gets service multisig nonces. diff --git a/test/DerolasAuction.t.sol b/test/DerolasAuction.t.sol index ff0f8af..b2d243c 100644 --- a/test/DerolasAuction.t.sol +++ b/test/DerolasAuction.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.30; import {Test} from "lib/forge-std/src/Test.sol"; -import {DerolasAuction, GameState} from "src/DerolasAuction.sol"; +import {DerolasAuction, GameState, NoAvailableIncentiveBalance} from "src/DerolasAuction.sol"; import {IToken} from "src/interfaces/IToken.sol"; import {IBalancerVaultAdmin} from "src/interfaces/IBalancerVaultAdmin.sol"; @@ -321,7 +321,7 @@ contract TestDerolasAuctionContract is Test { function testCanWithdrawIncentiveBalance() public { // Check that the owner can withdraw incentives - vm.expectRevert("No incentive token balance to withdraw"); + vm.expectRevert(NoAvailableIncentiveBalance.selector); c.withdrawIncentiveTokenBalance(); this.testTopupIncentivesBalance(); // Ensure incentives are topped up