https://github.com/blackhan-software/xpower-banq/tree/5f734cc14fd05afcf7ee221de0bb600f8ca2339c
The _capOf(address user) function used in the minting logic calculates a per-user minting cap that is dynamically reduced based on the totalHolders count. However, the totalHolders variable is directly incremented in the _update() function whenever a new account with zero balance receives tokens. This allows an attacker to manipulate totalHolders arbitrarily high by sending a minimal amount (e.g., 1 wei) to a large number of unique addresses.
As a result, the calculated mint cap can become vanishingly small for legitimate users, effectively blocking them from minting any amount of tokens. There is no enforced minimum cap floor, nor any bound, decay, or constraint on totalHolders. This creates a persistent and user-controlled denial-of-service (DoS) condition that impacts core functionality of the protocol.
function _capOf(address user) private view returns (uint256 limit, uint256 dt) {
uint256 balance = balanceOf(user);
uint256 total = totalSupply();
(limit, dt) = _relLimit();
if (balance > 0 && total > balance) {
limit = Math.mulDiv(limit, total - balance, total);
limit = Math.mulDiv(limit, total - balance, total);
limit = Math.mulDiv(limit, balance, total) * 12;
limit = limit / Math.sqrt(totalHolders + 2);
} else {
limit = limit / (totalHolders + 1);
}
}
and
function _update(address from, address to, uint256 value) internal virtual override {
if (from != address(0)) {
if (to != address(0)) {
// ...
} else {
if (value > 0 && balanceOf(from) == value) {
_decreaseHolders();
}
}
} else {
if (to != address(0)) {
if (value > 0 && balanceOf(to) == 0) {
_increaseHolders();
}
}
}
super._update(from, to, value);
_reindex();
}
details: The value of totalHolders can be arbitrarily inflated by any user. No minimum limit enforcement exists after division by Math.sqrt(totalHolders + 2). The result is that the limit can approach zero, even for well-funded accounts. Since the mint() function strictly requires that amount <= _capOf(user).limit, a legitimate user can be indefinitely prevented from minting. This is a persistent DoS condition that remains active until the attacker’s accounts are cleared, which is not automatically handled.
Availability: Critical functions like minting can be entirely frozen for targeted users. Integrity: The supply dynamics of the token are disrupted by attacker influence. Scope: All users are potentially impacted; attacker can target the entire protocol mint logic without needing privileged access.
Attacker creates N (e.g., 10,000) fresh addresses (can be EOAs or contracts). Attacker sends 1 wei of the token to each address. Each address increases totalHolders via _increaseHolders(). totalHolders becomes large → _capOf() is divided by sqrt(totalHolders + 2) → drastically reduced. mint() reverts due to RelExceeded() because cap is near-zero. Attack continues as long as attacker-controlled addresses exist.