Malicious first liquidity provider can DoS a stable pair pool
Summary
Stable pool invariant k formula lacks a sanity check that prevents it from being assigned a 0 value
Vulnerability Detail
The constant invariant k of a pool is showcased below.
function _k(uint x, uint y) internal view returns (uint) {
if (stable) {
uint _x = x * 1e18 / decimals0;
uint _y = y * 1e18 / decimals1;
uint _a = (_x * _y) / 1e18; // @audit rounds down to 0 if x * y < 1e18
uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);
return _a * _b / 1e18; // x3y+y3x >= k // @audit if _a = 0, then k = 0 too
} else {
return x * y; // xy >= k
}
}
k being assigned a 0 value is vulnerable as it would allow performing arbitrary swaps that would always abide by the constant market invariant since it is 0.
The vulnerability can be exploited in the following way:
Malicious first liquidity provider mints 10e6 of token0 and token1
Attacker can drain whatever is in the pool by invoking swap twice with inputs:
(1)
amount0Out = 0
amount1Out = token1.balanceOf(address(pool)) - 1
(2)
amount0Out = token0.balanceOf(address(pool)) - 1
amount1Out = 0
Both swaps will pass since the check below will pass as well due to _k(_reserve0, _reserve1) returning 0.
AMOW
High
Malicious first liquidity provider can DoS a stable pair pool
Summary
Stable pool invariant
k
formula lacks a sanity check that prevents it from being assigned a 0 valueVulnerability Detail
The constant invariant
k
of a pool is showcased below.k
being assigned a 0 value is vulnerable as it would allow performing arbitrary swaps that would always abide by the constant market invariant since it is 0.The vulnerability can be exploited in the following way:
Malicious first liquidity provider mints 10e6 of token0 and token1
Attacker can drain whatever is in the pool by invoking swap twice with inputs:
(1)
amount0Out = 0
amount1Out = token1.balanceOf(address(pool)) - 1
(2)
amount0Out = token0.balanceOf(address(pool)) - 1
amount1Out = 0
Both swaps will pass since the check below will pass as well due to _k(_reserve0, _reserve1) returning 0.
Attacker can perform the mint-swap continuously until
totalSupply
overflows, permanently DoS-ing the pool.Impact
Permanent pool DoS
Code Snippet
Tool used
Manual Review
Recommendation
Introduce a constant variable MIN_K and check
_k
against it when minting liquidity.Duplicate of #27