getCurrentBlockReward May Return Outdated Reward Due to Stale currentHalvingPeriod
getCurrentBlockReward reads the public currentHalvingPeriod variable directly without recomputing it via getHalvingPeriod(block.number), so external readers may see a stale reward rate before any internal call refreshes the cached period.
Description
The MattToken contract exposes a public variable currentHalvingPeriod, which is used to determine the current halving stage of the token emission schedule. This variable is used in the public function getCurrentBlockReward() to calculate the reward per block:
function getCurrentBlockReward() public view returns (uint256) {if (totalEmitted >= MAX_SUPPLY) return 0;uint256 baseReward = REWARD_PER_THREE_BLOCKS / 3;uint256 reward = baseReward >> currentHalvingPeriod; // @audit this can be outdated if getHalvingPeriod() is not called beforeuint256 remainingSupply = MAX_SUPPLY - totalEmitted;return reward > remainingSupply ? remainingSupply : reward;}
However, this function does not check whether the halving period has changed based on the number of blocks passed since the last update. The function getHalvingPeriod(uint256 blockNumber) does perform this logic:
function getHalvingPeriod(uint256 blockNumber) public view returns (uint256) {return (blockNumber - deploymentBlock) / BLOCKS_PER_HALVING;}
As a result, getCurrentBlockReward() may return a reward based on an outdated halving period, especially when the currentHalvingPeriod variable hasn't been updated through another internal call.
While this does not pose a direct security risk in the current implementation (since any call that uses getCurrentBlockReward() for actual minting will have updated currentHalvingPeriod beforehand), it does lead to inaccurate reward data being returned to any external readers or integrators that rely on the public getCurrentBlockReward() or the raw currentHalvingPeriod variable.
Impact
External integrators (frontends, indexers, dashboards) reading the public getCurrentBlockReward() may display a higher reward than the contract would actually mint at the next reward distribution that crosses a halving boundary. Internal minting paths are unaffected because they update the cached period first.
Recommendation
- Make
currentHalvingPeriodinternal so external consumers are forced to callgetHalvingPeriod()to obtain an up-to-date halving period. - Modify
getCurrentBlockReward()to first calculate the up-to-date halving period dynamically by callinggetHalvingPeriod()internally, e.g.:
function getCurrentBlockReward() public view returns (uint256) {if (totalEmitted >= MAX_SUPPLY) return 0;uint256 currentHalvingPeriod_ = getHalvingPeriod(block.number);uint256 baseReward = REWARD_PER_THREE_BLOCKS / 3;uint256 reward = baseReward >> currentHalvingPeriod_;uint256 remainingSupply = MAX_SUPPLY - totalEmitted;return reward > remainingSupply ? remainingSupply : reward;}

