Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent ProtocolFeeController from bricking pool initialization on revert #362

Merged
merged 46 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ead27e4
Default protocol fees to 0 if protocolFeeController reverts
zhongeric Sep 26, 2023
715408d
snapshots
zhongeric Sep 26, 2023
3376f73
fix comment
zhongeric Sep 26, 2023
609f3c1
add another malicious contract
snreynolds Sep 27, 2023
15eaeaf
remove useless var assignment
zhongeric Sep 28, 2023
6c274dd
make call and decode return in assembly
zhongeric Oct 3, 2023
5ea8c7b
fix typo in var assingmenet
zhongeric Oct 3, 2023
5d73281
clean up tests
zhongeric Oct 3, 2023
df322d6
update snapshots#
zhongeric Oct 3, 2023
ab3a130
check withdrawFee == 0 on tests too
zhongeric Oct 3, 2023
32f966e
add test
zhongeric Oct 3, 2023
1c2f854
separate out fetchProtocolFees and checkProtocolFees
zhongeric Oct 3, 2023
2c9957f
manually revert in setProtocolFees
zhongeric Oct 3, 2023
43ba742
Add success return value to fetchProtocolFEes
zhongeric Oct 3, 2023
3d2a30e
Add test for FeeTooLarge passing on initialzie but not on setProtocol…
zhongeric Oct 3, 2023
fab06ec
check low level call success
zhongeric Oct 4, 2023
1469530
clean up
zhongeric Oct 4, 2023
efd7329
add comment
zhongeric Oct 4, 2023
4dd12a3
simplify
zhongeric Oct 4, 2023
2427529
udpate snaps
zhongeric Oct 4, 2023
60d2814
fix compiler warnings
zhongeric Oct 4, 2023
203d6bb
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Oct 30, 2023
dc59dd2
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Nov 2, 2023
2a04559
fix result type
zhongeric Nov 2, 2023
d043b93
fix formatting
zhongeric Nov 2, 2023
7fa116b
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Nov 7, 2023
6acb150
Add more descriptive error message
zhongeric Nov 7, 2023
354729a
Change error name again
zhongeric Nov 7, 2023
ef25eab
Add comments
zhongeric Nov 7, 2023
a6c7070
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Nov 13, 2023
9b2e5aa
update gas specs
zhongeric Nov 13, 2023
173d4ea
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Nov 17, 2023
3cc3242
fix merge conflict t4ests
zhongeric Nov 17, 2023
85127b7
fix tests
zhongeric Nov 17, 2023
09b7e55
Add tests for setProtocolFee with invalid controllers
zhongeric Nov 17, 2023
6164e7e
Add missing snaps
zhongeric Nov 20, 2023
bd2f3a1
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Nov 21, 2023
71f2ff3
Feedback changes
zhongeric Nov 21, 2023
62aacbb
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Nov 21, 2023
08dc288
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Nov 27, 2023
1c7987f
fix initialize tests
zhongeric Nov 27, 2023
5a9b5d5
fix fmt
zhongeric Nov 27, 2023
d2f4f24
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Dec 4, 2023
119610d
Fix comment
zhongeric Dec 4, 2023
0344003
Merge branch 'main' into fix-catch-invalid-protocol-fees
zhongeric Dec 5, 2023
f087262
comments and revise order
zhongeric Dec 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion contracts/Fees.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ abstract contract Fees is IFees, Owned {
protocolWithdrawFee = uint16(updatedProtocolFees & 0xFFF);
zhongeric marked this conversation as resolved.
Show resolved Hide resolved

protocolFees = updatedProtocolFees;
} catch {}
} catch {
// note if the ProtocolFeeController reverts we default all protocol fees to 0
protocolSwapFee = 0;
protocolWithdrawFee = 0;
protocolFees = 0;
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
}
_checkProtocolFee(protocolSwapFee);
_checkProtocolFee(protocolWithdrawFee);
}
Expand Down
13 changes: 13 additions & 0 deletions contracts/test/RevertingProtocolFeeControllerTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {IProtocolFeeController} from "../interfaces/IProtocolFeeController.sol";
import {IPoolManager} from "../interfaces/IPoolManager.sol";
import {PoolId, PoolIdLibrary} from "../types/PoolId.sol";
import {PoolKey} from "../types/PoolKey.sol";

