68880 sc insight missing reward parameter in staked event breaks off chain accounting
Submitted on Mar 11th 2026 at 19:35:35 UTC by @Oxb4b for Audit Comp | Folks Finance: Staking Contracts
Report ID: #68880
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol
Impacts:
Description
Brief/Intro
The Staked event emits the deposited amount but omits the calculated reward value. This creates an observability gap where off-chain systems (indexers, dashboards, analytics) cannot accurately reconstruct user rewards from event logs alone. When staking period parameters are updated between stake execution and off-chain processing, the reconstructed reward will differ from the actual on-chain value, leading to incorrect accounting and user-facing data.
Vulnerability Details
Affected Code: Staking.sol, _stake() function
Current Implementation:
// Reward calculated using CURRENT period parameters
uint256 reward = (amount * stakingPeriod.aprBps * stakingPeriod.stakingDurationSeconds) / rewardBpsDenominator;
// Reward stored immutably in UserStake
userStakes[msg.sender].push(
UserStake({
amount: amount,
reward: reward, // ← Stored on-chain
claimedAmount: 0,
claimedReward: 0,
aprBps: stakingPeriod.aprBps,
stakeTime: uint64(block.timestamp),
unlockTime: uint64(block.timestamp) + stakingPeriod.stakingDurationSeconds,
unlockDuration: stakingPeriod.unlockDurationSeconds
})
);
// Event emission WITHOUT reward
emit Staked(msg.sender, periodIndex, params.referrer, stakeIndex, amount);
// ^^^^^^
// Missing: rewardRoot Cause:
The Staked event signature (defined in IStakingV1.sol) is:
Attack Path / Observability Failure Scenario:
T₀: User stakes 10 ETH with Period 0 (APR = 50%, duration = 30 days)
On-chain:
reward = 10 * 5000 * 30 days / (1e4 * 365 days) ≈ 0.4109 ETHstored in UserStakeEvent emitted:
Staked(user, 0, referrer, 0, 10 ether)— no reward field
T₁: Manager updates Period 0 parameters (legitimate governance action)
Period 0 APR changed from 50% → 10%
StakingPeriodUpdatedevent emitted
T₂: Off-chain indexer processes
Stakedevent from T₀Indexer reads current Period 0 parameters via RPC:
aprBps = 1000(10%)Indexer reconstructs:
reward = 10 * 1000 * 30 days / (1e4 * 365 days) ≈ 0.0821 ETHDiscrepancy: 0.0821 ETH (reconstructed) vs 0.4109 ETH (actual) — 80% error
Impact: User dashboard shows incorrect reward, audit trails fail, accounting mismatch
Impact and Likelihood Details
Impact: Low — No on-chain funds at risk, no user loss, no exploitable attack vector
Affected Systems:
Off-chain indexers (TheGraph subgraphs, custom backends)
User-facing dashboards showing staking positions
Analytics platforms tracking protocol metrics
Accounting/audit systems reconciling rewards
Mobile/light clients unable to make RPC calls
Likelihood: High — Occurs whenever:
Staking periods are updated (routine governance operation)
Network congestion delays event processing
Indexer catches up on historical events after period updates
Link to Proof of Concept
https://gist.github.com/tharunbethina/497b3263c659393a585118e2dbf2c74c
Proof of Concept
User stakes 10 ETH in Period 0 (50% APR, 30-day duration)
On-chain reward calculated and stored: 0.4109 ETH
Manager updates Period 0 APR to 10% (legitimate governance)
Off-chain indexer reconstructs reward from event data: 0.0821 ETH (uses updated APR)
Assertion:
reconstructedReward ≠ actualReward→ 80% discrepancy
Foundry Testcase gist:
https://gist.github.com/tharunbethina/497b3263c659393a585118e2dbf2c74c
Was this helpful?