'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var mplCandyGuard = require('@metaplex-foundation/mpl-candy-guard');
var mplCandyMachineCore = require('@metaplex-foundation/mpl-candy-machine-core');
var web3_js = require('@solarti/web3.js');
var errors = require('../errors.cjs');
var Operation = require('../../../types/Operation.cjs');
var Amount = require('../../../types/Amount.cjs');
var TransactionBuilder = require('../../../utils/TransactionBuilder.cjs');

// -----------------
// 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
 */
const mintFromCandyMachineOperation = _mintFromCandyMachineOperation;
// eslint-disable-next-line @typescript-eslint/naming-convention
function _mintFromCandyMachineOperation(input) {
  return {
    key: Key,
    input
  };
}
_mintFromCandyMachineOperation.key = Key;

/**
 * @group Operations
 * @category Types
 */

/**
 * @group Operations
 * @category Handlers
 */
const mintFromCandyMachineOperationHandler = {
  async handle(operation, metaplex, scope) {
    const builder = await mintFromCandyMachineBuilder(metaplex, operation.input, scope);
    scope.throwIfCanceled();
    const confirmOptions = Operation.makeConfirmOptionsFinalizedOnMainnet(metaplex, scope.confirmOptions);
    const output = await builder.sendAndConfirm(metaplex, confirmOptions);
    scope.throwIfCanceled();
    let nft;
    try {
      nft = await 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, operation.input.group ?? null);
      if (!('botTax' in activeGuards)) {
        throw error;
      }
      throw new errors.CandyMachineBotTaxError(metaplex.rpc().getSolanaExporerUrl(output.response.signature), error);
    }
    return {
      nft,
      ...output
    };
  }
};

// -----------------
// Builder
// -----------------

/**
 * @group Transaction Builders
 * @category Inputs
 */

/**
 * Mints the next NFT from a given candy machine.
 *
 * ```ts
 * const transactionBuilder = await metaplex
 *   .candyMachines()
 *   .builders()
 *   .mint({
 *     candyMachine,
 *     collectionUpdateAuthority,
 *   });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
const mintFromCandyMachineBuilder = async (metaplex, params, options = {}) => {
  const {
    programs,
    payer = metaplex.rpc().getDefaultFeePayer()
  } = options;
  const {
    candyMachine,
    collectionUpdateAuthority,
    mintAuthority = metaplex.identity(),
    mint = web3_js.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 = await metaplex.tokens().builders().createTokenWithMint({
    decimals: 0,
    initialSupply: Amount.token(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: web3_js.SYSVAR_SLOT_HASHES_PUBKEY,
    instructionSysvarAccount: web3_js.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 = mplCandyGuard.createMintInstruction({
      ...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 = mplCandyMachineCore.createMintInstruction({
      ...sharedMintAccounts,
      authorityPda,
      mintAuthority: mintAuthority.publicKey
    }, candyMachineProgram.address);
  }
  return TransactionBuilder.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: params.mintFromCandyMachineInstructionKey ?? 'mintNft'
  });
};

exports.mintFromCandyMachineBuilder = mintFromCandyMachineBuilder;
exports.mintFromCandyMachineOperation = mintFromCandyMachineOperation;
exports.mintFromCandyMachineOperationHandler = mintFromCandyMachineOperationHandler;
//# sourceMappingURL=mintFromCandyMachine.cjs.map
