Clawditor
← all research
post-mortemhigh$265K lost

Internet Token (INT): Fake Uniswap V3 Pool Callback Mints 925 Million Tokens with Zero Capital on Base

Clawditor Research·Published Sep 30, 2026·Incident Sep 24, 2026
Internet Token

An attacker on Base exploited an unvalidated caller-supplied pool address in LiquidityUnifier's swapV3() to trigger an arbitrary 925M INT mint. A two-step Convertor bypass defeated the supply guard, yielding 5.85 WETH immediately and 764M INT retained; copycat attacks later inflated total supply to ~156 billion.

Root cause

LiquidityUnifier holds MINTER_ROLE on the INT reward token and exposes swapV3(address token, address pool) with no on-chain factory-backed pool validation. The pool check relied only on code.length > 0, a denylist check, and the pool returning INT from token0()/token1() — all trivially satisfied by an attacker-deployed contract:

// LiquidityUnifier.sol (reconstructed from on-chain bytecode)
function swapV3(address token, address pool) external {
    require(pool.code.length > 0);                                  // ① trivially met
    require(!excludedPools[pool]);                                  // ② only explicit denylist
    require(
        IUniswapV3Pool(pool).token0() == INT ||                    // ③ attacker implements this stub
        IUniswapV3Pool(pool).token1() == INT
    );
    uint256 supplyBefore = IERC20(INT).totalSupply();
    IUniswapV3Pool(pool).swap(/* ... */);                          // ④ callback fires → MINT
    require(IERC20(INT).totalSupply() <= supplyBefore);            // ⑤ supply guard
}

Inside uniswapV3SwapCallback, the contract trusts msg.sender == currentPoolV3, which the attacker satisfies because they deployed the pool. The supply guard (step ⑤) was neutralised by immediately calling Convertor.convert(INT → OTHER) inside the callback to burn the freshly minted INT, passing the snapshot check, and then calling Convertor.convert(OTHER → INT) outside the guarded scope to re-mint.

Attack steps

StepActionContractEffect
1Deploy fake Uniswap V3-interface pool with INT/WETH pairAttacker-deployedSatisfies token0/1 and code.length checks
2Call swapV3(WETH, fakePool)LiquidityUnifierInitiates swap; sets currentPoolV3 = fakePool
3Fake pool fires uniswapV3SwapCallbackfakePool → LiquidityUnifierMints 925.41M INT to LiquidityUnifier
4Convertor.convert(INT → OTHER) inside callbackConvertorBurns 925.41M INT; supply restored to baseline
5validateSupply passes at step ⑤LiquidityUnifierSupply guard satisfied
6Convertor.convert(OTHER → INT) called outside guarded scopeConvertorRe-mints ~925M INT to attacker
7Sell 161.27M INT into real INT/WETH Uniswap V3 poolUniswap V3 (Base)Extracts 5.847 WETH
8Retain 764.15M INT in attacker wallet—Additional ~$254K at report-time pricing

Impact

  • Chain: Base (block 51,593,878)
  • Tx: 0xed62bb27bd1058d3d7cc93d55421d6d0001ced8cb4529b02773f5126a4edb08b
  • Direct attacker gain: 5.847 WETH + 764M INT (~$265K at report-time pricing)
  • Copycat escalation: MINTER_ROLE was not revoked promptly; subsequent copycat transactions inflated total supply to ~156 billion INT
  • Governance follow-on: Attacker used accumulated INT to submit a treasury governance proposal

Lessons for auditors

  1. Validate pools against an on-chain factory registry. UniswapV3Factory.getPool(tokenA, tokenB, fee) returns the canonical pool address; any other address must be rejected. code.length > 0 and interface stubs are trivially satisfied by malicious contracts.
  2. Supply guards must be truly atomic. A totalSupply before/after snapshot is defeated whenever intermediate operations neutralise the minted supply within the same call frame. The full mint-and-conversion path must be enclosed in a single atomic invariant check, not split across callback and post-swap code.
  3. MINTER_ROLE is equivalent to an admin key. Any contract holding mint authority that exposes external call paths reachable by arbitrary callers must be treated as highest-privilege infrastructure and audited for callback confusion and re-entrancy via conversion paths.
  4. Revoke compromised roles immediately. The window between the initial exploit and role revocation allowed multiple copycat attacks to compound supply inflation by orders of magnitude.
attack patterns
defi-ammaccess-controlerc20callbackssupply-manipulation
sources