Notebook To Strategy
tradingstrategy-ai/trade-executor
Transfer code from a backtesting Jupyter notebook to a Trade Executor strategy module
A skill your agent uses when discovering Uniswap pools, querying pool state, analyzing TVL/volume, finding optimal swap routes, or inspecting pool parameters.
$ npx skills add ccashwell/evm-cortex --skill pool-finder -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install ccashwell/evm-cortex pool-finder --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/ccashwell/evm-cortex.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/pool-finder .claude/skills/pool-finder && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "pool-finder" agent skill from https://github.com/ccashwell/evm-cortex/tree/main/skills/pool-finder into .claude/skills/pool-finder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pool-finder", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/ccashwell/evm-cortex/tree/main/skills/pool-finderType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add ccashwell/evm-cortex --skill pool-finder -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install ccashwell/evm-cortex pool-finder --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ccashwell/evm-cortex.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/pool-finder .agents/skills/pool-finder && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "pool-finder" agent skill from https://github.com/ccashwell/evm-cortex/tree/main/skills/pool-finder into .agents/skills/pool-finder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pool-finder", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ccashwell/evm-cortex --skill pool-finder -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install ccashwell/evm-cortex pool-finder --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ccashwell/evm-cortex.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/pool-finder .cursor/skills/pool-finder && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "pool-finder" agent skill from https://github.com/ccashwell/evm-cortex/tree/main/skills/pool-finder into .cursor/skills/pool-finder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pool-finder", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/ccashwell/evm-cortex.git --path skills/pool-finder--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add ccashwell/evm-cortex --skill pool-finder -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install ccashwell/evm-cortex pool-finder --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ccashwell/evm-cortex.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/pool-finder .gemini/skills/pool-finder && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "pool-finder" agent skill from https://github.com/ccashwell/evm-cortex/tree/main/skills/pool-finder into .gemini/skills/pool-finder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pool-finder", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install ccashwell/evm-cortex pool-finderInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add ccashwell/evm-cortex --skill pool-finder -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/ccashwell/evm-cortex.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/pool-finder .github/skills/pool-finder && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "pool-finder" agent skill from https://github.com/ccashwell/evm-cortex/tree/main/skills/pool-finder into .github/skills/pool-finder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pool-finder", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ccashwell/evm-cortex --skill pool-finder -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install ccashwell/evm-cortex pool-finder --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ccashwell/evm-cortex.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/pool-finder .opencode/skills/pool-finder && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "pool-finder" agent skill from https://github.com/ccashwell/evm-cortex/tree/main/skills/pool-finder into .opencode/skills/pool-finder/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pool-finder", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
pool-finderA skill your agent uses when discovering Uniswap pools, querying pool state, analyzing TVL/volume, finding optimal swap routes, or inspecting pool parameters.
Pool Finder is an agent skill from ccashwell/evm-cortex. 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.
Its SKILL.md is about 5.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Business, Finance & HR. It works with Uniswap and Ethereum. The repository describes itself as: Ethereum protocol engineering squad for AI coding assistants. The licence is MIT.
Read from SKILL.md and the folder at commit f8f3301. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are solidity, bash and graphql).
From the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
gateway.thegraph.comdevelopers.uniswap.orgFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Pool Finder loads about 5.5k tokens when it runs. Until then it costs about 63 tokens; SKILL.md has 581 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from ccashwell/evm-cortex at commit f8f3301, republished under its MIT licence (© ccashwell). 581 words, ~5,464 tokens.
.claude/skills/pool-finder/SKILL.md (or your agent's skills folder).| Contract | Address |
|---|---|
| V3 Factory | 0x1F98431c8aD98523631AE4a59f267346ea31F984 |
| V3 QuoterV2 | 0x61fFE014bA17989E743c5F6cB21bF9697530B21e |
| V3 SwapRouter02 | 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 |
| V3 NonfungiblePositionManager | 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 |
| V4 PoolManager | 0x000000000004444c5dc75cB358380D2e3dE08A90 |
| V4 StateView | 0x7fFE42C4a5DEeA5b0feC41C94C136Cf115597227 |
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);# 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}'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
doneETH/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%: 0x6c6Bc977E13Df9b0de53b251522280BB72383700V4 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.
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))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.
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));
}# Read through the StateView lens, NOT the PoolManager. getSlot0/getLiquidity/... are
# StateLibrary *internal* helpers built on extsload; the PoolManager exposes no such ABI
# and `cast call $POOL_MANAGER "getSlot0(bytes32)..."` reverts. Ethereum address below;
# per-chain StateView addresses: https://developers.uniswap.org/docs/protocols/v4/deployments
STATE_VIEW=0x7fFE42C4a5DEeA5b0feC41C94C136Cf115597227
# Get slot0: (sqrtPriceX96, tick, protocolFee, lpFee)
cast call $STATE_VIEW \
"getSlot0(bytes32)(uint160,int24,uint24,uint24)" \
<pool_id>
# Get active liquidity
cast call $STATE_VIEW \
"getLiquidity(bytes32)(uint128)" \
<pool_id>
# Get liquidity at a specific tick
cast call $STATE_VIEW \
"getTickLiquidity(bytes32,int24)(uint128,int128)" \
<pool_id> <tick>
# Get tick bitmap word
cast call $STATE_VIEW \
"getTickBitmap(bytes32,int16)(uint256)" \
<pool_id> <word_position>
# IStateView also exposes getTickInfo, getFeeGrowthGlobals, getPositionInfo, getFeeGrowthInside# abi.encode the PoolKey fields (cast needs a function-style signature) and hash them
cast keccak $(cast abi-encode \
"f(address,address,uint24,int24,address)" \
<currency0> <currency1> <fee> <tickSpacing> <hooks>)
# Example — mainnet native ETH/USDC 0.05% pool (currency0 = address(0), fee 500, tickSpacing 10, no hook):
cast keccak $(cast abi-encode "f(address,address,uint24,int24,address)" \
0x0000000000000000000000000000000000000000 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 500 10 \
0x0000000000000000000000000000000000000000)
# → 0x21c67e77068de97969ba93d4aab21826d33ca12bb9f565d8496e8fda8a82ca27| 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>
{
pools(first: 10, orderBy: totalValueLockedUSD, orderDirection: desc) {
id
token0 { symbol decimals id }
token1 { symbol decimals id }
feeTier
liquidity
sqrtPrice
tick
totalValueLockedUSD
volumeUSD
txCount
}
}{
pools(
where: {
or: [
{ token0: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" }
{ token1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" }
]
}
orderBy: totalValueLockedUSD
orderDirection: desc
first: 5
) {
id
token0 { symbol }
token1 { symbol }
feeTier
totalValueLockedUSD
volumeUSD
}
}{
ticks(
where: { pool: "<pool_id>" }
first: 1000
orderBy: tickIdx
) {
tickIdx
liquidityNet
liquidityGross
}
}{
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
}
}{
poolDayDatas(
where: { pool: "<pool_id>" }
orderBy: date
orderDirection: desc
first: 30
) {
date
volumeUSD
tvlUSD
feesUSD
liquidity
sqrtPrice
tick
open
high
low
close
}
}Always query all fee tiers for a pair. The deepest liquidity at the current tick determines the best execution:
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 {}
}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// 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);// 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;| 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 |
| Protocol fee share | Portion of swap fees diverted from LPs (V3 slot0.feeProtocol, V4 slot0.protocolFee) | Nonzero on Ethereum mainnet: V3 68/102 (1/4 or 1/6 of LP fees) since 2025-12-28, V4 static-fee pools since 2026-07-27 — subtract from LP fee-revenue estimates |
Decode V3 feeProtocol as token0 = feeProtocol % 16, token1 = feeProtocol >> 4, protocol share = 1/N (68 → 1/4 on the 0.01%/0.05% tiers, 102 → 1/6 on the 0.30% tier). V4 protocolFee packs two pips fees — lower 12 bits zeroForOne, upper 12 bits oneForZero — charged on swap input before the LP fee (mainnet ETH/USDC 500/10 reads 512125, i.e. 125 pips each way).
# 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)" 100IUniswapV3Factory 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 = 3543191142285914205922034; // sqrt(2e-9) * 2^96 ≈ 3.54e24 (with equal decimals it would be sqrt(2000) * 2^96 ≈ 3.54e30)
IUniswapV3Pool(pool).initialize(sqrtPriceX96);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);import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {FullMath} from "v4-core/src/libraries/FullMath.sol";
/// @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) {
// rawPrice = price * 10^decimals1 / 10^decimals0 (raw token1 units per raw token0 unit)
// sqrtPriceX96 = sqrt(rawPrice) * 2^96 = sqrt(rawPrice * 2^192)
// Scale by 2^192 BEFORE dividing so a sub-1 rawPrice (2000e6 / 1e18 = 2e-9) does not truncate to 0;
// FullMath.mulDiv keeps the 512-bit intermediate product from overflowing.
uint256 priceX192 = FullMath.mulDiv(price * (10 ** decimals1), 1 << 192, 10 ** decimals0);
return uint160(Math.sqrt(priceX192));
}// 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();
console2.log("--- Fee Tier:", labels[i], "---");
console2.log(" Pool:", pool);
console2.log(" Tick:", tick);
console2.log(" Liquidity:", liquidity);
}
}
}
}Run with: forge script script/PoolScanner.s.sol --sig "run(address,address)" <tokenA> <tokenB> --fork-url $ETH_RPC_URL
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Script, console2} from "forge-std/Script.sol";
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
contract TickInspector is Script {
function run(address pool, int24 tickLower, int24 tickUpper) external view {
IUniswapV3Pool p = IUniswapV3Pool(pool);
int24 spacing = p.tickSpacing();
(,int24 currentTick,,,,,) = p.slot0();
console2.log("Current tick:", currentTick);
console2.log("Tick spacing:", spacing);
int24 tick = tickLower - (tickLower % spacing);
while (tick <= tickUpper) {
(uint128 liquidityGross, int128 liquidityNet,,,,,,) = p.ticks(tick);
if (liquidityGross > 0) {
console2.log("Tick:", tick);
console2.log(" liquidityGross:", liquidityGross);
console2.log(" liquidityNet:", liquidityNet);
}
tick += spacing;
}
}
}Run with: forge script script/TickInspector.s.sol --sig "run(address,int24,int24)" <pool> <lower> <upper> --fork-url $ETH_RPC_URL
// 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";
import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol";
contract DepthComparator is Script {
IUniswapV3Factory constant FACTORY = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
IQuoterV2 constant QUOTER = IQuoterV2(0x61fFE014bA17989E743c5F6cB21bF9697530B21e);
function run(address tokenIn, address tokenOut, uint256 amountIn) external {
uint24[4] memory fees = [uint24(100), 500, 3000, 10000];
for (uint256 i = 0; i < fees.length; i++) {
address pool = FACTORY.getPool(tokenIn, tokenOut, fees[i]);
if (pool == address(0)) continue;
try QUOTER.quoteExactInputSingle(
IQuoterV2.QuoteExactInputSingleParams({
tokenIn: tokenIn,
tokenOut: tokenOut,
amountIn: amountIn,
fee: fees[i],
sqrtPriceLimitX96: 0
})
) returns (uint256 amountOut, uint160, uint32 ticksCrossed, uint256 gasEst) {
console2.log("Fee:", fees[i]);
console2.log(" amountOut:", amountOut);
console2.log(" ticksCrossed:", ticksCrossed);
console2.log(" gasEstimate:", gasEst);
} catch {}
}
}
}Run with: forge script script/DepthComparator.s.sol --sig "run(address,address,uint256)" <tokenIn> <tokenOut> <amount> --fork-url $ETH_RPC_URL
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Script, console2} from "forge-std/Script.sol";
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import {TickMath} from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
contract ArbDetector is Script {
IUniswapV3Factory constant FACTORY = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
function run(address tokenA, address tokenB) external view {
uint24[4] memory fees = [uint24(100), 500, 3000, 10000];
int24[] memory ticks = new int24[](4);
uint160[] memory prices = new uint160[](4);
uint256 count;
for (uint256 i = 0; i < fees.length; i++) {
address pool = FACTORY.getPool(tokenA, tokenB, fees[i]);
if (pool == address(0)) continue;
(uint160 sqrtPriceX96, int24 tick,,,,,) = IUniswapV3Pool(pool).slot0();
ticks[count] = tick;
prices[count] = sqrtPriceX96;
count++;
console2.log("Fee:", fees[i], "Tick:", tick);
}
if (count >= 2) {
int24 maxDiff;
for (uint256 i = 0; i < count; i++) {
for (uint256 j = i + 1; j < count; j++) {
int24 diff = ticks[i] > ticks[j] ? ticks[i] - ticks[j] : ticks[j] - ticks[i];
if (diff > maxDiff) maxDiff = diff;
}
}
console2.log("Max tick divergence:", maxDiff);
if (maxDiff > 10) {
console2.log(">> Potential cross-fee-tier arbitrage opportunity");
}
}
}
}WETH: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
USDC: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
USDT: 0xdAC17F958D2ee523a2206206994597C13D831ec7
DAI: 0x6B175474E89094C44Da98b954EedeAC495271d0F
WBTC: 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599
UNI: 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984
LINK: 0x514910771AF9Ca656af840dff83E8264EcF986CA
wstETH:0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0token0/token1 ordering matches expectationsliquidity is nonzero (pool has active LPs in range)slot0 price directlyPoolId correctly from PoolKey with proper currency orderingcast code--fork-url) for all onchain queries in scripts© ccashwell, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/pool-finder of ccashwell/evm-cortex.
Open the folder on GitHubat commit f8f3301
Pool Finder next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Pool Finder this skillccashwell/evm-cortex | 131 | — | ~5.5k | Automated safety check: Pass | MIT | |
| Notebook To Strategytradingstrategy-ai/trade-executor | 160 | — | ~1.1k | Automated safety check: Pass | Custom licence | |
| Building Blocksaustintgriffith/ethskills | 294 | — | ~2.9k | Automated safety check: Pass | None | |
| Clankeralsk1992/CloddsBot | 2.9k | — | ~782 | Automated safety check: Pass | MIT | |
| Dexalsk1992/CloddsBot | 2.9k | — | ~760 | Automated safety check: Pass | MIT | |
| Addressesaustintgriffith/ethskills | 294 | — | ~7.1k | Automated safety check: Pass | None |
tradingstrategy-ai/trade-executor
Transfer code from a backtesting Jupyter notebook to a Trade Executor strategy module
austintgriffith/ethskills
DeFi legos and protocol composability on Ethereum and L2s. An agent skill from austintgriffith/ethskills.
alsk1992/CloddsBot
Deploy ERC20 tokens with Uniswap V4 pools on Base, Ethereum, Arbitrum
alsk1992/CloddsBot
Cross-chain DEX market intelligence - trending tokens, gainers, losers, volume, stats across all chains and protocols
austintgriffith/ethskills
Verified contract addresses for major Ethereum protocols across mainnet and L2s.
LeoYeAI/openclaw-master-skills
Buy or sell XAUT (Tether Gold) on Ethereum. An agent skill from LeoYeAI/openclaw-master-skills.
ccashwell/evm-cortex
A skill your agent uses when preparing for a security audit, performing reconnaissance on a new codebase, or creating a protocol overview.
ccashwell/evm-cortex
A skill your agent uses when integrating with Aave V3 for lending, borrowing, flash loans, or building on top of Aave markets.
ccashwell/evm-cortex
Access control design patterns for Solidity protocols. An agent skill from ccashwell/evm-cortex.
ccashwell/evm-cortex
A skill your agent uses when running a local Ethereum node with Anvil.
ccashwell/evm-cortex
A skill your agent uses when performing systematic breadth-first review of all contracts during a security audit.
ccashwell/evm-cortex
A skill your agent uses when performing deep analysis of specific findings or high-risk areas during a security audit.
Categories
A skill your agent uses when discovering Uniswap pools, querying pool state, analyzing TVL/volume, finding optimal swap routes, or inspecting pool parameters. Pool Finder is an agent skill from ccashwell/evm-cortex. Use when discovering Uniswap pools, querying pool state, analyzing TVL/volume, finding optimal swap routes, or inspecting pool parameters.
Pool Finder fits situations like: discovering Uniswap pools; querying pool state; analyzing TVL/volume; finding optimal swap routes.
Run `npx skills add ccashwell/evm-cortex --skill pool-finder -a claude-code`. Or copy the skill folder (skills/pool-finder in ccashwell/evm-cortex) into .claude/skills/pool-finder in your project. Claude Code loads it when a task matches its description.
Run `npx skills add ccashwell/evm-cortex --skill pool-finder -a codex`. Or copy the skill folder (skills/pool-finder in ccashwell/evm-cortex) into .agents/skills/pool-finder in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ccashwell/evm-cortex --skill pool-finder -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/pool-finder, .gemini/skills/pool-finder, .github/skills/pool-finder and .opencode/skills/pool-finder in your project.
SKILL.md names no scripts, command-line tools or credentials: Pool Finder is instructions for the agent only.
SKILL.md names 2 domains. In commands or code: gateway.thegraph.com and developers.uniswap.org; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Pool Finder is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.5k tokens (SKILL.md is roughly 22k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Pool Finder: Notebook To Strategy (tradingstrategy-ai/trade-executor, 160 stars), Building Blocks (austintgriffith/ethskills, 294 stars), Clanker (alsk1992/CloddsBot, 2.9k stars) and Dex (alsk1992/CloddsBot, 2.9k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
ccashwell (a GitHub user) maintains it in ccashwell/evm-cortex, which has 131 GitHub stars. The repository holds 89 skills in this directory. The repository was last updated on September 30, 2026.
Source: ccashwell/evm-cortex on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.