Summary
There is no explicit check in the mint
function to ensure that the total supply of minted tokens does not exceed the MAX_SUPPLY
it is defined in the contract.
Vulnerability Details
The attacker repeatedly calls the mint
function to mint new tokens. Since the function does not enforce the MAX_SUPPLY
limit, the total supply of tokens can exceed the defined MAX_SUPPLY
.
https://github.com/Cyfrin/2024-07-templegold/blob/57a3e597e9199f9e9e0c26aab2123332eb19cc28/protocol/contracts/templegold/TempleGold.sol#L144C1-L157C1
function mint() external override onlyArbitrum {
VestingFactor memory vestingFactorCache = vestingFactor;
DistributionParams storage distributionParamsCache = distributionParams;
if (vestingFactorCache.numerator == 0) { revert ITempleGold.MissingParameter(); }
uint256 mintAmount = _getMintAmount(vestingFactorCache);
if (!_canDistribute(mintAmount)) { return; }
lastMintTimestamp = uint32(block.timestamp);
_distribute(distributionParamsCache, mintAmount);
}
Invoke the mint
Function Repeatedly: Use a script or manually call the mint
repeatedly function until the total supply exceeds MAX_SUPPLY
.
/ Example script to repeatedly call mint
const templeGold = new ethers.Contract(contractAddress, abi, signer);
async function exploitMint() {
for (let i = 0; i < 1000; i++) {
await templeGold.mint();
console.log(`Mint attempt ${i + 1} complete`);
}
}
exploitMint();
Impact
Exceeding the maximum supply can lead to inflation and devaluation of the token. An attacker or even a well-intentioned user can unintentionally mint more tokens than the MAX_SUPPLY
, leading to an overall supply that exceeds the intended cap.
Tools Used
Recommendations
Add a check in the mint
function to ensure that the total supply of minted tokens, including the current mintAmount
, does not exceed MAX_SUPPLY
.
function mint() external override onlyArbitrum {
VestingFactor memory vestingFactorCache = vestingFactor;
DistributionParams storage distributionParamsCache = distributionParams;
if (vestingFactorCache.numerator == 0) { revert ITempleGold.MissingParameter(); }
uint256 mintAmount = _getMintAmount(vestingFactorCache);
if (_totalDistributed + mintAmount > MAX_SUPPLY) {
revert ITempleGold.ExceedsMaxSupply();
}
if (!_canDistribute(mintAmount)) { return; }
lastMintTimestamp = uint32(block.timestamp);
_distribute(distributionParamsCache, mintAmount);
}