1

I'm trying to create SPL token with Non-Transferable Extension with metadata. Right now I can create token with each features only follow these guides, and fails to combine them. below is how I tried to create Non-transferable token with metadata.

const { connection } = useConnection();
const { publicKey : walletPublicKey, sendTransaction } = useWallet();

  const deployTokenSolana = useCallback(async () => {
    // Generate new keypair for Mint Account
    const mintKeypair = Keypair.generate();
    // Address for Mint Account
    const mint = mintKeypair.publicKey;
    // Authority that can mint new tokens
    const mintAuthority = walletPublicKey;

    const tokenATA = await getAssociatedTokenAddressSync(mintKeypair.publicKey, walletPublicKey, false, TOKEN_2022_PROGRAM_ID);

    // Metadata to store in Mint Account
    const metaData: TokenMetadata = {
      updateAuthority: walletPublicKey,
      mint: mint,
      name: "OPOS",
      symbol: "OPOS",
      uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",
      additionalMetadata: [
        ["description", "Only Possible On Solana"]
      ],
    };

    // Size of MetadataExtension 2 bytes for type, 2 bytes for length
    const metadataExtension = TYPE_SIZE + LENGTH_SIZE;
    // Size of metadata
    const metadataLen = pack(metaData).length;

    // Size of Mint Account with extension
    const mintLenNT = getMintLen([ExtensionType.NonTransferable]);
    const mintLenMP = getMintLen([ExtensionType.MetadataPointer]);
    const mintLen = mintLenNT + mintLenMP

    // Minimum lamports required for Mint Account
    const lamports = await connection.getMinimumBalanceForRentExemption(
      mintLen + metadataExtension + metadataLen
    );

    // Instruction to invoke System Program to create new account
    const createAccountInstruction = SystemProgram.createAccount({
      fromPubkey: walletPublicKey, // Account that will transfer lamports to created account
      newAccountPubkey: mint, // Address of the account to create
      space: mintLen, // Amount of bytes to allocate to the created account
      lamports, // Amount of lamports transferred to created account
      programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
    });

    // Instruction to initialize the MetadataPointer Extension
    const initializeMetadataPointerInstruction =
      createInitializeMetadataPointerInstruction(
        mint, // Mint Account address
        walletPublicKey, // Authority that can set the metadata address
        mint, // Account address that holds the metadata
        TOKEN_2022_PROGRAM_ID
      );

    // Instruction to initialize the NonTransferable Extension
    const initializeNonTransferableMintInstruction =
      createInitializeNonTransferableMintInstruction(
        mint, // Mint Account address
        TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
      );

    // Instruction to initialize Mint Account data
    const initializeMintInstruction = createInitializeMintInstruction(
      mint, // Mint Account Address
      tokenDecimals, // Decimals of Mint
      mintAuthority, // Designated Mint Authority
      walletPublicKey, // Optional Freeze Authority
      TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
    );

    // Instruction to initialize Metadata Account data
    const initializeMetadataInstruction = createInitializeInstruction({
      programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
      metadata: mint, // Account address that holds the metadata
      updateAuthority: walletPublicKey, // Authority that can update the metadata
      mint: mint, // Mint Account address
      mintAuthority: mintAuthority, // Designated Mint Authority
      name: metaData.name,
      symbol: metaData.symbol,
      uri: metaData.uri,
    });

    // Instruction to update metadata, adding custom field
    const updateFieldInstruction = createUpdateFieldInstruction({
      programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
      metadata: mint, // Account address that holds the metadata
      updateAuthority: updateAuthority, // Authority that can update the metadata
      field: metaData.additionalMetadata[0][0], // key
      value: metaData.additionalMetadata[0][1], // value
    });

    const associatedTokenAccountInstruction = createAssociatedTokenAccountInstruction(
      walletPublicKey,
      tokenATA,
      walletPublicKey,
      mintKeypair.publicKey,
      TOKEN_2022_PROGRAM_ID
    );

    const mintToInstruction = createMintToInstruction(
      mintKeypair.publicKey,
      tokenATA,
      walletPublicKey,
      tokenTotalSupply * Math.pow(10, tokenDecimals),
      undefined,
      TOKEN_2022_PROGRAM_ID
    );

    // Add instructions to new transaction
    const transaction = new Transaction().add(
      createAccountInstruction,
      initializeMetadataPointerInstruction,
      initializeNonTransferableMintInstruction,
      initializeMintInstruction,
      initializeMetadataInstruction,
      associatedTokenAccountInstruction,
      mintToInstruction
    );

    const signature = await sendTransaction(transaction, connection, { signers: [mintKeypair] });

    console.log(
      "\nCreate Mint Account:",
      `https://solana.fm/tx/${signature}?cluster=devnet-solana`
    );
  }, [walletPublicKey, connection, sendTransaction]);

Can anyone tell me what I'm doing wrong here?, or if there's any other approaches to implement this. Thanks

1
  • edited my post with my recent updates Commented Feb 14 at 10:46

1 Answer 1

4

Answering my own question, as I could solve the issue with this.

    // Size of MetadataExtension 2 bytes for type, 2 bytes for length
    const metadataExtension = TYPE_SIZE + LENGTH_SIZE;
    // Size of metadata
    const metadataLen = pack(metaData).length;
    
    // Size of Mint Account with extension
    const mintLen = getMintLen([
      ExtensionType.MetadataPointer,
      // ExtensionType.TokenMetadata,
      ExtensionType.NonTransferable,
    ]);
    
    // Minimum lamports required for Mint Account
    const lamports = await connection.getMinimumBalanceForRentExemption(
      mintLen + metadataExtension + metadataLen
    );
    
    // Instruction to invoke System Program to create new account
    const createAccountInstruction = SystemProgram.createAccount({
      fromPubkey: walletPublicKey, // Account that will transfer lamports to created account
      newAccountPubkey: mint, // Address of the account to create
      lamports, // Amount of lamports transferred to created account
      space: mintLen, // Amount of bytes to allocate to the created account
      programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
    });
    
    // Instruction to initialize the MetadataPointer Extension
    const initializeMetadataPointerInstruction =
      createInitializeMetadataPointerInstruction(
        mint, // Mint Account address
        updateAuthority, // Authority that can set the metadata address
        mint, // Account address that holds the metadata
        TOKEN_2022_PROGRAM_ID
      );
    
    // Instruction to initialize the NonTransferable Extension
    const initializeNonTransferableMintInstruction =
      createInitializeNonTransferableMintInstruction(
        mint, // Mint Account address
        TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
      );
    
    // Instruction to initialize Mint Account data
    const initializeMintInstruction = createInitializeMint2Instruction(
      mint, // Mint Account Address
      tokenDecimals, // Decimals of Mint
      mintAuthority, // Designated Mint Authority
      freezeAuthority, // Optional Freeze Authority (default: null)
      TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
    );
    
    // Instruction to initialize Metadata Account data
    const initializeMetadataInstruction = createInitializeInstruction({
      programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
      metadata: mint, // Account address that holds the metadata
      updateAuthority: walletPublicKey, // Authority that can update the metadata
      mint: mint, // Mint Account address
      mintAuthority: mintAuthority, // Designated Mint Authority
      name: metaData.name,
      symbol: metaData.symbol,
      uri: metaData.uri,
    });
    
    const tokenATA = await getAssociatedTokenAddressSync(mintKeypair.publicKey, walletPublicKey, false, TOKEN_2022_PROGRAM_ID);     

        const associatedTokenAccountInstruction = createAssociatedTokenAccountInstruction(
            walletPublicKey,
            tokenATA,
            walletPublicKey,
            mint,
            TOKEN_2022_PROGRAM_ID
        );

        const mintToInstruction = createMintToInstruction(
            mint,
            tokenATA,
            walletPublicKey,
            tokenTotalSupply * Math.pow(10, tokenDecimals),
            undefined,
            TOKEN_2022_PROGRAM_ID
        );

    // Add instructions to new transaction
    const transaction = new Transaction().add(
      createAccountInstruction,
      initializeMetadataPointerInstruction,
      initializeNonTransferableMintInstruction,
      // note: the above instructions are required before initializing the mint
      initializeMintInstruction,
      initializeMetadataInstruction,
      associatedTokenAccountInstruction,
      mintToInstruction,
    );

the problem might be calculating the mintLen and lamports for the extensions, and the order of instructions in transaction.

7
  • No probs, glad you posted your own answer to help others!
    – mikemaccana
    Commented Feb 14 at 19:27
  • Hi @cablespecific, I'm stuck in minting the SPL token SPL2022 with metadata on frontend. Could you please share how I can do this?
    – Bobz
    Commented Mar 13 at 6:56
  • I edited the example above for minting additionally Commented Mar 13 at 7:03
  • Thanks for reply - I already do this, whenever I start the frontend server I get an error: Buffer is not defined
    – Bobz
    Commented Mar 13 at 10:03
  • Whenever I install the latest version of Spltoken @0.3.11 got an error ReferenceError: Buffer is not defined at ./node_modules/@solana/spl-token-metadata/lib/cjs/state.js
    – Bobz
    Commented Mar 13 at 11:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.