import { AccountRole, compileTransactionMessage, createTransactionMessage, getCompiledTransactionMessageEncoder, address as parseAddress, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, } from '@solana/kit';
import { assert, rootLogger } from '@hyperlane-xyz/utils';
import { DEFAULT_COMPUTE_UNITS, LAMPORTS_PER_SIGNATURE, SYSTEM_PROGRAM_ADDRESS, } from '../constants.js';
import { createRpc } from '../rpc.js';
export class SvmProvider {
    rpc;
    rpcUrls;
    static async connect(rpcUrls, _chainId, _extraParams) {
        assert(rpcUrls.length > 0, 'At least one RPC URL is required');
        const rpc = createRpc(rpcUrls[0]);
        return new SvmProvider(rpc, rpcUrls);
    }
    constructor(rpc, rpcUrls) {
        this.rpc = rpc;
        this.rpcUrls = rpcUrls;
    }
    // ### QUERY BASE ###
    async isHealthy() {
        try {
            await this.rpc.getSlot().send();
            return true;
        }
        catch (error) {
            rootLogger.debug('SVM health check failed', { error });
            return false;
        }
    }
    getRpcUrls() {
        return this.rpcUrls;
    }
    async getHeight() {
        const slot = await this.rpc.getSlot().send();
        return Number(slot);
    }
    async getBalance(req) {
        const balance = await this.rpc.getBalance(parseAddress(req.address)).send();
        return balance.value;
    }
    async getTotalSupply(_req) {
        throw new Error('getTotalSupply not supported on Sealevel');
    }
    async estimateTransactionFee(req) {
        const signerAddresses = new Set();
        // Collect signers from instruction account metas
        for (const ix of req.transaction.instructions) {
            if (ix.accounts) {
                for (const account of ix.accounts) {
                    if (account.role >= AccountRole.READONLY_SIGNER) {
                        signerAddresses.add(account.address);
                    }
                }
            }
        }
        // Collect additional signers
        for (const signer of req.transaction.additionalSigners ?? []) {
            signerAddresses.add(signer.address);
        }
        // +1 for fee payer (may already be in the set, but overcount to be safe)
        const numSigners = signerAddresses.size + 1;
        const gasPrice = await this.queryBaseFeePerSignature();
        const fee = BigInt(numSigners) * BigInt(gasPrice);
        const gasUnits = BigInt(req.transaction.computeUnits ?? DEFAULT_COMPUTE_UNITS);
        return { gasUnits, gasPrice, fee };
    }
    /**
     * Queries the RPC for the base fee per signature using a minimal
     * unsigned message.
     */
    async queryBaseFeePerSignature() {
        try {
            const { value: latestBlockhash } = await this.rpc
                .getLatestBlockhash()
                .send();
            const baseMessage = createTransactionMessage({ version: 0 });
            const withFeePayer = setTransactionMessageFeePayer(SYSTEM_PROGRAM_ADDRESS, baseMessage);
            const withLifetime = setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, withFeePayer);
            const compiled = compileTransactionMessage(withLifetime);
            const messageBytes = getCompiledTransactionMessageEncoder().encode(compiled);
            const base64Message = Buffer.from(messageBytes).toString('base64');
            const result = await this.rpc.getFeeForMessage(base64Message).send();
            if (result.value != null) {
                return Number(result.value);
            }
        }
        catch (error) {
            rootLogger.debug('getFeeForMessage failed, using static fallback', {
                error,
            });
        }
        return LAMPORTS_PER_SIGNATURE;
    }
    async isMessageDelivered(_req) {
        throw new Error('isMessageDelivered not supported on Sealevel, use the Artifact API instead');
    }
    // ### QUERY WARP ###
    async getToken(_req) {
        throw new Error('getToken not supported on Sealevel, use the Artifact API instead');
    }
    async getRemoteRouters(_req) {
        throw new Error('getRemoteRouters not supported on Sealevel, use the Artifact API instead');
    }
    async getBridgedSupply(_req) {
        throw new Error('getBridgedSupply not supported on Sealevel, use the Artifact API instead');
    }
    async quoteRemoteTransfer(_req) {
        throw new Error('quoteRemoteTransfer not supported on Sealevel, use the Artifact API instead');
    }
}
//# sourceMappingURL=provider.js.map