Low (or Medium if you can prove it materially disrupts reward distribution, protocol accounting, or causes denial of service).
Proof of Concept (Illustrative)
Initial State
Alice stakes: 100 tokens
Bob stakes: 100 tokens
totalEligibleStake = 200
totalBonus = 20
Step 1: Attacker makes a large donation
The attacker calls:
contributeBonus(1_000_000e18);
State becomes:
totalBonus = 1,000,020
Step 2: Downstream impact
Suppose rewards are later calculated as:
reward = userStake * totalBonus / totalEligibleStake;
The unexpectedly large totalBonus now dramatically changes reward calculations.
Or suppose the frontend displays:
Estimated APY = totalBonus / totalEligibleStake
The displayed APY becomes unrealistically high, potentially misleading users into staking based on manipulated information.
Result
The attacker loses their donated tokens but successfully manipulates protocol economics or user perception. This is a griefing attack because the attacker accepts a financial loss to negatively affect the protocol or its users.
Restrict bonus contributions to a designated sponsor or governance-controlled address.
Introduce a maximum cap on totalBonus.
Limit the maximum amount per contribution.
Explicitly document that unrestricted third-party donations are an intended feature if this behavior is by design
- remove this code
function contributeBonus(uint256 amount) external nonReentrant whenPoolNotPaused {
if (amount == 0) revert InvalidAmount();
if (outcome != PoolStates.Outcome.UNRESOLVED) revert OutcomeAlreadySet();
if (block.timestamp >= expiry) revert StakingClosed();
_assertDepositsAllowed(_observePoolState());
// Balance-diff defense-in-depth — see `stake`.
// aderyn-fp-next-line(reentrancy-state-change)
uint256 balanceBefore = stakeToken.balanceOf(address(this));
stakeToken.safeTransferFrom(msg.sender, address(this), amount);
// aderyn-fp-next-line(reentrancy-state-change)
uint256 balanceAfter = stakeToken.balanceOf(address(this));
uint256 received = balanceAfter > balanceBefore ? balanceAfter - balanceBefore : 0;
if (received == 0) revert NoTokensReceived();
totalBonus += received;
emit BonusContributed(msg.sender, received);
In this functions there is grief donation bugs and explain how
}
+ add this code
address public sponsor;
modifier onlySponsor() {
require(msg.sender == sponsor, "Not sponsor");
_;
}
function contributeBonus(uint256 amount)
external
onlySponsor
nonReentrant
whenPoolNotPaused
{
if (amount == 0) revert InvalidAmount();
if (outcome != PoolStates.Outcome.UNRESOLVED) revert OutcomeAlreadySet();
if (block.timestamp >= expiry) revert StakingClosed();
_assertDepositsAllowed(_observePoolState());
uint256 balanceBefore = stakeToken.balanceOf(address(this));
stakeToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 balanceAfter = stakeToken.balanceOf(address(this));
uint256 received = balanceAfter - balanceBefore;
if (received == 0) revert NoTokensReceived();
totalBonus += received;
emit BonusContributed(msg.sender, received);