Describe the normal behavior
Under expected ERC20 standards, minting should be restricted to approved parties, with caps, emission controls, or governed inflation schedules to protect token value and prevent abuse. These controls ensure fairness and long-term sustainability of the token economy.
Likelihood:
Reason 1 // Describe WHEN this will occur (avoid using "if" statements)
Whenever a user calls buySnow()
or earnSnow()
, tokens are minted without supply caps or rate-limiting logic, allowing indefinite inflation.
Reason 2
Since buySnow()
and earnSnow()
are externally accessible and not bound to specific user roles or caps, any user can repeatedly trigger them, intentionally or unintentionally.
Impact:
Impact 1
Uncapped inflation can devalue the Snow token, harming investor trust and disrupting protocol economics.
Impact 2
Abuse of the minting path can result in griefing, denial of rewards, or exploitation of downstream DeFi integrations that rely on a stable token supply.
buySnow()
CopyEdit
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface ISnow { function buySnow(uint256 amount) external payable; function balanceOf(address account) external view returns (uint256); } interface IWETH { function approve(address spender, uint256 amount) external returns (bool); function transfer(address to, uint256 amount) external returns (bool); } contract SnowMintPoC { ISnow public snow; IWETH public weth; constructor(address _snow, address _weth) { snow = ISnow(_snow); weth = IWETH(_weth); } function attackWithWETH() external { // Approve Snow contract to pull WETH weth.approve(address(snow), type(uint256).max); // Buy 1,000,000 SNOW tokens uint256 mintAmount = 1_000_000 ether; snow.buySnow(mintAmount); // No cap, no access control } function viewBalance(address attacker) external view returns (uint256) { return snow.balanceOf(attacker); } }
This contract simulates an attacker using buySnow()
to mint 1,000,000 SNOW tokens.
It assumes the attacker has enough WETH and has pre-approved the Snow
contract.
The Snow
contract has no mint limit, so the call succeeds every time.
After deploying and calling attackWithWETH()
:
Attacker receives 1,000,000 SNOW tokens in a single transaction
Repeating this call mints more and more SNOW with no restriction
Total supply grows infinitely, breaking tokenomics
1. Introduce a Maximum Token Supply Cap
You must enforce a hard cap to prevent infinite minting.
Add this to the contract:
CopyEdit
uint256 public constant MAX_SUPPLY = 1_000_000 ether; // Example cap
Then update both minting functions to check the cap before minting:
CopyEdit
function buySnow(uint256 amount) external payable canFarmSnow { if (msg.value == (s_buyFee * amount)) { _enforceCap(amount); _mint(msg.sender, amount); } else { i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount)); _enforceCap(amount); _mint(msg.sender, amount); } s_earnTimer = block.timestamp; emit SnowBought(msg.sender, amount); } function earnSnow() external canFarmSnow { if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) { revert S__Timer(); } _enforceCap(1); _mint(msg.sender, 1); s_earnTimer = block.timestamp; emit SnowEarned(msg.sender, 1); } function _enforceCap(uint256 amountToMint) internal view { if (totalSupply() + amountToMint > MAX_SUPPLY) { revert("Mint would exceed max supply"); } }
If you don’t want public minting at all, restrict it with onlyOwner
or AccessControl
:
CopyEdit
function mint(address to, uint256 amount) external onlyOwner { _enforceCap(amount); _mint(to, amount); }
You can also use
MinterRole
if integrating with a DAO or emissions scheduler.
Prevent multiple wallets from calling earnSnow()
by tracking per-user earn history (mapping address => timestamp
)
Add cooldown periods and non-reentrancy to public minting functions
Consider Merkle Tree claims or staking models to earn tokens
Without a cap:
Total supply can balloon indefinitely
Liquidity pools can be drained
Token value will collapse (zero-trust economy)
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.