Files
mev-beta/orig/contracts/PoolDetector.sol
Administrator c54c569f30 refactor: move all remaining files to orig/ directory
Completed clean root directory structure:
- Root now contains only: .git, .env, docs/, orig/
- Moved all remaining files and directories to orig/:
  - Config files (.claude, .dockerignore, .drone.yml, etc.)
  - All .env variants (except active .env)
  - Git config (.gitconfig, .github, .gitignore, etc.)
  - Tool configs (.golangci.yml, .revive.toml, etc.)
  - Documentation (*.md files, @prompts)
  - Build files (Dockerfiles, Makefile, go.mod, go.sum)
  - Docker compose files
  - All source directories (scripts, tests, tools, etc.)
  - Runtime directories (logs, monitoring, reports)
  - Dependency files (node_modules, lib, cache)
  - Special files (--delete)

- Removed empty runtime directories (bin/, data/)

V2 structure is now clean:
- docs/planning/ - V2 planning documents
- orig/ - Complete V1 codebase preserved
- .env - Active environment config (not in git)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:53:05 +01:00

259 lines
7.9 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title PoolDetector
* @notice Efficient onchain pool detection for multiple DEX types
* @dev Gas-optimized detection using assembly and batched calls
*/
contract PoolDetector {
enum PoolType {
Unknown,
UniswapV2,
UniswapV3,
AlgebraV19,
AlgebraIntegral,
Balancer,
Curve,
Camelot,
SushiswapV2,
PancakeV3,
KyberSwap
}
struct PoolInfo {
address pool;
PoolType poolType;
address token0;
address token1;
uint256 fee;
bool isValid;
uint8 confidence;
}
/**
* @notice Detect pool type for a single address
* @param pool The pool address to identify
* @return info The pool information
*/
function detectPool(address pool) external view returns (PoolInfo memory info) {
info.pool = pool;
// Check if contract exists
uint256 codeSize;
assembly {
codeSize := extcodesize(pool)
}
if (codeSize == 0) {
info.isValid = false;
return info;
}
// Try to get token0 and token1 (common to all pools)
(bool hasToken0, address token0) = _tryToken0(pool);
(bool hasToken1, address token1) = _tryToken1(pool);
if (!hasToken0 || !hasToken1) {
info.isValid = false;
return info;
}
info.token0 = token0;
info.token1 = token1;
info.isValid = true;
// Try UniswapV3 specific methods
if (_isUniswapV3(pool)) {
info.poolType = PoolType.UniswapV3;
info.confidence = 95;
(bool hasFee, uint256 fee) = _tryFeeV3(pool);
if (hasFee) info.fee = fee;
return info;
}
// Try Algebra/Camelot (has globalState instead of slot0)
if (_isAlgebra(pool)) {
// Check for directional fees (Integral)
if (_hasDirectionalFees(pool)) {
info.poolType = PoolType.AlgebraIntegral;
info.confidence = 90;
} else {
info.poolType = PoolType.AlgebraV19;
info.confidence = 85;
}
return info;
}
// Try UniswapV2/Sushiswap
if (_isUniswapV2(pool)) {
// Distinguish by factory
address factory = _getFactory(pool);
if (factory == 0xc35DADB65012eC5796536bD9864eD8773aBc74C4) {
info.poolType = PoolType.SushiswapV2;
} else {
info.poolType = PoolType.UniswapV2;
}
info.confidence = 80;
return info;
}
// Unknown but valid pool
info.poolType = PoolType.Unknown;
info.confidence = 30;
}
/**
* @notice Batch detect multiple pools
* @param pools Array of pool addresses
* @return infos Array of pool information
*/
function detectPools(address[] calldata pools)
external
view
returns (PoolInfo[] memory infos)
{
infos = new PoolInfo[](pools.length);
for (uint256 i = 0; i < pools.length; i++) {
infos[i] = this.detectPool(pools[i]);
}
}
// Internal detection functions
function _isUniswapV3(address pool) private view returns (bool) {
// Check for slot0, fee, tickSpacing, maxLiquidityPerTick
(bool hasSlot0,) = _trySlot0(pool);
(bool hasFee,) = _tryFeeV3(pool);
(bool hasTickSpacing,) = _tryTickSpacing(pool);
(bool hasMaxLiquidity,) = _tryMaxLiquidityPerTick(pool);
return hasSlot0 && hasFee && hasTickSpacing && hasMaxLiquidity;
}
function _isAlgebra(address pool) private view returns (bool) {
// Has globalState instead of slot0
(bool hasGlobalState,) = _tryGlobalState(pool);
(bool hasSlot0,) = _trySlot0(pool);
return hasGlobalState && !hasSlot0;
}
function _isUniswapV2(address pool) private view returns (bool) {
// Has getReserves, no slot0 or globalState
(bool hasReserves,) = _tryGetReserves(pool);
(bool hasSlot0,) = _trySlot0(pool);
(bool hasGlobalState,) = _tryGlobalState(pool);
return hasReserves && !hasSlot0 && !hasGlobalState;
}
function _hasDirectionalFees(address pool) private view returns (bool) {
// Check for feeZto0 and fee0to1 (Algebra Integral)
(bool success,) = pool.staticcall(
abi.encodeWithSignature("feeZto0()")
);
return success;
}
// Low-level call helpers
function _tryToken0(address pool) private view returns (bool success, address token) {
bytes memory data;
(success, data) = pool.staticcall(
abi.encodeWithSignature("token0()")
);
if (success && data.length >= 32) {
token = abi.decode(data, (address));
}
}
function _tryToken1(address pool) private view returns (bool success, address token) {
bytes memory data;
(success, data) = pool.staticcall(
abi.encodeWithSignature("token1()")
);
if (success && data.length >= 32) {
token = abi.decode(data, (address));
}
}
function _trySlot0(address pool) private view returns (bool success, bytes memory data) {
(success, data) = pool.staticcall(
abi.encodeWithSignature("slot0()")
);
}
function _tryGlobalState(address pool) private view returns (bool success, bytes memory data) {
(success, data) = pool.staticcall(
abi.encodeWithSignature("globalState()")
);
}
function _tryGetReserves(address pool) private view returns (bool success, bytes memory data) {
(success, data) = pool.staticcall(
abi.encodeWithSignature("getReserves()")
);
}
function _tryFeeV3(address pool) private view returns (bool success, uint256 fee) {
bytes memory data;
(success, data) = pool.staticcall(
abi.encodeWithSignature("fee()")
);
if (success && data.length >= 32) {
fee = abi.decode(data, (uint256));
}
}
function _tryTickSpacing(address pool) private view returns (bool success, bytes memory data) {
(success, data) = pool.staticcall(
abi.encodeWithSignature("tickSpacing()")
);
}
function _tryMaxLiquidityPerTick(address pool) private view returns (bool success, bytes memory data) {
(success, data) = pool.staticcall(
abi.encodeWithSignature("maxLiquidityPerTick()")
);
}
function _getFactory(address pool) private view returns (address factory) {
(bool success, bytes memory data) = pool.staticcall(
abi.encodeWithSignature("factory()")
);
if (success && data.length >= 32) {
factory = abi.decode(data, (address));
}
}
/**
* @notice Efficient batch detection using multicall pattern
* @param pools Array of pools to check
* @return results Packed results for gas efficiency
*/
function batchDetectEfficient(address[] calldata pools)
external
view
returns (uint256[] memory results)
{
results = new uint256[](pools.length);
for (uint256 i = 0; i < pools.length; i++) {
// Pack result into single uint256:
// bits 0-7: pool type
// bits 8-15: confidence
// bits 16-23: has token0
// bits 24-31: has token1
// bits 32+: fee (if applicable)
PoolInfo memory info = this.detectPool(pools[i]);
results[i] = uint256(info.poolType) |
(uint256(info.confidence) << 8) |
(info.token0 != address(0) ? 1 << 16 : 0) |
(info.token1 != address(0) ? 1 << 24 : 0) |
(info.fee << 32);
}
}
}