docs: add flash loan, binding, and testing documentation
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>
This commit is contained in:
573
docs/FLASH_LOAN_DEPLOYMENT_GUIDE.md
Normal file
573
docs/FLASH_LOAN_DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,573 @@
|
||||
# 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
|
||||
|
||||
1. **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
|
||||
|
||||
2. **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)
|
||||
|
||||
3. **ABI Bindings**
|
||||
- Location: `bindings/balancer/vault.go`
|
||||
- Generated with abigen for Balancer Vault
|
||||
|
||||
4. **Calldata Encoding**
|
||||
- Function: `encodeArbitragePath()` in flashloan_providers.go
|
||||
- Encodes ArbitragePath struct for contract
|
||||
|
||||
### ⏳ Pending
|
||||
|
||||
1. **Smart Contract Deployment**
|
||||
- Deploy FlashLoanReceiver.sol to Arbitrum
|
||||
- Set receiver address in BalancerFlashLoanProvider
|
||||
- Fund contract with gas if needed
|
||||
|
||||
2. **Transaction Signing**
|
||||
- Implement private key management
|
||||
- Add transaction signer
|
||||
- Gas estimation logic
|
||||
|
||||
3. **ABI Encoding/Decoding**
|
||||
- Complete ABI encoding for ArbitragePath struct
|
||||
- Parse execution results from contract events
|
||||
|
||||
4. **Integration Testing**
|
||||
- Test on Arbitrum testnet
|
||||
- Fork testing with Tenderly/Hardhat
|
||||
- Mainnet dry-run testing
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment Steps
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
# 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:**
|
||||
```bash
|
||||
npx hardhat compile contracts/balancer/FlashLoanReceiver.sol
|
||||
```
|
||||
|
||||
**Using Foundry:**
|
||||
```bash
|
||||
forge build contracts/balancer/FlashLoanReceiver.sol
|
||||
```
|
||||
|
||||
### Step 2: Deploy to Arbitrum
|
||||
|
||||
**Deployment Script (Hardhat):**
|
||||
|
||||
```javascript
|
||||
// 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:**
|
||||
```bash
|
||||
npx hardhat run scripts/deploy-flash-receiver.js --network arbitrum
|
||||
```
|
||||
|
||||
**Using Foundry:**
|
||||
```bash
|
||||
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:
|
||||
|
||||
```go
|
||||
// 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:
|
||||
```bash
|
||||
export FLASH_LOAN_RECEIVER="0xYOUR_DEPLOYED_CONTRACT_ADDRESS"
|
||||
```
|
||||
|
||||
### Step 4: Generate Contract Bindings
|
||||
|
||||
Generate Go bindings for the deployed contract:
|
||||
|
||||
```bash
|
||||
# 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)**
|
||||
|
||||
```go
|
||||
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:
|
||||
|
||||
```go
|
||||
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**
|
||||
|
||||
```go
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
1. **Batch token approvals** - Approve max once instead of per transaction
|
||||
2. **Use V3 single-hop when possible** - Lower gas than multi-contract calls
|
||||
3. **Optimize path length** - 2-hop paths preferred
|
||||
4. **Monitor gas prices** - Only execute when gas < threshold
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Considerations
|
||||
|
||||
### Smart Contract Security
|
||||
|
||||
1. **Access Control**
|
||||
- Only owner can call `executeArbitrage()`
|
||||
- Only Balancer Vault can call `receiveFlashLoan()`
|
||||
|
||||
2. **Profit Validation**
|
||||
- Minimum profit threshold enforced on-chain
|
||||
- Prevents unprofitable execution
|
||||
|
||||
3. **Emergency Functions**
|
||||
- `emergencyWithdraw()` for stuck funds
|
||||
- `withdrawProfit()` for profit extraction
|
||||
|
||||
### Operational Security
|
||||
|
||||
1. **Private Key Management**
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
2. **Gas Price Limits**
|
||||
```go
|
||||
config := &ExecutionConfig{
|
||||
MaxGasPrice: big.NewInt(1000000000), // 1 gwei max
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
3. **Slippage Protection**
|
||||
- Set `MaxSlippage` appropriately
|
||||
- Default 5% is reasonable for volatile markets
|
||||
|
||||
---
|
||||
|
||||
## 📈 Monitoring & Alerts
|
||||
|
||||
### Integration with Alert System
|
||||
|
||||
```go
|
||||
// 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`:
|
||||
```bash
|
||||
# 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
|
||||
|
||||
1. **Deploy FlashLoanReceiver contract** to Arbitrum
|
||||
2. **Implement transaction signing** in Go
|
||||
3. **Complete ABI encoding** for ArbitragePath
|
||||
4. **Test on Arbitrum testnet**
|
||||
5. **Conduct security audit** of smart contract
|
||||
6. **Monitor 24-hour test results** before enabling execution
|
||||
7. **Start with small amounts** (0.01-0.1 ETH)
|
||||
8. **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*
|
||||
Reference in New Issue
Block a user