Skip to content

Commit ebe153e

Browse files
authored
Merge pull request #83 from euler-xyz/basehook-revert
Revert via BaseHook
2 parents 846bb37 + 6572c6c commit ebe153e

File tree

5 files changed

+47
-29
lines changed

5 files changed

+47
-29
lines changed

script/DeployPool.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ contract DeployPool is ScriptUtil {
4646
eulerAccount,
4747
uint160(
4848
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
49-
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG
49+
| Hooks.BEFORE_DONATE_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG
5050
),
5151
creationCode
5252
);

src/UniswapHook.sol

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ contract UniswapHook is BaseHook {
2727

2828
PoolKey internal _poolKey;
2929

30-
error AlreadyInitialized();
31-
error NativeConcentratedLiquidityUnsupported();
3230
error LockedHook();
3331

3432
constructor(address evc_, address _poolManager) BaseHook(IPoolManager(_poolManager)) {
@@ -143,25 +141,22 @@ contract UniswapHook is BaseHook {
143141
return (BaseHook.beforeSwap.selector, returnDelta, 0);
144142
}
145143

146-
/// @dev Each deployed hook only services one pair and prevent subsequent initializations
147-
function _beforeInitialize(address, PoolKey calldata, uint160) internal view override returns (bytes4) {
148-
// when the hook is deployed for the first time, the internal _poolKey is empty
149-
// upon activation, the internal _poolKey is initialized and set
150-
// once the hook contract is activated, do not allow subsequent initializations
151-
require(_poolKey.tickSpacing == 0, AlreadyInitialized());
152-
return BaseHook.beforeInitialize.selector;
153-
}
154-
155-
function _beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)
156-
internal
157-
pure
158-
override
159-
returns (bytes4)
160-
{
161-
revert NativeConcentratedLiquidityUnsupported();
162-
}
163-
164144
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
145+
/**
146+
* @dev Hook Permissions without overrides:
147+
* - beforeInitialize, beforeDoate, beforeAddLiquidity
148+
* We use BaseHook's original reverts to *intentionally* revert
149+
*
150+
* beforeInitialize: the hook reverts for initializations NOT going through EulerSwap.activateHook()
151+
* we want to prevent users from initializing other pairs with the same hook address
152+
*
153+
* beforeDonate: because the hook does not support native concentrated liquidity, any
154+
* donations are permanently irrecoverable. The hook reverts on beforeDonate to prevent accidental misusage
155+
*
156+
* beforeAddLiquidity: the hook reverts to prevent v3-CLAMM positions
157+
* because the hook is a "custom curve", any concentrated liquidity position sits idle and entirely unused
158+
* to protect users from accidentally creating non-productive positions, the hook reverts on beforeAddLiquidity
159+
*/
165160
return Hooks.Permissions({
166161
beforeInitialize: true,
167162
afterInitialize: false,
@@ -171,7 +166,7 @@ contract UniswapHook is BaseHook {
171166
afterRemoveLiquidity: false,
172167
beforeSwap: true,
173168
afterSwap: false,
174-
beforeDonate: false,
169+
beforeDonate: true,
175170
afterDonate: false,
176171
beforeSwapReturnDelta: true,
177172
afterSwapReturnDelta: false,

test/EulerSwapTestBase.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ contract EulerSwapTestBase is EVaultTestBase {
201201
holder,
202202
uint160(
203203
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
204-
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG
204+
| Hooks.BEFORE_DONATE_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG
205205
),
206206
creationCode
207207
);

test/FactoryTest.t.sol

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ contract FactoryTest is EulerSwapTestBase {
3535
function mineSalt(IEulerSwap.Params memory poolParams) internal view returns (address hookAddress, bytes32 salt) {
3636
uint160 flags = uint160(
3737
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
38-
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG
38+
| Hooks.BEFORE_DONATE_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG
3939
);
4040
bytes memory creationCode = MetaProxyDeployer.creationCodeMetaProxy(eulerSwapImpl, abi.encode(poolParams));
4141
(hookAddress, salt) = HookMiner.find(address(eulerSwapFactory), holder, flags, creationCode);
@@ -47,8 +47,10 @@ contract FactoryTest is EulerSwapTestBase {
4747
returns (address hookAddress, bytes32 salt)
4848
{
4949
// missing BEFORE_ADD_LIQUIDITY_FLAG
50-
uint160 flags =
51-
uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG);
50+
uint160 flags = uint160(
51+
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
52+
| Hooks.BEFORE_DONATE_FLAG
53+
);
5254
bytes memory creationCode = MetaProxyDeployer.creationCodeMetaProxy(eulerSwapImpl, abi.encode(poolParams));
5355
(hookAddress, salt) = HookMiner.find(address(eulerSwapFactory), holder, flags, creationCode);
5456
}

test/HookSwaps.t.sol

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {UniswapHook} from "../src/UniswapHook.sol";
99
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
1010
import {IPoolManager, PoolManagerDeployer} from "./utils/PoolManagerDeployer.sol";
1111
import {PoolSwapTest} from "@uniswap/v4-core/src/test/PoolSwapTest.sol";
12+
import {PoolDonateTest} from "@uniswap/v4-core/src/test/PoolDonateTest.sol";
1213
import {MinimalRouter} from "./utils/MinimalRouter.sol";
1314
import {PoolModifyLiquidityTest} from "@uniswap/v4-core/src/test/PoolModifyLiquidityTest.sol";
1415
import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol";
@@ -18,6 +19,7 @@ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
1819
import {CustomRevert} from "@uniswap/v4-core/src/libraries/CustomRevert.sol";
1920
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
2021
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
22+
import {BaseHook} from "v4-periphery/src/utils/BaseHook.sol";
2123

2224
contract HookSwapsTest is EulerSwapTestBase {
2325
using StateLibrary for IPoolManager;
@@ -28,6 +30,7 @@ contract HookSwapsTest is EulerSwapTestBase {
2830
PoolSwapTest public swapRouter;
2931
MinimalRouter public minimalRouter;
3032
PoolModifyLiquidityTest public liquidityManager;
33+
PoolDonateTest public donateRouter;
3134

3235
PoolSwapTest.TestSettings public settings = PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false});
3336

@@ -38,6 +41,7 @@ contract HookSwapsTest is EulerSwapTestBase {
3841
swapRouter = new PoolSwapTest(poolManager);
3942
minimalRouter = new MinimalRouter(poolManager);
4043
liquidityManager = new PoolModifyLiquidityTest(poolManager);
44+
donateRouter = new PoolDonateTest(poolManager);
4145

4246
deployEulerSwap(address(poolManager));
4347

@@ -133,13 +137,13 @@ contract HookSwapsTest is EulerSwapTestBase {
133137
assertTrue(perms.beforeAddLiquidity);
134138
assertTrue(perms.beforeSwap);
135139
assertTrue(perms.beforeSwapReturnDelta);
140+
assertTrue(perms.beforeDonate);
136141

137142
assertFalse(perms.afterInitialize);
138143
assertFalse(perms.afterAddLiquidity);
139144
assertFalse(perms.beforeRemoveLiquidity);
140145
assertFalse(perms.afterRemoveLiquidity);
141146
assertFalse(perms.afterSwap);
142-
assertFalse(perms.beforeDonate);
143147
assertFalse(perms.afterDonate);
144148
assertFalse(perms.afterSwapReturnDelta);
145149
assertFalse(perms.afterAddLiquidityReturnDelta);
@@ -163,7 +167,7 @@ contract HookSwapsTest is EulerSwapTestBase {
163167
CustomRevert.WrappedError.selector,
164168
address(eulerSwap),
165169
IHooks.beforeAddLiquidity.selector,
166-
abi.encodeWithSelector(UniswapHook.NativeConcentratedLiquidityUnsupported.selector),
170+
abi.encodeWithSelector(BaseHook.HookNotImplemented.selector),
167171
abi.encodeWithSelector(Hooks.HookCallFailed.selector)
168172
)
169173
);
@@ -186,13 +190,30 @@ contract HookSwapsTest is EulerSwapTestBase {
186190
CustomRevert.WrappedError.selector,
187191
address(eulerSwap),
188192
IHooks.beforeInitialize.selector,
189-
abi.encodeWithSelector(UniswapHook.AlreadyInitialized.selector),
193+
abi.encodeWithSelector(BaseHook.HookNotImplemented.selector),
190194
abi.encodeWithSelector(Hooks.HookCallFailed.selector)
191195
)
192196
);
193197
poolManager.initialize(newPoolKey, 79228162514264337593543950336);
194198
}
195199

200+
/// @dev revert on donations as they are irrecoverable if they were supported
201+
function test_revertDonate(uint256 amount0, uint256 amount1) public {
202+
PoolKey memory poolKey = eulerSwap.poolKey();
203+
204+
// hook intentionally reverts to prevent irrecoverable donations
205+
vm.expectRevert(
206+
abi.encodeWithSelector(
207+
CustomRevert.WrappedError.selector,
208+
address(eulerSwap),
209+
IHooks.beforeDonate.selector,
210+
abi.encodeWithSelector(BaseHook.HookNotImplemented.selector),
211+
abi.encodeWithSelector(Hooks.HookCallFailed.selector)
212+
)
213+
);
214+
donateRouter.donate(poolKey, amount0, amount1, "");
215+
}
216+
196217
function _swap(PoolKey memory key, bool zeroForOne, bool exactInput, uint256 amount) internal {
197218
IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({
198219
zeroForOne: zeroForOne,

0 commit comments

Comments
 (0)