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 { createMintNftInstruction, createSetCollectionDuringMintInstruction, } from '@metaplex-solarti/mpl-candy-machine';
import { Keypair, SYSVAR_CLOCK_PUBKEY, SYSVAR_INSTRUCTIONS_PUBKEY, SYSVAR_SLOT_HASHES_PUBKEY, } from '@solarti/web3.js';
import { parseCandyMachineV2CollectionAccount } from '../accounts';
import { assertCanMintCandyMachineV2 } from '../asserts';
import { CandyMachineV2BotTaxError } from '../errors';
import { findCandyMachineV2CollectionPda, findCandyMachineV2CreatorPda, } from '../pdas';
import { TransactionBuilder } from '../../../utils';
import { assertAccountExists, makeConfirmOptionsFinalizedOnMainnet, token, useOperation, } from '../../../types';
// -----------------
// Operation
// -----------------
const Key = 'MintCandyMachineV2Operation';
/**
 * Mint an NFT from an existing Candy Machine.
 *
 * ```ts
 * const { nft } = await metaplex
 *   .candyMachinesV2()
 *   .mint({ candyMachine };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
export const mintCandyMachineV2Operation = useOperation(Key);
/**
 * @group Operations
 * @category Handlers
 */
export const mintCandyMachineV2OperationHandler = {
    handle(operation, metaplex, scope) {
        return __awaiter(this, void 0, void 0, function* () {
            assertCanMintCandyMachineV2(operation.input.candyMachine, scope.payer);
            const builder = yield mintCandyMachineV2Builder(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) {
                throw new CandyMachineV2BotTaxError(metaplex.rpc().getSolanaExporerUrl(output.response.signature), error);
            }
            return Object.assign({ nft }, output);
        });
    },
};
/**
 * Mint an NFT from an existing Candy Machine.
 *
 * ```ts
 * const transactionBuilder = await metaplex
 *   .candyMachinesV2()
 *   .builders()
 *   .mint({ candyMachine });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
export const mintCandyMachineV2Builder = (metaplex, params, options = {}) => __awaiter(void 0, void 0, void 0, function* () {
    var _a, _b, _c;
    const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
    const { candyMachine, newMint = Keypair.generate(), newOwner = metaplex.identity().publicKey, newToken, } = params;
    const tokenMetadataProgram = metaplex
        .programs()
        .getTokenMetadata(programs).address;
    const newMetadata = metaplex.nfts().pdas().metadata({
        mint: newMint.publicKey,
        programs,
    });
    const newEdition = metaplex.nfts().pdas().masterEdition({
        mint: newMint.publicKey,
        programs,
    });
    const candyMachineCreator = findCandyMachineV2CreatorPda(candyMachine.address);
    const candyMachineCollectionAddress = findCandyMachineV2CollectionPda(candyMachine.address);
    const candyMachineCollectionAccount = parseCandyMachineV2CollectionAccount(yield metaplex.rpc().getAccount(candyMachineCollectionAddress));
    const tokenWithMintBuilder = yield metaplex
        .tokens()
        .builders()
        .createTokenWithMint({
        decimals: 0,
        initialSupply: token(1),
        mint: newMint,
        mintAuthority: payer,
        freezeAuthority: payer.publicKey,
        owner: newOwner,
        token: newToken,
        createMintAccountInstructionKey: params.createMintAccountInstructionKey,
        initializeMintInstructionKey: params.initializeMintInstructionKey,
        createAssociatedTokenAccountInstructionKey: params.createAssociatedTokenAccountInstructionKey,
        createTokenAccountInstructionKey: params.createTokenAccountInstructionKey,
        initializeTokenInstructionKey: params.initializeTokenInstructionKey,
        mintTokensInstructionKey: params.mintTokensInstructionKey,
    }, { payer, programs });
    const { tokenAddress } = tokenWithMintBuilder.getContext();
    const mintNftInstruction = createMintNftInstruction({
        candyMachine: candyMachine.address,
        candyMachineCreator,
        payer: payer.publicKey,
        wallet: candyMachine.walletAddress,
        metadata: newMetadata,
        mint: newMint.publicKey,
        mintAuthority: payer.publicKey,
        updateAuthority: payer.publicKey,
        masterEdition: newEdition,
        tokenMetadataProgram,
        clock: SYSVAR_CLOCK_PUBKEY,
        recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY,
        instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
    }, { creatorBump: candyMachineCreator.bump });
    if (candyMachine.whitelistMintSettings) {
        const whitelistToken = (_a = params.whitelistToken) !== null && _a !== void 0 ? _a : metaplex.tokens().pdas().associatedTokenAccount({
            mint: candyMachine.whitelistMintSettings.mint,
            owner: payer.publicKey,
        });
        mintNftInstruction.keys.push({
            pubkey: whitelistToken,
            isWritable: true,
            isSigner: false,
        }, {
            pubkey: candyMachine.whitelistMintSettings.mint,
            isWritable: true,
            isSigner: false,
        }, {
            pubkey: payer.publicKey,
            isWritable: false,
            isSigner: true,
        });
    }
    if (candyMachine.tokenMintAddress) {
        const payerToken = (_b = params.payerToken) !== null && _b !== void 0 ? _b : metaplex.tokens().pdas().associatedTokenAccount({
            mint: candyMachine.tokenMintAddress,
            owner: payer.publicKey,
        });
        mintNftInstruction.keys.push({
            pubkey: payerToken,
            isWritable: true,
            isSigner: false,
        }, {
            pubkey: payer.publicKey,
            isWritable: false,
            isSigner: true,
        });
    }
    return (TransactionBuilder.make()
        .setFeePayer(payer)
        .setContext({
        mintSigner: newMint,
        tokenAddress,
    })
        // Create token and mint accounts.
        .add(tokenWithMintBuilder)
        // Create the new NFT.
        .add({
        instruction: mintNftInstruction,
        signers: [payer, newMint],
        key: (_c = params.mintNftInstructionKey) !== null && _c !== void 0 ? _c : 'mintNft',
    })
        // Set the collection on the NFT.
        .when(candyMachineCollectionAccount.exists, (builder) => {
        var _a;
        assertAccountExists(candyMachineCollectionAccount);
        const collectionMint = candyMachineCollectionAccount.data.mint;
        const collectionMetadata = metaplex.nfts().pdas().metadata({
            mint: collectionMint,
            programs,
        });
        const collectionMasterEdition = metaplex.nfts().pdas().masterEdition({
            mint: collectionMint,
            programs,
        });
        const collectionAuthorityRecord = metaplex
            .nfts()
            .pdas()
            .collectionAuthorityRecord({
            mint: collectionMint,
            collectionAuthority: candyMachineCollectionAccount.publicKey,
            programs,
        });
        return builder.add({
            instruction: createSetCollectionDuringMintInstruction({
                candyMachine: candyMachine.address,
                metadata: newMetadata,
                payer: payer.publicKey,
                collectionPda: candyMachineCollectionAccount.publicKey,
                tokenMetadataProgram,
                instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
                collectionMint: candyMachineCollectionAccount.data.mint,
                collectionMetadata,
                collectionMasterEdition,
                authority: candyMachine.authorityAddress,
                collectionAuthorityRecord,
            }),
            signers: [payer],
            key: (_a = params.setCollectionInstructionKey) !== null && _a !== void 0 ? _a : 'setCollection',
        });
    }));
});
//# sourceMappingURL=mintCandyMachineV2.js.map