TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: medium
Invalid

`TempleGold` can reach max supply in a really short span despite numerator is set to small value

Summary

TempleGold has a MAX_SUPPLY variable which limits the max supply of the token, when mint is called, the amount mintable is determined by timelapsed since last mint. However, the formula uses timestamp instead of block number, which can cause max supply to be reach in a short time span.

Vulnerability Details

In mint function of TempleGold, we can see that the mintable amount is calculated in _getMintAmount:

function mint() external override onlyArbitrum {
VestingFactor memory vestingFactorCache = vestingFactor;
DistributionParams storage distributionParamsCache = distributionParams;
if (vestingFactorCache.numerator == 0) { revert ITempleGold.MissingParameter(); }
uint256 mintAmount = _getMintAmount(vestingFactorCache);
/// @dev no op silently
if (!_canDistribute(mintAmount)) { return; }
lastMintTimestamp = uint32(block.timestamp);
_distribute(distributionParamsCache, mintAmount);
}

To follow up, we see the mintable amount is determined by current timestamp, lastMintTimestamp, and numerator, and denominator set in the vesting factor:

function _getMintAmount(VestingFactor memory vestingFactorCache) private view returns (uint256 mintAmount) {
uint32 _lastMintTimestamp = lastMintTimestamp;
uint256 totalSupplyCache = _totalDistributed;
/// @dev if vesting factor is not set, return 0. `_lastMintTimestamp` is set when vesting factor is set
if (_lastMintTimestamp == 0) { return 0; }
mintAmount = TempleMath.mulDivRound((block.timestamp - _lastMintTimestamp) * (MAX_SUPPLY), vestingFactorCache.numerator, vestingFactorCache.denominator, false);
if (totalSupplyCache + mintAmount > MAX_SUPPLY) {
unchecked {
mintAmount = MAX_SUPPLY - totalSupplyCache;
}
}
}

For a better, the formula can be written as: (block.timestamp - _lastMintTimestamp) * MAX_SUPPLY * (numerator / denominator). According to the factor params set in the test files, we can see the numerator and denominator is set to 2e18 and 1000e18 respectively:

function _configureTempleGold() private {
ITempleGold.DistributionParams memory params;
params.escrow = 60 ether;
params.gnosis = 10 ether;
params.staking = 30 ether;
templeGold.setDistributionParams(params);
ITempleGold.VestingFactor memory factor;
factor.numerator = 2 ether;
factor.denominator = 1000 ether;
templeGold.setVestingFactor(factor);
_setVestingFactorTime = uint32(block.timestamp);
templeGold.setStaking(address(staking));
// whitelist
templeGold.authorizeContract(address(daiGoldAuction), true);
templeGold.authorizeContract(address(staking), true);
templeGold.authorizeContract(teamGnosis, true);
}

With those as presumption, the formula then becomes: timeDiff * MAX_SUPPLY * 2 / 1000, as the ether will cancel out. Which means, when (block.timestamp - _lastMintTimestamp) * 2 / 1000 reaches 1, max supply can be reached, and this makes the max time difference to be about 500 seconds, which is less than 10 minutes, and 41 blocks if we take block time as 12 seconds each.

The issue here is to use block.timestamp as one of the factor, making the calculated value to rise rapidly. Since in the test files, the factor is set to 2 / 1000, so I assume this will be a reasonable scale considered by the protocol team. If such factor is set in deployment, all TempleGold tokens can be minted quickly, and reaches max supply in a short time span.

Impact

Max supply can be met rapidly, causing users not have enough time to vest or react to. And potentially break the function of this token.

Tools Used

Manual review

Recommendations

Consider use block number, or add a limit for numerators, as when numerators is reasonablely small, the mintable amount can still grow at an insane rate.

Updates

Lead Judging Commences

inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.