"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SvmCpiEventsClient = void 0;
const tslib_1 = require("tslib");
const contracts_1 = require("@across-protocol/contracts");
const web3_v1_1 = require("@across-protocol/contracts/dist/src/svm/web3-v1");
const kit_1 = require("@solana/kit");
const utils_1 = require("../../utils");
const utils_2 = require("./utils");
const _1 = require("./");
const assert_1 = tslib_1.__importDefault(require("assert"));
class SvmCpiEventsClient {
    rpc;
    programAddress;
    programEventAuthority;
    idl;
    constructor(rpc, address, eventAuthority, idl) {
        this.rpc = rpc;
        this.programAddress = address;
        this.programEventAuthority = eventAuthority;
        this.idl = idl;
    }
    static async create(rpc) {
        const isTestnet = await (0, utils_2.isDevnet)(rpc);
        const programId = (0, contracts_1.getDeployedAddress)("SvmSpoke", (0, web3_v1_1.getSolanaChainId)(isTestnet ? "devnet" : "mainnet").toString());
        if (!programId)
            throw new Error("Program not found");
        return this.createFor(rpc, programId, contracts_1.SvmSpokeIdl);
    }
    static async createFor(rpc, programId, idl) {
        const programAddress = (0, kit_1.address)(programId);
        const [eventAuthority] = await (0, kit_1.getProgramDerivedAddress)({
            programAddress,
            seeds: ["__event_authority"],
        });
        return new SvmCpiEventsClient(rpc, programAddress, eventAuthority, idl);
    }
    async queryEvents(eventName, fromSlot, toSlot, options = { limit: 1000, commitment: "confirmed" }) {
        const events = await this.queryAllEvents(fromSlot, toSlot, options);
        return events.filter((event) => event.name === eventName);
    }
    async queryDerivedAddressEvents(eventName, derivedAddress, fromSlot, toSlot, options = { limit: 1000, commitment: "confirmed" }) {
        const events = await this.queryAllEvents(fromSlot, toSlot, options, derivedAddress);
        return events.filter((event) => event.name === eventName);
    }
    async queryAllEvents(fromSlot, toSlot, options = { limit: 1000, commitment: "confirmed" }, derivedAddress) {
        const addressToQuery = derivedAddress || this.programAddress;
        const allSignatures = [];
        let hasMoreSignatures = true;
        let currentOptions = { ...options, encoding: "json" };
        while (hasMoreSignatures) {
            const signatures = await this.rpc
                .getSignaturesForAddress(addressToQuery, currentOptions)
                .send();
            allSignatures.push(...signatures);
            if (signatures.length > 0) {
                currentOptions = { ...currentOptions, before: signatures[signatures.length - 1].signature };
            }
            if (fromSlot && allSignatures.length > 0 && allSignatures[allSignatures.length - 1].slot < fromSlot) {
                hasMoreSignatures = false;
            }
            hasMoreSignatures = Boolean(hasMoreSignatures && currentOptions.limit && signatures.length === currentOptions.limit);
        }
        const filteredSignatures = allSignatures.filter((signatureTransaction) => {
            if (fromSlot && signatureTransaction.slot < fromSlot)
                return false;
            if (toSlot && signatureTransaction.slot > toSlot)
                return false;
            return true;
        });
        const eventsWithSlots = await Promise.all(filteredSignatures.map(async (signatureTransaction) => {
            const events = await this.readEventsFromSignature(signatureTransaction.signature, options.commitment);
            return events.map((event) => ({
                ...event,
                confirmationStatus: signatureTransaction.confirmationStatus,
                blockTime: signatureTransaction.blockTime,
                signature: signatureTransaction.signature,
                slot: signatureTransaction.slot,
            }));
        }));
        return eventsWithSlots.flat();
    }
    async readEventsFromSignature(txSignature, commitment = "confirmed") {
        const txResult = await this.rpc
            .getTransaction(txSignature, { commitment, maxSupportedTransactionVersion: 0, encoding: "json" })
            .send();
        return this.processEventFromTx(txResult);
    }
    processEventFromTx(txResult) {
        if (!(0, utils_1.isDefined)(txResult) || (0, utils_1.isDefined)(txResult.meta?.err))
            return [];
        const events = [];
        const accountKeys = txResult.transaction.message.accountKeys;
        const messageAccountKeys = [...accountKeys];
        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 &&
                    this.programAddress === ixProgramId &&
                    this.programEventAuthority === singleIxAccount) {
                    const ixData = utils_1.bs58.decode(ix.data);
                    const eventData = Buffer.from(ixData.slice(8)).toString("base64");
                    const { name, data } = (0, utils_2.decodeEvent)(this.idl, eventData);
                    events.push({ program: this.programAddress, name, data });
                }
            }
        }
        return events;
    }
    async getDepositEventsFromSignature(originChainId, txSignature, commitment = "confirmed") {
        (0, assert_1.default)((0, utils_1.chainIsSvm)(originChainId), `Origin chain ${originChainId} is not an SVM chain`);
        const [events, txDetails] = await Promise.all([
            this.readEventsFromSignature(txSignature, commitment),
            this.rpc
                .getTransaction(txSignature, {
                commitment,
                maxSupportedTransactionVersion: 0,
                encoding: "json",
            })
                .send(),
        ]);
        const depositEvents = events?.filter((event) => event?.name === "FundsDeposited");
        if (!txDetails || !depositEvents?.length) {
            return;
        }
        return depositEvents.map((event) => {
            const unwrappedEventArgs = (0, _1.unwrapEventData)(event, [
                "depositId",
                "outputAmount",
            ]);
            const { data } = unwrappedEventArgs;
            return {
                ...data,
                depositor: (0, utils_1.toAddressType)(data.depositor, data.originChainId),
                recipient: (0, utils_1.toAddressType)(data.recipient, data.destinationChainId),
                exclusiveRelayer: (0, utils_1.toAddressType)(data.exclusiveRelayer, data.destinationChainId),
                inputToken: (0, utils_1.toAddressType)(data.inputToken, data.originChainId),
                outputToken: (0, utils_1.toAddressType)(data.outputToken, data.destinationChainId),
                depositTimestamp: Number(txDetails.blockTime),
                originChainId,
                messageHash: (0, utils_1.getMessageHash)(data.message),
                blockNumber: Number(txDetails.slot),
                txnIndex: 0,
                txnRef: txSignature,
                logIndex: 0,
            };
        });
    }
    async getFillEventsFromSignature(destinationChainId, txSignature, commitment = "confirmed") {
        (0, assert_1.default)((0, utils_1.chainIsSvm)(destinationChainId), `Destination chain ${destinationChainId} is not an SVM chain`);
        const [events, txDetails] = await Promise.all([
            this.readEventsFromSignature(txSignature, commitment),
            this.rpc
                .getTransaction(txSignature, {
                commitment,
                maxSupportedTransactionVersion: 0,
                encoding: "json",
            })
                .send(),
        ]);
        const fillEvents = events?.filter((event) => event?.name === "FilledRelay");
        if (!txDetails || !fillEvents?.length) {
            return;
        }
        return fillEvents.map((event) => {
            const unwrappedEventData = (0, _1.unwrapEventData)(event, [
                "depositId",
                "inputAmount",
            ]);
            const { data } = unwrappedEventData;
            return {
                ...data,
                depositor: (0, utils_1.toAddressType)(data.depositor, data.originChainId),
                recipient: (0, utils_1.toAddressType)(data.recipient, data.destinationChainId),
                exclusiveRelayer: (0, utils_1.toAddressType)(data.exclusiveRelayer, data.destinationChainId),
                inputToken: (0, utils_1.toAddressType)(data.inputToken, data.originChainId),
                outputToken: (0, utils_1.toAddressType)(data.outputToken, data.destinationChainId),
                fillTimestamp: Number(txDetails.blockTime),
                blockNumber: Number(txDetails.slot),
                txnRef: txSignature,
                txnIndex: 0,
                logIndex: 0,
                destinationChainId,
            };
        });
    }
    getProgramAddress() {
        return this.programAddress;
    }
    getRpc() {
        return this.rpc;
    }
}
exports.SvmCpiEventsClient = SvmCpiEventsClient;
//# sourceMappingURL=eventsClient.js.map