package track2 import ( "context" "fmt" "math/big" "time" "github.com/ethereum/go-ethereum/ethclient" "github.com/jackc/pgx/v5/pgxpool" ) // BlockIndexer indexes blocks for Track 2 type BlockIndexer struct { db *pgxpool.Pool client *ethclient.Client chainID int } // NewBlockIndexer creates a new block indexer func NewBlockIndexer(db *pgxpool.Pool, client *ethclient.Client, chainID int) *BlockIndexer { return &BlockIndexer{ db: db, client: client, chainID: chainID, } } // IndexBlock indexes a single block func (bi *BlockIndexer) IndexBlock(ctx context.Context, blockNumber uint64) error { block, err := bi.client.BlockByNumber(ctx, big.NewInt(int64(blockNumber))) if err != nil { return fmt.Errorf("failed to get block: %w", err) } // Check if block already indexed var exists bool checkQuery := `SELECT EXISTS(SELECT 1 FROM blocks WHERE chain_id = $1 AND number = $2)` err = bi.db.QueryRow(ctx, checkQuery, bi.chainID, blockNumber).Scan(&exists) if err != nil { return fmt.Errorf("failed to check block existence: %w", err) } if exists { return nil // Already indexed } // Insert block insertQuery := ` INSERT INTO blocks ( chain_id, number, hash, parent_hash, miner, difficulty, total_difficulty, size, gas_limit, gas_used, timestamp, transaction_count, base_fee_per_gas ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ON CONFLICT (chain_id, number) DO NOTHING ` _, err = bi.db.Exec(ctx, insertQuery, bi.chainID, block.Number().Int64(), block.Hash().Hex(), block.ParentHash().Hex(), block.Coinbase().Hex(), block.Difficulty().String(), "0", // total_difficulty block.Size(), block.GasLimit(), block.GasUsed(), time.Unix(int64(block.Time()), 0), len(block.Transactions()), block.BaseFee(), ) if err != nil { return fmt.Errorf("failed to insert block: %w", err) } return nil } // IndexLatestBlocks indexes the latest N blocks func (bi *BlockIndexer) IndexLatestBlocks(ctx context.Context, count int) error { header, err := bi.client.HeaderByNumber(ctx, nil) if err != nil { return fmt.Errorf("failed to get latest header: %w", err) } latestBlock := header.Number.Uint64() for i := 0; i < count && latestBlock-uint64(i) >= 0; i++ { blockNum := latestBlock - uint64(i) if err := bi.IndexBlock(ctx, blockNum); err != nil { // Log error but continue fmt.Printf("Failed to index block %d: %v\n", blockNum, err) } } return nil }