"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RetrySolanaRpcFactory = void 0;
const kit_1 = require("@solana/kit");
const rpc_transformers_1 = require("@solana/rpc-transformers");
const svm_1 = require("../../arch/svm");
const utils_1 = require("../../utils");
const baseRpcFactories_1 = require("./baseRpcFactories");
const rateLimitedRpcFactory_1 = require("./rateLimitedRpcFactory");
const utils_2 = require("./utils");
class RetrySolanaRpcFactory extends baseRpcFactories_1.SolanaClusterRpcFactory {
    retries;
    retryDelaySeconds;
    rateLimitedTransport;
    logger;
    constructor(retries, retryDelaySeconds, ...rateLimitedConstructorParams) {
        const superParams = rateLimitedConstructorParams.slice(-2);
        super(...superParams);
        this.retries = retries;
        this.retryDelaySeconds = retryDelaySeconds;
        if (this.retries < 0 || !Number.isInteger(this.retries)) {
            throw new Error(`retries cannot be < 0 and must be an integer. Currently set to ${this.retries}`);
        }
        if (this.retryDelaySeconds < 0) {
            throw new Error(`retryDelaySeconds cannot be < 0. Currently set to ${this.retryDelaySeconds}`);
        }
        const rateLimitedRpcFactory = new rateLimitedRpcFactory_1.RateLimitedSolanaRpcFactory(...rateLimitedConstructorParams);
        this.rateLimitedTransport = rateLimitedRpcFactory.createTransport();
        this.logger = rateLimitedRpcFactory.logger;
    }
    createTransport() {
        return (...args) => {
            return this._tryCall(() => this.rateLimitedTransport(...args), args);
        };
    }
    async _tryCall(transportCall, args) {
        const { method, params } = args[0].payload;
        let retryAttempt = 0;
        while (true) {
            try {
                const response = await transportCall();
                (0, rpc_transformers_1.getThrowSolanaErrorResponseTransformer)()(response, { methodName: method, params });
                return response;
            }
            catch (error) {
                if (retryAttempt++ >= this.retries || (0, utils_2.shouldFailImmediate)(method, error)) {
                    throw error;
                }
                const { retryDelaySeconds } = this;
                const exponentialBackoff = retryDelaySeconds * Math.pow(2, retryAttempt - 1);
                const jitter = 1 + 2 * Math.random();
                const delayS = this.isRateLimitResponse(error) ? exponentialBackoff + jitter : retryDelaySeconds;
                await (0, utils_1.delay)(delayS);
            }
        }
    }
    isRateLimitResponse(error) {
        if (!(0, svm_1.isSolanaError)(error)) {
            return false;
        }
        const { __code: code } = error.context;
        return code === kit_1.SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR && error.context.statusCode === 429;
    }
}
exports.RetrySolanaRpcFactory = RetrySolanaRpcFactory;
//# sourceMappingURL=retryRpcFactory.js.map