"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findProgramAddress = findProgramAddress;
exports.readEvents = readEvents;
exports.processEventFromTx = processEventFromTx;
exports.readEventsUntilFound = readEventsUntilFound;
exports.getEvent = getEvent;
exports.readProgramEvents = readProgramEvents;
exports.subscribeToCpiEventsForProgram = subscribeToCpiEventsForProgram;
exports.stringifyCpiEvent = stringifyCpiEvent;
var tslib_1 = require("tslib");
var anchor_1 = require("@coral-xyz/anchor");
var web3_js_1 = require("@solana/web3.js");
var borsh_1 = require("borsh");
var conversionUtils_1 = require("./conversionUtils");
function findProgramAddress(label, program, extraSeeds) {
    var seeds = [Buffer.from(anchor_1.utils.bytes.utf8.encode(label))];
    if (extraSeeds) {
        for (var _i = 0, extraSeeds_1 = extraSeeds; _i < extraSeeds_1.length; _i++) {
            var extraSeed = extraSeeds_1[_i];
            if (typeof extraSeed === "string") {
                seeds.push(Buffer.from(anchor_1.utils.bytes.utf8.encode(extraSeed)));
            }
            else if (Array.isArray(extraSeed)) {
                seeds.push(Buffer.from(extraSeed));
            }
            else if (Buffer.isBuffer(extraSeed)) {
                seeds.push(extraSeed);
            }
            else {
                seeds.push(extraSeed.toBuffer());
            }
        }
    }
    var res = web3_js_1.PublicKey.findProgramAddressSync(seeds, program);
    return { publicKey: res[0], bump: res[1] };
}
function readEvents(connection_1, txSignature_1, programs_1) {
    return tslib_1.__awaiter(this, arguments, void 0, function (connection, txSignature, programs, commitment) {
        var txResult;
        if (commitment === void 0) { commitment = "confirmed"; }
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4, connection.getTransaction(txSignature, { commitment: commitment, maxSupportedTransactionVersion: 0 })];
                case 1:
                    txResult = _a.sent();
                    if (txResult === null)
                        return [2, []];
                    return [2, processEventFromTx(txResult, programs)];
            }
        });
    });
}
function processEventFromTx(txResult, programs) {
    var _a, _b, _c, _d;
    var eventAuthorities = new Map();
    for (var _i = 0, programs_1 = programs; _i < programs_1.length; _i++) {
        var program = programs_1[_i];
        eventAuthorities.set(program.programId.toString(), findProgramAddress("__event_authority", program.programId).publicKey);
    }
    var events = [];
    var messageAccountKeys = txResult.transaction.message.getAccountKeys({
        accountKeysFromLookups: (_a = txResult.meta) === null || _a === void 0 ? void 0 : _a.loadedAddresses,
    });
    for (var _e = 0, _f = (_c = (_b = txResult.meta) === null || _b === void 0 ? void 0 : _b.innerInstructions) !== null && _c !== void 0 ? _c : []; _e < _f.length; _e++) {
        var ixBlock = _f[_e];
        for (var _g = 0, _h = ixBlock.instructions; _g < _h.length; _g++) {
            var ix = _h[_g];
            for (var _j = 0, programs_2 = programs; _j < programs_2.length; _j++) {
                var program = programs_2[_j];
                var ixProgramId = messageAccountKeys.get(ix.programIdIndex);
                var singleIxAccount = ix.accounts.length === 1 ? messageAccountKeys.get(ix.accounts[0]) : undefined;
                if (ixProgramId !== undefined &&
                    singleIxAccount !== undefined &&
                    program.programId.equals(ixProgramId) &&
                    ((_d = eventAuthorities.get(ixProgramId.toString())) === null || _d === void 0 ? void 0 : _d.equals(singleIxAccount))) {
                    var ixData = anchor_1.utils.bytes.bs58.decode(ix.data);
                    var eventData = anchor_1.utils.bytes.base64.encode(Buffer.from(new Uint8Array(ixData).slice(8)));
                    var event_1 = program.coder.events.decode(eventData);
                    events.push({
                        program: program.programId,
                        data: event_1 === null || event_1 === void 0 ? void 0 : event_1.data,
                        name: event_1 === null || event_1 === void 0 ? void 0 : event_1.name,
                    });
                }
            }
        }
    }
    return events;
}
function readEventsUntilFound(connection, txSignature, programs) {
    return tslib_1.__awaiter(this, void 0, void 0, function () {
        var startTime, txResult;
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    startTime = Date.now();
                    txResult = null;
                    _a.label = 1;
                case 1:
                    if (!(Date.now() - startTime < 5000)) return [3, 4];
                    return [4, connection.getTransaction(txSignature, {
                            commitment: "confirmed",
                            maxSupportedTransactionVersion: 0,
                        })];
                case 2:
                    txResult = _a.sent();
                    if (txResult !== null)
                        return [2, processEventFromTx(txResult, programs)];
                    return [4, new Promise(function (resolve) { return setTimeout(resolve, 50); })];
                case 3:
                    _a.sent();
                    return [3, 1];
                case 4: throw new Error("No event found within 5 seconds");
            }
        });
    });
}
function getEvent(events, program, eventName) {
    for (var _i = 0, events_1 = events; _i < events_1.length; _i++) {
        var event_2 = events_1[_i];
        if (event_2.name === eventName && program.toString() === event_2.program.toString()) {
            return event_2.data;
        }
    }
    throw new Error("Event " + eventName + " not found");
}
function readProgramEvents(connection_1, program_1) {
    return tslib_1.__awaiter(this, arguments, void 0, function (connection, program, finality, options) {
        var allSignatures, signatures, eventsWithSlots;
        var _this = this;
        if (finality === void 0) { finality = "confirmed"; }
        if (options === void 0) { options = { limit: 1000 }; }
        return tslib_1.__generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    allSignatures = [];
                    _a.label = 1;
                case 1: return [4, connection.getSignaturesForAddress(program.programId, options, finality)];
                case 2:
                    signatures = _a.sent();
                    allSignatures.push.apply(allSignatures, signatures);
                    if (signatures.length > 0) {
                        options = tslib_1.__assign(tslib_1.__assign({}, options), { before: signatures[signatures.length - 1].signature });
                    }
                    if (options.limit && signatures.length < options.limit)
                        return [3, 4];
                    _a.label = 3;
                case 3: return [3, 1];
                case 4: return [4, Promise.all(allSignatures.map(function (signature) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                        var events;
                        return tslib_1.__generator(this, function (_a) {
                            switch (_a.label) {
                                case 0: return [4, readEvents(connection, signature.signature, [program], finality)];
                                case 1:
                                    events = _a.sent();
                                    return [2, events.map(function (event) { return (tslib_1.__assign(tslib_1.__assign({}, event), { confirmationStatus: signature.confirmationStatus || "Unknown", blockTime: signature.blockTime || 0, signature: signature.signature, slot: signature.slot, name: event.name || "Unknown" })); })];
                            }
                        });
                    }); }))];
                case 5:
                    eventsWithSlots = _a.sent();
                    return [2, eventsWithSlots.flat()];
            }
        });
    });
}
function subscribeToCpiEventsForProgram(connection, program, callback) {
    var _this = this;
    var subscriptionId = connection.onLogs(new web3_js_1.PublicKey(findProgramAddress("__event_authority", program.programId).publicKey.toString()), function (logs) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
        var _a;
        return tslib_1.__generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    _a = callback;
                    return [4, readEvents(connection, logs.signature, [program], "confirmed")];
                case 1:
                    _a.apply(void 0, [_b.sent()]);
                    return [2];
            }
        });
    }); }, "confirmed");
    return subscriptionId;
}
var DepositId = (function () {
    function DepositId(properties) {
        this.value = properties.value;
    }
    return DepositId;
}());
var depositIdSchema = new Map([[DepositId, { kind: "struct", fields: [["value", [32]]] }]]);
function parseDepositId(value) {
    var restAreZero = value.slice(4).every(function (byte) { return byte === 0; });
    if (restAreZero) {
        var u32Value = new DataView(value.buffer).getUint32(0, true);
        return u32Value.toString();
    }
    var depositId = borsh_1.deserialize(depositIdSchema, DepositId, Buffer.from(value));
    return new anchor_1.BN(depositId.value).toString();
}
function stringifyCpiEvent(obj, eventName) {
    var _a, _b;
    if ((_b = (_a = obj === null || obj === void 0 ? void 0 : obj.constructor) === null || _a === void 0 ? void 0 : _a.toString()) === null || _b === void 0 ? void 0 : _b.includes("PublicKey")) {
        if (obj.toString().startsWith("111111111111")) {
            return (0, conversionUtils_1.publicKeyToEvmAddress)(obj);
        }
        return obj.toString();
    }
    else if (anchor_1.BN.isBN(obj)) {
        return obj.toString();
    }
    else if (typeof obj === "bigint") {
        return obj.toString();
    }
    else if (Array.isArray(obj) && obj.length == 32) {
        return Buffer.from(obj).toString("hex");
    }
    else if (Array.isArray(obj)) {
        return obj.map(function (obj) { return stringifyCpiEvent(obj, eventName); });
    }
    else if (obj !== null && typeof obj === "object") {
        return Object.fromEntries(Object.entries(obj).map(function (_a) {
            var key = _a[0], value = _a[1];
            if (Array.isArray(value) && value.length === 32) {
                if (key === "depositId" || key === "deposit_id") {
                    var parsedValue = parseDepositId(new Uint8Array(value));
                    return [key, parsedValue];
                }
                else if (eventName.toLowerCase() === "fundsdeposited" &&
                    (key === "outputAmount" || key === "output_amount")) {
                    return [key, (0, conversionUtils_1.u8Array32ToInt)(new Uint8Array(value)).toString()];
                }
                else if ((eventName.toLowerCase() === "filledrelay" || eventName.toLowerCase() === "requestedslowfill") &&
                    (key === "inputAmount" || key === "input_amount")) {
                    return [key, (0, conversionUtils_1.u8Array32ToInt)(new Uint8Array(value)).toString()];
                }
            }
            return [key, stringifyCpiEvent(value, eventName)];
        }));
    }
    return obj;
}
//# sourceMappingURL=solanaProgramUtils.js.map