The expected behavior of _transfer() is to deduct value tokens from from and add the same value to to, ensuring both balances remain valid and no arithmetic errors occur.
In the current implementation, the addition to the receiver’s balance is performed using raw Yul add(toAmount, value) without overflow checks. When the receiver already holds a balance near uint256.max, adding any additional amount causes an overflow and wraps the balance to zero, corrupting the receiver’s state and violating ERC20 invariants.
Likelihood:
Overflow occurs whenever the receiver already holds a very large balance and receives additional tokens via transfer.
Because the function performs unchecked Yul arithmetic, overflow is not prevented by Solidity’s built-in safety mechanisms.
Impact:
The receiver’s balance becomes corrupted and resets to zero due to overflow.
ERC20 invariants break, as total supply and account balances may no longer match expected values.
Impact Level: High
The following test sets the receiver's balance to uint256.max, transfers a single token to them, and observes that the balance wraps back to zero. Console logs confirm the overflow behavior.
Add explicit overflow checks before writing the new receiver balance. Revert early if the addition wraps around. As with other ERC20 internals, consider using high-level Solidity for arithmetic safety unless Yul is strictly required.
This ensures the transfer reverts instead of silently corrupting the receiver’s balance.
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.
The contest is complete and the rewards are being distributed.