4

Similar to https://stackoverflow.com/questions/69701491/add-meta-data-to-solana-token-with-solana-web3-js, however, the only answer for that question is in Rust and the question asker ended up moving to eth instead!

I know I can create an NFT with SPL:

const mint = await createMint(
  connection,
  wallet,
  wallet.publicKey,
  wallet.publicKey,
  0
);

Or with Metaplex:

const metaplexNFTs = Metaplex.make(connection)
  .use(keypairIdentity(keypair))
  .use(bundlrStorage())
  .nfts()

createOutput = await metaplexNFTs.create(
  {
    uri: metadataURL,
    name: "Some token name", <- I'd like to be able to add 'name' but also other metadata with @solana/spl-token
    sellerFeeBasisPoints: 0, 
  }
);

But can I create an NFT with custom data just using web3.js and spl-token, not @metaplex-foundation/js?

How do I mint an SPL token with custom Metadata using @solana/web3.js and @solana/spl-token?

Update: I found this answer in my research so I know it's possible:

Create an SPL token first with supply = 1 and decimal = 0 so the wallets can identify your asset (done, easy!)

Create another account preferably derived from the mint address of your SPL token using PDAs

Finally, save your custom metadata in the second account and use it in any way you need.

I just need help or a pointer to the right docs. I am comfortable adding instructions to a larger transaction with new Transaction().add() etc. Just need to know what to add!

4 Answers 4

4

You'll still end up needing to use metaplex since metaplex controls the Token Metadata Program. Specifically, you'll need to use createCreateMetadataAccountV3Instruction from @metaplex-foundation/mpl-token-metadata

https://metaplex-foundation.github.io/metaplex-program-library/docs/token-metadata/index.html#createCreateMetadataAccountV3Instruction

You can use @solana/spl-token to create a mint like you mentioned, and then @solana/web3.js to built and send a transaction.

1
  • Thanks @john! Main thing I was concerned about was avoiding the Metaplex JS SDK @metaplex-foundation/js, happy to use the on-chain Metaplex program (I've edited the question to make this clear). I ended up finding createCreateMetadataAccountV3Instruction() myself (see my answer below), but since you're not me and you found the answer I'll mark this one as correct,
    – mikemaccana
    Commented Jan 28, 2023 at 23:23
4

Answering my own question to help others based on Adding Metadata for Fungible SPL tokens (Metaplex) and Need assistance creating a custom type of NFT, updating deprecated function signatures, and some other research.

Plain spl-token, no @metaplex-foundation/js. Using a single function from @metaplex-foundation/mpl-token-metadata though but @metaplex-foundation/mpl-token-metadata seems to be more stable than @metaplex-foundation/js.

import {
  Transaction,
  type PublicKey,
  PublicKey as PublicKeyConstructor,
  Connection,
  Keypair,
  sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
  createCreateMetadataAccountV2Instruction,
  type DataV2,
} from "@metaplex-foundation/mpl-token-metadata";
import { log } from "console";
import { Buffer } from "buffer";
import { createMint } from "@solana/spl-token";

// Use a read URL for your connection, and a real user.
const connection = new Connection("...");
const steve = new Keypair();

const METAPLEX_TOKEN_METADATA_PROGRAM_ADDRESS =
  "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";

const metaplexTokenMetadataProgram = new PublicKeyConstructor(
  METAPLEX_TOKEN_METADATA_PROGRAM_ADDRESS
);

const createMetadataAccount = async (
  metadataPDA: PublicKey,
  mint: PublicKey,
  payer: PublicKey,
  metadataData: any
) => {
  const transaction = new Transaction().add(
    createCreateMetadataAccountV2Instruction(
      {
        metadata: metadataPDA,
        mint: mint,
        mintAuthority: payer,
        payer: payer,
        updateAuthority: payer,
      },
      {
        createMetadataAccountArgsV2: {
          data: metadataData,
          isMutable: true,
        },
      }
    )
  );
  return transaction;
};

const getMetadataPDA = (mint: PublicKey): PublicKeyConstructor => {
  return PublicKeyConstructor.findProgramAddressSync(
    [
      Buffer.from("metadata"),
      metaplexTokenMetadataProgram.toBuffer(),
      mint.toBuffer(),
    ],
    metaplexTokenMetadataProgram
  )[0];
};

// Doesn't have to be DataV2, if you're not making art NFTs
const metadata: DataV2 = {
  name: "Portal Recovery Token",
  symbol: "Test",
  uri: "", // Arweave URI link which uses metaplex standard if you're making art NFTs
  sellerFeeBasisPoints: 0,
  creators: null,
  collection: null,
  uses: null,
};

const addMetadata = async (mintAddress: PublicKey, connection: Connection) => {
  const metadataAccount = await getMetadataPDA(mintAddress);
  const transactionToSend = await createMetadataAccount(
    metadataAccount,
    mintAddress,
    steve.publicKey,
    metadata
  );
  const transactionId = await sendAndConfirmTransaction(
    connection,
    transactionToSend,
    [steve]
  );
  log(transactionId);
};

const mintNFT = async () => {
  // Create a SPL token first with supply = 1 and decimal = 0 so the wallets can identify your asset (done, easy!)
  const mintAddress = await createMint(
    connection,
    steve,
    steve.publicKey,
    steve.publicKey,
    0
  );
  // Create another account preferably derived from the mint address of your SPL token using PDAs
  // Finally, save your custom metadata in the second account and use it in any way you need.
  // To create metadata account on any SPL token you can create using (JS version)
  const transactionId = await addMetadata(mintAddress, connection);
};
2
  • createMetadataAccountArgsV2 is not defined
    – Iulian
    Commented Dec 8, 2023 at 10:44
  • @Iulian createMetadataAccountArgsV2 is a key in an object in the code above, not a variable.
    – mikemaccana
    Commented Nov 27 at 17:58
2

Maybe a bit dated, but some example code from Strata if nobody else posts a full answer:

https://github.com/StrataFoundation/strata/blob/master/packages/marketplace-ui/src/components/form/ManualForm.tsx#L66

1
  • Thanks, that uses "@strata-foundation/react" to do most of the work, so it's not quite the minimal spl and web3.js answer I was hoping for, I can dive into there to see how that works though...
    – mikemaccana
    Commented Jan 28, 2023 at 23:03
2

I found that the easiest way for me was to create the token via spl-token CLI then add metadata via metaboss (https://metaboss.rs/create.html) You can also use metaboss for both tasks (creating then adding metadata)

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.