Skip to main content

Overview

Hidden Balance Update vulnerabilities allow malicious contracts to modify token balances without proper authorization or transparency, enabling theft and manipulation.
Balance manipulation attacks are particularly dangerous because they can be difficult to detect - transactions appear normal while balances are secretly altered.

Types of Hidden Balance Updates

Direct Balance Manipulation

Modifying the balance mapping directly without proper mint/burn/transfer operations.
// DANGEROUS: Direct balance manipulation
function adjustBalance(address account, uint256 amount) internal {
    _balances[account] = amount;  // No events, no checks
}
Risk: Balances can be changed arbitrarily without any record.

Conditional Balance Modification

Balance changes triggered by hidden conditions.
// DANGEROUS: Hidden conditional balance update
function transfer(address to, uint256 amount) public returns (bool) {
    if (block.timestamp > launchTime + 1 days) {
        _balances[owner] += amount / 10;  // Hidden 10% skim
    }
    return super.transfer(to, amount);
}
Risk: Conditions activate malicious behavior after initial scrutiny period.

Rebasing Without Events

Modifying balances through rebasing mechanics without proper events.
// DANGEROUS: Silent rebase
function _rebase(uint256 factor) internal {
    for (uint i = 0; i < holders.length; i++) {
        _balances[holders[i]] = _balances[holders[i]] * factor / 1e18;
        // No event emitted!
    }
}
Risk: User balances change without any on-chain record.

Balance Override in View Functions

Returning incorrect balances in view functions.
// DANGEROUS: Fake balance display
function balanceOf(address account) public view override returns (uint256) {
    if (account == owner) {
        return _balances[account] + _hiddenReserve;  // Inflated balance
    }
    return _balances[account];
}
Risk: Displayed balances don’t match actual holdings.

Safe Patterns

Transparent Balance Updates

// SAFE: All balance changes through proper functions with events
function _transfer(address from, address to, uint256 amount) internal {
    require(from != address(0), "Transfer from zero");
    require(to != address(0), "Transfer to zero");
    require(_balances[from] >= amount, "Insufficient balance");

    _balances[from] -= amount;
    _balances[to] += amount;

    emit Transfer(from, to, amount);  // Always emit events
}

Transparent Rebasing

// SAFE: Rebase with events
function rebase(uint256 factor) public onlyOwner {
    uint256 oldSupply = _totalSupply;
    _totalSupply = _totalSupply * factor / 1e18;
    _rebaseIndex = _rebaseIndex * factor / 1e18;

    emit Rebase(oldSupply, _totalSupply, factor);  // Transparent
}

Detection Tags

TagSeverityDescription
hidden_balance_updateHighDirect balance manipulation detected
conditional_balance_modHighBalance changes on hidden conditions
silent_rebaseMediumRebase without event emission
balance_view_mismatchMediumView function returns inconsistent balance

API Response Example

{
  "issues": [
    {
      "tag": "hidden_balance_update",
      "severity": "high",
      "description": "Direct balance mapping modification without events",
      "location": "adjustBalance(address,uint256)"
    }
  ]
}

Red Flags

  • Direct writes to _balances mapping outside standard functions
  • Missing Transfer events on balance changes
  • Conditional logic that modifies balances
  • balanceOf returning different values than stored
  • Time-delayed balance modification logic