contract RevertingProtocolFeeControllerTest is IProtocolFeeController {
function protocolFeesForPool(PoolKey memory key) external view returns (uint24) {
revert();
}
}
2 changes: 1 addition & 1 deletion test/__snapshots__/PoolManager.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PoolManager bytecode size 1`] = `28706`;
exports[`PoolManager bytecode size 1`] = `28718`;
62 changes: 62 additions & 0 deletions test/foundry-tests/PoolManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol";
import {PoolLockTest} from "../../contracts/test/PoolLockTest.sol";
import {PoolId, PoolIdLibrary} from "../../contracts/types/PoolId.sol";
import {ProtocolFeeControllerTest} from "../../contracts/test/ProtocolFeeControllerTest.sol";
import {RevertingProtocolFeeControllerTest} from "../../contracts/test/RevertingProtocolFeeControllerTest.sol";
import {FeeLibrary} from "../../contracts/libraries/FeeLibrary.sol";
import {Position} from "../../contracts/libraries/Position.sol";

Expand Down Expand Up @@ -71,6 +72,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot, IERC1155
PoolSwapTest swapRouter;
PoolLockTest lockTest;
ProtocolFeeControllerTest protocolFeeController;
RevertingProtocolFeeControllerTest revertingProtocolFeeController;

address ADDRESS_ZERO = address(0);
address EMPTY_HOOKS = address(0xf000000000000000000000000000000000000000);
Expand All @@ -86,6 +88,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot, IERC1155
lockTest = new PoolLockTest(manager);
swapRouter = new PoolSwapTest(manager);
protocolFeeController = new ProtocolFeeControllerTest();
revertingProtocolFeeController = new RevertingProtocolFeeControllerTest();

MockERC20(Currency.unwrap(currency0)).mint(address(this), 10 ether);
MockERC20(Currency.unwrap(currency1)).mint(address(this), 10 ether);
Expand Down Expand Up @@ -227,6 +230,65 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot, IERC1155
assertEq(slot0.sqrtPriceX96, sqrtPriceX96);
}

function testPoolManagerInitializeSucceedsWithRevertingFeeController(uint160 sqrtPriceX96) public {
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
// Assumptions tested in Pool.t.sol
vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO);
vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO);

address hookEmptyAddr = EMPTY_HOOKS;
MockHooks impl = new MockHooks();
vm.etch(hookEmptyAddr, address(impl).code);
MockHooks mockHooks = MockHooks(hookEmptyAddr);

PoolKey memory key =
PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: mockHooks, tickSpacing: 60});

manager.setProtocolFeeController(revertingProtocolFeeController);
// expect initialize to succeed even though the controller reverts
vm.expectEmit(true, true, true, true);
emit Initialize(key.toId(), key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks);
manager.initialize(key, sqrtPriceX96, ZERO_BYTES);
// protocol fees should default to 0
(Pool.Slot0 memory slot0,,,) = manager.pools(key.toId());
assertEq(slot0.protocolFees >> 12, 0);
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
}

function testPoolManagerInitializeSucceedsAndSetsHookFeeIfControllerReverts(uint160 sqrtPriceX96) public {
// Assumptions tested in Pool.t.sol
vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO);
vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO);

address payable mockAddr = payable(address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG)));

address hookAddr = address(99); // can't be a zero address, but does not have to have any other hook flags specified
MockHooks impl = new MockHooks();
vm.etch(hookAddr, address(impl).code);
MockHooks hook = MockHooks(hookAddr);

PoolKey memory key = PoolKey({
currency0: currency0,
currency1: currency1,
fee: FeeLibrary.HOOK_SWAP_FEE_FLAG | uint24(3000),
hooks: hook,
tickSpacing: 60
});

manager.setProtocolFeeController(revertingProtocolFeeController);
// expect initialize to succeed even though the controller reverts
int24 tick = manager.initialize(key, sqrtPriceX96, ZERO_BYTES);
(Pool.Slot0 memory slot0,,,) = manager.pools(key.toId());
assertEq(slot0.sqrtPriceX96, sqrtPriceX96);
// protocol fees should default to 0
assertEq(slot0.protocolFees >> 12, 0);
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
// hook fees can still be set
assertEq(uint16(slot0.hookFees >> 12), 0);
hook.setSwapFee(key, 3000);
manager.setHookFees(key);

(slot0,,,) = manager.pools(key.toId());
assertEq(uint16(slot0.hookFees >> 12), 3000);
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
}

function testPoolManagerInitializeRevertsWithSameTokenCombo(uint160 sqrtPriceX96) public {
// Assumptions tested in Pool.t.sol
vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO);
Expand Down