#!/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 ""