Files
mev-beta/pkg/uniswap/multicall.go
Krypto Kajun c7142ef671 fix(critical): fix empty token graph + aggressive settings for 24h execution
CRITICAL BUG FIX:
- MultiHopScanner.updateTokenGraph() was EMPTY - adding no pools!
- Result: Token graph had 0 pools, found 0 arbitrage paths
- All opportunities showed estimatedProfitETH: 0.000000

FIX APPLIED:
- Populated token graph with 8 high-liquidity Arbitrum pools:
  * WETH/USDC (0.05% and 0.3% fees)
  * USDC/USDC.e (0.01% - common arbitrage)
  * ARB/USDC, WETH/ARB, WETH/USDT
  * WBTC/WETH, LINK/WETH
- These are REAL verified pool addresses with high volume

AGGRESSIVE THRESHOLD CHANGES:
- Min profit: 0.0001 ETH → 0.00001 ETH (10x lower, ~$0.02)
- Min ROI: 0.05% → 0.01% (5x lower)
- Gas multiplier: 5x → 1.5x (3.3x lower safety margin)
- Max slippage: 3% → 5% (67% higher tolerance)
- Max paths: 100 → 200 (more thorough scanning)
- Cache expiry: 2min → 30sec (fresher opportunities)

EXPECTED RESULTS (24h):
- 20-50 opportunities with profit > $0.02 (was 0)
- 5-15 execution attempts (was 0)
- 1-2 successful executions (was 0)
- $0.02-$0.20 net profit (was $0)

WARNING: Aggressive settings may result in some losses
Monitor closely for first 6 hours and adjust if needed

Target: First profitable execution within 24 hours

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 04:18:27 -05:00

249 lines
6.2 KiB
Go

package uniswap
import (
"context"
"fmt"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
// Multicall3 address on Arbitrum
var Multicall3Address = common.HexToAddress("0xcA11bde05977b3631167028862bE2a173976CA11")
// Multicall3 ABI (simplified - only aggregate3 function)
const Multicall3ABI = `[{
"inputs": [{
"components": [{
"internalType": "address",
"name": "target",
"type": "address"
}, {
"internalType": "bool",
"name": "allowFailure",
"type": "bool"
}, {
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}],
"internalType": "struct Multicall3.Call3[]",
"name": "calls",
"type": "tuple[]"
}],
"name": "aggregate3",
"outputs": [{
"components": [{
"internalType": "bool",
"name": "success",
"type": "bool"
}, {
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}],
"stateMutability": "payable",
"type": "function"
}]`
// Call3 represents a single call in Multicall3
type Call3 struct {
Target common.Address
AllowFailure bool
CallData []byte
}
// Result3 represents the result of a Multicall3 call
type Result3 struct {
Success bool
ReturnData []byte
}
// MulticallBatcher batches multiple RPC calls into a single Multicall3 transaction
type MulticallBatcher struct {
client *ethclient.Client
multicallABI abi.ABI
}
// NewMulticallBatcher creates a new multicall batcher
func NewMulticallBatcher(client *ethclient.Client) (*MulticallBatcher, error) {
parsedABI, err := abi.JSON(strings.NewReader(Multicall3ABI))
if err != nil {
return nil, fmt.Errorf("failed to parse Multicall3 ABI: %w", err)
}
return &MulticallBatcher{
client: client,
multicallABI: parsedABI,
}, nil
}
// ExecuteMulticall executes multiple calls in a single transaction
func (m *MulticallBatcher) ExecuteMulticall(ctx context.Context, calls []Call3) ([]Result3, error) {
if len(calls) == 0 {
return []Result3{}, nil
}
// Pack the aggregate3 call
data, err := m.multicallABI.Pack("aggregate3", calls)
if err != nil {
return nil, fmt.Errorf("failed to pack multicall data: %w", err)
}
// Create the call message
msg := ethereum.CallMsg{
To: &Multicall3Address,
Data: data,
}
// Execute the call
result, err := m.client.CallContract(ctx, msg, nil)
if err != nil {
return nil, fmt.Errorf("failed to execute multicall: %w", err)
}
// Unpack the results
var results []Result3
err = m.multicallABI.UnpackIntoInterface(&results, "aggregate3", result)
if err != nil {
return nil, fmt.Errorf("failed to unpack multicall results: %w", err)
}
return results, nil
}
// BatchPoolDataCalls batches slot0, liquidity, token0, token1, and fee calls for a pool
func (m *MulticallBatcher) BatchPoolDataCalls(ctx context.Context, poolAddress common.Address, poolABI abi.ABI) (
slot0Data []byte,
liquidityData []byte,
token0Data []byte,
token1Data []byte,
feeData []byte,
err error,
) {
// Prepare call data for each function
slot0CallData, err := poolABI.Pack("slot0")
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to pack slot0: %w", err)
}
liquidityCallData, err := poolABI.Pack("liquidity")
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to pack liquidity: %w", err)
}
token0CallData, err := poolABI.Pack("token0")
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to pack token0: %w", err)
}
token1CallData, err := poolABI.Pack("token1")
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to pack token1: %w", err)
}
feeCallData, err := poolABI.Pack("fee")
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to pack fee: %w", err)
}
// Create multicall calls
calls := []Call3{
{Target: poolAddress, AllowFailure: true, CallData: slot0CallData},
{Target: poolAddress, AllowFailure: true, CallData: liquidityCallData},
{Target: poolAddress, AllowFailure: true, CallData: token0CallData},
{Target: poolAddress, AllowFailure: true, CallData: token1CallData},
{Target: poolAddress, AllowFailure: true, CallData: feeCallData},
}
// Execute multicall
results, err := m.ExecuteMulticall(ctx, calls)
if err != nil {
return nil, nil, nil, nil, nil, err
}
// Extract results
if len(results) != 5 {
return nil, nil, nil, nil, nil, fmt.Errorf("expected 5 results, got %d", len(results))
}
// Return raw data (caller will unpack based on success flags)
return results[0].ReturnData,
results[1].ReturnData,
results[2].ReturnData,
results[3].ReturnData,
results[4].ReturnData,
nil
}
// BatchMultiplePoolData batches pool data calls for multiple pools
func (m *MulticallBatcher) BatchMultiplePoolData(ctx context.Context, pools []common.Address, poolABI abi.ABI) (
results map[common.Address]map[string][]byte,
err error,
) {
results = make(map[common.Address]map[string][]byte)
if len(pools) == 0 {
return results, nil
}
// Prepare all calls
var allCalls []Call3
callMap := make(map[int]struct {
poolAddr common.Address
funcName string
})
functions := []string{"slot0", "liquidity", "token0", "token1", "fee"}
callIndex := 0
for _, poolAddr := range pools {
for _, funcName := range functions {
callData, err := poolABI.Pack(funcName)
if err != nil {
// Skip this call if packing fails
continue
}
allCalls = append(allCalls, Call3{
Target: poolAddr,
AllowFailure: true,
CallData: callData,
})
callMap[callIndex] = struct {
poolAddr common.Address
funcName string
}{poolAddr, funcName}
callIndex++
}
}
// Execute multicall
multicallResults, err := m.ExecuteMulticall(ctx, allCalls)
if err != nil {
return nil, fmt.Errorf("failed to execute multicall: %w", err)
}
// Parse results
for i, result := range multicallResults {
if info, ok := callMap[i]; ok {
if _, exists := results[info.poolAddr]; !exists {
results[info.poolAddr] = make(map[string][]byte)
}
if result.Success {
results[info.poolAddr][info.funcName] = result.ReturnData
}
}
}
return results, nil
}