import { BN, Program, Wallet } from "@coral-xyz/anchor";
import {
    AccountInfo,
    AddressLookupTableAccount,
    ComputeBudgetProgram,
    Connection,
    GetProgramAccountsResponse,
    Keypair,
    PublicKey,
    SystemProgram,
    Transaction,
    TransactionInstruction,
    TransactionMessage,
    TransactionSignature,
    VersionedTransaction
} from "@solana/web3.js";
import { BasketsIDL } from "./basketsIDL";
import {
    BuyStateChainData,
    BASKETS_PROGRAM_PDA,
    RebalanceInfo,
    REBALANCE_FEE_ACCOUNT,
    Side, BUY_FEE_ACCOUNT,
    ADDITIONAL_FEE,
    ADDITIONAL_UNITS,
    TokenSettings,
    TOKEN_LIST_ADDRESS,
    JupSwapData,
    JUP_AGGREGATOR,
    BUY_FEE_WALLET,
    BEYOND_LST_BASKET,
    TransactionToSend,
} from "./config";
import {
    sendSignedTransactions,
    signTransactionsWithWallet,
    signVersionedTransactions,
} from "./utils";
import { Basket } from "./basketState";
import { AccountLayout, NATIVE_MINT, TOKEN_PROGRAM_ID, createAssociatedTokenAccountInstruction, createSyncNativeInstruction, getAssociatedTokenAddressSync } from "./splTokenHelpers";

import { buildBuyBasketIx, buildBuyBasketWithMultipleTokensIx, buildBuyBasketWithSingleTokenIx, buildClaimTokensFromBuyStateIxs, buildMintFromBuyStateIx, updateOraclesTxs } from "./instructionsBuilder";

export class BuyState {
    public ownAddress: PublicKey;
    public data: BuyStateChainData;
    public basket: Basket;

    constructor(
        ownAddress: PublicKey,
        buyStateData: BuyStateChainData,
        basket: Basket,
    ) {
        this.ownAddress = ownAddress;
        this.data = buyStateData;
        this.basket = basket;
    }

    static async loadFromRawData(
        program: Program<BasketsIDL>,
        rawData: {
            pubkey: PublicKey;
            account: AccountInfo<Buffer>;
        },
        basket?: Basket,
    ): Promise<BuyState> {
        let buyStateData = program.coder.accounts.decode("buyState", rawData.account.data);
        if (!basket)
            basket = await Basket.loadFromPubkey(program, buyStateData.fund);
        return new BuyState(
            rawData.pubkey,
            buyStateData,
            basket
        )
    }

    static async loadMultiple(
        program: Program<BasketsIDL>,
        rawDatas: GetProgramAccountsResponse,
    ): Promise<BuyState[]> {
        let buyStateDatas = [];
        let baskets: PublicKey[] = [];
        for (let i = 0; i < rawDatas.length; i++) {
            let decoded = program.coder.accounts
                .decode("buyState", rawDatas[i].account.data);
            buyStateDatas.push(
                rawDatas[i]
            );
            baskets.push(decoded.fund); 
        }
        let basketsData = await program.provider.connection
            .getMultipleAccountsInfo(baskets, "confirmed");
        let buyStates: BuyState[] = [];
        for (let i = 0; i < buyStateDatas.length; i++) {
            let basket = Basket.loadFromRawData(
                program,
                {
                    pubkey: baskets[i],
                    //@ts-ignore
                    account: basketsData[i],
                }
            );
            buyStates.push(await this.loadFromRawData(
                program,
                buyStateDatas[i],
                basket
            ));
        }
        return buyStates;
    }

    static async loadFromPubkey(
        program: Program<BasketsIDL>,
        buyState: PublicKey,
        basket?: Basket,
    ): Promise<BuyState> {
        let buyStateData = await program.account.buyState.fetch(buyState, "confirmed");
        if (!basket)
            basket = await Basket.loadFromPubkey(program, buyStateData.fund);
        return new BuyState(
            buyState,
            //@ts-ignore
            buyStateData,
            basket,
        );
    }

