A migration bumped the pair contract's precision constant from 1,000 to 10,000 — but the change did not reach the swap() function's constant-product sanity check, which still multiplied the reserves by 1000**2 while the balances on the other side of the comparison were scaled by 10,000. The invariant was 100× too loose. Anyone could swap in dust and take nearly the whole reserve. The project had even been told about a related issue in an audit, patched it, and the patch was not enough.
uint balance0Adjusted = balance0.mul(10000).sub(amount0In.mul(16)); uint balance1Adjusted = balance1.mul(10000).sub(amount1In.mul(16)); require( balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), // ❌ should be 10000**2 'UraniumSwap: K' ); // left side scaled by 10000² = 100,000,000 // right side scaled by 1000² = 1,000,000 // → the K invariant is satisfied by 1/100th of the value it should require. → result: swap in dust, walk out with the pool
constant so the compiler makes the mistake impossible. And every core invariant deserves a property test that would fail loudly at 100× drift.Entries in the SAFE database that describe this failure. These share its failure class.
Every figure on this page comes from the post-mortems above, not from us. Losses are US dollars at the time of the incident.
Arithmetic is one of the 203 classes the SaferICO scanner checks for. It will not review your signing process — but it will read your Solidity.