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 remove #145

Open
wants to merge 88 commits into
base: base-middleware
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
62f093a
discover deltas bug
Jun1on Jul 10, 2024
fcd3b34
forbid after remove returns delta
Jun1on Jul 10, 2024
bb2641c
override deltas edge case with internal delegatecall
Jun1on Jul 11, 2024
266c61e
fix bugs
Jun1on Jul 11, 2024
43717e8
add frontrun protection
Jun1on Jul 12, 2024
8f41cf5
fix bug
Jun1on Jul 12, 2024
802f466
remove unused struct
Jun1on Jul 14, 2024
feb48f2
use msg.data instead of params
Jun1on Jul 15, 2024
26524f4
add FailedImplementationCall error
Jun1on Jul 15, 2024
f3cc913
fix removeLiquidity in tests
Jun1on Jul 15, 2024
c7353bb
increase foundry gas_limit
Jun1on Jul 16, 2024
33b1775
update to solidity ^0.8.24
Jun1on Jul 16, 2024
23a8644
enforce implementations to be mined
Jun1on Jul 17, 2024
3cb9689
enforece remove implementations to be mined
Jun1on Jul 17, 2024
a54a92e
fix breaking ci (#157)
snreynolds Jul 17, 2024
087a262
Update core in periphery (#156)
hensha256 Jul 17, 2024
f242024
Clean up repo (#159)
hensha256 Jul 18, 2024
4ecb379
add posm to issue template (#166)
snreynolds Jul 19, 2024
9d1b36e
fix PR comments
Jun1on Jul 18, 2024
7602a8a
discover deltas bug
Jun1on Jul 10, 2024
368e318
override deltas edge case with internal delegatecall
Jun1on Jul 11, 2024
5e66d57
update imports
Jun1on Jul 19, 2024
17885b9
fix conflict
Jun1on Jul 19, 2024
a9cfd38
Shared actions router (#160)
hensha256 Jul 22, 2024
e5db8e2
create variant that bans deltas
Jun1on Jul 22, 2024
1abe26d
add position manager [wip] (#141)
snreynolds Jul 23, 2024
675e85b
separate middleware if they return deltas
Jun1on Jul 23, 2024
845aa81
add more tests and fix bug
Jun1on Jul 23, 2024
b98912f
add useless variables
Jun1on Jul 23, 2024
f1a680f
force MiddlewareRemove to return delta
Jun1on Jul 23, 2024
6f094c3
fix import (#190)
dianakocsis Jul 24, 2024
181b9ea
fix failing test
Jun1on Jul 24, 2024
e13ba65
reduce gas limit to 5M
Jun1on Jul 24, 2024
a266de8
posm: Native Token (#189)
saucepoint Jul 24, 2024
800e169
add natspec
Jun1on Jul 24, 2024
d3ee037
add state view contract (#187)
snreynolds Jul 24, 2024
6c4a6b6
Safe transfer ETH in posm (#193)
hensha256 Jul 24, 2024
511ce32
Test folder cleanup (#191)
hensha256 Jul 24, 2024
51f9aae
add return value griefing test
Jun1on Jul 24, 2024
dcadfc7
use using, fix import (#195)
snreynolds Jul 25, 2024
ffde495
Update core... again (#198)
hensha256 Jul 25, 2024
1c1d153
CurrencySettler -> DeltaResolver (#186)
hensha256 Jul 25, 2024
f3fc0ac
fix overflow griefing
Jun1on Jul 25, 2024
98e7fa5
uncomment
Jun1on Jul 25, 2024
d7c5302
feat: store PoolPositionId (#196)
snreynolds Jul 25, 2024
1d1d91e
Reentrancy lock posm (#204)
hensha256 Jul 25, 2024
cb902e1
clean up with baseremove
Jun1on Jul 25, 2024
2a9026f
update against latest core (#206)
snreynolds Jul 25, 2024
b973438
remove isApprovedOrOwner from increase (#207)
dianakocsis Jul 26, 2024
540664f
test override
Jun1on Jul 26, 2024
10f748e
use position key helper instead of manually hashing (#213)
saucepoint Jul 27, 2024
2603731
feat: Use permit2 transferFrom (#208)
snreynolds Jul 29, 2024
de79c94
Routing (#58)
ewilz Jul 29, 2024
9c134d1
Support native routing (#224)
hensha256 Jul 30, 2024
a96a8f6
posm : use actions router, remove return vals (#214)
snreynolds Jul 30, 2024
f37734c
rename lib (#228)
hensha256 Jul 30, 2024
b065dcd
Add transfer support (#218)
snreynolds Jul 31, 2024
463d030
some posm tests (#194)
saucepoint Jul 31, 2024
db359ca
Decode posm params in calldata (#226)
hensha256 Jul 31, 2024
0f03176
test SafeCallback (#220)
saucepoint Jul 31, 2024
f38a66f
fix testing compiler warnings (#232)
snreynolds Jul 31, 2024
b54c7bc
remove matching flags requirement
Jun1on Jul 31, 2024
e12bd98
Merge remote-tracking branch 'origin/main' into middleware-remove
Jun1on Jul 31, 2024
eda7534
clean imports
Jun1on Jul 31, 2024
e77f698
Slippage checks routing (#233)
hensha256 Aug 1, 2024
c77742c
Optimise actions encoding (#234)
hensha256 Aug 1, 2024
1f473ec
Make _handleAction virtual in position manager (#240)
qiwu7 Aug 1, 2024
256f7a7
clean imports
Jun1on Aug 1, 2024
598b02e
chore: remove recipient from QuoteParams (#241)
ConjunctiveNormalForm Aug 1, 2024
2e1cf9b
add gas snapshots
Jun1on Aug 1, 2024
c9cbfe2
make function abstract
Jun1on Aug 1, 2024
fff8413
Multi CurrencyDeltas Library (#245)
saucepoint Aug 2, 2024
83ca829
posm slippage (#225)
saucepoint Aug 2, 2024
a421a06
Router settle with balance (#235)
hensha256 Aug 2, 2024
a44f768
posm: Clear (#243)
saucepoint Aug 2, 2024
1bbeef5
Decode router params calldata (#230)
hensha256 Aug 2, 2024
8011598
use helper function and optimize gas
Jun1on Aug 2, 2024
f8f6deb
move files from contracts to src
Jun1on Aug 2, 2024
92d6f70
create HookFees library
Jun1on Aug 2, 2024
8c91ab3
Merge branch 'main' into middleware-remove
Jun1on Aug 2, 2024
8d99c45
Merge remote-tracking branch 'origin/base-middleware' into middleware…
Jun1on Aug 2, 2024
b230ee2
fix conflicts
Jun1on Aug 2, 2024
3eab6cb
store fee in factory
Jun1on Aug 2, 2024
e52fb04
remove BaseMiddlewareFactory
Jun1on Aug 2, 2024
fb02259
Create ABOUT_MIDDLEWARE.md
Jun1on Aug 2, 2024
5e7e959
require success on override
Jun1on Aug 5, 2024
2b62627
update docs
Jun1on Aug 5, 2024
564f941
add fuzz test
Jun1on Aug 5, 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
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ jobs:
with:
version: nightly

- name: Run tests
- name: Check format
run: forge fmt --check
9 changes: 0 additions & 9 deletions contracts/interfaces/IBaseHook.sol

This file was deleted.

2 changes: 1 addition & 1 deletion contracts/interfaces/IMiddlewareFactory.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
pragma solidity ^0.8.24;

interface IMiddlewareFactory {
event MiddlewareCreated(address implementation, address middleware);
Expand Down
15 changes: 12 additions & 3 deletions contracts/middleware/BaseMiddleware.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";

contract BaseMiddleware is Proxy {
/// @notice The address of the pool manager
IPoolManager public immutable poolManager;
IPoolManager public immutable manager;
address public immutable implementation;

constructor(IPoolManager _poolManager, address _impl) {
poolManager = _poolManager;
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason the implementation needs to have the same flags as the middleware?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, will remove this actually

if (uint160(address(this)) & Hooks.ALL_HOOK_MASK != uint160(_impl) & Hooks.ALL_HOOK_MASK) {
revert FlagsMismatch();
}
}
}
14 changes: 5 additions & 9 deletions contracts/middleware/BaseMiddlewareFactory.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
pragma solidity ^0.8.24;

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;

IPoolManager public immutable poolManager;
IPoolManager public immutable manager;

constructor(IPoolManager _poolManager) {
poolManager = _poolManager;
constructor(IPoolManager _manager) {
manager = _manager;
}

function getImplementation(address middleware) external view override returns (address implementation) {
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}(poolManager, implementation));
middleware = address(new BaseMiddleware{salt: salt}(manager, implementation));
}
}
95 changes: 95 additions & 0 deletions contracts/middleware/MiddlewareRemove.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol";
import {BaseMiddleware} from "./BaseMiddleware.sol";
import {BaseHook} from "../BaseHook.sol";
import {BalanceDeltaLibrary} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {CustomRevert} from "@uniswap/v4-core/src/libraries/CustomRevert.sol";
import {NonZeroDeltaCount} from "@uniswap/v4-core/src/libraries/NonZeroDeltaCount.sol";
import {IExttload} from "@uniswap/v4-core/src/interfaces/IExttload.sol";
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";

contract MiddlewareRemove is BaseMiddleware {
using CustomRevert for bytes4;
using Hooks for IHooks;
using StateLibrary for IPoolManager;
using PoolIdLibrary for PoolKey;

error HookPermissionForbidden(address hooks);
error HookModifiedPrice();
error HookModifiedDeltas();
error FailedImplementationCall();

bytes internal constant ZERO_BYTES = bytes("");
uint256 public constant GAS_LIMIT = 10_000_000;

constructor(IPoolManager _manager, address _impl) BaseMiddleware(_manager, _impl) {}

function beforeRemoveLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
bytes calldata hookData
) external returns (bytes4) {
address(this).delegatecall{gas: GAS_LIMIT}(abi.encodeWithSelector(this._callAndEnsurePrice.selector, msg.data));
return BaseHook.beforeRemoveLiquidity.selector;
}

function _callAndEnsurePrice(bytes calldata data) external {
(, PoolKey memory key,,) = abi.decode(data[4:], (address, PoolKey, IPoolManager.ModifyLiquidityParams, bytes));

(uint160 priceBefore,,,) = manager.getSlot0(key.toId());
(bool success,) = address(implementation).delegatecall(data);
if (!success) {
revert FailedImplementationCall();
}
(uint160 priceAfter,,,) = manager.getSlot0(key.toId());
if (priceAfter != priceBefore) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an awesome check! Similar to my comment below I wonder if for each check we can abstract them out. And then have a Middlewear contract that inherits any combination of these checks to allow for all sorts of combinations of checks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would be cool, like lego blocks you can stack to create middleware checks. but is this level of modularity really necessary? i feel like there are only a few middleware designs that truly make sense. and let's say uniswap frontend decides to whitelist middleware-remove hooks for example. there's not really any incentive for new devs to make new middleware designs, as one is all we really need.

unless... the plan is to make these protections modular on the frontend. eg: middleware XYZ has these permissions but is blocked from these middleware selections. almost like an anti-virus checklist that shows up per hook. this would give so much flexibility to the hook developer!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah exactly I like the lego block metaphor

// purpousely revert to cause the whole hook to reset
revert HookModifiedPrice();
}
}

function afterRemoveLiquidity(
address sender,
PoolKey calldata key,
IPoolManager.ModifyLiquidityParams calldata params,
BalanceDelta delta,
bytes calldata hookData
) external returns (bytes4, BalanceDelta) {
address(this).delegatecall{gas: GAS_LIMIT}(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason to delegatecall here? Is it cause we need the GAS_LIMIT enforcement for the whole remainder or the call? What if we just enforced that gasLeft() > GAS_LIMIT by some proportion so that we could internally call _callAndEnsurePrice and then do a delegatecall() with the gas limit externally.. just feels odd that we delegatecall twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, so this is a solution that took me a while to figure out. it looks strange, but i need to perform a try catch and keep msg.sender the same. you can't try catch an internal function, and if i do .call instead of .delegatecall, msg.sender is now the middleware.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the try/catch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh well technically not a try/catch. it just doesn't require success

abi.encodeWithSelector(this._callAndEnsureZeroDeltas.selector, msg.data)
);
return (BaseHook.afterRemoveLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);
}

function _callAndEnsureZeroDeltas(bytes calldata data) external {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just not allow the hook to have the flags for AFTER_ADD_LIQUIDITY_RETURNS_DELTA_FLAG and AFTER_REMOVE_LIQUIDITY_RETURNS_DELTA_FLAG

also I can semi see a case where we'd just want to protect no reverts on remove, but not necessarily no deltas? I wonder if we could split out these two implementations and then have a third implementation that inherits them both?

Copy link
Contributor Author

@Jun1on Jun1on Jul 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so i already have a flag check in the _ensureValidFlags override. (we don't care about add liquidity btw)
so the reason why i disallow changing deltas is because of a bug i discovered while writing this hook: if the hook improperly creates deltas but does not resolve them, the entire remove liquidity will revert because of CurrencyNotSettled. this is a revert i can not catch directly. therefore, if the hook does "unbalanced" accounting by any means, the entire hook will revert and a safe vanilla withdraw will occur.

(while typing this i realized that i also need to handle this in beforeRemoveLiquidity as well)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah gotcha - maybe a comment in the contract explaining this? However this problem is not really solved by the middlewear I think. IE... it doesn't really matter if we revert here or revert from within the hook, the user still won't be able to remove their liquidity...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it actually just reverts the entire hook call. see my testFeeOnRemove hook, how the swap still goes through

bytes32 slot = bytes32(NonZeroDeltaCount.NONZERO_DELTA_COUNT_SLOT);
uint256 countBefore = uint256(IExttload(address(manager)).exttload(slot));
(bool success,) = address(implementation).delegatecall(data);
if (!success) {
revert FailedImplementationCall();
}
uint256 countAfter = uint256(IExttload(address(manager)).exttload(slot));
if (countAfter != countBefore) {
// purpousely revert to cause the whole hook to reset
revert HookModifiedDeltas();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly are you trying to prevent with this check? A hook could technically still modify deltas on the pool so long as they settle it back to the count before the call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's totally ok if the hook modifies deltas as long as they settle it. as i mentioned previously, the goal is to catch reverts when a hook incorrectly settles deltas. if countAfter != countBefore, it's a heuristic that the hook messed something up, in which the entire thing reverts (see why i needed two delegatecalls?) and it becomes a vanilla hookless withdrawal.

}
}

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));
}
}
}
14 changes: 14 additions & 0 deletions contracts/middleware/MiddlewareRemoveFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {BaseMiddlewareFactory} from "./BaseMiddlewareFactory.sol";
import {MiddlewareRemove} from "./MiddlewareRemove.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";

contract MiddlewareRemoveFactory is BaseMiddlewareFactory {
constructor(IPoolManager _manager) BaseMiddlewareFactory(_manager) {}

function _deployMiddleware(address implementation, bytes32 salt) internal override returns (address middleware) {
middleware = address(new MiddlewareRemove{salt: salt}(manager, implementation));
}
}
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ optimizer_runs = 1000000
ffi = true
fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"}]
evm_version = "cancun"
gas_limit = "3000000000"

[profile.ci]
fuzz_runs = 100000
Expand Down
52 changes: 29 additions & 23 deletions test/BaseMiddlewareFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ contract BaseMiddlewareFactoryTest is Test, Deployers {

BaseMiddlewareFactory factory;
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 @@ -37,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 @@ -117,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