// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import {console} from "forge-std/console.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; import {ModifyLiquidityParams} from "@uniswap/v4-core/src/types/PoolOperation.sol"; import {PoolModifyLiquidityTest} from "@uniswap/v4-core/src/test/PoolModifyLiquidityTest.sol"; import {LiquidityAmounts} from "v4-periphery/src/libraries/LiquidityAmounts.sol"; import {MockToken} from "../src/mocks/MockToken.sol"; import {PriceMath} from "../src/libraries/PriceMath.sol"; import {SyncPools} from "./SyncPools.s.sol"; /// @notice Deepens every spot pool: full-range liquidity worth ADD_USDC per side at the current pool price. /// MockTokens, minted to the broadcaster. Env: ADD_USDC (whole USDC, default 25_000_000), SPOT_CONFIG. contract AddLiquidity is SyncPools { using StateLibrary for IPoolManager; using PoolIdLibrary for PoolKey; function run() external override { string memory cfg = vm.readFile(vm.envOr("SPOT_CONFIG", string("../backend/config/spot.json"))); IPoolManager pm = IPoolManager(vm.parseJsonAddress(cfg, ".poolManager")); PoolModifyLiquidityTest router = PoolModifyLiquidityTest(vm.parseJsonAddress(cfg, ".liquidityRouter")); uint256 usdc = vm.envOr("ADD_USDC", uint256(25_000_000)) * 1e6; string[3] memory syms = ["BTC", "ETH", "HYPE"]; vm.startBroadcast(); (, address me,) = vm.readCallers(); for (uint256 i; i < syms.length; i++) { (PoolKey memory key, address base) = poolKey(cfg, syms[i]); uint128 before = pm.getLiquidity(key.toId()); add(pm, router, key, base, usdc, me); console.log(syms[i], "L before / after:", before, pm.getLiquidity(key.toId())); } vm.stopBroadcast(); } function add( IPoolManager pm, PoolModifyLiquidityTest router, PoolKey memory key, address base, uint256 usdc, address me ) internal { bool base0 = Currency.unwrap(key.currency0) == base; (uint160 sqrtP,,,) = pm.getSlot0(key.toId()); uint8 dec = MockToken(base).decimals(); uint256 baseAmt = usdc * 10 ** dec / PriceMath.fromSqrtPriceX96(sqrtP, dec, base0); (uint256 a0, uint256 a1) = base0 ? (baseAmt, usdc) : (usdc, baseAmt); int24 lo = TickMath.minUsableTick(key.tickSpacing); int24 hi = TickMath.maxUsableTick(key.tickSpacing); uint128 liq = LiquidityAmounts.getLiquidityForAmounts( sqrtP, TickMath.getSqrtPriceAtTick(lo), TickMath.getSqrtPriceAtTick(hi), a0, a1 ); MockToken(Currency.unwrap(key.currency0)).mint(me, a0 + 1); // +1 wei: core rounds up MockToken(Currency.unwrap(key.currency1)).mint(me, a1 + 1); MockToken(Currency.unwrap(key.currency0)).approve(address(router), type(uint256).max); MockToken(Currency.unwrap(key.currency1)).approve(address(router), type(uint256).max); router.modifyLiquidity( key, ModifyLiquidityParams({tickLower: lo, tickUpper: hi, liquidityDelta: int256(uint256(liq)), salt: 0}), abi.encode(me) // MaasHook: trusted router credits hookData LP (must be tx.origin) ); } }