Skip to main content

pool-finder

Use when discovering Uniswap pools, querying pool state, analyzing TVL/volume, finding optimal swap routes, or inspecting pool parameters. Covers V3 Factory queries, V4 PoolManager state, subgraph queries, and cast/forge inspection commands.

설치로 이동

소스 정보

저장소
ccashwell/evm-cortex
최근 소스 활동
2026년 4월 10일 16:31
감지된 SKILL.md 언어
영어
스타
131
포크
18

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
pool-finder
description
Use when discovering Uniswap pools, querying pool state, analyzing TVL/volume, finding optimal swap routes, or inspecting pool parameters. Covers V3 Factory queries, V4 PoolManager state, subgraph queries, and cast/forge inspection commands.
# Uniswap Pool Discovery & Analysis ## Key Addresses (Ethereum Mainnet) | Contract | Address | |----------|---------| | V3 Factory | `0x1F98431c8aD98523631AE4a59f267346ea31F984` | | V3 QuoterV2 | `0x61fFE014bA17989E743c5F6cB21bF9697530B21e` | | V3 SwapRouter02 | `0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45` | | V3 NonfungiblePositionManager | `0xC36442b4a4522E871399CD717aBDD847Ab11FE88` | | V4 PoolManager | `0x000000000004444c5dc75cb358380d2e3de08a90` | ## V3 Pool Discovery ### Factory Query ```solidity import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; IUniswapV3Factory factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); // Fee tiers and their tick spacings: // 100 (0.01%) — tickSpacing 1 — stablecoin pairs // 500 (0.05%) — tickSpacing 10 — correlated pairs, high volume // 3000 (0.30%) — tickSpacing 60 — standard pairs // 10000 (1.00%) — tickSpacing 200 — exotic / low-volume pairs address pool = factory.getPool(tokenA, tokenB, fee); ``` ### Using cast ```bash # Find ETH/USDC 0.3% pool on Ethereum mainnet cast call 0x1F98431c8aD98523631AE4a59f267346ea31F984 \ "getPool(address,address,uint24)(address)" \ 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \ 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \ 3000 # Read pool slot0 — returns (sqrtPriceX96, tick, observationIndex, observationCardinality, # observationCardinalityNext, feeProtocol, unlocked) cast call <pool_address> "slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)" # Active liquidity in the current tick range cast call <pool_address> "liquidity()(uint128)" # Pool configuration cast call <pool_address> "fee()(uint24)" cast call <pool_address> "tickSpacing()(int24)" cast call <pool_address> "token0()(address)" cast call <pool_address> "token1()(address)" # Oracle observation buffer size cast call <pool_address> "slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)" | \ awk '{print "observationCardinality:", $4}' ``` ### Scanning All Fee Tiers ```bash FACTORY=0x1F98431c8aD98523631AE4a59f267346ea31F984 TOKEN_A=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 # WETH TOKEN_B=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 # USDC for FEE in 100 500 3000 10000; do POOL=$(cast call $FACTORY "getPool(address,address,uint24)(address)" $TOKEN_A $TOKEN_B $FEE) if [ "$POOL" != "0x0000000000000000000000000000000000000000" ]; then LIQ=$(cast call $POOL "liquidity()(uint128)") echo "Fee: $FEE Pool: $POOL Liquidity: $LIQ" fi done ``` ## V3 High-Volume Pools (Ethereum Mainnet) ``` ETH/USDC 0.05%: 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 ETH/USDC 0.30%: 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8 ETH/USDT 0.05%: 0x11b815efB8f581194ae79006d24E0d814B7697F6 ETH/USDT 0.30%: 0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36 WBTC/ETH 0.30%: 0xCBCdF9626bC03E24f779434178A73a0B4bad62eD WBTC/ETH 0.05%: 0x4585FE77225b41b697C938B018E2Ac67Ac5a20c0 USDC/USDT 0.01%: 0x3416cF6C708Da44DB2624D63ea0AAef7113527C6 DAI/USDC 0.01%: 0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168 DAI/USDC 0.05%: 0x6c6Bc977E13Df9b0de53b251522280BB72383700 ``` ## V4 Pool Discovery V4 pools do not have individual addresses. All pool state lives inside the singleton PoolManager. A pool is identified by its `PoolKey`, hashed into a `PoolId`. ### PoolKey Construction ```solidity import {PoolKey} from "v4-core/src/types/PoolKey.sol"; import {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol"; import {Currency} from "v4-core/src/types/Currency.sol"; import {IHooks} from "v4-core/src/interfaces/IHooks.sol"; using PoolIdLibrary for PoolKey; // currency0 MUST be numerically less than currency1 PoolKey memory key = PoolKey({ currency0: Currency.wrap(token0), currency1: Currency.wrap(token1), fee: 3000, // or LPFeeLibrary.DYNAMIC_FEE_FLAG for hook-managed fees tickSpacing: 60, hooks: IHooks(hookAddress) // address(0) for vanilla pool }); PoolId id = key.toId(); // keccak256(abi.encode(key)) ``` ### Currency Ordering V4 enforces `currency0 < currency1`. Native ETH is represented as `Currency.wrap(address(0))`, which sorts below any ERC-20 address. For two ERC-20 tokens, the one with the numerically smaller address is `currency0`. ```solidity function orderCurrencies(address a, address b) pure returns (Currency c0, Currency c1) { (c0, c1) = a < b ? (Currency.wrap(a), Currency.wrap(b)) : (Currency.wrap(b), Currency.wrap(a)); } ``` ### Reading V4 Pool State with cast ```bash POOL_MANAGER=0x000000000004444c5dc75cb358380d2e3de08a90 # Get slot0: (sqrtPriceX96, tick, protocolFee, lpFee) cast call $POOL_MANAGER \ "getSlot0(bytes32)(uint160,int24,uint24,uint24)" \ <pool_id> # Get active liquidity cast call $POOL_MANAGER \ "getLiquidity(bytes32)(uint128)" \ <pool_id> # Get liquidity at a specific tick cast call $POOL_MANAGER \ "getTickLiquidity(bytes32,int24)(uint128,int128)" \ <pool_id> <tick> # Get tick bitmap word cast call $POOL_MANAGER \ "getTickBitmap(bytes32,int16)(uint256)" \ <pool_id> <word_position> ``` ### Computing a PoolId Offchain ```bash # Encode the PoolKey struct and hash it cast keccak $(cast abi-encode \ "(address,address,uint24,int24,address)" \ <currency0> <currency1> <fee> <tickSpacing> <hooks>) ``` ## Subgraph Queries ### V3 Subgraph Endpoints | Network | Subgraph ID | |---------|-------------| | Ethereum | `5zvR82QoaXYFyDEKLZ9t6v9adgnptxYpKpSbxtgVENFV` | | Base | `43Hwfi3dJSoGpyas9VwNoDAv55yjgGrPpNSmbQZArzMG` | | Arbitrum | `FbCGRftH4a3yZugY7TnbYgPJVEv2LvMT6oF1fxPe9aJM` | | Optimism | `Cghf4LfVqPiFw6fp6Y5X5Ubc8UpmUhSfJL82zwiBFLaj` | | Polygon | `3hCPRGf4z88VC5rsBKU5AA9FBBq5nF3jbKJG7VZCbhjm` | Endpoint format: `https://gateway.thegraph.com/api/[api-key]/subgraphs/id/<subgraph-id>` ### Top Pools by TVL ```graphql { pools(first: 10, orderBy: totalValueLockedUSD, orderDirection: desc) { id token0 { symbol decimals id } token1 { symbol decimals id } feeTier liquidity sqrtPrice tick totalValueLockedUSD volumeUSD txCount } } ``` ### Find Pools for a Specific Token ```graphql { pools( where: { or: [ { token0: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } { token1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } ] } orderBy: totalValueLockedUSD orderDirection: desc first: 5 ) { id token0 { symbol } token1 { symbol } feeTier totalValueLockedUSD volumeUSD } } ``` ### Tick-Level Liquidity Distribution ```graphql { ticks( where: { pool: "<pool_id>" } first: 1000 orderBy: tickIdx ) { tickIdx liquidityNet liquidityGross } } ``` ### Active Positions ```graphql { positions( where: { pool: "<pool_id>", liquidity_gt: "0" } first: 100 orderBy: liquidity orderDirection: desc ) { id owner tickLower { tickIdx } tickUpper { tickIdx } liquidity depositedToken0 depositedToken1 collectedFeesToken0 collectedFeesToken1 } } ``` ### Historical Volume (Daily Snapshots) ```graphql { poolDayDatas( where: { pool: "<pool_id>" } orderBy: date orderDirection: desc first: 30 ) { date volumeUSD tvlUSD feesUSD liquidity sqrtPrice tick open high low close } } ``` ## Optimal Route Finding ### Comparing Fee Tiers Always query all fee tiers for a pair. The deepest liquidity at the current tick determines the best execution: ```solidity import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol"; IQuoterV2 quoter = IQuoterV2(0x61fFE014bA17989E743c5F6cB21bF9697530B21e); uint24[4] memory fees = [uint24(100), 500, 3000, 10000]; uint256 bestOut; uint24 bestFee; for (uint256 i = 0; i < fees.length; i++) { try quoter.quoteExactInputSingle( IQuoterV2.QuoteExactInputSingleParams({ tokenIn: WETH, tokenOut: USDC, amountIn: 1 ether, fee: fees[i], sqrtPriceLimitX96: 0 }) ) returns (uint256 amountOut, uint160, uint32, uint256) { if (amountOut > bestOut) { bestOut = amountOut; bestFee = fees[i]; } } catch {} } ``` ### Multi-Hop Routing Direct swaps are not always optimal. Common intermediate hops: ``` ETH → WBTC may route as ETH → USDC → WBTC LINK → UNI may route as LINK → ETH → UNI Low-cap → Low-cap almost always routes through ETH or USDC ``` ```solidity // Multi-hop quote: tokenA -> WETH -> tokenB bytes memory path = abi.encodePacked( tokenA, uint24(3000), WETH, uint24(500), tokenB ); (uint256 amountOut, uint160[] memory sqrtPriceX96AfterList, uint32[] memory initializedTicksCrossedList, uint256 gasEstimate) = quoter.quoteExactInput(path, amountIn); ``` ### Price Impact Estimation ```solidity // sqrtPriceX96After from the quote tells you where price lands // Compare to current sqrtPriceX96 to estimate impact (uint160 sqrtPriceCurrent,,,,,,) = pool.slot0(); uint256 priceBefore = uint256(sqrtPriceCurrent) * uint256(sqrtPriceCurrent) / (1 << 192); uint256 priceAfter = uint256(sqrtPriceAfter) * uint256(sqrtPriceAfter) / (1 << 192); uint256 impactBps = (priceBefore - priceAfter) * 10_000 / priceBefore; ``` ## Pool Health Indicators | Metric | What It Means | Warning Sign | |--------|--------------|--------------| | TVL trend | Total value committed by LPs | Declining over 7+ days = liquidity flight | | Volume / TVL ratio | Fee generation efficiency | < 0.01 daily = stagnant pool | | Active liquidity concentration | How tightly LPs bracket current price | Wide spread = high slippage for traders | | Active position count | Number of LPs with in-range liquidity | < 5 positions = fragile liquidity | | Fee revenue vs IL | LP profitability | Negative = LPs losing money, expect exits | | Oracle cardinality | V3 observation buffer size (slot0 field 4) | Default 1 = no TWAP history | | Tick crossing frequency | How often price moves through tick boundaries | Very high = volatile, may deter LPs | ### Reading Oracle Cardinality ```bash # observationCardinalityNext is the 5th return value of slot0 cast call <pool_address> "slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)" # Increase cardinality for better TWAP support (anyone can call, pays gas) cast send <pool_address> "increaseObservationCardinalityNext(uint16)" 100 ``` ## Pool Creation ### V3 Pool Creation ```solidity IUniswapV3Factory factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); address pool = factory.createPool(tokenA, tokenB, fee); // Initialize with starting price (sqrtPriceX96 format) // For 1 token0 = 2000 token1 (e.g., 1 ETH = 2000 USDC with 18/6 decimals): // sqrtPriceX96 = sqrt(2000 * 1e6 / 1e18) * 2^96 uint160 sqrtPriceX96 = 3543191142285914205922034323215; // example IUniswapV3Pool(pool).initialize(sqrtPriceX96); ``` ### V4 Pool Initialization ```solidity import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; IPoolManager poolManager = IPoolManager(0x000000000004444c5dc75cb358380d2e3de08a90); PoolKey memory key = PoolKey({ currency0: Currency.wrap(token0), currency1: Currency.wrap(token1), fee: 3000, tickSpacing: 60, hooks: IHooks(address(0)) }); poolManager.initialize(key, sqrtPriceX96); ``` ### sqrtPriceX96 Calculator ```solidity /// @notice Computes sqrtPriceX96 from a human-readable price ratio /// @param price The price of token0 in terms of token1 (e.g., 2000 for 1 ETH = 2000 USDC) /// @param decimals0 Decimals of token0 /// @param decimals1 Decimals of token1 function computeSqrtPriceX96( uint256 price, uint8 decimals0, uint8 decimals1 ) pure returns (uint160) { // adjustedPrice = price * 10^decimals0 / 10^decimals1 // sqrtPriceX96 = sqrt(adjustedPrice) * 2^96 uint256 adjustedPrice = price * (10 ** decimals1) / (10 ** decimals0); uint256 sqrtPrice = Math.sqrt(adjustedPrice); return uint160(sqrtPrice << 96); } ``` ## Forge Scripts for Pool Analysis ### Query All Fee Tiers for a Token Pair ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {Script, console2} from "forge-std/Script.sol"; import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; contract PoolScanner is Script { IUniswapV3Factory constant FACTORY = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); function run(address tokenA, address tokenB) external view { uint24[4] memory fees = [uint24(100), 500, 3000, 10000]; string[4] memory labels = ["0.01%", "0.05%", "0.30%", "1.00%"]; for (uint256 i = 0; i < fees.length; i++) { address pool = FACTORY.getPool(tokenA, tokenB, fees[i]); if (pool != address(0)) { (uint160 sqrtPriceX96, int24 tick,,,,,) = IUniswapV3Pool(pool).slot0(); uint128 liquidity = IUniswapV3Pool(pool).liquidity();
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기