159 lines
3.9 KiB
Go
159 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/ethclient"
|
|
)
|
|
|
|
type BlacklistEntry struct {
|
|
Address string `json:"address"`
|
|
FailureCount int `json:"failure_count"`
|
|
LastReason string `json:"last_reason"`
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
IsBlacklisted bool `json:"is_blacklisted"`
|
|
}
|
|
|
|
func main() {
|
|
// Read blacklist
|
|
data, err := ioutil.ReadFile("logs/pool_blacklist.json")
|
|
if err != nil {
|
|
log.Fatal("Failed to read blacklist:", err)
|
|
}
|
|
|
|
var entries []BlacklistEntry
|
|
if err := json.Unmarshal(data, &entries); err != nil {
|
|
log.Fatal("Failed to parse blacklist:", err)
|
|
}
|
|
|
|
// Connect to Arbitrum
|
|
client, err := ethclient.Dial("https://arb1.arbitrum.io/rpc")
|
|
if err != nil {
|
|
log.Fatal("Failed to connect:", err)
|
|
}
|
|
|
|
fmt.Println("Analyzing Valid Failing Pools")
|
|
fmt.Println("=============================")
|
|
fmt.Println()
|
|
|
|
// Function selectors
|
|
token0Selector := []byte{0x0d, 0xfe, 0x16, 0x81} // token0()
|
|
token1Selector := []byte{0xd2, 0x12, 0x20, 0xa7} // token1()
|
|
feeSelector := []byte{0xdd, 0xca, 0x3f, 0x43} // fee()
|
|
slot0Selector := []byte{0x38, 0x50, 0xc7, 0xbd} // slot0()
|
|
reservesSelector := []byte{0x09, 0x02, 0xf1, 0xac} // getReserves()
|
|
|
|
uniV3Count := 0
|
|
uniV2Count := 0
|
|
otherCount := 0
|
|
noContractCount := 0
|
|
|
|
// Test first 20 valid entries
|
|
tested := 0
|
|
for _, entry := range entries {
|
|
if !entry.IsBlacklisted || tested >= 20 {
|
|
continue
|
|
}
|
|
|
|
poolAddress := common.HexToAddress(entry.Address)
|
|
fmt.Printf("Testing %s (reason: %s):\n", entry.Address[:10]+"...", entry.LastReason)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
|
|
// Check if contract exists
|
|
code, err := client.CodeAt(ctx, poolAddress, nil)
|
|
if err != nil || len(code) == 0 {
|
|
fmt.Println(" ❌ No contract")
|
|
noContractCount++
|
|
cancel()
|
|
tested++
|
|
continue
|
|
}
|
|
|
|
// Test token0
|
|
result, err := client.CallContract(ctx, ethereum.CallMsg{
|
|
To: &poolAddress,
|
|
Data: token0Selector,
|
|
}, nil)
|
|
hasToken0 := err == nil && len(result) >= 32
|
|
|
|
// Test token1
|
|
result, err = client.CallContract(ctx, ethereum.CallMsg{
|
|
To: &poolAddress,
|
|
Data: token1Selector,
|
|
}, nil)
|
|
hasToken1 := err == nil && len(result) >= 32
|
|
|
|
// Test fee (V3)
|
|
result, err = client.CallContract(ctx, ethereum.CallMsg{
|
|
To: &poolAddress,
|
|
Data: feeSelector,
|
|
}, nil)
|
|
hasFee := err == nil && len(result) >= 32
|
|
|
|
// Test slot0 (V3)
|
|
result, err = client.CallContract(ctx, ethereum.CallMsg{
|
|
To: &poolAddress,
|
|
Data: slot0Selector,
|
|
}, nil)
|
|
hasSlot0 := err == nil && len(result) >= 32
|
|
|
|
// Test getReserves (V2)
|
|
result, err = client.CallContract(ctx, ethereum.CallMsg{
|
|
To: &poolAddress,
|
|
Data: reservesSelector,
|
|
}, nil)
|
|
hasReserves := err == nil && len(result) >= 96
|
|
|
|
// Determine pool type
|
|
if hasToken0 && hasToken1 {
|
|
if hasFee && hasSlot0 {
|
|
fmt.Println(" ✅ UniswapV3 Pool")
|
|
uniV3Count++
|
|
} else if hasReserves {
|
|
fmt.Println(" ✅ UniswapV2/Sushiswap Pool")
|
|
uniV2Count++
|
|
} else {
|
|
fmt.Println(" ⚠️ Has tokens but unknown type")
|
|
otherCount++
|
|
}
|
|
} else {
|
|
fmt.Printf(" ❌ Not standard AMM (token0:%v, token1:%v)\n", hasToken0, hasToken1)
|
|
otherCount++
|
|
}
|
|
|
|
cancel()
|
|
tested++
|
|
}
|
|
|
|
fmt.Println()
|
|
fmt.Println("Summary")
|
|
fmt.Println("=======")
|
|
fmt.Printf("UniswapV3: %d\n", uniV3Count)
|
|
fmt.Printf("UniswapV2: %d\n", uniV2Count)
|
|
fmt.Printf("Other/Unknown: %d\n", otherCount)
|
|
fmt.Printf("No Contract: %d\n", noContractCount)
|
|
fmt.Println()
|
|
|
|
// Analyze failure reasons
|
|
reasonCounts := make(map[string]int)
|
|
for _, entry := range entries {
|
|
if entry.IsBlacklisted {
|
|
reasonCounts[entry.LastReason]++
|
|
}
|
|
}
|
|
|
|
fmt.Println("Failure Reasons")
|
|
fmt.Println("===============")
|
|
for reason, count := range reasonCounts {
|
|
fmt.Printf("%s: %d\n", reason, count)
|
|
}
|
|
}
|