pragma solidity ^0.8.26;
import {Test} from "forge-std/Test.sol";
import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol";
import {PoolSwapTest} from "v4-core/test/PoolSwapTest.sol";
import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol";
import {Hooks} from "v4-core/libraries/Hooks.sol";
import {HookMiner} from "v4-periphery/src/utils/HookMiner.sol";
import {LPFeeLibrary} from "v4-core/libraries/LPFeeLibrary.sol";
import {TickMath} from "v4-core/libraries/TickMath.sol";
import {LiquidityAmounts} from "@uniswap/v4-core/test/utils/LiquidityAmounts.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {TokenLaunchHook} from "../src/TokenLaunchHook.sol";
import {Currency} from "v4-core/types/Currency.sol";
import {SwapParams, ModifyLiquidityParams} from "v4-core/types/PoolOperation.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {ERC1155TokenReceiver} from "solmate/src/tokens/ERC1155.sol";
contract LackOfPoolIsolationTest is Test, Deployers, ERC1155TokenReceiver {
using LPFeeLibrary for uint24;
TokenLaunchHook hook;
MockERC20 tokenA;
MockERC20 tokenB;
Currency ethCurrency = Currency.wrap(address(0));
Currency tokenACurrency;
Currency tokenBCurrency;
PoolKey keyA;
PoolKey keyB;
uint256 phase1Duration = 100;
uint256 phase2Duration = 100;
uint256 phase1LimitBps = 100;
uint256 phase2LimitBps = 500;
uint256 phase1Cooldown = 5;
uint256 phase2Cooldown = 2;
uint256 phase1PenaltyBps = 1000;
uint256 phase2PenaltyBps = 500;
address user = address(0xABCD);
function setUp() public {
deployFreshManagerAndRouters();
tokenA = new MockERC20("TOKENA", "TKNA", 18);
tokenB = new MockERC20("TOKENB", "TKNB", 18);
tokenACurrency = Currency.wrap(address(tokenA));
tokenBCurrency = Currency.wrap(address(tokenB));
tokenA.mint(address(this), 1_000 ether);
tokenB.mint(address(this), 1_000 ether);
tokenA.mint(user, 100 ether);
tokenA.approve(address(swapRouter), type(uint256).max);
tokenA.approve(address(modifyLiquidityRouter), type(uint256).max);
tokenB.approve(address(swapRouter), type(uint256).max);
tokenB.approve(address(modifyLiquidityRouter), type(uint256).max);
vm.prank(user);
tokenA.approve(address(swapRouter), type(uint256).max);
bytes memory creationCode = type(TokenLaunchHook).creationCode;
bytes memory args = abi.encode(
manager,
phase1Duration,
phase2Duration,
phase1LimitBps,
phase2LimitBps,
phase1Cooldown,
phase2Cooldown,
phase1PenaltyBps,
phase2PenaltyBps
);
uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_INITIALIZE_FLAG);
(address mined, bytes32 salt) =
HookMiner.find(address(this), flags, creationCode, args);
hook = new TokenLaunchHook{salt: salt}(
IPoolManager(manager),
phase1Duration,
phase2Duration,
phase1LimitBps,
phase2LimitBps,
phase1Cooldown,
phase2Cooldown,
phase1PenaltyBps,
phase2PenaltyBps
);
require(address(hook) == mined, "Hook address mismatch");
(keyA,) = initPool(ethCurrency, tokenACurrency, hook, LPFeeLibrary.DYNAMIC_FEE_FLAG, SQRT_PRICE_1_1);
uint160 sqrtPriceAtTickUpper = TickMath.getSqrtPriceAtTick(60);
uint256 ethToAdd = 10 ether;
uint128 liqDelta = LiquidityAmounts.getLiquidityForAmount0(
SQRT_PRICE_1_1, sqrtPriceAtTickUpper, ethToAdd
);
modifyLiquidityRouter.modifyLiquidity{value: ethToAdd}(
keyA,
ModifyLiquidityParams({
tickLower: -60,
tickUpper: 60,
liquidityDelta: int256(uint256(liqDelta)),
salt: bytes32(0)
}),
ZERO_BYTES
);
}
function test_PoolBInitializationResetsPoolAState() public {
vm.deal(user, 1 ether);
vm.startPrank(user);
SwapParams memory params = SwapParams({
zeroForOne: true,
amountSpecified: -int256(0.001 ether),
sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
});
PoolSwapTest.TestSettings memory settings =
PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false});
swapRouter.swap{value: 0.001 ether}(keyA, params, settings, ZERO_BYTES);
vm.stopPrank();
vm.roll(block.number + phase1Duration + 1);
vm.startPrank(user);
swapRouter.swap{value: 0.001 ether}(keyA, params, settings, ZERO_BYTES);
vm.stopPrank();
assertEq(hook.currentPhase(), 2, "Pool A should now be in Phase 2");
(keyB,) = initPool(ethCurrency, tokenBCurrency, hook, LPFeeLibrary.DYNAMIC_FEE_FLAG, SQRT_PRICE_1_1);
vm.startPrank(user);
swapRouter.swap{value: 0.001 ether}(keyA, params, settings, ZERO_BYTES);
vm.stopPrank();
assertEq(hook.getCurrentPhase(), 1, "Phase should have been reset to 1 due to Pool B initialization");
assertEq(hook.currentPhase(), 1, "Global currentPhase was overwritten by Pool B initialization");
}
}
[⠊] Compiling...
[⠢] Compiling 2 files with Solc 0.8.26
[⠆] Solc 0.8.26 finished in 58.76ms
No files changed, compilation skipped
Ran 1 test for test/LackOfPoolIsolation.t.sol:LackOfPoolIsolationTest
[PASS] test_PoolBInitializationResetsPoolAState() (gas: 442172)
Traces:
[464872] LackOfPoolIsolationTest::test_PoolBInitializationResetsPoolAState()
├─ [0] VM::deal(0x000000000000000000000000000000000000ABcD, 1000000000000000000 [1e18])
│ └─ ← [Return]
├─ [0] VM::startPrank(0x000000000000000000000000000000000000ABcD)
│ └─ ← [Return]
├─ [158624] PoolSwapTest::swap{value: 1000000000000000}(PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), TestSettings({ takeClaims: false, settleUsingBurn: false }), 0x)
│ ├─ [152820] PoolManager::unlock(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000abcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015cf58144ef33af1e14b5208015d11f9143e27b90000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000004fab6b386539b743f64ea7fd80dcedb0f7a890800000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffc72815b39800000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000)
│ │ ├─ [150561] PoolSwapTest::unlockCallback(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000abcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015cf58144ef33af1e14b5208015d11f9143e27b90000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000004fab6b386539b743f64ea7fd80dcedb0f7a890800000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffc72815b39800000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000)
│ │ │ ├─ [862] PoolManager::exttload(0xc6bd81b73d1fd76b6ab7b2a3e675f602f162633118b2ec0b74d6456669dd8579) [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ ├─ [2552] MockERC20::balanceOf(0x000000000000000000000000000000000000ABcD) [staticcall]
│ │ │ │ └─ ← [Return] 100000000000000000000 [1e20]
│ │ │ ├─ [2552] MockERC20::balanceOf(PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ │ │ └─ ← [Return] 10000000000000000000 [1e19]
│ │ │ ├─ [862] PoolManager::exttload(0x85be7c2bd5cfd9e6e3a30072d5be012f0c0649c579d3433f4d5ee458bdb429be) [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ ├─ [110236] PoolManager::swap(PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), 0x)
│ │ │ │ ├─ [76095] TokenLaunchHook::beforeSwap(PoolSwapTest: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), 0x)
│ │ │ │ │ ├─ [2378] PoolManager::extsload(0x150354b2d6d2bc502e6f774e51ffab4638eb0768330b66d2c8d382c1598ebd1c) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000b4fb077cf724ed8bd3
│ │ │ │ │ └─ ← [Return] 0x575e24b4, 0, 4194304 [4.194e6]
│ │ │ │ ├─ emit Swap(id: 0x6109270a1f35ae82fb136621355ea4a2980baebc2e0a4c5b44084a1faf3b35f9, sender: PoolSwapTest: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], amount0: -1000000000000000 [-1e15], amount1: 999999700464594 [9.999e14], sqrtPriceX96: 79228138782624522581392935400 [7.922e28], liquidity: 3338502497096994491347 [3.338e21], tick: -1, fee: 0)
│ │ │ │ └─ ← [Return] -340282366920938463463374607431768211455000000299535406 [-3.402e53]
│ │ │ ├─ [862] PoolManager::exttload(0xc6bd81b73d1fd76b6ab7b2a3e675f602f162633118b2ec0b74d6456669dd8579) [staticcall]
│ │ │ │ └─ ← [Return] 0xfffffffffffffffffffffffffffffffffffffffffffffffffffc72815b398000
│ │ │ ├─ [552] MockERC20::balanceOf(0x000000000000000000000000000000000000ABcD) [staticcall]
│ │ │ │ └─ ← [Return] 100000000000000000000 [1e20]
│ │ │ ├─ [552] MockERC20::balanceOf(PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ │ │ └─ ← [Return] 10000000000000000000 [1e19]
│ │ │ ├─ [862] PoolManager::exttload(0x85be7c2bd5cfd9e6e3a30072d5be012f0c0649c579d3433f4d5ee458bdb429be) [staticcall]
│ │ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000038d7e92ebf3d2
│ │ │ ├─ [1425] PoolManager::settle{value: 1000000000000000}()
│ │ │ │ └─ ← [Return] 1000000000000000 [1e15]
│ │ │ ├─ [10243] PoolManager::take(MockERC20: [0x15cF58144EF33af1e14b5208015d11F9143E27b9], 0x000000000000000000000000000000000000ABcD, 999999700464594 [9.999e14])
│ │ │ │ ├─ [8511] MockERC20::transfer(0x000000000000000000000000000000000000ABcD, 999999700464594 [9.999e14])
│ │ │ │ │ ├─ emit Transfer(from: PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: 0x000000000000000000000000000000000000ABcD, amount: 999999700464594 [9.999e14])
│ │ │ │ │ └─ ← [Return] true
│ │ │ │ └─ ← [Stop]
│ │ │ └─ ← [Return] 0xfffffffffffffffffffc72815b398000000000000000000000038d7e92ebf3d2
│ │ └─ ← [Return] 0xfffffffffffffffffffc72815b398000000000000000000000038d7e92ebf3d2
│ └─ ← [Return] -340282366920938463463374607431768211455000000299535406 [-3.402e53]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [0] VM::roll(102)
│ └─ ← [Return]
├─ [0] VM::startPrank(0x000000000000000000000000000000000000ABcD)
│ └─ ← [Return]
├─ [66671] PoolSwapTest::swap{value: 1000000000000000}(PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), TestSettings({ takeClaims: false, settleUsingBurn: false }), 0x)
│ ├─ [63367] PoolManager::unlock(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000abcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015cf58144ef33af1e14b5208015d11f9143e27b90000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000004fab6b386539b743f64ea7fd80dcedb0f7a890800000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffc72815b39800000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000)
│ │ ├─ [61108] PoolSwapTest::unlockCallback(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000abcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015cf58144ef33af1e14b5208015d11f9143e27b90000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000004fab6b386539b743f64ea7fd80dcedb0f7a890800000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffc72815b39800000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000)
│ │ │ ├─ [862] PoolManager::exttload(0xc6bd81b73d1fd76b6ab7b2a3e675f602f162633118b2ec0b74d6456669dd8579) [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ ├─ [552] MockERC20::balanceOf(0x000000000000000000000000000000000000ABcD) [staticcall]
│ │ │ │ └─ ← [Return] 100000999999700464594 [1e20]
│ │ │ ├─ [552] MockERC20::balanceOf(PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ │ │ └─ ← [Return] 9999000000299535406 [9.999e18]
│ │ │ ├─ [862] PoolManager::exttload(0x85be7c2bd5cfd9e6e3a30072d5be012f0c0649c579d3433f4d5ee458bdb429be) [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ ├─ [32883] PoolManager::swap(PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), 0x)
│ │ │ │ ├─ [15448] TokenLaunchHook::beforeSwap(PoolSwapTest: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), 0x)
│ │ │ │ │ └─ ← [Return] 0x575e24b4, 0, 4194304 [4.194e6]
│ │ │ │ ├─ emit Swap(id: 0x6109270a1f35ae82fb136621355ea4a2980baebc2e0a4c5b44084a1faf3b35f9, sender: PoolSwapTest: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], amount0: -1000000000000000 [-1e15], amount1: 999999101394141 [9.999e14], sqrtPriceX96: 79228115050998924497711328286 [7.922e28], liquidity: 3338502497096994491347 [3.338e21], tick: -1, fee: 0)
│ │ │ │ └─ ← [Return] -340282366920938463463374607431768211455000000898605859 [-3.402e53]
│ │ │ ├─ [862] PoolManager::exttload(0xc6bd81b73d1fd76b6ab7b2a3e675f602f162633118b2ec0b74d6456669dd8579) [staticcall]
│ │ │ │ └─ ← [Return] 0xfffffffffffffffffffffffffffffffffffffffffffffffffffc72815b398000
│ │ │ ├─ [552] MockERC20::balanceOf(0x000000000000000000000000000000000000ABcD) [staticcall]
│ │ │ │ └─ ← [Return] 100000999999700464594 [1e20]
│ │ │ ├─ [552] MockERC20::balanceOf(PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ │ │ └─ ← [Return] 9999000000299535406 [9.999e18]
│ │ │ ├─ [862] PoolManager::exttload(0x85be7c2bd5cfd9e6e3a30072d5be012f0c0649c579d3433f4d5ee458bdb429be) [staticcall]
│ │ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000038d7e6f36dcdd
│ │ │ ├─ [1425] PoolManager::settle{value: 1000000000000000}()
│ │ │ │ └─ ← [Return] 1000000000000000 [1e15]
│ │ │ ├─ [4643] PoolManager::take(MockERC20: [0x15cF58144EF33af1e14b5208015d11F9143E27b9], 0x000000000000000000000000000000000000ABcD, 999999101394141 [9.999e14])
│ │ │ │ ├─ [2911] MockERC20::transfer(0x000000000000000000000000000000000000ABcD, 999999101394141 [9.999e14])
│ │ │ │ │ ├─ emit Transfer(from: PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: 0x000000000000000000000000000000000000ABcD, amount: 999999101394141 [9.999e14])
│ │ │ │ │ └─ ← [Return] true
│ │ │ │ └─ ← [Stop]
│ │ │ └─ ← [Return] 0xfffffffffffffffffffc72815b398000000000000000000000038d7e6f36dcdd
│ │ └─ ← [Return] 0xfffffffffffffffffffc72815b398000000000000000000000038d7e6f36dcdd
│ └─ ← [Return] -340282366920938463463374607431768211455000000898605859 [-3.402e53]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [247] TokenLaunchHook::currentPhase() [staticcall]
│ └─ ← [Return] 2
├─ [37794] PoolManager::initialize(PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x212224D2F2d262cd093eE13240ca4873fcCBbA3C, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), 79228162514264337593543950336 [7.922e28])
│ ├─ emit Initialize(id: 0x58f806814ff03315f9702c99f6f0afcd0177dead72e3ce6e67d7c22fe0772247, currency0: 0x0000000000000000000000000000000000000000, currency1: MockERC20: [0x212224D2F2d262cd093eE13240ca4873fcCBbA3C], fee: 8388608 [8.388e6], tickSpacing: 60, hooks: TokenLaunchHook: [0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080], sqrtPriceX96: 79228162514264337593543950336 [7.922e28], tick: 0)
│ ├─ [7535] TokenLaunchHook::afterInitialize(LackOfPoolIsolationTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x212224D2F2d262cd093eE13240ca4873fcCBbA3C, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), 79228162514264337593543950336 [7.922e28], 0)
│ │ ├─ [2378] PoolManager::extsload(0x3b03c9799262ef63a1adbbc9f94a575a4ea21cab063913c1f6dcc10cef672126) [staticcall]
│ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ └─ ← [Return] 0x6fe7e6eb
│ └─ ← [Return] 0
├─ [0] VM::startPrank(0x000000000000000000000000000000000000ABcD)
│ └─ ← [Return]
├─ [95524] PoolSwapTest::swap{value: 1000000000000000}(PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), TestSettings({ takeClaims: false, settleUsingBurn: false }), 0x)
│ ├─ [92220] PoolManager::unlock(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000abcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015cf58144ef33af1e14b5208015d11f9143e27b90000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000004fab6b386539b743f64ea7fd80dcedb0f7a890800000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffc72815b39800000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000)
│ │ ├─ [89961] PoolSwapTest::unlockCallback(0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000abcd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015cf58144ef33af1e14b5208015d11f9143e27b90000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000004fab6b386539b743f64ea7fd80dcedb0f7a890800000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffc72815b39800000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000)
│ │ │ ├─ [862] PoolManager::exttload(0xc6bd81b73d1fd76b6ab7b2a3e675f602f162633118b2ec0b74d6456669dd8579) [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ ├─ [552] MockERC20::balanceOf(0x000000000000000000000000000000000000ABcD) [staticcall]
│ │ │ │ └─ ← [Return] 100001999998801858735 [1e20]
│ │ │ ├─ [552] MockERC20::balanceOf(PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ │ │ └─ ← [Return] 9998000001198141265 [9.998e18]
│ │ │ ├─ [862] PoolManager::exttload(0x85be7c2bd5cfd9e6e3a30072d5be012f0c0649c579d3433f4d5ee458bdb429be) [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000000
│ │ │ ├─ [61736] PoolManager::swap(PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), 0x)
│ │ │ │ ├─ [24401] TokenLaunchHook::beforeSwap(PoolSwapTest: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], PoolKey({ currency0: 0x0000000000000000000000000000000000000000, currency1: 0x15cF58144EF33af1e14b5208015d11F9143E27b9, fee: 8388608 [8.388e6], tickSpacing: 60, hooks: 0x4faB6B386539b743f64ea7fD80dcEDb0F7A89080 }), SwapParams({ zeroForOne: true, amountSpecified: -1000000000000000 [-1e15], sqrtPriceLimitX96: 4295128740 [4.295e9] }), 0x)
│ │ │ │ │ ├─ [378] PoolManager::extsload(0x150354b2d6d2bc502e6f774e51ffab4638eb0768330b66d2c8d382c1598ebd1c) [staticcall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000b4fb077cf724ed8bd3
│ │ │ │ │ └─ ← [Return] 0x575e24b4, 0, 4294304 [4.294e6]
│ │ │ │ ├─ emit Swap(id: 0x6109270a1f35ae82fb136621355ea4a2980baebc2e0a4c5b44084a1faf3b35f9, sender: PoolSwapTest: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], amount0: -1000000000000000 [-1e15], amount1: 899998679049934 [8.999e14], sqrtPriceX96: 79228093692548041685680346687 [7.922e28], liquidity: 3338502497096994491347 [3.338e21], tick: -1, fee: 100000 [1e5])
│ │ │ │ └─ ← [Return] -340282366920938463463374607431768211455100001320950066 [-3.402e53]
│ │ │ ├─ [862] PoolManager::exttload(0xc6bd81b73d1fd76b6ab7b2a3e675f602f162633118b2ec0b74d6456669dd8579) [staticcall]
│ │ │ │ └─ ← [Return] 0xfffffffffffffffffffffffffffffffffffffffffffffffffffc72815b398000
│ │ │ ├─ [552] MockERC20::balanceOf(0x000000000000000000000000000000000000ABcD) [staticcall]
│ │ │ │ └─ ← [Return] 100001999998801858735 [1e20]
│ │ │ ├─ [552] MockERC20::balanceOf(PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ │ │ └─ ← [Return] 9998000001198141265 [9.998e18]
│ │ │ ├─ [862] PoolManager::exttload(0x85be7c2bd5cfd9e6e3a30072d5be012f0c0649c579d3433f4d5ee458bdb429be) [staticcall]
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000003328b459026ce
│ │ │ ├─ [1425] PoolManager::settle{value: 1000000000000000}()
│ │ │ │ └─ ← [Return] 1000000000000000 [1e15]
│ │ │ ├─ [4643] PoolManager::take(MockERC20: [0x15cF58144EF33af1e14b5208015d11F9143E27b9], 0x000000000000000000000000000000000000ABcD, 899998679049934 [8.999e14])
│ │ │ │ ├─ [2911] MockERC20::transfer(0x000000000000000000000000000000000000ABcD, 899998679049934 [8.999e14])
│ │ │ │ │ ├─ emit Transfer(from: PoolManager: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: 0x000000000000000000000000000000000000ABcD, amount: 899998679049934 [8.999e14])
│ │ │ │ │ └─ ← [Return] true
│ │ │ │ └─ ← [Stop]
│ │ │ └─ ← [Return] 0xfffffffffffffffffffc72815b39800000000000000000000003328b459026ce
│ │ └─ ← [Return] 0xfffffffffffffffffffc72815b39800000000000000000000003328b459026ce
│ └─ ← [Return] -340282366920938463463374607431768211455100001320950066 [-3.402e53]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [862] TokenLaunchHook::getCurrentPhase() [staticcall]
│ └─ ← [Return] 1
├─ [247] TokenLaunchHook::currentPhase() [staticcall]
│ └─ ← [Return] 1
└─ ← [Return]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 565.42ms (1.15ms CPU time)
Ran 1 test suite in 584.14ms (565.42ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
- uint256 public currentPhase;
- uint256 public lastPhaseUpdateBlock;
- uint256 public launchStartBlock;
- uint256 public initialLiquidity;
- mapping(address => uint256) public addressSwappedAmount;
- mapping(address => uint256) public addressLastSwapBlock;
- mapping(address => uint256) public addressTotalSwaps;
- mapping(address => uint256) public addressPenaltyCount;
+ using PoolIdLibrary for PoolKey;
+
+ struct PoolState {
+ uint256 currentPhase;
+ uint256 lastPhaseUpdateBlock;
+ uint256 launchStartBlock;
+ uint256 initialLiquidity;
+ // NOTE: per-user tracking must also be pool-scoped
+ mapping(address => uint256) swappedAmount;
+ mapping(address => uint256) lastSwapBlock;
+ mapping(address => uint256) totalSwaps;
+ mapping(address => uint256) penaltyCount;
+ }
+
+ mapping(PoolId => PoolState) internal poolState;
- function _afterInitialize(address, PoolKey calldata key, uint160, int24) internal override returns (bytes4) {
- launchStartBlock = block.number;
- uint128 liquidity = StateLibrary.getLiquidity(poolManager, key.toId());
- initialLiquidity = uint256(liquidity);
- currentPhase = 1;
- lastPhaseUpdateBlock = block.number;
- return BaseHook.afterInitialize.selector;
- }
+ function _afterInitialize(address, PoolKey calldata key, uint160, int24) internal override returns (bytes4) {
+ PoolState storage S = poolState[key.toId()];
+ S.launchStartBlock = block.number;
+ uint128 liq = StateLibrary.getLiquidity(poolManager, key.toId());
+ S.initialLiquidity = uint256(liq);
+ S.currentPhase = 1;
+ S.lastPhaseUpdateBlock = block.number;
+ return BaseHook.afterInitialize.selector;
+ }
- if (launchStartBlock == 0) revert PoolNotInitialized();
+ PoolState storage S = poolState[key.toId()];
+ if (S.launchStartBlock == 0) revert PoolNotInitialized();
- uint256 blocksSinceLaunch = block.number - launchStartBlock;
+ uint256 blocksSinceLaunch = block.number - S.launchStartBlock;
- if (newPhase != currentPhase) { ... currentPhase = newPhase; lastPhaseUpdateBlock = block.number; }
+ if (newPhase != S.currentPhase) { /* reset per-address for THIS pool only */ S.currentPhase = newPhase; S.lastPhaseUpdateBlock = block.number; }
- uint256 maxSwapAmount = (initialLiquidity * phaseLimitBps) / 10000;
+ uint256 maxSwapAmount = (S.initialLiquidity * phaseLimitBps) / 10000;
- if (!applyPenalty && addressSwappedAmount[sender] + swapAmount > maxSwapAmount) { applyPenalty = true; }
- addressSwappedAmount[sender] += swapAmount;
- addressLastSwapBlock[sender] = block.number;
+ if (!applyPenalty && S.swappedAmount[sender] + swapAmount > maxSwapAmount) { applyPenalty = true; }
+ S.swappedAmount[sender] += swapAmount;
+ S.lastSwapBlock[sender] = block.number;