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

Middleware protect #152

Open
wants to merge 54 commits into
base: middleware-remove
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
128aacf
create outline
Jun1on Jul 15, 2024
6801684
add liquidity protection
Jun1on Jul 15, 2024
5e6c2ae
add quoter check in beforeswap
Jun1on Jul 15, 2024
9f6788e
increase foundry gas_limit
Jun1on Jul 16, 2024
e3ad3c7
forge fmt
Jun1on Jul 16, 2024
d3e40ff
add function to handle errors
Jun1on Jul 16, 2024
052f21d
enforce implementation address mined
Jun1on Jul 16, 2024
71962b1
fix error bug
Jun1on Jul 17, 2024
9a60e41
bring back random function
Jun1on Jul 17, 2024
daf74ce
Recipient mapping! (#246)
hensha256 Aug 2, 2024
fdf28e4
allow batching sig based approvals through p2 forwarder (#238)
snreynolds Aug 2, 2024
ebf7a4d
Take portion (#250)
hensha256 Aug 2, 2024
30863cc
Pay with balance, and use delta as amount in (#249)
hensha256 Aug 2, 2024
a9e463d
Settle pair (#242)
dianakocsis Aug 2, 2024
e764aec
posm: add staking through subscribers (#229)
snreynolds Aug 2, 2024
912536c
make msgSender public (#253)
hensha256 Aug 2, 2024
eee5a0e
posm: CLEAR_OR_TAKE (#252)
saucepoint Aug 2, 2024
6fe5428
TAKE_PAIR (#254)
dianakocsis Aug 3, 2024
1f28ac2
ERC721Permit (#210)
saucepoint Aug 3, 2024
2f15bb2
Take (#257)
snreynolds Aug 3, 2024
86b5ea3
multicall: bubble up revert reason (#236)
saucepoint Aug 4, 2024
d65f158
Optimise permit hashing (#260)
hensha256 Aug 4, 2024
0c956bf
Replace OZ EIP712 (#256)
saucepoint Aug 4, 2024
60a983e
Take command in router (#261)
hensha256 Aug 4, 2024
dfa1865
posm: Rename File Collisions (#263)
saucepoint Aug 4, 2024
2d07bd4
Align constants with UR (#267)
hensha256 Aug 4, 2024
e2d2508
One BPS library (#268)
hensha256 Aug 4, 2024
eb0cf58
slippage params routing (#264)
hensha256 Aug 4, 2024
41bbc7d
add liquidity view (#270)
snreynolds Aug 4, 2024
ea5f9ec
add bytes, clean up compiliation (#269)
snreynolds Aug 4, 2024
f402aa7
actions with no unlock (#231)
hensha256 Aug 5, 2024
d1f9005
ERC721Permit - PermitForAll (#271)
saucepoint Aug 5, 2024
3b93674
Wrap reverts thrown by subscribers (#273)
gretzke Aug 5, 2024
df47aa9
Some cleanup (#276)
snreynolds Aug 5, 2024
20718d5
Make PositionManager.transferFrom virtual (#278)
brockmiller Aug 5, 2024
cf4e2ad
Use custom revert (#277)
snreynolds Aug 5, 2024
af688af
add mint position event (#279)
snreynolds Aug 5, 2024
bf3b8ad
Provide feesAccrued to subscriber.notifyModifyLiquidity (#282)
saucepoint Aug 6, 2024
17f1a49
OZ: posm - restore permissioning on increase (#290)
saucepoint Aug 7, 2024
7cad2f6
fix: slippage checks (#285)
snreynolds Aug 8, 2024
b890da6
nit: make multicall external (#292)
snreynolds Aug 8, 2024
4d56687
OZ: Remove contract balance swap input (#286)
hensha256 Aug 8, 2024
469f856
move sub unsub (#287)
snreynolds Aug 8, 2024
5ad4439
add view quoter
Jun1on Aug 8, 2024
656afa4
Merge branch 'middleware-remove' into middleware-protect
Jun1on Aug 9, 2024
05ad967
uninheret from middleware-remove
Jun1on Aug 13, 2024
54c12c8
add gas tests with revert quoter
Jun1on Aug 13, 2024
4ad5a3f
Merge branch 'view-quoter' into middleware-protect
Jun1on Aug 13, 2024
8139431
forge test --isolate
Jun1on Aug 13, 2024
81e9692
use view quoter
Jun1on Aug 13, 2024
3f69c05
optimize quoter
Jun1on Aug 13, 2024
6632806
update docs
Jun1on Aug 14, 2024
cea56de
use tstore
Jun1on Aug 15, 2024
5eec68a
update gas snapshots
Jun1on Aug 15, 2024
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
Prev Previous commit
Next Next commit
enforce implementation address mined
  • Loading branch information
Jun1on committed Jul 16, 2024
commit 052f21dadcd3c76729a997f3db3c178b65749ed5
1 change: 1 addition & 0 deletions .forge-snapshots/NormalSwap.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
79041
1 change: 1 addition & 0 deletions .forge-snapshots/ProtectedSwap.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
205221
1 change: 1 addition & 0 deletions .forge-snapshots/UnprotectedSwap.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
130276
9 changes: 0 additions & 9 deletions contracts/interfaces/IBaseHook.sol

This file was deleted.

9 changes: 9 additions & 0 deletions contracts/middleware/BaseMiddleware.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ contract BaseMiddleware is Proxy {
IPoolManager public immutable manager;
address public immutable implementation;

error FlagsMismatch();

constructor(IPoolManager _manager, address _impl) {
_ensureValidFlags(_impl);
manager = _manager;
implementation = _impl;
}
Expand All @@ -27,4 +30,10 @@ contract BaseMiddleware is Proxy {
receive() external payable {
_delegate(_implementation());
}

function _ensureValidFlags(address _impl) internal view virtual {
if (uint160(address(this)) & Hooks.ALL_HOOK_MASK != uint160(_impl) & Hooks.ALL_HOOK_MASK) {
revert FlagsMismatch();
}
}
}
6 changes: 1 addition & 5 deletions contracts/middleware/BaseMiddlewareFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ pragma solidity ^0.8.19;
import {IMiddlewareFactory} from "../interfaces/IMiddlewareFactory.sol";
import {BaseMiddleware} from "./BaseMiddleware.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
import {IBaseHook} from "../interfaces/IBaseHook.sol";

contract BaseMiddlewareFactory is IMiddlewareFactory {
mapping(address => address) private _implementations;
Expand All @@ -23,12 +20,11 @@ contract BaseMiddlewareFactory is IMiddlewareFactory {

function createMiddleware(address implementation, bytes32 salt) external override returns (address middleware) {
middleware = _deployMiddleware(implementation, salt);
Hooks.validateHookPermissions(IHooks(middleware), IBaseHook(implementation).getHookPermissions());
_implementations[middleware] = implementation;
emit MiddlewareCreated(implementation, middleware);
}

function _deployMiddleware(address implementation, bytes32 salt) internal virtual returns (address middleware) {
return address(new BaseMiddleware{salt: salt}(manager, implementation));
middleware = address(new BaseMiddleware{salt: salt}(manager, implementation));
}
}
50 changes: 38 additions & 12 deletions contracts/middleware/MiddlewareProtect.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "@uniswap/v4-core/src/type
import {console} from "./../../lib/forge-gas-snapshot/lib/forge-std/src/console.sol";
import {LPFeeLibrary} from "@uniswap/v4-core/src/libraries/LPFeeLibrary.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";

contract MiddlewareProtect is MiddlewareRemove {
using CustomRevert for bytes4;
Expand All @@ -29,25 +30,17 @@ contract MiddlewareProtect is MiddlewareRemove {
using LPFeeLibrary for uint24;
using BalanceDeltaLibrary for BalanceDelta;

error ForbiddenDynamicFee();
error HookModifiedOutput();
error ForbiddenDynamicFee();
error MustEnableAfterSwapFlag();

// todo: use tstore
BalanceDelta private quote;

uint160 public constant MIN_PRICE_LIMIT = TickMath.MIN_SQRT_PRICE + 1;
uint160 public constant MAX_PRICE_LIMIT = TickMath.MAX_SQRT_PRICE - 1;

constructor(IPoolManager _manager, address _impl) MiddlewareRemove(_manager, _impl) {
IHooks middleware = IHooks(address(this));
if (
middleware.hasPermission(Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG)
|| middleware.hasPermission(Hooks.AFTER_SWAP_RETURNS_DELTA_FLAG)
|| middleware.hasPermission(Hooks.AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG)
) {
HookPermissionForbidden.selector.revertWith(address(this));
}
}
constructor(IPoolManager _manager, address _impl) MiddlewareRemove(_manager, _impl) {}

function beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96, bytes calldata hookData)
external
Expand Down Expand Up @@ -92,7 +85,13 @@ contract MiddlewareProtect is MiddlewareRemove {
external
returns (bytes4, int128)
{
if (delta != quote) revert HookModifiedOutput();
IHooks implementation = IHooks(address(implementation));
if (implementation.hasPermission(Hooks.BEFORE_SWAP_FLAG)) {
if (delta != quote) revert HookModifiedOutput();
if (!implementation.hasPermission(Hooks.AFTER_SWAP_FLAG)) {
return (BaseHook.afterSwap.selector, 0);
}
}
(bool success, bytes memory returnData) = address(implementation).delegatecall(msg.data);
if (!success) {
_handleRevert(returnData);
Expand Down Expand Up @@ -120,4 +119,31 @@ contract MiddlewareProtect is MiddlewareRemove {
revert(add(32, returnData), mload(returnData))
}
}

function _ensureValidFlags(address _impl) internal view virtual override {
IHooks This = IHooks(address(this));
if (
This.hasPermission(Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG)
|| This.hasPermission(Hooks.AFTER_SWAP_RETURNS_DELTA_FLAG)
|| This.hasPermission(Hooks.AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG)
|| This.hasPermission(Hooks.AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG)
) {
HookPermissionForbidden.selector.revertWith(address(this));
}
if (This.hasPermission(Hooks.BEFORE_SWAP_FLAG)) {
if (
uint160(address(this)) & Hooks.ALL_HOOK_MASK
!= uint160(_impl) & Hooks.ALL_HOOK_MASK | Hooks.AFTER_SWAP_FLAG
) {
if (This.hasPermission(Hooks.AFTER_SWAP_FLAG)) {
revert FlagsMismatch();
} else {
// both flags match, but dev must enable AFTER_SWAP_FLAG
revert MustEnableAfterSwapFlag();
}
}
} else if (uint160(address(this)) & Hooks.ALL_HOOK_MASK != uint160(_impl) & Hooks.ALL_HOOK_MASK) {
revert FlagsMismatch();
}
}
}
2 changes: 1 addition & 1 deletion contracts/middleware/MiddlewareProtectFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ contract MiddlewareProtectFactory is BaseMiddlewareFactory {
constructor(IPoolManager _manager) BaseMiddlewareFactory(_manager) {}

function _deployMiddleware(address implementation, bytes32 salt) internal override returns (address middleware) {
return address(new MiddlewareProtect{salt: salt}(manager, implementation));
middleware = address(new MiddlewareProtect{salt: salt}(manager, implementation));
}
}
15 changes: 10 additions & 5 deletions contracts/middleware/MiddlewareRemove.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ contract MiddlewareRemove is BaseMiddleware {
bytes internal constant ZERO_BYTES = bytes("");
uint256 public constant GAS_LIMIT = 10_000_000;

constructor(IPoolManager _manager, address _impl) BaseMiddleware(_manager, _impl) {
if (IHooks(address(this)).hasPermission(Hooks.AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG)) {
HookPermissionForbidden.selector.revertWith(address(this));
}
}
constructor(IPoolManager _manager, address _impl) BaseMiddleware(_manager, _impl) {}

function beforeRemoveLiquidity(
address sender,
Expand Down Expand Up @@ -87,4 +83,13 @@ contract MiddlewareRemove is BaseMiddleware {
revert HookModifiedDeltas();
}
}

function _ensureValidFlags(address _impl) internal view virtual override {
if (uint160(address(this)) & Hooks.ALL_HOOK_MASK != uint160(_impl) & Hooks.ALL_HOOK_MASK) {
revert FlagsMismatch();
}
if (IHooks(address(this)).hasPermission(Hooks.AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG)) {
HookPermissionForbidden.selector.revertWith(address(this));
}
}
}
2 changes: 1 addition & 1 deletion contracts/middleware/MiddlewareRemoveFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ contract MiddlewareRemoveFactory is BaseMiddlewareFactory {
constructor(IPoolManager _manager) BaseMiddlewareFactory(_manager) {}

function _deployMiddleware(address implementation, bytes32 salt) internal override returns (address middleware) {
return address(new MiddlewareRemove{salt: salt}(manager, implementation));
middleware = address(new MiddlewareRemove{salt: salt}(manager, implementation));
}
}
51 changes: 29 additions & 22 deletions test/BaseMiddlewareFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ contract BaseMiddlewareFactoryTest is Test, Deployers {
Counter counter;
address middleware;

uint160 COUNTER_FLAGS = uint160(
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG | Hooks.AFTER_ADD_LIQUIDITY_FLAG | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG
| Hooks.AFTER_REMOVE_LIQUIDITY_FLAG | Hooks.BEFORE_DONATE_FLAG | Hooks.AFTER_DONATE_FLAG
);

function setUp() public {
deployFreshManagerAndRouters();
(currency0, currency1) = deployMintAndApprove2Currencies();
Expand All @@ -36,55 +42,56 @@ contract BaseMiddlewareFactoryTest is Test, Deployers {
token1 = TestERC20(Currency.unwrap(currency1));

factory = new BaseMiddlewareFactory(manager);
counter = new Counter(manager);
counter = Counter(address(COUNTER_FLAGS));
vm.etch(address(counter), address(new Counter(manager)).code);

token0.approve(address(router), type(uint256).max);
token1.approve(address(router), type(uint256).max);

uint160 flags = uint160(
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG | Hooks.AFTER_ADD_LIQUIDITY_FLAG | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG
| Hooks.AFTER_REMOVE_LIQUIDITY_FLAG | Hooks.BEFORE_DONATE_FLAG | Hooks.AFTER_DONATE_FLAG
);

(address hookAddress, bytes32 salt) = HookMiner.find(
address(factory), flags, type(BaseMiddleware).creationCode, abi.encode(address(manager), address(counter))
address(factory),
COUNTER_FLAGS,
type(BaseMiddleware).creationCode,
abi.encode(address(manager), address(counter))
);
middleware = factory.createMiddleware(address(counter), salt);
assertEq(hookAddress, middleware);
}

function testRevertOnSameDeployment() public {
uint160 flags = uint160(
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG | Hooks.AFTER_ADD_LIQUIDITY_FLAG | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG
| Hooks.AFTER_REMOVE_LIQUIDITY_FLAG | Hooks.BEFORE_DONATE_FLAG | Hooks.AFTER_DONATE_FLAG
);
(address hookAddress, bytes32 salt) = HookMiner.find(
address(factory), flags, type(BaseMiddleware).creationCode, abi.encode(address(manager), address(counter))
address(factory),
COUNTER_FLAGS,
type(BaseMiddleware).creationCode,
abi.encode(address(manager), address(counter))
);
factory.createMiddleware(address(counter), salt);
// second deployment should revert
vm.expectRevert(bytes(""));
vm.expectRevert(ZERO_BYTES);
factory.createMiddleware(address(counter), salt);
}

function testRevertOnIncorrectFlags() public {
Counter counter2 = new Counter(manager);
uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG);
Counter counter2 = Counter(address(COUNTER_FLAGS));
vm.etch(address(counter), address(new Counter(manager)).code);
uint160 incorrectFlags = uint160(Hooks.BEFORE_INITIALIZE_FLAG);

(address hookAddress, bytes32 salt) = HookMiner.find(
address(factory), flags, type(BaseMiddleware).creationCode, abi.encode(address(manager), address(counter2))
address(factory),
incorrectFlags,
type(BaseMiddleware).creationCode,
abi.encode(address(manager), address(counter2))
);
address implementation = address(counter2);
vm.expectRevert(abi.encodePacked(bytes16(Hooks.HookAddressNotValid.selector), hookAddress));
vm.expectRevert(BaseMiddleware.FlagsMismatch.selector);
factory.createMiddleware(implementation, salt);
}

function testRevertOnIncorrectFlagsMined() public {
Counter counter2 = new Counter(manager);
Counter counter2 = Counter(address(COUNTER_FLAGS));
vm.etch(address(counter), address(new Counter(manager)).code);
address implementation = address(counter2);
vm.expectRevert(); // HookAddressNotValid
vm.expectRevert(BaseMiddleware.FlagsMismatch.selector);
factory.createMiddleware(implementation, bytes32("who needs to mine a salt?"));
}

Expand Down Expand Up @@ -116,7 +123,7 @@ contract BaseMiddlewareFactoryTest is Test, Deployers {
assertEq(counterProxy.afterSwapCount(id), 1);

// counter does not store data itself
assertEq(counter.lastHookData(), bytes(""));
assertEq(counter.lastHookData(), ZERO_BYTES);
assertEq(counter.beforeSwapCount(id), 0);
assertEq(counter.afterSwapCount(id), 0);
}
Expand Down
Loading
Loading