60525 sc insight levelcirculatingsupplyupdated not emitted during supply changes

Submitted on Nov 23rd 2025 at 19:11:24 UTC by @Rhaydden for Audit Comp | Vechain | Stargate Hayabusaarrow-up-right

  • Report ID: #60525

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/immunefi-team/audit-comp-vechain-stargate-hayabusa/tree/main/packages/contracts/contracts/StargateNFT/libraries/Levels.sol

  • Impacts:

Description

Finding description and impact

LevelCirculatingSupplyUpdated event is documentedarrow-up-right and exposed in IStargateNFT as being emitted “when the circulating supply of a token level is updated.” In the current implementation, the event is only emitted on level creation (addLevel) and not when circulating supply actually changes thereafter (mint, burn, migrate).

In Levels.solarrow-up-right, the event is emitted once inside addLevel:

emit LevelCirculatingSupplyUpdated($.MAX_LEVEL_ID, 0, _levelAndSupply.circulatingSupply);

Then all subsequent supply changes go through _incrementLevelCirculatingSupply / _decrementLevelCirculatingSupply, which call _checkpointLevelCirculatingSupply:

function _incrementLevelCirculatingSupply(...) internal {
    uint208 circulatingSupply = _getCirculatingSupply($, _levelId);
    _checkpointLevelCirculatingSupply($, _levelId, circulatingSupply + 1);
}

function _decrementLevelCirculatingSupply(...) internal {
    uint208 circulatingSupply = _getCirculatingSupply($, _levelId);
    _checkpointLevelCirculatingSupply($, _levelId, circulatingSupply - 1);
}

function _checkpointLevelCirculatingSupply(...) internal {
    $.circulatingSupply[_levelId].push(Clock._clock(), _circulatingSupply);
}

As we see, no event is emitted in these paths.

Impact

Any offchain indexers and dApps relying on LevelCirculatingSupplyUpdated to track per-level supply will get incorrect values after the initial add. They’ll observe one initial “0 → initialSupply” event and then miss all later increments/decrements. The on-chain state is correct and queryable through the checkpoints (getLevelsCirculatingSupplies and getCirculatingSupplyAtBlock), and consumers could reconstruct supply from TokenMinted/TokenBurned. Albeit, the public interface and natspec promise an event-based feed which doesnt actually exist post creation.

Severity: Insight. Optimizations and Enhancements

Emit LevelCirculatingSupplyUpdated whenever supply changes.

Proof of Concept

Proof of concept

Create and add the poc below to a packages/contracts/test/unit/StargateNFT/CirculatingSupplyEvents.test.ts file:

Was this helpful?