Solana Analytics Starter Guide (Part 3): NFT, pNFT, xNFT, cNFT
Covering the structure of the NFT, programmable NFT (pNFT), executable NFT (xNFT), and compressed NFT (cNFT) - with example queries along the way.
This is the third of a four part series covering basic Solana analysis.
→ part 3: Solana NFT structure and mints (NFT, pNFT, xNFT, cNFT)
Coming from an Ethereum background? Start with this guide translating EVM concepts to their Solana counterparts.
For this guide, you should already be familiar with the structure of Solana instructions, how a token transfer works, and basic associated account patterns.
By the end of the four guides, you should be able to understand the core concepts of any of the covered queries. My DMs on Twitter are always open for questions or ideas.
Basic SQL knowledge will be helpful. If you’re unfamiliar, go through this tutorial.
Table of Contents:
You’ll learn to understand and query:
a breakdown of NFT accounts and structure
how NFTs are minted
the different kinds of NFT standards (NFT, pNFT, xNFT, cNFT)
The NFT standards in Solana evolve very quickly, there are some such as inscriptions, mpl404, and mpl core that I have not included here.
What’s in an NFT?
Throwing you right into it, these are the accounts that make up an NFT:
Token Account: this is your “associated account” that holds your balance of the NFT. Usually called just an “address” in our data tables (like in account_activity, daily_balances, latest_balances).
Mint Account: this is the “token mint address” you already learned about in the last guide. Every single NFT has it’s own mint address, because we need the supply to be 1 with 0 decimals. It’s created from spl token, just like any other token.
Master Edition: this is the magic account that forces your mint account to have a supply of 1, and takes over the “mint” authority so that no more can be minted. This is what makes it an NFT!
Metadata Account: this is the account where stuff like the URI, token name/symbol, and collection mint are tied to.
Collection Mint: this is the defacto way of tying NFTs together into a collection. It is another “token mint address” and is referenced in the NFT metadata account. These must be verified per token to be considered part of the collection.
Tokens sometimes have “ids” as numbers that are mentioned in the token name. But for data analysis purposes, think of each token mint/master/metadata account as a “unique token identifier” (the three are unique individually and together).
Metaplex is the protocol that created this NFT standard, and you can check for mints by querying the mpl_token_metadata program in Dune. The base token/mint is still the SPL token program that we covered in the last two parts on DEXs.
Enough overview; let’s look at the lifecycle of one mint.
I’ve created a tokens_solana.nft table that saves you the work of putting together the metadata and accounts on all NFTs (cNFTs are unique on leaf_id and account_merkle_tree).
If you’re interested in trading analysis, use the nft_solana.trades table.
Minting and Auctions
One of the popular NFT collections on Solana is “Mad Lads”. This one is mine:
There is a lot happening here, and some accounts/metadata are not even shown in the UI above. To have a true understanding of an NFT, we need to walk through the mint transaction.
Now I know, scrolling through that looks really daunting. There are over 30 nested instruction calls happening in here. Let’s focus on just instruction #7, where MintV2 was called on the program CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR.
This program is known as “candy machine”, and comes in 3 different version (as of Nov 2023). It helps handle payments on NFT mints, and in this case does a “burn to redeem”. This Mad Lad mint required you to pay 6.9 SOL and have a MAD token to burn. You can actually see this happens before the MintV2 instruction call in instructions #3 and #5, respectively.
Now, instruction #7.1 is the key logic you need to understand:
This Create instruction handles the token mint account, metadata account, and master edition account all in one call.
NOTE: There are other methods in the mpl_token_metadata program that let you attach the metadata and master edition accounts in separate calls instead of all in one. For example, fungible tokens attach just a metadata account. You can find my logic for getting all NFT mints and metadata here
All of the token metadata that gets stored in the Metadata account is currently undecoded in the “instruction data” hex field. If you scroll down on the explorer page, you’ll see the hex decoded into json (we do the same thing in our decoded table “mpl_token_metadata_solana.mpl_token_metadata_call_Create”). A few key things to note about the metadata:
Metadata can be updated at any time by the delegate. The delegate is set in the Delegate instruction, which is instruction #8 here. Can be updated per collection or per token.
There is a collection mint specified in here, but some older NFTs do not include a collection mint in the call. I’ve found it fairly inconsistent to get the collection mint from here, and instead I grab it when the Verify function is called in instruction #7.24. Only tokens that have been verified can truly count as part of a collection mint, since technically anyone can just attach a collection mint address to their NFT metadata.
Royalties are defined under the “sellerFeeBasisPoints”. 100 basis points are in 1%, so 500 basis points means 5% royalties here.
The “creators” struct tells marketplaces who to send what percentage of royalty fees to. Before “collection mint” was implemented, teams used to use the first creator id (index 0) as a makeshift collection id indicator. This creator is sometimes referred to as the “Verified Creator”.
The key instruction here that makes this token “non fungible” is that the token mint account’s “mint authority” gets transferred from the minter to the “master edition” account in instruction #7.10. The “master edition” also gets control of the freeze authority (used for transferring tokens) in instruction #7.11.
The NFT is finally minted to the associated account in instruction #7.21, and then transfers are frozen again. You may wonder why transfer freezing matters - this is how royalties get enforced, among other composable features.
Take your time to go through and digest this, and go poke around the Metaplex docs and rust program docs.
I’ve put together a query to showcase how to get all NFT holders and minters for a given collection mint - the one below is for Mad Lads. Pick any of the account fields and see if you can find the original mint transaction!
There have been over 100 million NFTs minted as of November 2023:
Metaplex earns fees on token metadata mints; you can find their protocol fees explained here. These fees are paid by collectors, not creators. Metaplex collects fees from metadata accounts using instruction enum 54 (0x36) and has collected 20,000 SOL in fees as of November 2023.
If you want to dive more into NFT mints stats like the ones above, check out this dashboard.
NFT standards (NFT, pNFT, xNFT, cNFT)
Now that you understand the main NFT structure and minting pattern, let’s break down the four main NFT standards you need to navigate.
The legacy NFT standard has the same accounts involved but less complex minting patterns, no royalty enforcement, the spl token is called for transfers, and many don’t use collection mints (so use the first creator id instead for data analysis). New collections should follow the collect mint pattern.
The programmable NFT (pNFT) is the most common for collections today, and the Mad Lad mint above is a pNFT. This standard came out with MIP-1, introducing default token freezing, royalty enforcement, and ruleset accounts. You can read more about the ruleSet accounts that give it flexibility in the docs here or here. Transfers (and mint/burn/delegate/revoke) now call the metadata program, which then handles the calls to the spl token program.
The compressed NFT (cNFT) is a lot more complex, as it gets rid of accounts so that you can save on minting costs. You mint a leaf of a merkle tree instead off of the bubblegum program, and the metadata is “attached” in the call data but not stored in any metadata account. A collection can have multiple merkle trees to hold all its tokens.
One of the top collections for cNFTs is the “Tensorians”, here is an example token:
The key thing you need to know is that the leaf id is just an incremented count of mints per tree. So you can literally do a row_number() on the merkle tree to get the leaf id of a specific mint. The mint of the token above can be found here, starting at instruction #3.33 after all the burns of the other cNFTs. You’ll see leaf ids referenced often as “index” or “nonce” as well - they are the same thing.
This query below gives you cNFT balances for a collection mint, using the same tokens_solana.nft table:
Thanks to @Ichigo for the help answering all my compression questions! Check out his long form guide explaining compression.
The executable NFT (xNFT) is more of an attachment than its own token. You can use xBackpack to run frontend/mobile programs attached to some NFT collection mint. There are also non-nft apps like Jito Staking.
xNFT apps are react code stored on IPFS, where the hash is the uploaded onchain in an app and attached to the NFT collection mint. Here is the transaction where the Mad Lads staking xNFT was created (see the J1S9 collection mint address). The staking program looks like this within the xBackpack wallet:
Check out OSK’s xNFT dashboard to learn more about this standard, and follow them on Twitter!
Onto the last guide
Congrats, you now understand NFTs on Solana! They’re super flexible programs and fun to analyze once you get used to their structure.
In the last guide, I’ll cover how to understand and decode all Solana events types.
Some relevant dashboards for you to check out are: