G’day everyone! 0xtygra here.
Last week I started looking into the structure of Solana NFT collections. I’m working on Solana support for PopRank, and our recent Good Minds Art Drops on the Ethereum L2 Arbitrum sparked some interesting internal conversations around more ways in which we can add to the Good Minds ecosystem across chains. As such, understanding how Solana NFT collections work felt like the right move. Oh, and how could I forget the y00ts hype?
This field note is intended to be a high level overview of how Ethereum NFT collections are structured vs Solana. I’m not Solana dev (yet), so I’ll keep it simple, but some small technical knowledge of how Ethereum contracts work will go a long way! I’ll add some helpful links at the end for anyone that wants to dive deeper.
Every unique entity you interact with on Ethereum is an “account”. Your wallet, the contract for your favourite NFT collection, the contract for the shitcoin you didn’t buy that 10x’d in a week and -100xd the week after - they’re all “account”s. Unlike your wallet though, called an “Externally Owned Account (EOA) as a user controls it, a contract is an account that stores, and is controlled by, code. Not only does it store code, it also stores data. You can interact with this type of account by calling the methods defined on it.
Every account on the Ethereum is identified by its address. One collection usually has just one address associated with it, that points to the deployed ERC721(a) contract of that collection.
The Ethereum NFT standard is called ERC721, as it was the 721st Ethereum Improvement Proposal that defined this standard. It outlines methods like setApprovalForAll, tokenURI, transferFrom, ownerOf, among others. You may have heard of the ERC721a contract, pioneered by the Azuki collection. This is a flavour of the ERC721 contract that does some cool things in order to make the mint functionality as efficient as possible - it’s still an ERC721 compliant contract standard though.
The two main pieces of information defining an NFT are its owner, and its metadata. The contract internally stores a mapping of the owners of each token, queryable by the ownerOf method. To track the metadata, the contract also defines a tokenURI method, which takes in a tokenId and returns the given token’s metadata.
When we mint a token on an Ethereum contract, the contract is simply adding another tokenId to its internal storage, and broadcasting something called a “Transfer event”, from the null address to the minter. When thinking about a Transfer, it’s slightly different to how physical transfers work. You aren’t really “moving” the token, you’re moreso updating the contract’s internal ledger to store the new owner’s address. Kind of like buying a house - when you buy a house, (usually) they don’t pick up the house and deliver it to you, instead you are transferred the ownership of the house, and maybe you broadcast this transfer event on Twitter.
So, to summarise, an Ethereum NFT collection is (usually) a single contract deployed on Ethereum, which is an account that stores data and code and is controlled by its code. This contract handles all the functionality for all the tokens in the contract, ie: it stores who owns which tokens, as well as a link to each token’s metadata.
Similar to Ethereum, everything on the Solana network is also an account. A Solana account is an entity on the blockchain, identified by an address, that stores something. What we would call a “smart contract” or “contract” on Ethereum is called a “program” on Solana. A Solana program is an account that stores code, and is marked as “executable”, telling you that you can run functions on the program. Unlike Ethereum contracts though which can store a mix of code and data, a Solana program only stores code - no data, or what developers often call “state”. If a program needs to store or modify data, it does so by interacting with other accounts that store just data and no code.
This leads to some interesting outcomes. On Ethereum, in most cases a contract’s code is interacting with itself, ie: it’s reading from or writing to its own data. Most ERC721 contracts as a result end up deploying mostly code that every other ERC721 contract has already deployed. ie: collection A needs to deploy its own version of the transferFrom method, even if it’s the same as every other collection’s, in order for it to transfer collection A’s tokens.
On Solana though, programs can only store code, so they have no state. As such, most collections can actually reuse the one main deployed, on-chain program (Solana’s Token Program), as there’s no difference between that and deploying the same program themselves.
The best analogy to understand the Token Program is that of a printer. Unless you have some unique needs, nowadays I find I don’t really need my own printer. When I need to print, I go to my local library, which has just one printer. I need to print different documents to everyone else though, which I achieve by plugging in my own USB to the one, shared printer, which gives it my specific data that it needs to print.
This one program is used to mint almost all Solana NFTs, and is actually the owner of all tokens it mints. This begs the question: if just one program owns all tokens it mints, and many different collections are using the same program, how do we differentiate collections?
Similar to Ethereum and its “deployer addresses”, aka the wallet used to deploy a contract, different Solana collections are interacting with the same Token Program but with different wallets. Take DeGods for example, they will have the DeGods deployer wallet, which interacted with the Token Program to mint all 10k DeGods. The Token Program will create a new account for each token minted (more on this later), and while the Token Program owns all of these accounts, the DeGods deployer wallet is set as the “Update Authority” of these accounts. Aka, it’s allowed to actually change the data on these accounts if it wants.
We touched on another difference just above too. Whereas in Ethereum, a collection has just 1 address, and all the data and logic for all the tokens, their owners, metadata, etc, all exists on the one contract address, with Solana, each token is its own account with its own address. The below image is a great visualisation for what a single token looks like, and how it relates to your wallet.
Overview | Metaplex Docs
Wallet Account
Mint Account
The Mint Account stores all the data describing a given token. It’s the account that’s created during the mint process for a token.
“PDA” stands for “Program Derived Address”, which is like a sub-account within another account. Don’t worry about it too much though, just think of the “Metadata Account” as a section within the “Mint Account” that holds all the token metadata
Token Account
At first the structure might seem alien to those experienced with Ethereum, but, I believe it makes more sense over time. The fact that there are multiple accounts used to store data for a single token is simply an example of a separation of concerns. Solana separates the concepts of the token’s data and its ownership into two different accounts. When you transfer the token to someone else, the Mint Account isn’t affected at all, just the Token Account’s data.
So now we have an idea of how an individual token is created, where it’s metadata is stored, and where its ownership data is stored. What the collection as a whole?
Each token is its own separate account, so we need a way to somehow create an umbrella for all these individual tokens. Ethereum achieves this by virtue of the fact that all the tokens’ data actually live within the one address on the network, what we call the “contract address”. Solana achieves this in a different way.
On Solana, the concept of a collection is actually represented by another token, called the “On-chain Collection”. The same wallet that was used to mint the actual individual NFTs of a collection mints a final token which stores collection-level metadata such as: the collection name, description, token symbol, royalty percentage, and more.
Tokens within the collection are updated to reference this collection token via their metadata. This update is performed by the same wallet that was used to create the rest of the tokens in the collection, which “verifies” that the token does indeed belong to this collection. Without this verification step, anyone could mint a new token and then point to another collection’s On-chain Collection.
Both Ethereum and Solana have support for Non Fungible Tokens, but have noticeably different underlying structures.
Ethereum has one main contract which stores all the logic to interact with its tokens (ie: logic to transfer a token from one wallet to another), as well as all the data associated with each token (owner, metadata, etc) and the collection as a whole.
Solana on the other hand, due to the fact that executable programs have no state, has one program that is reused across collections, where each collections interacts with it using a different wallet. Each token minted by this program is its own account (and therefore address) on the network, and there’s even /another/ account that tracks the ownership of each token. A collection itself too is a token, and each token within a collection will point to the collection token in its metadata.
Phew, that was a long one, thanks for sticking with it! I hope you enjoyed the first instance of the Good Minds Field Notes.
I’m 0xtygra on Twitter, if you enjoyed this feel free to give me a follow, or to reach out with any questions :)