Skip to content

Commit 24e5bd5

Browse files
authored
Merge pull request #63 from euler-xyz/sauce/initialize-once
Initialize Once Constraint
2 parents 1a714a4 + c99a5d6 commit 24e5bd5

File tree

4 files changed

+42
-5
lines changed

4 files changed

+42
-5
lines changed

src/UniswapHook.sol

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ contract UniswapHook is BaseHook {
2727

2828
PoolKey internal _poolKey;
2929

30+
error AlreadyInitialized();
3031
error NativeConcentratedLiquidityUnsupported();
3132

3233
constructor(address evc_, address _poolManager) BaseHook(IPoolManager(_poolManager)) {
@@ -118,6 +119,15 @@ contract UniswapHook is BaseHook {
118119
return (BaseHook.beforeSwap.selector, returnDelta, 0);
119120
}
120121

122+
/// @dev Each deployed hook only services one pair and prevent subsequent initializations
123+
function _beforeInitialize(address, PoolKey calldata, uint160) internal view override returns (bytes4) {
124+
// when the hook is deployed for the first time, the internal _poolKey is empty
125+
// upon activation, the internal _poolKey is initialized and set
126+
// once the hook contract is activated, do not allow subsequent initializations
127+
require(_poolKey.tickSpacing == 0, AlreadyInitialized());
128+
return BaseHook.beforeInitialize.selector;
129+
}
130+
121131
function _beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)
122132
internal
123133
pure
@@ -129,7 +139,7 @@ contract UniswapHook is BaseHook {
129139

130140
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
131141
return Hooks.Permissions({
132-
beforeInitialize: false,
142+
beforeInitialize: true,
133143
afterInitialize: false,
134144
beforeAddLiquidity: true,
135145
afterAddLiquidity: false,

test/EulerSwapTestBase.t.sol

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ contract EulerSwapTestBase is EVaultTestBase {
4141
// use the canonical miner to find a valid 'implementation' address
4242
(, bytes32 salt) = v4HookMiner.find(
4343
address(this),
44-
uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG),
44+
uint160(
45+
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
46+
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG
47+
),
4548
type(EulerSwap).creationCode,
4649
abi.encode(address(evc), poolManager_)
4750
);
@@ -208,7 +211,10 @@ contract EulerSwapTestBase is EVaultTestBase {
208211
(address predictedAddr, bytes32 salt) = HookMiner.find(
209212
address(eulerSwapFactory),
210213
holder,
211-
uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG),
214+
uint160(
215+
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
216+
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG
217+
),
212218
creationCode
213219
);
214220

test/FactoryTest.t.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ contract FactoryTest is EulerSwapTestBase {
3333
}
3434

3535
function mineSalt(IEulerSwap.Params memory poolParams) internal view returns (address hookAddress, bytes32 salt) {
36-
uint160 flags =
37-
uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG);
36+
uint160 flags = uint160(
37+
Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG
38+
| Hooks.BEFORE_ADD_LIQUIDITY_FLAG
39+
);
3840
bytes memory creationCode = MetaProxyDeployer.creationCodeMetaProxy(eulerSwapImpl, abi.encode(poolParams));
3941
(hookAddress, salt) = HookMiner.find(address(eulerSwapFactory), holder, flags, creationCode);
4042
}

test/HookSwaps.t.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,25 @@ contract HookSwapsTest is EulerSwapTestBase {
155155
vm.stopPrank();
156156
}
157157

158+
/// @dev initializing a new pool on an existing eulerswap instance will revert
159+
function test_revertSubsequentInitialize() public {
160+
PoolKey memory poolKey = eulerSwap.poolKey();
161+
PoolKey memory newPoolKey = eulerSwap.poolKey();
162+
newPoolKey.currency0 = CurrencyLibrary.ADDRESS_ZERO;
163+
164+
// hook intentionally reverts to prevent subsequent initializations
165+
vm.expectRevert(
166+
abi.encodeWithSelector(
167+
CustomRevert.WrappedError.selector,
168+
address(eulerSwap),
169+
IHooks.beforeInitialize.selector,
170+
abi.encodeWithSelector(UniswapHook.AlreadyInitialized.selector),
171+
abi.encodeWithSelector(Hooks.HookCallFailed.selector)
172+
)
173+
);
174+
poolManager.initialize(newPoolKey, 79228162514264337593543950336);
175+
}
176+
158177
function _swap(PoolKey memory key, bool zeroForOne, bool exactInput, uint256 amount) internal {
159178
IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({
160179
zeroForOne: zeroForOne,

0 commit comments

Comments
 (0)