Skip to content

Instantly share code, notes, and snippets.

@cordt-sei
Created November 29, 2024 20:53
Show Gist options
  • Save cordt-sei/747393f195ffc8c7f7c2a786b802937f to your computer and use it in GitHub Desktop.
Save cordt-sei/747393f195ffc8c7f7c2a786b802937f to your computer and use it in GitHub Desktop.
import { ethers } from "ethers";
export class SeiLogsFetcher {
constructor(providerUrl, maxBatchSize = 2000, maxLogs = 10000) {
this.provider = new ethers.JsonRpcProvider(providerUrl);
this.maxBatchSize = maxBatchSize;
this.maxLogs = maxLogs;
}
async getLogs(filter, logLevel = "INFO") {
const { fromBlock, toBlock, address, topics } = filter;
let currentBlock = fromBlock;
let aggregatedLogs = [];
const log = (level, message) => {
if (level === "DEBUG" || logLevel === "DEBUG") console.log(message);
};
while (currentBlock <= toBlock) {
const batchEnd = Math.min(currentBlock + this.maxBatchSize - 1, toBlock);
log("DEBUG", `Querying block range: ${currentBlock} to ${batchEnd}`);
try {
const batchFilter = {
fromBlock: currentBlock,
toBlock: batchEnd,
address,
topics,
};
// Updated to call `sei_getLogs` using ethers' `provider.send` method
const logs = await this.provider.send("sei_getLogs", [batchFilter]);
aggregatedLogs.push(...logs);
log("INFO", `Fetched logs for range ${currentBlock} to ${batchEnd}, count: ${logs.length}`);
if (logs.length === this.maxLogs) {
log(
"DEBUG",
`Exactly ${this.maxLogs} logs returned for range ${currentBlock} to ${batchEnd}. Splitting further.`
);
// Recursively split the range
const midpoint = Math.floor((currentBlock + batchEnd) / 2);
aggregatedLogs.push(...await this.getLogs({ ...batchFilter, toBlock: midpoint }, logLevel));
aggregatedLogs.push(...await this.getLogs({ ...batchFilter, fromBlock: midpoint + 1 }, logLevel));
}
currentBlock = batchEnd + 1;
} catch (error) {
console.error(`Error fetching logs for range ${currentBlock} to ${batchEnd}: ${error.message}`);
throw error; // Let the calling application handle this
}
}
return aggregatedLogs;
}
}

Using SeiLogsFetcher in Your Application

Integration Steps

  1. Import the Module: Import getLogs_ext.js into your application:

    import { SeiLogsFetcher } from './getLogs_ext.js'; // Adjust the path if needed
  2. Replace Direct Calls to sei_getLogs: Replace any direct calls to the Ethereum node's sei_getLogs method with an instance of SeiLogsFetcher:

    const fetcher = new SeiLogsFetcher("http://localhost:8545");
    const logs = await fetcher.getLogs(filter, "INFO");
  3. Ensure Filter Compatibility: If using frameworks like ethers.js, ensure the filter object passed to getLogs is compatible with the sei_getLogs method format. No additional adjustments are required for seamless integration.

  4. Abstract Network Limitations: Use SeiLogsFetcher as a transparent solution to handle sei_getLogs block and log limits. Internally, it manages batching, splitting, and aggregation, presenting a unified and complete response to your application.


Example Usage

import { SeiLogsFetcher } from './getLogs_ext.js';

(async () => {
    const providerUrl = "http://localhost:8545";
    const fetcher = new SeiLogsFetcher(providerUrl);

    const filter = {
        fromBlock: 107432958,
        toBlock: 107433958,
        address: "0xe8eae1aaB1b4BBa29eAbaEfe7df4510833b6491e",
        topics: [], // Add topics if needed
    };

    try {
        const logs = await fetcher.getLogs(filter, "INFO");
        console.log("Aggregated logs:", logs);
    } catch (error) {
        console.error("Failed to fetch logs:", error);
    }
})();

Middleware for Express.js Applications

SeiLogsFetcher can preprocess log-fetching requests in Express.js:

import express from "express";
import { SeiLogsFetcher } from "./getLogs_ext.js"; // Adjust the path as needed

const app = express();
const PORT = 3000;

// Initialize the SeiLogsFetcher
const providerUrl = "http://localhost:8545";
const fetcher = new SeiLogsFetcher(providerUrl);

// Middleware to handle log-fetching requests
app.use(express.json()); // Parse incoming JSON requests

app.post("/getLogs", async (req, res) => {
    const { filter, logLevel = "INFO" } = req.body; // Expect filter and log level in the request body

    try {
        const logs = await fetcher.getLogs(filter, logLevel);
        res.json({
            success: true,
            logs,
        });
    } catch (error) {
        console.error(`Failed to fetch logs: ${error.message}`);
        res.status(500).json({
            success: false,
            error: error.message,
        });
    }
});

app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Compatibility Notes

  • INFO Mode:

    • Logs essential information (batch range, log counts) to the console.
    • Provides a clean, aggregated response, ideal for production use.
  • DEBUG Mode:

    • Outputs detailed logs, including batch ranges, splitting decisions, and responses, to the console only.
    • The returned response remains clean and consistent, unaffected by the debug output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment