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 { createMintInstruction as createMintFromGuardInstruction } from '@metaplex-foundation/mpl-candy-guard';
import { createMintInstruction as createMintFromMachineInstruction } from '@metaplex-foundation/mpl-candy-machine-core';
import { Keypair, SYSVAR_INSTRUCTIONS_PUBKEY, SYSVAR_SLOT_HASHES_PUBKEY, } from '@solarti/web3.js';
import { CandyMachineBotTaxError } from '../errors';
import { TransactionBuilder } from '../../../utils';
import { makeConfirmOptionsFinalizedOnMainnet, token as tokenAmount, } from '../../../types';
// -----------------
// Operation
// -----------------
const Key = 'MintFromCandyMachineOperation';
/**
 * Mints the next NFT from a given candy machine.
 *
 * ```ts
 * const { nft } = await metaplex
 *   .candyMachines()
 *   .mint({
 *     candyMachine,
 *     collectionUpdateAuthority,
 *   };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
export const mintFromCandyMachineOperation = _mintFromCandyMachineOperation;
// eslint-disable-next-line @typescript-eslint/naming-convention
function _mintFromCandyMachineOperation(input) {
    return { key: Key, input };
}
_mintFromCandyMachineOperation.key = Key;
/**
 * @group Operations
 * @category Handlers
 */
export const mintFromCandyMachineOperationHandler = {
    handle(operation, metaplex, scope) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const builder = yield mintFromCandyMachineBuilder(metaplex, operation.input, scope);
            scope.throwIfCanceled();
            const confirmOptions = makeConfirmOptionsFinalizedOnMainnet(metaplex, scope.confirmOptions);
            const output = yield builder.sendAndConfirm(metaplex, confirmOptions);
            scope.throwIfCanceled();
            let nft;
            try {
                nft = (yield metaplex.nfts().findByMint({
                    mintAddress: output.mintSigner.publicKey,
                    tokenAddress: output.tokenAddress,
                }, scope));
            }
            catch (error) {
                const { candyGuard } = operation.input.candyMachine;
                if (!candyGuard) {
                    throw error;
                }
                const activeGuards = metaplex
                    .candyMachines()
                    .guards()
                    .resolveGroupSettings(candyGuard.guards, candyGuard.groups, (_a = operation.input.group) !== null && _a !== void 0 ? _a : null);
                if (!('botTax' in activeGuards)) {
                    throw error;
                }
                throw new CandyMachineBotTaxError(metaplex.rpc().getSolanaExporerUrl(output.response.signature), error);
            }
            return Object.assign({ nft }, output);
        });
    },
};
/**
 * Mints the next NFT from a given candy machine.
 *
 * ```ts
 * const transactionBuilder = await metaplex
 *   .candyMachines()
 *   .builders()
 *   .mint({
 *     candyMachine,
 *     collectionUpdateAuthority,
 *   });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
export const mintFromCandyMachineBuilder = (metaplex, params, options = {}) => __awaiter(void 0, void 0, void 0, function* () {
    var _a;
    const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
    const { candyMachine, collectionUpdateAuthority, mintAuthority = metaplex.identity(), mint = Keypair.generate(), owner = payer.publicKey, group = null, guards = {}, token, } = params;
    // Programs.
    const candyMachineProgram = metaplex.programs().getCandyMachine(programs);
    const candyGuardProgram = metaplex.programs().getCandyGuard(programs);
    const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
    const tokenProgram = metaplex.programs().getToken(programs);
    const systemProgram = metaplex.programs().getSystem(programs);
    // PDAs.
    const authorityPda = metaplex.candyMachines().pdas().authority({
        candyMachine: candyMachine.address,
        programs,
    });
    const nftMetadata = metaplex.nfts().pdas().metadata({
        mint: mint.publicKey,
        programs,
    });
    const nftMasterEdition = metaplex.nfts().pdas().masterEdition({
        mint: mint.publicKey,
        programs,
    });
    const collectionMetadata = metaplex.nfts().pdas().metadata({
        mint: candyMachine.collectionMintAddress,
        programs,
    });
    const collectionMasterEdition = metaplex.nfts().pdas().masterEdition({
        mint: candyMachine.collectionMintAddress,
        programs,
    });
    const collectionAuthorityRecord = metaplex
        .nfts()
        .pdas()
        .collectionAuthorityRecord({
        mint: candyMachine.collectionMintAddress,
        collectionAuthority: authorityPda,
        programs,
    });
    // Transaction Builder that prepares the mint and token accounts.
    const tokenWithMintBuilder = yield metaplex
        .tokens()
        .builders()
        .createTokenWithMint({
        decimals: 0,
        initialSupply: tokenAmount(1),
        mint,
        mintAuthority: payer,
        freezeAuthority: payer.publicKey,
        owner,
        token,
        createMintAccountInstructionKey: params.createMintAccountInstructionKey,
        initializeMintInstructionKey: params.initializeMintInstructionKey,
        createAssociatedTokenAccountInstructionKey: params.createAssociatedTokenAccountInstructionKey,
        createTokenAccountInstructionKey: params.createTokenAccountInstructionKey,
        initializeTokenInstructionKey: params.initializeTokenInstructionKey,
        mintTokensInstructionKey: params.mintTokensInstructionKey,
    }, { payer, programs });
    const { tokenAddress } = tokenWithMintBuilder.getContext();
    // Shared mint accounts
    const sharedMintAccounts = {
        candyMachine: candyMachine.address,
        payer: payer.publicKey,
        nftMetadata,
        nftMint: mint.publicKey,
        nftMintAuthority: payer.publicKey,
        nftMasterEdition,
        collectionAuthorityRecord,
        collectionMint: candyMachine.collectionMintAddress,
        collectionMetadata,
        collectionMasterEdition,
        collectionUpdateAuthority,
        candyMachineProgram: candyMachineProgram.address,
        tokenMetadataProgram: tokenMetadataProgram.address,
        tokenProgram: tokenProgram.address,
        systemProgram: systemProgram.address,
        recentSlothashes: SYSVAR_SLOT_HASHES_PUBKEY,
        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
    };
    // Mint instruction.
    let mintNftInstruction;
    let mintNftSigners;
    if (!!candyMachine.candyGuard) {
        const { candyGuard } = candyMachine;
        const guardClient = metaplex.candyMachines().guards();
        const parsedMintSettings = guardClient.parseMintSettings(candyMachine.address, candyGuard, payer, guards, group, programs);
        mintNftSigners = [payer, mint, ...parsedMintSettings.signers];
        mintNftInstruction = createMintFromGuardInstruction(Object.assign(Object.assign({}, sharedMintAccounts), { candyGuard: candyMachine.candyGuard.address, candyMachineAuthorityPda: authorityPda }), {
            mintArgs: parsedMintSettings.arguments,
            label: group,
        }, candyGuardProgram.address);
        mintNftInstruction.keys.push(...parsedMintSettings.accountMetas);
    }
    else {
        mintNftSigners = [payer, mint, mintAuthority];
        mintNftInstruction = createMintFromMachineInstruction(Object.assign(Object.assign({}, sharedMintAccounts), { authorityPda, mintAuthority: mintAuthority.publicKey }), candyMachineProgram.address);
    }
    return (TransactionBuilder.make()
        .setFeePayer(payer)
        .setContext({ tokenAddress, mintSigner: mint })
        // Create token and mint accounts.
        .add(tokenWithMintBuilder)
        // Mint the new NFT.
        .add({
        instruction: mintNftInstruction,
        signers: mintNftSigners,
        key: (_a = params.mintFromCandyMachineInstructionKey) !== null && _a !== void 0 ? _a : 'mintNft',
    }));
});
//# sourceMappingURL=mintFromCandyMachine.js.map