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 { createCreateMetadataAccountV3Instruction, } from '@metaplex-solarti/mpl-token-metadata';
import { Keypair } from '@solarti/web3.js';
import { assertSft } from '../models';
import { TransactionBuilder } from '../../../utils';
import { isSigner, makeConfirmOptionsFinalizedOnMainnet, toPublicKey, useOperation, } from '../../../types';
// -----------------
// Operation
// -----------------
const Key = 'CreateSftOperation';
/**
 * Creates a new SFT.
 *
 * ```ts
 * const { sft } = await metaplex
 *   .nfts()
 *   .createSft({
 *     name: 'My SFT',
 *     uri: 'https://example.com/my-sft',
 *     sellerFeeBasisPoints: 250, // 2.5%
 *   };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
export const createSftOperation = useOperation(Key);
/**
 * @group Operations
 * @category Handlers
 */
export const createSftOperationHandler = {
    handle: (operation, metaplex, scope) => __awaiter(void 0, void 0, void 0, function* () {
        var _a;
        const { useNewMint = Keypair.generate(), useExistingMint, tokenOwner, tokenAddress: tokenSigner, } = operation.input;
        const mintAddress = useExistingMint !== null && useExistingMint !== void 0 ? useExistingMint : useNewMint.publicKey;
        const associatedTokenAddress = tokenOwner
            ? metaplex.tokens().pdas().associatedTokenAccount({
                mint: mintAddress,
                owner: tokenOwner,
                programs: scope.programs,
            })
            : null;
        const tokenAddress = tokenSigner
            ? toPublicKey(tokenSigner)
            : associatedTokenAddress;
        let tokenExists;
        if (!!useExistingMint && !!tokenAddress) {
            const tokenAccount = yield metaplex.rpc().getAccount(tokenAddress);
            tokenExists = tokenAccount.exists;
        }
        else {
            tokenExists = false;
        }
        const builder = yield createSftBuilder(metaplex, Object.assign(Object.assign({}, operation.input), { useNewMint, tokenExists }), scope);
        scope.throwIfCanceled();
        const confirmOptions = makeConfirmOptionsFinalizedOnMainnet(metaplex, scope.confirmOptions);
        const output = yield builder.sendAndConfirm(metaplex, confirmOptions);
        scope.throwIfCanceled();
        const sft = yield metaplex.nfts().findByMint({
            mintAddress: output.mintAddress,
            tokenAddress: (_a = output.tokenAddress) !== null && _a !== void 0 ? _a : undefined,
        }, scope);
        scope.throwIfCanceled();
        assertSft(sft);
        return Object.assign(Object.assign({}, output), { sft });
    }),
};
/**
 * Creates a new SFT.
 *
 * ```ts
 * const transactionBuilder = await metaplex
 *   .nfts()
 *   .builders()
 *   .createSft({
 *     name: 'My SFT',
 *     uri: 'https://example.com/my-sft',
 *     sellerFeeBasisPoints: 250, // 2.5%
 *   });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
export const createSftBuilder = (metaplex, params, options = {}) => __awaiter(void 0, void 0, void 0, function* () {
    var _b, _c, _d, _e, _f;
    const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
    const { useNewMint = Keypair.generate(), updateAuthority = metaplex.identity(), mintAuthority = metaplex.identity(), } = params;
    const mintAndTokenBuilder = yield createMintAndTokenForSftBuilder(metaplex, params, { programs, payer }, useNewMint);
    const { mintAddress, tokenAddress } = mintAndTokenBuilder.getContext();
    const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
    const metadataPda = metaplex.nfts().pdas().metadata({
        mint: mintAddress,
        programs,
    });
    const creatorsInput = (_b = params.creators) !== null && _b !== void 0 ? _b : [
        {
            address: updateAuthority.publicKey,
            authority: updateAuthority,
            share: 100,
        },
    ];
    const creators = creatorsInput.length > 0
        ? creatorsInput.map((creator) => (Object.assign(Object.assign({}, creator), { verified: creator.address.equals(updateAuthority.publicKey) })))
        : null;
    const createMetadataInstruction = createCreateMetadataAccountV3Instruction({
        metadata: metadataPda,
        mint: mintAddress,
        mintAuthority: mintAuthority.publicKey,
        payer: payer.publicKey,
        updateAuthority: updateAuthority.publicKey,
    }, {
        createMetadataAccountArgsV3: {
            data: {
                name: params.name,
                symbol: (_c = params.symbol) !== null && _c !== void 0 ? _c : '',
                uri: params.uri,
                sellerFeeBasisPoints: params.sellerFeeBasisPoints,
                creators,
                collection: params.collection
                    ? { key: params.collection, verified: false }
                    : null,
                uses: (_d = params.uses) !== null && _d !== void 0 ? _d : null,
            },
            isMutable: (_e = params.isMutable) !== null && _e !== void 0 ? _e : true,
            collectionDetails: params.isCollection
                ? { __kind: 'V1', size: 0 } // Program will hardcode size to zero anyway.
                : null,
        },
    }, tokenMetadataProgram.address);
    // When the payer is different than the update authority, the latter will
    // not be marked as a signer and therefore signing as a creator will fail.
    createMetadataInstruction.keys[4].isSigner = true;
    const verifyAdditionalCreatorInstructions = creatorsInput
        .filter((creator) => {
        return (!!creator.authority &&
            !creator.address.equals(updateAuthority.publicKey));
    })
        .map((creator) => {
        return metaplex.nfts().builders().verifyCreator({
            mintAddress,
            creator: creator.authority,
        }, { programs, payer });
    });
    return (TransactionBuilder.make()
        .setFeePayer(payer)
        .setContext({
        mintAddress,
        metadataAddress: metadataPda,
        tokenAddress,
    })
        // Create the mint and token accounts before minting 1 token to the owner.
        .add(mintAndTokenBuilder)
        // Create metadata account.
        .add({
        instruction: createMetadataInstruction,
        signers: [payer, mintAuthority, updateAuthority],
        key: (_f = params.createMetadataInstructionKey) !== null && _f !== void 0 ? _f : 'createMetadata',
    })
        // Verify additional creators.
        .add(...verifyAdditionalCreatorInstructions)
        // Verify collection.
        .when(!!params.collection && !!params.collectionAuthority, (builder) => {
        var _a, _b;
        return builder.add(metaplex
            .nfts()
            .builders()
            .verifyCollection({
            mintAddress,
            collectionMintAddress: params.collection,
            collectionAuthority: params.collectionAuthority,
            isDelegated: (_a = params.collectionAuthorityIsDelegated) !== null && _a !== void 0 ? _a : false,
            isSizedCollection: (_b = params.collectionIsSized) !== null && _b !== void 0 ? _b : true,
        }, { payer, programs }));
    }));
});
const createMintAndTokenForSftBuilder = (metaplex, params, options, useNewMint) => __awaiter(void 0, void 0, void 0, function* () {
    var _g, _h;
    const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
    const { mintAuthority = metaplex.identity(), freezeAuthority = metaplex.identity().publicKey, tokenExists = false, } = params;
    const mintAddress = (_g = params.useExistingMint) !== null && _g !== void 0 ? _g : useNewMint.publicKey;
    const associatedTokenAddress = params.tokenOwner
        ? metaplex.tokens().pdas().associatedTokenAccount({
            mint: mintAddress,
            owner: params.tokenOwner,
            programs,
        })
        : null;
    const tokenAddress = params.tokenAddress
        ? toPublicKey(params.tokenAddress)
        : associatedTokenAddress;
    const builder = TransactionBuilder.make()
        .setFeePayer(payer)
        .setContext({
        mintAddress,
        tokenAddress,
    });
    // Create the mint account if it doesn't exist.
    if (!params.useExistingMint) {
        builder.add(yield metaplex
            .tokens()
            .builders()
            .createMint({
            decimals: (_h = params.decimals) !== null && _h !== void 0 ? _h : 0,
            mint: useNewMint,
            mintAuthority: mintAuthority.publicKey,
            freezeAuthority,
            createAccountInstructionKey: params.createMintAccountInstructionKey,
            initializeMintInstructionKey: params.initializeMintInstructionKey,
        }, { programs, payer }));
    }
    // Create the token account if it doesn't exist.
    const isNewToken = !!params.tokenAddress && isSigner(params.tokenAddress);
    const isNewAssociatedToken = !!params.tokenOwner;
    if (!tokenExists && (isNewToken || isNewAssociatedToken)) {
        builder.add(yield metaplex
            .tokens()
            .builders()
            .createToken({
            mint: mintAddress,
            owner: params.tokenOwner,
            token: params.tokenAddress,
            createAssociatedTokenAccountInstructionKey: params.createAssociatedTokenAccountInstructionKey,
            createAccountInstructionKey: params.createTokenAccountInstructionKey,
            initializeTokenInstructionKey: params.initializeTokenInstructionKey,
        }, { programs, payer }));
    }
    // Mint provided amount to the token account.
    if (tokenAddress && params.tokenAmount) {
        builder.add(yield metaplex.tokens().builders().mint({
            mintAddress,
            toToken: tokenAddress,
            toTokenExists: true,
            amount: params.tokenAmount,
            mintAuthority,
            mintTokensInstructionKey: params.mintTokensInstructionKey,
        }, { programs, payer }));
    }
    return builder;
});
//# sourceMappingURL=createSft.js.map