    static computeMintAmountWithMultipleTokens(
        tokenList: TokenSettings[],
        basket: Basket,
        contribution: {token: PublicKey, amount: number}[],
        oraclePrices: number[],
    ): number {
        let contributions = [];
        let totalContribution = 0;
        let basketWorth = 0;
        for (let i = 0; i < basket.data.numOfTokens.toNumber(); i++) {
            let tokenPrice = oraclePrices[basket.data.currentCompToken[i].toNumber()];
            let amount = 0;
            for (let j = 0; j < contribution.length; j++)
                if (contribution[j].token.toBase58() == tokenList[basket.data.currentCompToken[i].toNumber()].tokenMint)
                    amount = contribution[j].amount;
            totalContribution += tokenPrice * amount;
            contributions.push(tokenPrice * amount);
            basketWorth += tokenPrice
                * parseInt(basket.data.currentCompAmount[i].toString())
                / 10 ** tokenList[basket.data.currentCompToken[i].toNumber()].decimals;
        }
        let valueToRebalance = 0;
        for (let i = 0; i < basket.data.numOfTokens.toNumber(); i++) {
            let recommendedContribution = totalContribution
                * basket.data.targetWeight[i].toNumber()
                / basket.data.weightSum.toNumber();
            if (recommendedContribution > contributions[i])
                valueToRebalance += recommendedContribution + contributions[i]; else
                valueToRebalance += contributions[i] - recommendedContribution;
        }
        totalContribution -= valueToRebalance * basket.data.rebalanceSlippage.toNumber() / 10000;
        if (basket.data.supplyOutstanding.toNumber() == 0)
            return totalContribution / 100; else
            return totalContribution
                * basket.data.supplyOutstanding.toNumber()
                / 10 ** 6
                / basketWorth;
    }

    // static async multipleTokensDeposit(
    //     program: Program<BasketsIDL>,
    //     wallet: Wallet,
    //     tokenList: TokenSettings[],
    //     basket: Basket,
    //     contribution: {token: PublicKey, amount: number}[],
    //     lamports: number,
    //     updateOracles: boolean, // NEDD TO IMPLEMENT
    // ): Promise <TransactionSignature> {
    //     let connection: Connection = program.provider.connection;
    //     let tokenMints = basket.data.currentCompToken.slice(0, basket.data.numOfTokens.toNumber())
    //         .map(token => new PublicKey(tokenList[token.toNumber()].tokenMint));
    //     tokenMints.push(basket.data.fundToken);
    //     let buyerAtas = tokenMints.map(token => getAssociatedTokenAddressSync(
    //         token,
    //         wallet.publicKey,
    //         true,
    //     ));
    //     let infobuyerAtas = await connection.getMultipleAccountsInfo(buyerAtas, "confirmed");
    //     let preTransaction = new Transaction();
    //     preTransaction.instructions = infobuyerAtas
    //         .map((info, id) => { return {id: id, info: info} })
    //         .filter(x => x.info == null).map(x =>
    //             createAssociatedTokenAccountInstruction(
    //                 wallet.publicKey,
    //                 buyerAtas[x.id],
    //                 wallet.publicKey,
    //                 tokenMints[x.id],
    //             )
    //         );
    //     let wSolIndex = tokenMints.findIndex(mint => mint.toBase58() == NATIVE_MINT.toBase58()); 
    //     if (wSolIndex != -1) {
    //         //@ts-ignore
    //         let info: AccountInfo<Buffer> = infobuyerAtas[wSolIndex];
    //         let amount = 0;
    //         for (let i = 0; i < contribution.length; i++) {
    //             if (contribution[i].token.toBase58() == NATIVE_MINT.toBase58())
    //                 amount = contribution[i].amount;
    //         }
    //         let toDeposit = Math.floor(amount * 10**9);
    //         if (info) {
    //             let parsedInfo = AccountLayout.decode(info.data);
    //             toDeposit -= parseInt(parsedInfo.amount.toString());
    //         }
    //         if (toDeposit > 0) {
    //             preTransaction.add(
    //                 SystemProgram.transfer({
    //                     fromPubkey: wallet.publicKey,
    //                     toPubkey: buyerAtas[wSolIndex],
    //                     lamports: toDeposit
    //                 }),
    //             ).add(
    //                 createSyncNativeInstruction(buyerAtas[wSolIndex], TOKEN_PROGRAM_ID)
    //             );
    //         }
    //     }
       
