9

I'm minting Solana NFTs. Candy machine v2 was recently released and v1 is deprecated.

If I create a v2 candy machine and mint some NFTs, how can I later find the hashes from all the tokens that were minted?

emersonthis
  • 32,822
  • 59
  • 210
  • 375

1 Answers1

15

If you have the candy machine id, you can find all mints with this:

import { Connection } from '@metaplex/js';
import { Metadata, MetadataProgram } from '@metaplex-foundation/mpl-token-metadata';
const connection = new Connection('mainnet-beta');
const MAX_NAME_LENGTH = 32;
const MAX_URI_LENGTH = 200;
const MAX_SYMBOL_LENGTH = 10;
const MAX_CREATOR_LEN = 32 + 1 + 1;



const candyMachineId: string = 'BdNtsrV26ZHdqDFxmDfLib6CrcUNj4ePorhppHreRgER';

export async function fetchHashTable(hash: string){
  const metadataAccounts = await MetadataProgram.getProgramAccounts(
    connection,
    {
      filters: [
        {
          memcmp: {
            offset:
              1 +
              32 +
              32 +
              4 +
              MAX_NAME_LENGTH +
              4 +
              MAX_URI_LENGTH +
              4 +
              MAX_SYMBOL_LENGTH +
              2 +
              1 +
              4 +
              0 * MAX_CREATOR_LEN,
            bytes: hash,
          },
        },
      ],
    },
  )

  const mintHashes: any = []

  for (let index = 0; index < metadataAccounts.length; index++) {
    const account = metadataAccounts[index];
    const accountInfo: any = await connection.getParsedAccountInfo(account.pubkey);
    const metadata = new Metadata(hash.toString(), accountInfo.value);
    mintHashes.push(metadata.data.mint)
  }
  console.log(mintHashes)
}
fetchHashTable(candyMachineId)

You can find this and more on the solana cookbook

EDIT: The above was for Candy Machine V1.

For Candy Machine V2, you would do the following:

import { Connection, clusterApiUrl, PublicKey } from '@solana/web3.js';
import bs58 from 'bs58';

const connection = new Connection(clusterApiUrl('mainnet-beta'));
const MAX_NAME_LENGTH = 32;
const MAX_URI_LENGTH = 200;
const MAX_SYMBOL_LENGTH = 10;
const MAX_CREATOR_LEN = 32 + 1 + 1;
const MAX_CREATOR_LIMIT = 5;
const MAX_DATA_SIZE = 4 + MAX_NAME_LENGTH + 4 + MAX_SYMBOL_LENGTH + 4 + MAX_URI_LENGTH + 2 + 1 + 4 + MAX_CREATOR_LIMIT * MAX_CREATOR_LEN;
const MAX_METADATA_LEN = 1 + 32 + 32 + MAX_DATA_SIZE + 1 + 1 + 9 + 172;
const CREATOR_ARRAY_START = 1 + 32 + 32 + 4 + MAX_NAME_LENGTH + 4 + MAX_URI_LENGTH + 4 + MAX_SYMBOL_LENGTH + 2 + 1 + 4;

const TOKEN_METADATA_PROGRAM = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
const CANDY_MACHINE_V2_PROGRAM = new PublicKey('cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ');
const candyMachineId = new PublicKey('ENTER_YOUR_CANDY_MACHINE_ID_HERE');

const getMintAddresses = async (firstCreatorAddress: PublicKey) => {
  const metadataAccounts = await connection.getProgramAccounts(
      TOKEN_METADATA_PROGRAM,
      {
        // The mint address is located at byte 33 and lasts for 32 bytes.
        dataSlice: { offset: 33, length: 32 },

        filters: [
          // Only get Metadata accounts.
          { dataSize: MAX_METADATA_LEN },

          // Filter using the first creator.
          {
            memcmp: {
              offset: CREATOR_ARRAY_START,
              bytes: firstCreatorAddress.toBase58(),
            },
          },
        ],
      },
  );

  return metadataAccounts.map((metadataAccountInfo) => (
      bs58.encode(metadataAccountInfo.account.data)
  ));
};

const getCandyMachineCreator = async (candyMachine: PublicKey): Promise<[PublicKey, number]> => (
    PublicKey.findProgramAddress(
        [Buffer.from('candy_machine'), candyMachine.toBuffer()],
        CANDY_MACHINE_V2_PROGRAM,
    )
);

(async () => {

  const candyMachineCreator = await getCandyMachineCreator(candyMachineId);
  getMintAddresses(candyMachineCreator[0]);

})();

Make sure you replace ENTER_YOUR_CANDY_MACHINE_ID_HERE with your candy machine id

Jacob Creech
  • 1,797
  • 2
  • 11
  • This looks great!! Thanks so much. If you have time, can you explain this code a bit? In particular, what's happening in `filters.memcmp.offset`? I don't see that in the cookbook. I'll test this tonight and add a bounty tomorrow. – emersonthis Jan 06 '22 at 17:58
  • @emersonthis There's a good overview on how to use those params in the [cookbook](https://solanacookbook.com/ingredients/get-program-accounts.html). Ultimately it is helping you find the right offset for what you are looking for in the accounts. – Jacob Creech Jan 06 '22 at 18:49
  • I'm not getting any results when I run this code. I `npm i`ed the two packages and replaced `candyMachineId` with the address of my candymachine. But when I run the script with `ts-node` I get an empty array `[ ]`. I know the minting worked because I see all the NFTs in my wallet. Any ideas what I'm doing wrong? – emersonthis Jan 09 '22 at 03:08
  • I have a hunch: I minted using candy machine *v2*... is this script incompatible with the newest release? – emersonthis Jan 09 '22 at 03:20
  • May have been candy machine v2. We're updating today with references for v2. For now, this is the PR with the code for v2 until it gets pulled in https://github.com/solana-dev-adv/solana-cookbook/pull/184/files – Jacob Creech Jan 10 '22 at 15:25
  • Added V2 answer above – Jacob Creech Jan 10 '22 at 16:07
  • I saw that PR in the repo. Exciting! I'll try it tonight. Thanks! – emersonthis Jan 10 '22 at 22:28
  • V2 sample didn't work. `getCandyMachineCreator` returns an address that ends with a `,255`, and the `getMintAddresses` function always returns an empty array even when I can fetch the mint addresses using websites like ME and pentacle. – Richard Ore May 13 '22 at 15:16
  • 1
    I keep getting a timeout on multiple different endpoints when running getMintAdresses – Tomvkgames Jun 22 '22 at 08:38
  • 1
    getProgramAccounts, like what the name said, get a program's acccounts. If a program only has one account, you call this api, it takes almost 0 ms, but like Candy Machine Program, it has enormous accounts, millions? when you call it, it scans its huge data, takes almost forever. So calling getProgramAccounts for candymachine is almost unusuable. – diwatu Jan 28 '23 at 19:14