F-2025-0002·state-desynchronization

Tier updates not reflected in lockPeriods array leads to stale staking period information

Fixedstakingnft-boostrewards
TL;DR

updateTier mutates the tiers mapping but never updates the lockPeriods array, so getLockPeriods returns stale data and integrators see periods that no longer match the actual valid set.

Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
LOW
Exploitability
MEDIUM
02Section · Description

Description

The StakingContract maintains staking period information in two separate data structures: tiers and lockPeriods. When updating tiers through the updateTier function, the corresponding lockPeriods array is not updated to reflect these changes:

solidity
function updateTier(uint numDays, uint basisPoints) external onlyOwner {
require(basisPoints <= 10000, "Invalid basis points");
uint oldRate = tiers[numDays];
tiers[numDays] = basisPoints;
// lockPeriods is not updated here
emit TierUpdated(numDays, oldRate, basisPoints);
}

This creates a misalignment between the actual available staking periods in tiers and what is exposed through the getLockPeriods() function:

solidity
function getLockPeriods() external view returns (uint[] memory) {
return lockPeriods; // Returns stale data that doesn't reflect tier updates
}
03Section · Impact

Impact

  • Stale Data: getLockPeriods() returns outdated information that does not reflect the current state of available staking periods after tier updates.
  • UI / Integration Inconsistency: Users and integrated protocols receive incorrect information about available staking periods because getLockPeriods() does not reflect updates made through updateTier.
  • Protocol Confusion: Creates confusion about which staking periods are actually valid, as the two data structures maintain different states.
04Section · Recommendation

Recommendation

Modify updateTier to maintain synchronisation between tiers and lockPeriods:

solidity
function updateTier(uint numDays, uint basisPoints) external onlyOwner {
require(basisPoints <= 10000, "Invalid basis points");
uint oldRate = tiers[numDays];
// If this is a new period, add it to lockPeriods
if (tiers[numDays] == 0) {
lockPeriods.push(numDays);
}
// Update the tier
tiers[numDays] = basisPoints;
emit TierUpdated(numDays, oldRate, basisPoints);
}
05Section · Resolution

Resolution

Ample Protocol: Fixed.

Zealynx: Verified.

Status
Fixed
F-2025-0002

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx