A lot of new Web3 social media sites have cropped up recently, most promising (or implying to promise) being censorship resistant or completely uncensorable. The fact of the matter is that most of these networks are in fact subject to censoring because of how they implement the NFT token standard.
What is an NFT?#
A non-fungible token (NFT) is simply a contract on an Ethereum virtual machine (such as Ethereum, Polygon, Optimism, etc.) that implements (most commonly) either the ERC-721 or ERC-1155 standard. These documents describe the minimum specification that must be implemented by a contract to be considered an NFT. For the remainder of this document, whenever I discuss NFTs I'll be talking about the ERC-721 standard which is the older and more widely implemented NFT standard.
The three attributes most unique to an NFT that make it stand out from fungible tokens is that each NFT is represented by a unique numeric ID, there are no fractional parts of a NFT (you cannot own one-half of one), and each token contract implements the tokenURI()
function.
What Is a Token URI?#
A uniform resource indicator (URI) is a unique sequence of characters that identifies a logical or physical resource used by web technologies.
We most commonly think of http://
or https://
URLs (which are a specific type of URI, learn more at Wikipedia) and when it comes to NFTs, the token URI returned by the tokenURI()
function is usually a Web URL of these types.
The same ERC-721 specification described above also defines a token metadata standard that is used to describe an NFT. This typically points to a JSON file hosted on a Web site that looks something like this:
{
"description": "This is a description of this particular NFT.",
"external_url": "https://example.com/",
"image": "https://example.com/image.png",
"name": "NFT Name",
"properties": {
"anything": "value goes here!"
}
}
That image
property is how sites like OpenSea typically get the image used to display your NFT in their interface and it's how most NFTs are attached to "art." The description
field is also frequently displayed with your NFT on the same sites.
The Risk of Censorship#
Many NFT-based social media sites like to tout that all your posts are stored on the blockchain
and they are censorship resistant.
Unfortunately, these claims are often not quite true.
As described above, the tokenURI()
of a specific NFT typically lives on a Web server that is run by the social media site. This Web server is often under the direct control of that social media platform, meaning they can modify the JSON returned at the token URI link to whatever they want. Far from being "stored in the blockchain" or a "censor free environment," these sites are actually not storing any data on the blockchain and are subject to being censored.
Look again above at the data returned by the tokenURI()
function. Remember the description
and image
fields? These are literally the content of your social media post, and the only thing living on the blockchain is a URI that points to that JSON file that exists on the social media site's server, where they can do whatever they want with that file, including censor it.
For simplicity's sake, most NFTs implement the tokenURI()
function as a very simple one that just concatenates a predetermined URL (like https://example.com/
) with the token ID (which is passed into the function as its only parameter) and an extension (resulting in something like https://example.com/1.json
). This way, when an NFT is minted, very little data is actually stored on the blockchain. Really, the only thing stored is that a token with a particular ID was minted.
Truly Uncensorable NFTs#
Is it possible to have an uncensorable NFT? Yes. How about an NFT where all the data truly does live on the blockchain? Yes again (with some caveats). Let's delve into each question in detail below.
Uncensorable NFTs#
We can create an NFT that is uncensorable by leveraging another popular technology associated with the crypto world, decentralized file storage such as IPFS or Arweave. Both of these services offer decentralized and, more importantly, immutable data storage. So, theoretically, a social media app could generate the JSON metadata file for an NFT at time of creation, store it on one of these platforms, then mint the NFT itself and passing in the appropriate ipfs://
or ar://
URI which is then stored on the blockchain.
Since these file storage services are immutable, data stored on them at a particular URI can never be modified or deleted (small caveat: IPFS does do garbage collection where files that are not "pinned" have a possibility of being removed; you can ensure your particular NFT URI remains alive by pinning them redundantly yourself; see more in the IPFS docs).
Why isn't this method more popularly used? Most likely it's due to the slightly increased complexity of needing to upload all generated JSON files to IPFS or Arweave and then sending the proper link to the NFT contract and storing that link. This makes minting transaction more complex and will increase the gas cost.
Storing All Data on the Blockchain#
We can store all NFT data on the blockchain by passing in that data to a mint function on the NFT contract, including the description, the image URI (bonus points if you use decentralized file storage to store it, as described above), and all the attributes. Then, each of these must be stored on the blockchain in a smart contract. Once the tokenURI()
function is called, you can actually generate a Base64 encoded data:
URI consisting of these components. Sites like OpenSea can read and process these URIs and will display the data properly.
A great example of this is the Uniswap V3 Positions NFT contract that mints NFTs representing the positions the holder has in a particular pool. All of the data resides on the blockchain and the tokenURI()
function returns something that looks like:
data:application/json;base64,eyJuYW1lIjoiVW5pc3dhcCAtIDElIC0gTUFSRS9XRVRIIC0gMC4wMDEwMDgxPD45OT...
I've shortened the URI because it's about 13,000 characters long! This is because not only are the description, attributes, etc. stored on the blockchain, but the NFT contract also generates an animated SVG image that is used to represent the token. It then Base64 encodes this SVG and uses it as a field in the JSON file that is then Base64 encoded again!
Here's an example of a decoded Uniswap V3 URI:
{
"name": "Uniswap - 1% - MARE/WETH - 0.0010081<>991430000000000000",
"description": "This NFT represents a liquidity position in a Uniswap V3 MARE-WETH pool. The owner of this NFT can modify or redeem the position.\n\nPool Address: 0x94adf857e6f0bdad207c9934a27a632b5cf29ca6\nMARE Address: 0xc5a1973e1f736e2ad991573f3649f4f4a44c3028\nWETH Address: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\nFee Tier: 1%\nToken ID: 160857\n\n⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure token addresses match the expected tokens, as token symbols may be imitated.",
"image": "data:image/svg+xml;base64,PHN2ZyB3aWR..."
}
Once again, I've shortened the URI in the image
field because it is nearly 10,000 characters long.
Why don't more platforms use this method? Well, that's obvious, because the cost of storing all the data on the blockchain would become prohibitively expensive, especially on a network like Ethereum.
Big Caveat#
Just because an NFT contract stores all its metadata on IPFS or on the blockchain, it doesn't necessarily mean it is uncensorable! The token contract may have functions that the contract owner can call that will modify a token's URI or metadata stored on the blockchain. You should always closely inspect the contracts or find a trusted source who can. And never trust a contract that does not publish its source code!
How To Tell If an NFT Is Uncensorable#
How can you tell if a NFT is uncensorable? This is fairly easy for an ERC-721 NFT. Just go to the NFT's page on OpenSea, scroll down and expand the Details section, make note of the particular token ID you're interested in, and click on the contract link.
That will open the contract in a blockchain explorer. About halfway down the screen, click on Contract and then click on Read Contract.
Finally, scroll down until you find the tokenURI()
function, click on it, then enter the token ID you noted above. Click on the Query button and it should return your token's URI.
If your token URI begins with data:
, ipfs://
, ipns://
, or ar://
, then your token is truly uncensorable (probably, see caveat above). If it begins with data:
then your token is actually stored on the blockchain!
Closing#
I hope this helped improve your understanding of what an NFT is and how they can be censored. I have an idea of creating a few more posts on this topic where I actually write an example NFT contract that is censorable and show you how it can be censored, maybe even allow people to mint copies of their censorable NFTs and then they can watch as I censor it. Then I can make an uncensorable version.
Leave a comment here and let me know what you thought about this little write-up or if you have any questions. You can reach me on Authencity or on Mastodon.
Edited: A prior version of this article mentioned that Authencity did not store all of a post's data on the blockchain. Marc from Authencity contacted me and pointed out the getTokenData()
non-standard function and it does in fact appear to prove the contract stores all of a post's data on the blockchain. I apologize for missing this and retract that statement; the article has been edited appropriately.