Additional documentation and testing infrastructure: ## Documentation Added - PROFIT_ROADMAP.md - 4-week profitability roadmap - PRODUCTION_DEPLOYMENT.md - Production deployment guide - docs/FLASH_LOAN_DEPLOYMENT_GUIDE.md - Flash loan implementation - docs/FLASH_LOAN_IMPLEMENTATION_SUMMARY.md - Flash loan summary - docs/BINDING_CONSISTENCY_GUIDE.md - Contract binding guidelines - docs/BINDING_QUICK_START.md - Quick start for bindings - docs/COMPLETE_FORK_TESTING_GUIDE.md - Fork testing guide ## Testing Scripts Added - scripts/generate-test-report.sh - Generate test reports - scripts/monitor-24h-test.sh - 24-hour monitoring - scripts/start-24h-test.sh - Start long-running tests - scripts/stop-24h-test.sh - Stop test runs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
Flash Loan Execution - Deployment & Integration Guide
Status: Framework Complete, Contracts Ready for Deployment
Last Updated: October 26, 2025
📋 Overview
This guide covers the deployment and integration of the MEV bot's flash loan execution system, which enables real arbitrage execution using flash loans from multiple providers.
Architecture Summary
┌─────────────────────────────────────────────────────────────┐
│ MEV Bot Go Process │
│ │
│ ┌────────────────┐ ┌──────────────────┐ │
│ │ Arbitrage │────────▶│ Flash Loan │ │
│ │ Detector │ │ Provider │ │
│ └────────────────┘ └────────┬─────────┘ │
│ │ │
└──────────────────────────────────────┼───────────────────────┘
│ RPC Call
▼
┌──────────────────────────────────┐
│ FlashLoanReceiver Contract │
│ (Deployed on Arbitrum) │
└────────┬─────────────────────────┘
│
┌───────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Balancer │ │ Uniswap V2 │ │ Uniswap V3 │
│ Vault │ │ Router │ │ Router │
│ (0% fee) │ │ (0.3% fee) │ │ (0.05%-1% fee) │
└─────────────┘ └──────────────┘ └──────────────────┘
🎯 Implementation Status
✅ Complete
-
Solidity Smart Contract
- Location:
contracts/balancer/FlashLoanReceiver.sol - Features:
- Balancer flash loan integration
- Uniswap V2/V3 swap execution
- Profit calculation and validation
- Owner-only access control
- Emergency withdrawal
- Location:
-
Go Execution Framework
- Location:
pkg/execution/ - Files:
executor.go- Core execution engine (316 lines)flashloan_providers.go- Provider implementations (360+ lines)alerts.go- Alert system (291 lines)
- Location:
-
ABI Bindings
- Location:
bindings/balancer/vault.go - Generated with abigen for Balancer Vault
- Location:
-
Calldata Encoding
- Function:
encodeArbitragePath()in flashloan_providers.go - Encodes ArbitragePath struct for contract
- Function:
⏳ Pending
-
Smart Contract Deployment
- Deploy FlashLoanReceiver.sol to Arbitrum
- Set receiver address in BalancerFlashLoanProvider
- Fund contract with gas if needed
-
Transaction Signing
- Implement private key management
- Add transaction signer
- Gas estimation logic
-
ABI Encoding/Decoding
- Complete ABI encoding for ArbitragePath struct
- Parse execution results from contract events
-
Integration Testing
- Test on Arbitrum testnet
- Fork testing with Tenderly/Hardhat
- Mainnet dry-run testing
🚀 Deployment Steps
Prerequisites
# Install dependencies
npm install --save-dev hardhat @nomiclabs/hardhat-ethers ethers
npm install @openzeppelin/contracts
# Or use Foundry (recommended for production)
curl -L https://foundry.paradigm.xyz | bash
foundryup
Step 1: Compile Contract
Using Hardhat:
npx hardhat compile contracts/balancer/FlashLoanReceiver.sol
Using Foundry:
forge build contracts/balancer/FlashLoanReceiver.sol
Step 2: Deploy to Arbitrum
Deployment Script (Hardhat):
// scripts/deploy-flash-receiver.js
const hre = require("hardhat");
async function main() {
const BALANCER_VAULT = "0xBA12222222228d8Ba445958a75a0704d566BF2C8";
console.log("Deploying FlashLoanReceiver...");
const FlashLoanReceiver = await hre.ethers.getContractFactory("FlashLoanReceiver");
const receiver = await FlashLoanReceiver.deploy(BALANCER_VAULT);
await receiver.deployed();
console.log("✅ FlashLoanReceiver deployed to:", receiver.address);
console.log(" Owner:", await receiver.owner());
console.log(" Vault:", await receiver.vault());
// Save deployment info
const fs = require("fs");
fs.writeFileSync("deployment.json", JSON.stringify({
address: receiver.address,
owner: await receiver.owner(),
vault: BALANCER_VAULT,
timestamp: new Date().toISOString()
}, null, 2));
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Deploy:
npx hardhat run scripts/deploy-flash-receiver.js --network arbitrum
Using Foundry:
forge create contracts/balancer/FlashLoanReceiver.sol:FlashLoanReceiver \
--rpc-url $ARBITRUM_RPC \
--private-key $PRIVATE_KEY \
--constructor-args 0xBA12222222228d8Ba445958a75a0704d566BF2C8 \
--verify
Step 3: Configure MEV Bot
After deployment, update the Go code with the deployed contract address:
// pkg/execution/flashloan_providers.go
func NewBalancerFlashLoanProvider(client *ethclient.Client, logger *logger.Logger) *BalancerFlashLoanProvider {
return &BalancerFlashLoanProvider{
client: client,
logger: logger,
vaultAddress: common.HexToAddress("0xBA12222222228d8Ba445958a75a0704d566BF2C8"),
// UPDATE THIS with deployed contract address:
receiverAddress: common.HexToAddress("0xYOUR_DEPLOYED_CONTRACT_ADDRESS"),
}
}
Or use environment variable:
export FLASH_LOAN_RECEIVER="0xYOUR_DEPLOYED_CONTRACT_ADDRESS"
Step 4: Generate Contract Bindings
Generate Go bindings for the deployed contract:
# Get contract ABI
cat contracts/balancer/FlashLoanReceiver.sol | \
solc --abi - > contracts/balancer/FlashLoanReceiver.abi
# Generate Go bindings
abigen --abi contracts/balancer/FlashLoanReceiver.abi \
--pkg execution \
--type FlashLoanReceiver \
--out pkg/execution/flashloan_receiver.go
🔧 Integration Implementation
Complete the TODO Items
1. Transaction Signing (pkg/execution/transaction_signer.go - NEW FILE)
package execution
import (
"context"
"crypto/ecdsa"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
type TransactionSigner struct {
privateKey *ecdsa.PrivateKey
chainID *big.Int
client *ethclient.Client
}
func NewTransactionSigner(privateKeyHex string, client *ethclient.Client) (*TransactionSigner, error) {
privateKey, err := crypto.HexToECDSA(privateKeyHex)
if err != nil {
return nil, err
}
chainID, err := client.ChainID(context.Background())
if err != nil {
return nil, err
}
return &TransactionSigner{
privateKey: privateKey,
chainID: chainID,
client: client,
}, nil
}
func (ts *TransactionSigner) SignAndSend(ctx context.Context, tx *types.Transaction) (common.Hash, error) {
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(ts.chainID), ts.privateKey)
if err != nil {
return common.Hash{}, err
}
err = ts.client.SendTransaction(ctx, signedTx)
if err != nil {
return common.Hash{}, err
}
return signedTx.Hash(), nil
}
func (ts *TransactionSigner) GetTransactor() (*bind.TransactOpts, error) {
auth, err := bind.NewKeyedTransactorWithChainID(ts.privateKey, ts.chainID)
if err != nil {
return nil, err
}
return auth, nil
}
2. Complete ABI Encoding
Update encodeArbitragePath() to use proper ABI encoding:
import (
"github.com/ethereum/go-ethereum/accounts/abi"
"strings"
)
func (b *BalancerFlashLoanProvider) encodeArbitragePath(
opportunity *arbitrage.ArbitragePath,
config *ExecutionConfig,
) ([]byte, error) {
// Define ABI for ArbitragePath struct
arbitragePathABI := `[{
"components": [
{"name": "tokens", "type": "address[]"},
{"name": "exchanges", "type": "address[]"},
{"name": "fees", "type": "uint24[]"},
{"name": "isV3", "type": "bool[]"},
{"name": "minProfit", "type": "uint256"}
],
"name": "path",
"type": "tuple"
}]`
contractABI, err := abi.JSON(strings.NewReader(arbitragePathABI))
if err != nil {
return nil, err
}
// Prepare data (same as before)
numHops := len(opportunity.TokenPath) - 1
exchanges := make([]common.Address, numHops)
fees := make([]*big.Int, numHops)
isV3 := make([]bool, numHops)
// ... (populate arrays) ...
// Encode using ABI
encoded, err := contractABI.Pack("path",
opportunity.TokenPath,
exchanges,
fees,
isV3,
minProfit,
)
if err != nil {
return nil, err
}
return encoded, nil
}
3. Complete ExecuteFlashLoan
func (b *BalancerFlashLoanProvider) ExecuteFlashLoan(
ctx context.Context,
opportunity *arbitrage.ArbitragePath,
config *ExecutionConfig,
) (*ExecutionResult, error) {
startTime := time.Now()
// ... (validation and encoding as before) ...
// Create contract instance
receiver, err := NewFlashLoanReceiver(b.receiverAddress, b.client)
if err != nil {
return nil, err
}
// Get transactor
auth, err := config.Signer.GetTransactor()
if err != nil {
return nil, err
}
// Set gas price and limit
auth.GasPrice = config.MaxGasPrice
auth.GasLimit = 500000 // Estimate based on path length
// Call executeArbitrage
tx, err := receiver.ExecuteArbitrage(auth, tokens, amounts, userData)
if err != nil {
return &ExecutionResult{
OpportunityID: opportunity.ID,
Success: false,
Error: err,
ExecutionTime: time.Since(startTime),
}, err
}
// Wait for receipt
receipt, err := bind.WaitMined(ctx, b.client, tx)
if err != nil {
return &ExecutionResult{
OpportunityID: opportunity.ID,
Success: false,
TxHash: tx.Hash(),
Error: err,
ExecutionTime: time.Since(startTime),
}, err
}
// Parse events to get actual profit
var actualProfit *big.Int
for _, log := range receipt.Logs {
event, err := receiver.ParseArbitrageExecuted(*log)
if err == nil {
actualProfit = event.Profit
break
}
}
return &ExecutionResult{
OpportunityID: opportunity.ID,
Success: receipt.Status == 1,
TxHash: tx.Hash(),
GasUsed: receipt.GasUsed,
ActualProfit: actualProfit,
EstimatedProfit: opportunity.NetProfit,
ExecutionTime: time.Since(startTime),
Timestamp: time.Now(),
}, nil
}
✅ Testing Strategy
1. Local Fork Testing
# Start Hardhat node with Arbitrum fork
npx hardhat node --fork https://arb1.arbitrum.io/rpc
# Deploy contract to local fork
npx hardhat run scripts/deploy-flash-receiver.js --network localhost
# Run Go tests against local fork
export ARBITRUM_RPC_ENDPOINT="http://localhost:8545"
export FLASH_LOAN_RECEIVER="0x..."
go test ./pkg/execution/... -v
2. Arbitrum Testnet
# Deploy to Arbitrum Sepolia testnet
npx hardhat run scripts/deploy-flash-receiver.js --network arbitrum-sepolia
# Test with testnet RPC
export ARBITRUM_RPC_ENDPOINT="https://sepolia-rollup.arbitrum.io/rpc"
./mev-bot start --dry-run
3. Mainnet Dry-Run
# Test on mainnet without executing
export EXECUTION_MODE="simulation"
./mev-bot start
📊 Gas Optimization
Estimated Gas Costs
| Operation | Gas Estimate | Cost (@ 0.1 gwei) |
|---|---|---|
| Contract deployment | 1,500,000 | 0.00015 ETH |
| 2-hop arbitrage | 300,000 | 0.00003 ETH |
| 3-hop arbitrage | 450,000 | 0.000045 ETH |
| 4-hop arbitrage | 600,000 | 0.00006 ETH |
Optimization Tips
- Batch token approvals - Approve max once instead of per transaction
- Use V3 single-hop when possible - Lower gas than multi-contract calls
- Optimize path length - 2-hop paths preferred
- Monitor gas prices - Only execute when gas < threshold
🔐 Security Considerations
Smart Contract Security
-
Access Control
- Only owner can call
executeArbitrage() - Only Balancer Vault can call
receiveFlashLoan()
- Only owner can call
-
Profit Validation
- Minimum profit threshold enforced on-chain
- Prevents unprofitable execution
-
Emergency Functions
emergencyWithdraw()for stuck fundswithdrawProfit()for profit extraction
Operational Security
-
Private Key Management
# NEVER commit private keys to git # Use environment variables or secret managers export EXECUTOR_PRIVATE_KEY="0x..." # Or use hardware wallets (Ledger/Trezor) # Or use AWS KMS / Google Cloud KMS -
Gas Price Limits
config := &ExecutionConfig{ MaxGasPrice: big.NewInt(1000000000), // 1 gwei max // ... } -
Slippage Protection
- Set
MaxSlippageappropriately - Default 5% is reasonable for volatile markets
- Set
📈 Monitoring & Alerts
Integration with Alert System
// In main.go or orchestrator
alertSystem := execution.NewAlertSystem(&execution.AlertConfig{
EnableConsoleAlerts: true,
EnableWebhook: true,
WebhookURL: os.Getenv("SLACK_WEBHOOK"),
MinProfitForAlert: big.NewInt(1e16), // 0.01 ETH
MinROIForAlert: 0.05, // 5%
}, logger)
// Send execution alerts
result, err := executor.ExecuteOpportunity(ctx, opportunity)
if err == nil {
alertSystem.SendExecutionAlert(result)
}
Dashboard Metrics
Add to monitoring/dashboard.sh:
# Execution metrics
EXECUTIONS=$(grep -c "Arbitrage executed successfully" "${LATEST_LOG}")
EXECUTION_PROFIT=$(grep "profit=" "${LATEST_LOG}" | awk '{sum+=$NF} END {print sum}')
echo " Executions: ${EXECUTIONS}"
echo " Total Profit: ${EXECUTION_PROFIT} ETH"
🎯 Next Steps
- Deploy FlashLoanReceiver contract to Arbitrum
- Implement transaction signing in Go
- Complete ABI encoding for ArbitragePath
- Test on Arbitrum testnet
- Conduct security audit of smart contract
- Monitor 24-hour test results before enabling execution
- Start with small amounts (0.01-0.1 ETH)
- Scale gradually as confidence builds
📚 Reference
- Balancer Vault: 0xBA12222222228d8Ba445958a75a0704d566BF2C8
- Flash Loan Docs: https://docs.balancer.fi/reference/contracts/flash-loans.html
- Arbitrum RPC: https://docs.arbitrum.io/build-decentralized-apps/reference/node-providers
- Go-Ethereum Docs: https://geth.ethereum.org/docs
Last Updated: October 26, 2025 Status: Ready for Deployment