Files
mev-beta/scripts/deploy-pool-detector.sh

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 ""