    //     let transaction = new Transaction();
    //     transaction.instructions = [
    //         await buildBuyBasketWithMultipleTokensIx(program, tokenList, wallet.publicKey, basket, contribution),
    //         ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}),
    //         ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports})
    //     ];

    //     let signedTransactions = await signTransactionsWithWallet(
    //         connection,
    //         wallet,
    //         [
    //             {transaction: preTransaction, signers: []},
    //             {transaction: transaction, signers: []}
    //         ]
    //     ); 
    //     let txs = await sendSignedTransactions(
    //         connection,
    //         signedTransactions,
    //         2
    //     );
    //     return txs[1];
    // }

    static computeMintAmountWithSingleToken(
        tokenList: TokenSettings[],
        basket: Basket,
        tokenSettings: TokenSettings,
        amount: number,
        oraclePrices: number[],
    ): number {
        let tokenWorth = [];
        let basketWorth = 0;
        for (let i = 0; i < basket.data.numOfTokens.toNumber(); i++) {
            let tokenPrice = oraclePrices[basket.data.currentCompToken[i].toNumber()];
            let usdValue = tokenPrice
                * parseInt(basket.data.currentCompAmount[i].toString())
                / 10 ** tokenList[basket.data.currentCompToken[i].toNumber()].decimals;
            tokenWorth.push(usdValue);
            basketWorth += usdValue;
        }
        let totalFee = 10;
        totalFee += basket.data.hostFee.toNumber();
        totalFee += basket.data.managerFee.toNumber();
        if (basket.ownAddress.toBase58() == BEYOND_LST_BASKET.toBase58()) totalFee = 0;

        amount = amount * (10000 - totalFee) / 10000;
        let totalContribution = amount * oraclePrices[tokenSettings.id];

        let valueToRebalance = 0;
        for (let i = 0; i < basket.data.numOfTokens.toNumber(); i++) {
            let valueBefore = tokenWorth[i];
            let valueAfter = tokenWorth[i];
            if (basket.data.currentCompToken[i].toNumber() == tokenSettings.id)
                valueAfter += totalContribution;
            let targetValueBefore = basketWorth * 
                basket.data.targetWeight[i].toNumber() /
                basket.data.weightSum.toNumber();
            let targetValueAfter = (basketWorth + totalContribution) * 
                basket.data.targetWeight[i].toNumber() /
                basket.data.weightSum.toNumber();
            if (basket.data.currentCompToken[i].toNumber() != tokenSettings.id) {
                valueToRebalance += Math.max(targetValueAfter, valueAfter) -
                    Math.max(targetValueBefore, valueAfter)
            } else {
                if (valueAfter <= targetValueAfter) { continue; }
                let overflow = valueAfter - targetValueAfter;
                if (valueBefore <= targetValueBefore)
                    valueToRebalance += overflow;
                else
                    valueToRebalance += overflow - (valueBefore - targetValueBefore);
            }
        }
        if (basket.ownAddress.toBase58() == BEYOND_LST_BASKET.toBase58()) valueToRebalance = 0;
        totalContribution -= valueToRebalance * 300 / 10000;
        if (basket.data.supplyOutstanding.toNumber() == 0)
            return totalContribution / 100; else
            return totalContribution
                * basket.data.supplyOutstanding.toNumber()
                / 10 ** 6
                / basketWorth;
    }

