"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readProgramEvents = readProgramEvents;
exports.readEvents = readEvents;
const anchor_1 = require("@coral-xyz/anchor");
const web3_v2_js_1 = __importDefault(require("@solana/web3-v2.js"));
/**
 * Reads all events for a specific program.
 */
async function readProgramEvents(rpc, program, anchorIdl, finality = "confirmed", options = { limit: 1000 }) {
    const allSignatures = [];
    // Fetch all signatures in sequential batches
    while (true) {
        const signatures = await rpc.getSignaturesForAddress(program, options).send();
        allSignatures.push(...signatures);
        // Update options for the next batch. Set before to the last fetched signature.
        if (signatures.length > 0) {
            options = { ...options, before: signatures[signatures.length - 1].signature };
        }
        if (options.limit && signatures.length < options.limit)
            break; // Exit early if the number of signatures < limit
    }
    // Fetch events for all signatures in parallel
    const eventsWithSlots = await Promise.all(allSignatures.map(async (signatureTransaction) => {
        const events = await readEvents(rpc, signatureTransaction.signature, program, anchorIdl, finality);
        return events.map((event) => ({
            ...event,
            confirmationStatus: signatureTransaction.confirmationStatus || "Unknown",
            blockTime: signatureTransaction.blockTime || 0,
            signature: signatureTransaction.signature,
            slot: signatureTransaction.slot,
            name: event.name || "Unknown",
        }));
    }));
    return eventsWithSlots.flat();
}
/**
 * Reads events from a transaction.
 */
async function readEvents(rpc, txSignature, programId, programIdl, commitment = "confirmed") {
    const txResult = await rpc.getTransaction(txSignature, { commitment, maxSupportedTransactionVersion: 0 }).send();
    if (txResult === null)
        return [];
    return processEventFromTx(txResult, programId, programIdl);
}
/**
 * Processes events from a transaction.
 */
async function processEventFromTx(txResult, programId, programIdl) {
    if (!txResult)
        return [];
    const eventAuthorities = new Map();
    const events = [];
    const [pda] = await web3_v2_js_1.default.getProgramDerivedAddress({ programAddress: programId, seeds: ["__event_authority"] });
    eventAuthorities.set(programId, pda);
    const accountKeys = txResult.transaction.message.accountKeys;
    const messageAccountKeys = [...accountKeys];
    // Order matters here. writable accounts must be processed before readonly accounts.
    // See https://docs.anza.xyz/proposals/versioned-transactions#new-transaction-format
    messageAccountKeys.push(...(txResult?.meta?.loadedAddresses?.writable ?? []));
    messageAccountKeys.push(...(txResult?.meta?.loadedAddresses?.readonly ?? []));
    for (const ixBlock of txResult.meta?.innerInstructions ?? []) {
        for (const ix of ixBlock.instructions) {
            const ixProgramId = messageAccountKeys[ix.programIdIndex];
            const singleIxAccount = ix.accounts.length === 1 ? messageAccountKeys[ix.accounts[0]] : undefined;
            if (ixProgramId !== undefined &&
                singleIxAccount !== undefined &&
                programId == ixProgramId &&
                eventAuthorities.get(ixProgramId.toString()) == singleIxAccount) {
                const ixData = anchor_1.utils.bytes.bs58.decode(ix.data);
                const eventData = anchor_1.utils.bytes.base64.encode(Buffer.from(new Uint8Array(ixData).slice(8)));
                let event = new anchor_1.BorshEventCoder(programIdl).decode(eventData);
                events.push({
                    program: programId,
                    data: event?.data,
                    name: event?.name,
                });
            }
        }
    }
    return events;
}
