twifag-Note-10179-1

Token ID: 2

ERC-721 1 Transfers

Metadata

{
  "title": "NFT Social Media and the Risk of Censorship",
  "tags": [
    "post",
    "NFT",
    "censorship",
    "decentralization",
    "ERC-721",
    "IPFS",
    "Arweave",
    "JSON"
  ],
  "summary": "How can you tell if an NFT is truly uncensorable or if all its data is stored on the blockchain?  A lot of Web3 social media sites have popped up recently making some claims that appear to be less than true.  I'll attempt to break the subject down and make it a bit more understandable.",
  "sources": [
    "xlog"
  ],
  "external_urls": [
    "https://blog.twifag.com/nft-risk-of-censorship"
  ],
  "date_published": "2022-10-06T19:12:26.684Z",
  "content": "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.\n\n## What is an NFT?\n\nA <dfn>non-fungible token (<abbr>NFT</abbr>)</dfn> is simply a contract on an Ethereum virtual machine (such as Ethereum, Polygon, Optimism, etc.) that implements (most commonly) either the [ERC-721](https://eips.ethereum.org/EIPS/eip-721) or [ERC-1155](https://eips.ethereum.org/EIPS/eip-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.\n\nThe 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.\n\n## What Is a Token URI?\n\nA <dfn>uniform resource indicator (<abbr>URI</abbr>)</dfn> is <q cite=\"https://en.wikipedia.org/wiki/Uniform_Resource_Identifier\">a unique sequence of characters that identifies a logical or physical resource used by web technologies.</q>  We most commonly think of `http://` or `https://` URLs (which are a specific type of URI, learn more at [Wikipedia](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier)) and when it comes to NFTs, the token URI returned by the `tokenURI()` function is usually a Web URL of these types.\n\nThe 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](https://en.wikipedia.org/wiki/JSON) file hosted on a Web site that looks something like this:\n\n```json\n{\n\t\"description\": \"This is a description of this particular NFT.\",\n\t\"external_url\": \"https://example.com/\",\n\t\"image\": \"https://example.com/image.png\",\n\t\"name\": \"NFT Name\",\n\t\"properties\": {\n\t\t\"anything\": \"value goes here!\"\n\t}\n}\n```\n\nThat `image` property is how sites like [OpenSea](https://opensea.io/) 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.\n\n## The Risk of Censorship\n\nMany NFT-based social media sites like to tout that all your posts are <q>stored on the blockchain</q> and they are <q>censorship resistant.</q>  Unfortunately, these claims are often not quite true.\n\nAs 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.\n\nLook 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.\n\nFor 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.\n\n## Truly Uncensorable NFTs\n\nIs 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.\n\n### Uncensorable NFTs\n\nWe can create an NFT that is uncensorable by leveraging another popular technology associated with the crypto world, *decentralized file storage* such as [IPFS](https://ipfs.io/) or [Arweave](https://www.arweave.org/).  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.\n\nSince 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](https://docs.ipfs.tech/how-to/pin-files/)).\n\nWhy 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.\n\n### Storing All Data on the Blockchain\n\nWe 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](https://en.wikipedia.org/wiki/Base64) encoded `data:` URI consisting of these components.  Sites like OpenSea can read and process these URIs and will display the data properly.\n\nA 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:\n\n```\ndata:application/json;base64,eyJuYW1lIjoiVW5pc3dhcCAtIDElIC0gTUFSRS9XRVRIIC0gMC4wMDEwMDgxPD45OT...\n```\n\nI'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!\n\nHere's an example of a decoded Uniswap V3 URI:\n\n```json\n{\n\t\"name\": \"Uniswap - 1% - MARE/WETH - 0.0010081<>991430000000000000\",\n\t\"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.\",\n\t\"image\": \"data:image/svg+xml;base64,PHN2ZyB3aWR...\"\n}\n```\n\nOnce again, I've shortened the URI in the `image` field because it is nearly 10,000 characters long.\n\nWhy 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.\n\n## Big Caveat\n\nJust 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!\n\n## How To Tell If an NFT Is Uncensorable\n\nHow 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.\n\n![OpenSea Details Example](ipfs://bafybeicon2irptajwtqyvz4d65wvb44eknr5lo6aqa5ejfdz5jts5l5vmu/mjowdbreuto.png)\n\nThat will open the contract in a blockchain explorer. About halfway down the screen, click on Contract and then click on Read Contract.\n\n![Etherscan Read Contract Example](ipfs://bafybeihcsh3xdgv6aksjpqpu3hruh4apvxv5d4mf5loxvdz4mahyfg2dou/mjowejptjgm.png)\n\nFinally, 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.\n\n![Etherscan tokenURI Query Example](ipfs://bafybeicj42ou6pvlc7kvs2kczjgc4sxgbtuan66ns3wusm2latrm3n3pmu/mjowettdlnr.png)\n\nIf your token URI begins with `data:`, `ipfs://`, `ipns://`, or `ar://`, then your token is truly uncensorable (probably, see [caveat](#big-caveat) above).  If it begins with `data:` then your token is actually stored on the blockchain!\n\n## Closing\n\nI 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.\n\nLeave 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](https://authencity.io/user/twifag) or on [Mastodon](https://pone.social/@corpubro).\n\n*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.",
  "attributes": [
    {
      "value": "nft-risk-of-censorship",
      "trait_type": "xlog_slug"
    }
  ]
}