"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendTransactionWithLookupTable = sendTransactionWithLookupTable;
exports.sendTransactionWithExistingLookupTable = sendTransactionWithExistingLookupTable;
const anchor_1 = require("@coral-xyz/anchor");
const web3_js_1 = require("@solana/web3.js");
/**
 * Sends a transaction using an Address Lookup Table for large numbers of accounts.
 */
async function sendTransactionWithLookupTable(connection, instructions, sender, additionalSigners = []) {
    // Maximum number of accounts that can be added to Address Lookup Table (ALT) in a single transaction.
    const maxExtendedAccounts = 30;
    // Consolidate addresses from all instructions into a single array for the ALT.
    const lookupAddresses = Array.from(new Set(instructions.flatMap((instruction) => [
        instruction.programId,
        ...instruction.keys.map((accountMeta) => accountMeta.pubkey),
    ])));
    // Create instructions for creating and extending the ALT.
    const [lookupTableInstruction, lookupTableAddress] = await web3_js_1.AddressLookupTableProgram.createLookupTable({
        authority: sender.publicKey,
        payer: sender.publicKey,
        recentSlot: await connection.getSlot(),
    });
    // Submit the ALT creation transaction
    await anchor_1.web3.sendAndConfirmTransaction(connection, new anchor_1.web3.Transaction().add(lookupTableInstruction), [sender], {
        commitment: "confirmed",
        skipPreflight: true,
    });
    // Extend the ALT with all accounts making sure not to exceed the maximum number of accounts per transaction.
    for (let i = 0; i < lookupAddresses.length; i += maxExtendedAccounts) {
        const extendInstruction = web3_js_1.AddressLookupTableProgram.extendLookupTable({
            lookupTable: lookupTableAddress,
            authority: sender.publicKey,
            payer: sender.publicKey,
            addresses: lookupAddresses.slice(i, i + maxExtendedAccounts),
        });
        await anchor_1.web3.sendAndConfirmTransaction(connection, new anchor_1.web3.Transaction().add(extendInstruction), [sender], {
            commitment: "confirmed",
            skipPreflight: true,
        });
    }
    // Wait for slot to advance. LUTs only active after slot advance.
    const initialSlot = await connection.getSlot();
    while ((await connection.getSlot()) === initialSlot) {
        await new Promise((resolve) => setTimeout(resolve, 50));
    }
    // Fetch the AddressLookupTableAccount
    const lookupTableAccount = (await connection.getAddressLookupTable(lookupTableAddress)).value;
    if (lookupTableAccount === null)
        throw new Error("AddressLookupTableAccount not fetched");
    // Create the versioned transaction
    const versionedTx = new web3_js_1.VersionedTransaction(new web3_js_1.TransactionMessage({
        payerKey: sender.publicKey,
        recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
        instructions,
    }).compileToV0Message([lookupTableAccount]));
    // Sign and submit the versioned transaction.
    versionedTx.sign([sender, ...additionalSigners]);
    const txSignature = await connection.sendTransaction(versionedTx);
    // Confirm the versioned transaction
    let block = await connection.getLatestBlockhash();
    await connection.confirmTransaction({ signature: txSignature, blockhash: block.blockhash, lastValidBlockHeight: block.lastValidBlockHeight }, "confirmed");
    return { txSignature, lookupTableAddress };
}
/**
 * Sends a transaction using existing Address Lookup Table.
 */
async function sendTransactionWithExistingLookupTable(connection, instructions, lookupTableAccount, sender, additionalSigners = []) {
    // Create the versioned transaction
    const versionedTx = new web3_js_1.VersionedTransaction(new web3_js_1.TransactionMessage({
        payerKey: sender.publicKey,
        recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
        instructions,
    }).compileToV0Message([lookupTableAccount]));
    // Sign and submit the versioned transaction.
    versionedTx.sign([sender, ...additionalSigners]);
    const txSignature = await connection.sendTransaction(versionedTx);
    // Confirm the versioned transaction
    let block = await connection.getLatestBlockhash();
    await connection.confirmTransaction({ signature: txSignature, blockhash: block.blockhash, lastValidBlockHeight: block.lastValidBlockHeight }, "confirmed");
    return txSignature;
}
