"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CachedSolanaRpcFactory = void 0;
const rpc_transformers_1 = require("@solana/rpc-transformers");
const superstruct_1 = require("superstruct");
const baseRpcFactories_1 = require("./baseRpcFactories");
const utils_1 = require("../utils");
const utils_2 = require("../../utils");
const retryRpcFactory_1 = require("./retryRpcFactory");
class CachedSolanaRpcFactory extends baseRpcFactories_1.SolanaClusterRpcFactory {
    redisClient;
    getTransactionCachePrefix;
    retryTransport;
    retryRpcClient;
    constructor(providerCacheNamespace, redisClient, ...retryConstructorParams) {
        const superParams = retryConstructorParams.slice(-2);
        super(...superParams);
        this.redisClient = redisClient;
        const retryRpcFactory = new retryRpcFactory_1.RetrySolanaRpcFactory(...retryConstructorParams);
        this.retryTransport = retryRpcFactory.createTransport();
        this.retryRpcClient = retryRpcFactory.createRpcClient();
        const cachePrefix = `${providerCacheNamespace},${new URL(this.clusterUrl).hostname},${this.chainId}`;
        this.getTransactionCachePrefix = `${cachePrefix}:getTransaction,`;
    }
    createTransport() {
        return async (...args) => {
            const { method, params } = args[0].payload;
            const cacheType = this.redisClient ? this.cacheType(method) : utils_1.CacheType.NONE;
            if (cacheType === utils_1.CacheType.NONE) {
                return this.retryTransport(...args);
            }
            const redisKey = this.buildRedisKey(method, params);
            const redisResult = await this.redisClient?.get(redisKey);
            if (redisResult) {
                return JSON.parse(redisResult, utils_2.jsonReviverWithBigInts);
            }
            return this.requestAndCacheFinalized(...args);
        };
    }
    async requestAndCacheFinalized(...args) {
        const { method, params } = args[0].payload;
        if (method !== "getTransaction")
            return this.retryTransport(...args);
        if (!this.isGetTransactionParams(params))
            return this.retryTransport(...args);
        try {
            const getSignatureStatusesResponse = await this.retryRpcClient
                .getSignatureStatuses([params[0]], {
                searchTransactionHistory: true,
            })
                .send();
            if (getSignatureStatusesResponse.value[0]?.confirmationStatus !== "finalized") {
                return this.retryTransport(...args);
            }
        }
        catch {
            return this.retryTransport(...args);
        }
        const getTransactionResponse = await this.retryTransport(...args);
        try {
            (0, rpc_transformers_1.getThrowSolanaErrorResponseTransformer)()(getTransactionResponse, { methodName: method, params });
        }
        catch {
            return getTransactionResponse;
        }
        const redisKey = this.buildRedisKey(method, params);
        await this.redisClient?.set(redisKey, JSON.stringify(getTransactionResponse, utils_2.jsonReplacerWithBigInts), Number.POSITIVE_INFINITY);
        return getTransactionResponse;
    }
    buildRedisKey(method, params) {
        switch (method) {
            case "getTransaction":
                return this.getTransactionCachePrefix + JSON.stringify(params, utils_2.jsonReplacerWithBigInts);
            default:
                throw new Error(`CachedSolanaRpcFactory::buildRedisKey: invalid JSON-RPC method ${method}`);
        }
    }
    cacheType(method) {
        if (method === "getTransaction") {
            return utils_1.CacheType.NO_TTL;
        }
        else {
            return utils_1.CacheType.NONE;
        }
    }
    isGetTransactionParams(params) {
        return (0, superstruct_1.is)(params, (0, superstruct_1.tuple)([
            (0, superstruct_1.string)(),
            (0, superstruct_1.optional)((0, superstruct_1.object)()),
        ]));
    }
}
exports.CachedSolanaRpcFactory = CachedSolanaRpcFactory;
//# sourceMappingURL=cachedRpcFactory.js.map