    static async singleTokenDeposit(
        program: Program<BasketsIDL>,
        wallet: Wallet,
        tokenList: TokenSettings[],
        basket: Basket,
        tokenMint: PublicKey,
        amount: number,
        lamports: number,
        updateOracles: boolean, /// NEED To Implement
    ): Promise <TransactionSignature> {
        let connection: Connection = program.provider.connection;
        let buyerTokenAccount = getAssociatedTokenAddressSync(tokenMint, wallet.publicKey, true);
        let buyerBasketTokenAccount = getAssociatedTokenAddressSync(basket.data.fundToken, wallet.publicKey, true);
        let symmetryFeeAccount = getAssociatedTokenAddressSync(tokenMint, BUY_FEE_WALLET);
        let hostFeeAccount = getAssociatedTokenAddressSync(tokenMint, basket.data.hostPubkey, true);
        let managerFeeAccount = getAssociatedTokenAddressSync(
            tokenMint,
            basket.data.feeDelegate.toBase58() == PublicKey.default.toBase58()
                ? basket.data.manager : basket.data.feeDelegate,
            true
        );
        let infobuyerAtas = await connection.getMultipleAccountsInfo(
            [buyerTokenAccount, buyerBasketTokenAccount, symmetryFeeAccount, hostFeeAccount, managerFeeAccount],
            "confirmed"
        );
        let preTransaction = new Transaction();
        if (!infobuyerAtas[0])
            preTransaction.add(createAssociatedTokenAccountInstruction(
                wallet.publicKey,
                buyerTokenAccount,
                wallet.publicKey,
                tokenMint
            ))
        if (!infobuyerAtas[1])
            preTransaction.add(createAssociatedTokenAccountInstruction(
                wallet.publicKey,
                buyerBasketTokenAccount,
                wallet.publicKey,
                basket.data.fundToken
            ))
        if (!infobuyerAtas[2])
            preTransaction.add(createAssociatedTokenAccountInstruction(
                wallet.publicKey,
                symmetryFeeAccount,
                BUY_FEE_WALLET,
                tokenMint
            ))
        if (tokenMint.toBase58() == NATIVE_MINT.toBase58()) {
            //@ts-ignore
            let info: AccountInfo<Buffer> = infobuyerAtas[0];
            let toDeposit = Math.floor(amount * 10**9);
            if (info) {
                let parsedInfo = AccountLayout.decode(info.data);
                toDeposit -= parseInt(parsedInfo.amount.toString());
            }
            if (toDeposit > 0) {
                preTransaction.add(
                    SystemProgram.transfer({
                        fromPubkey: wallet.publicKey,
                        toPubkey: buyerTokenAccount,
                        lamports: toDeposit
                    }),
                ).add(
                    createSyncNativeInstruction(buyerTokenAccount, TOKEN_PROGRAM_ID)
                );
            }
        }
        preTransaction.add(ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}));
        preTransaction.add(ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports}));

        let mainIx = await buildBuyBasketWithSingleTokenIx(program, tokenList, wallet.publicKey, basket, tokenMint, amount);

        let transaction = new Transaction();
        transaction.instructions = [
            mainIx,
            ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}),
            ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports})
        ];

        let signedTransactions = await signTransactionsWithWallet(
            connection,
            wallet,
            [
                {transaction: preTransaction, signers: []},
                {transaction: transaction, signers: []}
            ]
        ); 
        let txs = await sendSignedTransactions(
            connection,
            signedTransactions,
            2
        );
        return txs[1];
    }

    static async createNew(
        program: Program<BasketsIDL>,
        wallet: Wallet,
        tokenList: TokenSettings[],
        basket: Basket,
        amount: number,
        lamports: number = ADDITIONAL_FEE,
    ): Promise<BuyState> {
        let connection: Connection = program.provider.connection;
        let buyData = await buildBuyBasketIx(program, tokenList, wallet.publicKey, basket, amount);
        let transaction = new Transaction();
        transaction.instructions = [
            buyData,
            ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}),
            ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports})
        ];

        let signedTransactions = await signTransactionsWithWallet(
            connection,
            wallet,
            [{transaction: transaction, signers: []}]
        );
       
        await sendSignedTransactions(
            connection,
            signedTransactions,
            1
        );

        return await BuyState.loadFromPubkey(program, buyData.keys[11].pubkey, basket);
    }

    async update(program: Program<BasketsIDL>): Promise<void> {
        //@ts-ignore
        this.data = await program.account.buyState.fetch(this.ownAddress, "confirmed");
    }

    getBuyStateRebalanceInfo(
        tokenList: TokenSettings[],
    ): RebalanceInfo[] {
        let currentTokens = Array.from(this.data.token, x => x.toNumber());
        let amountToSpend = Array.from(this.data.amountToSpend, x => parseInt(x.toString())); 
        let rebalanceInfos: RebalanceInfo[] = [];
        for (let i = 1; i < currentTokens.length; i++)
            if (amountToSpend[i] > 0)
                rebalanceInfos.push({
                    tokenId: currentTokens[i],
                    tokenAccountFrom: tokenList[0].pdaTokenAccount,
                    mintFrom: tokenList[0].tokenMint,
                    oracleFrom: tokenList[0].oracleAccount,
                    tokenAccountTo: tokenList[currentTokens[i]].pdaTokenAccount,
                    mintTo: tokenList[currentTokens[i]].tokenMint,
                    oracleTo: tokenList[currentTokens[i]].oracleAccount,
                    amountFrom: amountToSpend[i],
                    decimals: tokenList[0].decimals,
                    volume: amountToSpend[i] / 10 * tokenList[0].decimals,
                    side: Side.From
                })
        return rebalanceInfos.filter(x => x.volume > 0.005);
    }
    
    async rebalanceBuyState(
        program: Program<BasketsIDL>,
        wallet: Wallet,
        tokenList: TokenSettings[],
        jupSwapDatas: JupSwapData[],
        lamports: number,
        updateOraclesTxData: TransactionToSend[],
        lookups: AddressLookupTableAccount[],
    ): Promise<TransactionSignature[]> {
        let basket = this.basket;
        let transactionsData: TransactionToSend[] = updateOraclesTxData;
        for (let i = 0; i < jupSwapDatas.length; i++) {
            let rebalanceData = jupSwapDatas[i];
            if (!rebalanceData)
                continue;
            let tokenId = rebalanceData.toTokenId;
            let ix = (rebalanceData.type == "Simple") ?
                await program.methods
                    .rebalanceBuyState(
                        tokenId,
                        new BN(rebalanceData.fromAmount),
                        rebalanceData.dataLength,
                        Array.from(rebalanceData.data),
                    )
                    .accounts({
                        fundState: basket.ownAddress,
                        buyState: this.ownAddress,
                        tokenList: TOKEN_LIST_ADDRESS,
                        oracleSol: new PublicKey(tokenList[1].oracleAccount),
                        oracleToken: new PublicKey(tokenList[tokenId].oracleAccount),
                        oracleUsdc: new PublicKey(tokenList[0].oracleAccount),
                        pdaAccount: BASKETS_PROGRAM_PDA,
                        pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount),
                        pdaUsdcAccount: new PublicKey(tokenList[0].pdaTokenAccount),
                        rebalanceFeeAccount: REBALANCE_FEE_ACCOUNT,
                        tokenProgram: TOKEN_PROGRAM_ID,
                    })
                    .remainingAccounts(rebalanceData.accounts)
                    .instruction() :
                await program.methods
                    .rebalanceBuyStateTransitive(
                        tokenId,
                        new BN(rebalanceData.fromAmount),
                        rebalanceData.firstIxEnd,
                        rebalanceData.dataLength,
                        rebalanceData.firstIxAccounts,
                        Array.from(rebalanceData.data),
                    )
                    .accounts({
                        fundState: basket.ownAddress,
                        buyState: this.ownAddress,
                        tokenList: TOKEN_LIST_ADDRESS,
                        oracleSol: new PublicKey(tokenList[1].oracleAccount),
                        oracleToken: new PublicKey(tokenList[tokenId].oracleAccount),
                        oracleUsdc: new PublicKey(tokenList[0].oracleAccount),
                        pdaAccount: BASKETS_PROGRAM_PDA,
                        pdaTokenAccount: new PublicKey(tokenList[tokenId].pdaTokenAccount),
                        pdaMidAccount: new PublicKey(rebalanceData.midTokenPda),
                        pdaUsdcAccount: new PublicKey(tokenList[0].pdaTokenAccount),
                        rebalanceFeeAccount: REBALANCE_FEE_ACCOUNT,
                        tokenProgram: TOKEN_PROGRAM_ID,
                    })
                    .remainingAccounts(rebalanceData.accounts)
                    .instruction() 
            transactionsData.push({
                payerKey: wallet.publicKey,
                instructions: [
                    ix,
                    ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}),
                    ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports})
                ],
                lookupTables: [...lookups, ...rebalanceData.lookupTableAccounts]
            });
        }
        const blockhash = (await program.provider.connection.getLatestBlockhash("confirmed")).blockhash;
        let signedTransactions = await signVersionedTransactions(
            wallet,
            transactionsData.map(tx => new VersionedTransaction(
                new TransactionMessage({
                    payerKey: tx.payerKey,
                    recentBlockhash: blockhash,
                    instructions: tx.instructions,
                }).compileToV0Message(tx.lookupTables)
            ))
        );
        return await sendSignedTransactions(
            program.provider.connection,
            signedTransactions,
            updateOraclesTxData.length > 0 ? 1 : 0,
        );
    }

    async mint(
        program: Program<BasketsIDL>,
        swbProgram: Program,
        wallet: Wallet,
        tokenList: TokenSettings[],
        lookups: AddressLookupTableAccount[],
        lamports: number,
        updateOracles: boolean,
    ): Promise<TransactionSignature[]> {
        let transactionsData: TransactionToSend[] = [];
        if (updateOracles)
            transactionsData = await updateOraclesTxs(
                swbProgram,
                wallet.publicKey,
                this.basket.getSwbFeeds(tokenList),
                lamports,
            );
        transactionsData.map(tx => tx.lookupTables = [...lookups, ...tx.lookupTables])
        transactionsData.push({
            payerKey: wallet.publicKey,
            instructions: [
                await buildMintFromBuyStateIx(program, tokenList, wallet.publicKey, this.basket, this),
                ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}),
                ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports})
            ],
            lookupTables: lookups
        });
        const blockhash = (await program.provider.connection.getLatestBlockhash("confirmed")).blockhash;
        let signedTransactions = await signVersionedTransactions(
            wallet,
            transactionsData.map(tx => new VersionedTransaction(
                new TransactionMessage({
                    payerKey: tx.payerKey,
                    recentBlockhash: blockhash,
                    instructions: tx.instructions,
                }).compileToV0Message(tx.lookupTables)
            ))
        );
        return await sendSignedTransactions(
            program.provider.connection,
            signedTransactions,
            signedTransactions.length,
        );
    }

    async claimTokens(
        program: Program<BasketsIDL>,
        wallet: Wallet,
        tokenList: TokenSettings[],
        lamports: number = ADDITIONAL_FEE,
    ): Promise<TransactionSignature[]> {
        let buyer = this.data.buyer;
        let connection = program.provider.connection;
        let transactions: Transaction[] = [];
        let ixId = 0;
        let claimIxs = await buildClaimTokensFromBuyStateIxs(program, tokenList, wallet.publicKey, this.basket, this);
        for (let i = 0; i < this.data.token.length; i++) {
            if (parseInt(this.data.amountBought[i].toString()) == 0 && i != 0)
                continue;
            let transaction = new Transaction();
            let tokenId = this.data.token[i].toNumber();
            let userTokenAccount = getAssociatedTokenAddressSync(
                new PublicKey(tokenList[tokenId].tokenMint),
                buyer,
                true,
            );
            let infoAta = await connection.getAccountInfo(userTokenAccount, "confirmed");
            if (!infoAta)
                transaction.add(
                    await createAssociatedTokenAccountInstruction(
                        wallet.publicKey,
                        userTokenAccount,
                        buyer,
                        new PublicKey(tokenList[tokenId].tokenMint),
                    )
                );
            transaction.instructions = [
                ...transaction.instructions,
                claimIxs[ixId],
                ComputeBudgetProgram.setComputeUnitLimit({units: ADDITIONAL_UNITS}),
                ComputeBudgetProgram.setComputeUnitPrice({microLamports: lamports})
            ]
            transactions.push(transaction);
            ixId = ixId + 1;
        }
        let signedTransactions = await signTransactionsWithWallet(
            connection,
            wallet,
            transactions.map(transaction => {
                return { transaction: transaction, signers: []}
            })
        );
        return await sendSignedTransactions(
            connection,
            signedTransactions,
        );
    }
}
