For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 Hayabusa

  • 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 documented 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.sol, 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?