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>
318 lines
9.6 KiB
Bash
Executable File
318 lines
9.6 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Deploy PoolDetector contract to Arbitrum
|
|
# This script compiles and deploys the PoolDetector contract using forge
|
|
|
|
set -e
|
|
|
|
echo "🚀 Starting PoolDetector contract deployment..."
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Check if forge is installed
|
|
if ! command -v forge &> /dev/null; then
|
|
echo -e "${RED}Error: Forge not found. Installing Foundry...${NC}"
|
|
curl -L https://foundry.paradigm.xyz | bash
|
|
source ~/.bashrc
|
|
foundryup
|
|
fi
|
|
|
|
# Check environment variables
|
|
if [ -z "$ARBITRUM_RPC_ENDPOINT" ]; then
|
|
echo -e "${RED}Error: ARBITRUM_RPC_ENDPOINT not set${NC}"
|
|
echo "Please set: export ARBITRUM_RPC_ENDPOINT='your_rpc_endpoint'"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$PRIVATE_KEY" ]; then
|
|
echo -e "${YELLOW}Warning: PRIVATE_KEY not set. Using default test key for local deployment${NC}"
|
|
# Default test private key (DO NOT USE IN PRODUCTION)
|
|
export PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
|
|
fi
|
|
|
|
# Create foundry project structure if it doesn't exist
|
|
if [ ! -d "contracts/foundry" ]; then
|
|
echo "Creating Foundry project structure..."
|
|
mkdir -p contracts/foundry/src
|
|
mkdir -p contracts/foundry/script
|
|
|
|
# Create foundry.toml
|
|
cat > contracts/foundry/foundry.toml << 'EOF'
|
|
[profile.default]
|
|
src = "src"
|
|
out = "out"
|
|
libs = ["lib"]
|
|
solc = "0.8.19"
|
|
optimizer = true
|
|
optimizer_runs = 200
|
|
|
|
[rpc_endpoints]
|
|
arbitrum = "${ARBITRUM_RPC_ENDPOINT}"
|
|
|
|
[etherscan]
|
|
arbitrum = { key = "${ARBISCAN_API_KEY}" }
|
|
EOF
|
|
fi
|
|
|
|
# Copy contract to foundry src
|
|
echo "Copying PoolDetector contract..."
|
|
cp contracts/PoolDetector.sol contracts/foundry/src/
|
|
|
|
# Create interfaces for the pools in foundry src
|
|
cat > contracts/foundry/src/IPoolInterfaces.sol << 'EOF'
|
|
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
interface IUniswapV2Pair {
|
|
function token0() external view returns (address);
|
|
function token1() external view returns (address);
|
|
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
|
|
}
|
|
|
|
interface IUniswapV3Pool {
|
|
function token0() external view returns (address);
|
|
function token1() external view returns (address);
|
|
function fee() external view returns (uint24);
|
|
function slot0() external view returns (
|
|
uint160 sqrtPriceX96,
|
|
int24 tick,
|
|
uint16 observationIndex,
|
|
uint16 observationCardinality,
|
|
uint16 observationCardinalityNext,
|
|
uint8 feeProtocol,
|
|
bool unlocked
|
|
);
|
|
function liquidity() external view returns (uint128);
|
|
}
|
|
|
|
interface IAlgebraPool {
|
|
function token0() external view returns (address);
|
|
function token1() external view returns (address);
|
|
function globalState() external view returns (
|
|
uint160 price,
|
|
int24 tick,
|
|
uint16 fee,
|
|
uint16 timepointIndex,
|
|
uint8 communityFeeToken0,
|
|
uint8 communityFeeToken1,
|
|
bool unlocked
|
|
);
|
|
function liquidity() external view returns (uint128);
|
|
}
|
|
|
|
interface IAlgebraIntegralPool {
|
|
function token0() external view returns (address);
|
|
function token1() external view returns (address);
|
|
function globalState() external view returns (
|
|
uint160 price,
|
|
int24 tick,
|
|
int24 prevInitializedTick,
|
|
int24 nextInitializedTick,
|
|
uint16 feeZto,
|
|
uint16 feeOtz,
|
|
uint16 timepointIndex,
|
|
uint8 communityFee,
|
|
bool unlocked
|
|
);
|
|
function liquidity() external view returns (uint128);
|
|
}
|
|
EOF
|
|
|
|
# Update PoolDetector.sol to import interfaces
|
|
sed -i '1a\\nimport "./IPoolInterfaces.sol";' contracts/foundry/src/PoolDetector.sol 2>/dev/null || \
|
|
sed -i '' '1a\\nimport "./IPoolInterfaces.sol";' contracts/foundry/src/PoolDetector.sol 2>/dev/null || true
|
|
|
|
# Create deployment script
|
|
cat > contracts/foundry/script/DeployPoolDetector.s.sol << 'EOF'
|
|
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "forge-std/Script.sol";
|
|
import "../src/PoolDetector.sol";
|
|
|
|
contract DeployPoolDetector is Script {
|
|
function run() external {
|
|
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
|
|
|
vm.startBroadcast(deployerPrivateKey);
|
|
|
|
PoolDetector detector = new PoolDetector();
|
|
|
|
console.log("PoolDetector deployed at:", address(detector));
|
|
|
|
vm.stopBroadcast();
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Navigate to foundry directory
|
|
cd contracts/foundry
|
|
|
|
# Install dependencies if needed
|
|
if [ ! -d "lib/forge-std" ]; then
|
|
echo "Installing Forge dependencies..."
|
|
forge install foundry-rs/forge-std --no-commit
|
|
fi
|
|
|
|
# Build the contract
|
|
echo -e "${YELLOW}Building contracts...${NC}"
|
|
forge build
|
|
|
|
# Deploy to local anvil fork for testing first
|
|
echo -e "${YELLOW}Starting Anvil fork for testing...${NC}"
|
|
# Kill any existing anvil process
|
|
pkill anvil 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# Start anvil fork in background
|
|
anvil --fork-url "$ARBITRUM_RPC_ENDPOINT" --port 8545 &
|
|
ANVIL_PID=$!
|
|
sleep 5
|
|
|
|
# Deploy to local fork first
|
|
echo -e "${YELLOW}Deploying to local fork for testing...${NC}"
|
|
DEPLOYED_ADDRESS=$(forge script script/DeployPoolDetector.s.sol:DeployPoolDetector \
|
|
--rpc-url http://localhost:8545 \
|
|
--broadcast \
|
|
--private-key $PRIVATE_KEY \
|
|
2>&1 | grep "PoolDetector deployed at:" | awk '{print $NF}')
|
|
|
|
if [ -z "$DEPLOYED_ADDRESS" ]; then
|
|
echo -e "${RED}Failed to deploy contract to local fork${NC}"
|
|
kill $ANVIL_PID 2>/dev/null
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${GREEN}✅ Successfully deployed to local fork at: $DEPLOYED_ADDRESS${NC}"
|
|
|
|
# Test the deployed contract with some known pools
|
|
echo -e "${YELLOW}Testing deployed contract with known pools...${NC}"
|
|
|
|
# Create test script
|
|
cat > script/TestPoolDetector.s.sol << EOF
|
|
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "forge-std/Script.sol";
|
|
import "../src/PoolDetector.sol";
|
|
|
|
contract TestPoolDetector is Script {
|
|
function run() external view {
|
|
address detectorAddress = vm.envAddress("POOL_DETECTOR_ADDRESS");
|
|
PoolDetector detector = PoolDetector(detectorAddress);
|
|
|
|
// Test with known UniswapV3 pool
|
|
address uniV3Pool = 0xC6962004f452bE9203591991D15f6b388e09E8D0;
|
|
string memory poolType = detector.detectPool(uniV3Pool);
|
|
console.log("Pool", uniV3Pool, "detected as:", poolType);
|
|
|
|
// Test batch detection
|
|
address[] memory pools = new address[](3);
|
|
pools[0] = 0xC6962004f452bE9203591991D15f6b388e09E8D0; // UniswapV3
|
|
pools[1] = 0xA961F0473dA4864C5eD28e00FcC53a3AAb056c1b; // UniswapV2
|
|
pools[2] = 0x0000000000000000000000000000000000000000; // Invalid
|
|
|
|
string[] memory types = detector.batchDetect(pools);
|
|
for (uint i = 0; i < types.length; i++) {
|
|
console.log("Batch result", i, ":", types[i]);
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Run test
|
|
POOL_DETECTOR_ADDRESS=$DEPLOYED_ADDRESS forge script script/TestPoolDetector.s.sol:TestPoolDetector \
|
|
--rpc-url http://localhost:8545 \
|
|
2>&1 | grep -E "Pool|Batch result"
|
|
|
|
# Kill anvil
|
|
kill $ANVIL_PID 2>/dev/null
|
|
|
|
echo ""
|
|
echo -e "${GREEN}Local testing complete!${NC}"
|
|
echo ""
|
|
|
|
# Ask user if they want to deploy to mainnet
|
|
read -p "Deploy to Arbitrum mainnet? (y/n) " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
echo -e "${YELLOW}⚠️ Deploying to Arbitrum mainnet...${NC}"
|
|
|
|
# Deploy to mainnet
|
|
MAINNET_ADDRESS=$(forge script script/DeployPoolDetector.s.sol:DeployPoolDetector \
|
|
--rpc-url "$ARBITRUM_RPC_ENDPOINT" \
|
|
--broadcast \
|
|
--verify \
|
|
--private-key $PRIVATE_KEY \
|
|
2>&1 | grep "PoolDetector deployed at:" | awk '{print $NF}')
|
|
|
|
if [ -z "$MAINNET_ADDRESS" ]; then
|
|
echo -e "${RED}Failed to deploy to mainnet${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
DEPLOYED_ADDRESS=$MAINNET_ADDRESS
|
|
echo -e "${GREEN}✅ Successfully deployed to Arbitrum mainnet at: $DEPLOYED_ADDRESS${NC}"
|
|
else
|
|
echo -e "${YELLOW}Skipping mainnet deployment. Using local fork address.${NC}"
|
|
fi
|
|
|
|
# Return to project root
|
|
cd ../..
|
|
|
|
# Update .env file
|
|
echo -e "${YELLOW}Updating environment configuration...${NC}"
|
|
|
|
# Check if .env exists
|
|
if [ ! -f .env ]; then
|
|
cp .env.example .env 2>/dev/null || touch .env
|
|
fi
|
|
|
|
# Update or add POOL_DETECTOR_ADDRESS in .env
|
|
if grep -q "POOL_DETECTOR_ADDRESS=" .env; then
|
|
sed -i "s/POOL_DETECTOR_ADDRESS=.*/POOL_DETECTOR_ADDRESS=$DEPLOYED_ADDRESS/" .env 2>/dev/null || \
|
|
sed -i '' "s/POOL_DETECTOR_ADDRESS=.*/POOL_DETECTOR_ADDRESS=$DEPLOYED_ADDRESS/" .env
|
|
else
|
|
echo "POOL_DETECTOR_ADDRESS=$DEPLOYED_ADDRESS" >> .env
|
|
fi
|
|
|
|
# Also export for current session
|
|
export POOL_DETECTOR_ADDRESS=$DEPLOYED_ADDRESS
|
|
|
|
# Create Go binding for the deployed contract
|
|
echo -e "${YELLOW}Generating Go binding for PoolDetector...${NC}"
|
|
|
|
# Extract ABI from compiled contract
|
|
if [ -f "contracts/foundry/out/PoolDetector.sol/PoolDetector.json" ]; then
|
|
# Extract just the ABI array
|
|
jq '.abi' contracts/foundry/out/PoolDetector.sol/PoolDetector.json > contracts/abis/PoolDetector.json
|
|
|
|
# Generate Go binding
|
|
abigen --abi contracts/abis/PoolDetector.json \
|
|
--pkg pooldetector \
|
|
--type PoolDetector \
|
|
--out pkg/bindings/pooldetector/detector.go
|
|
|
|
echo -e "${GREEN}✅ Go binding generated at pkg/bindings/pooldetector/detector.go${NC}"
|
|
fi
|
|
|
|
# Summary
|
|
echo ""
|
|
echo "=========================================="
|
|
echo -e "${GREEN}🎉 PoolDetector Deployment Complete!${NC}"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Contract Address: $DEPLOYED_ADDRESS"
|
|
echo "Environment Variable: POOL_DETECTOR_ADDRESS=$DEPLOYED_ADDRESS"
|
|
echo ""
|
|
echo "The contract has been deployed and configured."
|
|
echo "The MEV bot will now use this contract for pool detection."
|
|
echo ""
|
|
echo "To use in your code:"
|
|
echo " - Go binding: pkg/bindings/pooldetector/detector.go"
|
|
echo " - Contract address: os.Getenv(\"POOL_DETECTOR_ADDRESS\")"
|
|
echo "" |