var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { FailedToConfirmTransactionError, FailedToConfirmTransactionWithResponseError, FailedToSendTransactionError, ParsedProgramError, UnknownProgramError, } from '../../errors';
import { assertSol, getSignerHistogram, isErrorWithLogs, lamports, } from '../../types';
import { zipMap } from '../../utils';
/**
 * @group Modules
 */
export class RpcClient {
    constructor(metaplex) {
        this.metaplex = metaplex;
    }
    prepareTransaction(transaction, signers) {
        return __awaiter(this, void 0, void 0, function* () {
            let blockhashWithExpiryBlockHeight;
            if (!('records' in transaction) &&
                transaction.recentBlockhash &&
                transaction.lastValidBlockHeight) {
                blockhashWithExpiryBlockHeight = {
                    blockhash: transaction.recentBlockhash,
                    lastValidBlockHeight: transaction.lastValidBlockHeight,
                };
            }
            else {
                blockhashWithExpiryBlockHeight = yield this.getLatestBlockhash();
            }
            if ('records' in transaction) {
                signers = [...transaction.getSigners(), ...signers];
                transaction = transaction.toTransaction(blockhashWithExpiryBlockHeight);
            }
            return { transaction, signers, blockhashWithExpiryBlockHeight };
        });
    }
    signTransaction(transaction, signers) {
        return __awaiter(this, void 0, void 0, function* () {
            const { keypairs, identities } = getSignerHistogram(signers);
            // Keypair signers.
            if (keypairs.length > 0) {
                transaction.partialSign(...keypairs);
            }
            // Identity signers.
            for (let i = 0; i < identities.length; i++) {
                yield identities[i].signTransaction(transaction);
            }
            return transaction;
        });
    }
    sendTransaction(transaction, sendOptions = {}, signers = []) {
        return __awaiter(this, void 0, void 0, function* () {
            const prepared = yield this.prepareTransaction(transaction, signers);
            transaction = prepared.transaction;
            signers = prepared.signers;
            const defaultFeePayer = this.getDefaultFeePayer();
            if (!transaction.feePayer && defaultFeePayer) {
                transaction.feePayer = defaultFeePayer.publicKey;
                signers = [defaultFeePayer, ...signers];
            }
            transaction = yield this.signTransaction(transaction, signers);
            const rawTransaction = transaction.serialize();
            try {
                return yield this.metaplex.connection.sendRawTransaction(rawTransaction, sendOptions);
            }
            catch (error) {
                throw this.parseProgramError(error, transaction);
            }
        });
    }
    confirmTransaction(signature, blockhashWithExpiryBlockHeight, commitment) {
        return __awaiter(this, void 0, void 0, function* () {
            let rpcResponse;
            try {
                rpcResponse = yield this.metaplex.connection.confirmTransaction(Object.assign({ signature }, blockhashWithExpiryBlockHeight), commitment);
            }
            catch (error) {
                throw new FailedToConfirmTransactionError(error);
            }
            if (rpcResponse.value.err) {
                throw new FailedToConfirmTransactionWithResponseError(rpcResponse);
            }
            return rpcResponse;
        });
    }
    sendAndConfirmTransaction(transaction, confirmOptions, signers = []) {
        return __awaiter(this, void 0, void 0, function* () {
            const prepared = yield this.prepareTransaction(transaction, signers);
            const { blockhashWithExpiryBlockHeight } = prepared;
            transaction = prepared.transaction;
            signers = prepared.signers;
            const signature = yield this.sendTransaction(transaction, confirmOptions, signers);
            const confirmResponse = yield this.confirmTransaction(signature, blockhashWithExpiryBlockHeight, confirmOptions === null || confirmOptions === void 0 ? void 0 : confirmOptions.commitment);
            return Object.assign({ signature, confirmResponse }, blockhashWithExpiryBlockHeight);
        });
    }
    getAccount(publicKey, commitment) {
        return __awaiter(this, void 0, void 0, function* () {
            const accountInfo = yield this.metaplex.connection.getAccountInfo(publicKey, commitment);
            return this.getUnparsedMaybeAccount(publicKey, accountInfo);
        });
    }
    accountExists(publicKey, commitment) {
        return __awaiter(this, void 0, void 0, function* () {
            const balance = yield this.metaplex.connection.getBalance(publicKey, commitment);
            return balance > 0;
        });
    }
    getMultipleAccounts(publicKeys, commitment) {
        return __awaiter(this, void 0, void 0, function* () {
            const accountInfos = yield this.metaplex.connection.getMultipleAccountsInfo(publicKeys, commitment);
            return zipMap(publicKeys, accountInfos, (publicKey, accountInfo) => {
                return this.getUnparsedMaybeAccount(publicKey, accountInfo);
            });
        });
    }
    getProgramAccounts(programId, configOrCommitment) {
        return __awaiter(this, void 0, void 0, function* () {
            const accounts = yield this.metaplex.connection.getProgramAccounts(programId, configOrCommitment);
            return accounts.map(({ pubkey, account }) => (Object.assign(Object.assign({}, account), { publicKey: pubkey, lamports: lamports(account.lamports) })));
        });
    }
    airdrop(publicKey, amount, commitment) {
        return __awaiter(this, void 0, void 0, function* () {
            assertSol(amount);
            const signature = yield this.metaplex.connection.requestAirdrop(publicKey, amount.basisPoints.toNumber());
            const blockhashWithExpiryBlockHeight = yield this.getLatestBlockhash();
            const confirmResponse = yield this.confirmTransaction(signature, blockhashWithExpiryBlockHeight, commitment);
            return Object.assign({ signature, confirmResponse }, blockhashWithExpiryBlockHeight);
        });
    }
    getBalance(publicKey, commitment) {
        return __awaiter(this, void 0, void 0, function* () {
            const balance = yield this.metaplex.connection.getBalance(publicKey, commitment);
            return lamports(balance);
        });
    }
    getRent(bytes, commitment) {
        return __awaiter(this, void 0, void 0, function* () {
            const rent = yield this.metaplex.connection.getMinimumBalanceForRentExemption(bytes, commitment);
            return lamports(rent);
        });
    }
    getLatestBlockhash(commitmentOrConfig = 'finalized') {
        return __awaiter(this, void 0, void 0, function* () {
            return this.metaplex.connection.getLatestBlockhash(commitmentOrConfig);
        });
    }
    getSolanaExporerUrl(signature) {
        let clusterParam = '';
        switch (this.metaplex.cluster) {
            case 'devnet':
                clusterParam = '?cluster=devnet';
                break;
            case 'testnet':
                clusterParam = '?cluster=testnet';
                break;
            case 'localnet':
            case 'custom':
                const url = encodeURIComponent(this.metaplex.connection.rpcEndpoint);
                clusterParam = `?cluster=custom&customUrl=${url}`;
                break;
        }
        return `https://explorer.solana.com/tx/${signature}${clusterParam}`;
    }
    setDefaultFeePayer(payer) {
        this.defaultFeePayer = payer;
        return this;
    }
    getDefaultFeePayer() {
        return this.defaultFeePayer
            ? this.defaultFeePayer
            : this.metaplex.identity();
    }
    getUnparsedMaybeAccount(publicKey, accountInfo) {
        if (!accountInfo) {
            return { publicKey, exists: false };
        }
        return Object.assign(Object.assign({}, accountInfo), { publicKey, exists: true, lamports: lamports(accountInfo.lamports) });
    }
    parseProgramError(error, transaction) {
        var _a, _b, _c, _d, _e;
        // Ensure the error as logs.
        if (!isErrorWithLogs(error)) {
            return new FailedToSendTransactionError(error);
        }
        // Parse the instruction number.
        const regex = /Error processing Instruction (\d+):/;
        const instruction = (_b = (_a = error.message.match(regex)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : null;
        // Ensure there is an instruction number given to find the program.
        if (!instruction) {
            return new FailedToSendTransactionError(error);
        }
        // Get the program ID from the instruction in the transaction.
        const instructionNumber = parseInt(instruction, 10);
        const programId = (_e = (_d = (_c = transaction.instructions) === null || _c === void 0 ? void 0 : _c[instructionNumber]) === null || _d === void 0 ? void 0 : _d.programId) !== null && _e !== void 0 ? _e : null;
        // Ensure we were able to find a program ID for the instruction.
        if (!programId) {
            return new FailedToSendTransactionError(error);
        }
        // Find a registered program if any.
        let program;
        try {
            program = this.metaplex.programs().get(programId);
        }
        catch (_programNotFoundError) {
            return new FailedToSendTransactionError(error);
        }
        // Ensure an error resolver exists on the program.
        if (!program.errorResolver) {
            return new UnknownProgramError(program, error);
        }
        // Finally, resolve the error.
        const resolvedError = program.errorResolver(error);
        return resolvedError
            ? new ParsedProgramError(program, resolvedError)
            : new UnknownProgramError(program, error);
    }
}
//# sourceMappingURL=RpcClient.js.map