"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SolanaPlatform = void 0;
const connect_sdk_1 = require("@wormhole-foundation/connect-sdk");
const chain_1 = require("./chain");
const spl_token_1 = require("@solana/spl-token");
const web3_js_1 = require("@solana/web3.js");
const address_1 = require("./address");
const types_1 = require("./types");
const lodash_1 = require("lodash");
/**
 * @category Solana
 */
class SolanaPlatform extends connect_sdk_1.PlatformContext {
    static _platform = types_1._platform;
    constructor(network, config) {
        super(network, config ?? (0, connect_sdk_1.networkPlatformConfigs)(network, SolanaPlatform._platform));
    }
    getRpc(chain, config = {
        commitment: 'confirmed',
        disableRetryOnRateLimit: true,
    }) {
        if (chain in this.config)
            return new web3_js_1.Connection(this.config[chain].rpc, config);
        throw new Error('No configuration available for chain: ' + chain);
    }
    getChain(chain, rpc) {
        if (chain in this.config)
            return new chain_1.SolanaChain(chain, this, rpc);
        throw new Error('No configuration available for chain: ' + chain);
    }
    static nativeTokenId(network, chain) {
        if (!SolanaPlatform.isSupportedChain(chain))
            throw new Error(`invalid chain: ${chain}`);
        return connect_sdk_1.Wormhole.chainAddress(chain, address_1.SolanaZeroAddress);
    }
    static isNativeTokenId(network, chain, tokenId) {
        if (!this.isSupportedChain(chain))
            return false;
        if (tokenId.chain !== chain)
            return false;
        const native = this.nativeTokenId(network, chain);
        return native == tokenId;
    }
    static isSupportedChain(chain) {
        const platform = (0, connect_sdk_1.chainToPlatform)(chain);
        return platform === SolanaPlatform._platform;
    }
    static async getDecimals(chain, rpc, token) {
        if ((0, lodash_1.isNative)(token))
            return connect_sdk_1.decimals.nativeDecimals(SolanaPlatform._platform);
        let mint = await rpc.getParsedAccountInfo(new address_1.SolanaAddress(token).unwrap());
        if (!mint || !mint.value)
            throw new Error('could not fetch token details');
        const { decimals: numDecimals } = mint.value.data
            .parsed.info;
        return numDecimals;
    }
    static async getBalance(chain, rpc, walletAddress, token) {
        if ((0, lodash_1.isNative)(token))
            return BigInt(await rpc.getBalance(new web3_js_1.PublicKey(walletAddress)));
        const splToken = await rpc.getTokenAccountsByOwner(new web3_js_1.PublicKey(walletAddress), { mint: new address_1.SolanaAddress(token).unwrap() });
        if (!splToken.value[0])
            return null;
        const balance = await rpc.getTokenAccountBalance(splToken.value[0].pubkey);
        return BigInt(balance.value.amount);
    }
    static async getBalances(chain, rpc, walletAddress, tokens) {
        let native;
        if (tokens.includes('native')) {
            native = BigInt(await rpc.getBalance(new web3_js_1.PublicKey(walletAddress)));
        }
        const splParsedTokenAccounts = await rpc.getParsedTokenAccountsByOwner(new web3_js_1.PublicKey(walletAddress), {
            programId: new web3_js_1.PublicKey(spl_token_1.TOKEN_PROGRAM_ID),
        });
        const balancesArr = tokens.map((token) => {
            if ((0, lodash_1.isNative)(token)) {
                return { ['native']: native };
            }
            const addrString = new address_1.SolanaAddress(token).toString();
            const amount = splParsedTokenAccounts.value.find((v) => v?.account.data.parsed?.info?.mint === token)?.account.data.parsed?.info?.tokenAmount?.amount;
            if (!amount)
                return { [addrString]: null };
            return { [addrString]: BigInt(amount) };
        });
        return balancesArr.reduce((obj, item) => Object.assign(obj, item), {});
    }
    static async sendWait(chain, rpc, stxns, opts) {
        const { blockhash, lastValidBlockHeight } = await this.latestBlock(rpc);
        const txhashes = await Promise.all(stxns.map((stxn) => rpc.sendRawTransaction(stxn, 
        // Set the commitment level to match the rpc commitment level
        // otherwise, it defaults to finalized
        opts ?? { preflightCommitment: rpc.commitment })));
        const results = await Promise.all(txhashes.map((signature) => {
            return rpc.confirmTransaction({
                signature,
                blockhash,
                lastValidBlockHeight,
            }, rpc.commitment);
        }));
        const erroredTxs = results
            .filter((result) => result.value.err)
            .map((result) => result.value.err);
        if (erroredTxs.length > 0)
            throw new Error(`Failed to confirm transaction: ${erroredTxs}`);
        return txhashes;
    }
    static async latestBlock(rpc, commitment) {
        return rpc.getLatestBlockhash(commitment ?? rpc.commitment);
    }
    static async getLatestBlock(rpc) {
        return await rpc.getSlot();
    }
    static async getLatestFinalizedBlock(rpc) {
        const { lastValidBlockHeight } = await this.latestBlock(rpc, 'finalized');
        return lastValidBlockHeight;
    }
    static chainFromChainId(genesisHash) {
        const netChain = connect_sdk_1.nativeChainIds.platformNativeChainIdToNetworkChain(SolanaPlatform._platform, genesisHash);
        if (!netChain)
            throw new Error(`No matching genesis hash to determine network and chain: ${genesisHash}`);
        const [network, chain] = netChain;
        return [network, chain];
    }
    static async chainFromRpc(rpc) {
        const gh = await rpc.getGenesisHash();
        return SolanaPlatform.chainFromChainId(gh);
    }
}
exports.SolanaPlatform = SolanaPlatform;
//# sourceMappingURL=platform.js.map