Where the Wild Media Are
This article is part of the Alpacawhal guest author series Ghost Palette, where we invite established crypto artists to write from their own perspective on technical topics related to the scene.
In this edition, we invite OG crypto artist obxium to write about NFTs, media, and how artists can verify the integrity of their art media.
Hello!
The intended audience for this information are artists or collectors who already have a fundamental grasp of what data make up the typical crypto art NFT and are comfortable with fairly deep technical details. You have been warned! 😭
The idea is that a complete work of crypto art is the token + media, as it makes little sense to try and pass off the object as a complete work of art if one of these two are missing. Okay so far?
One more time with fun details!
The workflow basically goes like this...
- Artist interacts with Web 3 app, uploads their art media, enters token metadata, and pays for transaction fees with a connected web 3 wallet (i.e. Metamask, WalletConnect, whatever).
- Web 3 application interacts with smart contract to mint the actual token(s) with metadata according to the artist's intententions as expressed in the Web 3 app. The art media is sent to file storage like the Interplanetary File System or IPFS as it is known.
- The media is then pointed to (aka "pinned" to) the token URL as a way to match the two up and form the unholy matrimony that we all know and love as a work of "crypto art". 🎉
Easy and makes such perfect sense right?
Notice any problems tho?
This splitting up of the token into a part that lives "on-chain" and a part that lives in some rando file system is weird and can lead to issues. Computering is hard tho and it is physically unpossible to put the media into the token right now unless that media is super tiny so this is how we do...
Besides being awkward, the disconnect here between media and token sets up other sources of sadness, too. Artists and collectors alike should be aware that the following things can and do happen.
- Media and token are permanently disassociated. The media and token can diverge and you are left with a token with no pinned media that could possibly still retain some utility (e.g. let the holder into the secret party). The token holder is going to have a difficult time proving that it is indeed a painting of duct tape banana'd to a wall, however. 🍌
- Centralized service outage. Maybe it didn't die, but a server somewhere got super drunk and the media became unavailable just as you were showing it in a critical show... or attempting a viewing with a potential buyer, or anything important to an artist where the public is involved. Embarrassing and unfortunate!
- Artist's original media differs from that stored for the token. In this case, somehow the exact art media is not present in the storage. The media appears okay in the case of images, but it is not exactly the same as what the artist provided. It could actually be a compressed or resized version, or worse.
There is not much an artist can do about points 1 and 2, besides pinning their own media files and providing redundancy (you nerd). In the case of point 3, you cannot always determine why it occurred, but you can at least verify that it has occurred.
Dive so deep into this here crypto art that your brain asplode...
The way that you do this is to compute a cryptographic hash of the media file, after the file is finalized locally, but before it is uploaded, and then again after the token is minted by examining a copy of the file downloaded from its storage URL.
You then compare both hashes for a match.
I will show you how to do so now, using my own live art on 4 of the popular platforms, Known Origin, MakersPlace, SuperRare, and Rarible. I am using command line utilities here, but you can also compare hashes with websites to do the entire process in a browser, too.
Since this is my art, I can store the original media files in a trusted location for the comparisons and that location is of course, obxium.com. All my examples here are static 2D illustrations, but the technique works for any kind of data.
KnownOrigin

This is my latest KnownOrigin artwork "Cute Alpacawhal BTC," in an edition of 5.
You can download the original art media and validate it from obxium.com.
curl -sO https://obxium.com/img/cute-alpacawhal-btc.png
Check that the file matches our expecatations as PNG data.
file cute-alpacawhal-btc.png
The output shows the original PNG image data.
cute-alpacawhal-btc.png: PNG image data, 2160 x 2160, 8-bit/color RGB, non-interlaced
Now validate the file against its known SHA 256 summary.
printf "99e5ba641d86dadafd8e4988b63feca2890e054659d173fbe784ef82885fb08f cute-alpacawhal-btc.png" | shasum -a 256 --check
If all is good, then the following output will be printed.
cute-alpacawhal-btc.png: OK
Being the original image, a match is fully expected here. Now, let's take a look at the file that was stored by KnownOrigin when the NFT was minted.

KnownOrigin's web 3 app places the tokenURI data in a div
with the class token-uri
, so you can locate it in the page source, as shown.
Download the JSON file that contains the token meta from the token-uri
value, and print it with jq
to crack it open and feast on the sweet, sweet metadata inside.
curl -s https://ipfs.infura.io/ipfs/Qmae1W6HkYrFjn1VUKZmqMYzmWtKcnChuDyk3SQQhxkMd4 | jq
{
"name": "Cute Alpacawhal BTC",
"description": "This is part of the \"Radical Alpacawhals of Crypto\" series. They appear on all the different platforms in random amounts repping various lifestyle brands and so on.",
"attributes": {
"artist": "obxium",
"scarcity": "rare",
"tags": [
"Alpacawhal",
"BTC",
"Gold",
"obxium",
"2020",
"cute"
],
"asset_type": "image/png",
"asset_size_in_bytes": 647799
},
"external_uri": "https://knownorigin.io/artists/0xf65db13b5ee031cb0ebba525ef21aa6c586681b3",
"image": "https://ipfs.infura.io/ipfs/QmWDsDdxhvEHQgMW3K4tHVNZhGwyMDHASu9TZbuRpE8rtb/asset.png",
"artist": "0xf65db13b5ee031cb0ebba525ef21aa6c586681b3"
}
It's all the cool stuff about my artwork, neato! Next, get the media file (named asset.png) by downloading the value of the image
field.
curl -sO https://ipfs.infura.io/ipfs/QmWDsDdxhvEHQgMW3K4tHVNZhGwyMDHASu9TZbuRpE8rtb/asset.png
Now you can validate the file against its known SHA 256 summary.
printf "99e5ba641d86dadafd8e4988b63feca2890e054659d173fbe784ef82885fb08f asset.png" | shasum -a 256 --check
If all is good, then the following output will be printed.
asset.png: OK
Nice, it looks like the cryptographic hash of the image KnownOrigin associated with my "Cute Alpacawhal BTC" NFT exactly matches that of my original artwork. 🙌
MakersPlace

My most recent MakersPlace artwork "GHOSTCHAINZ."
You can download the original art media and validate it from obxium.com.
curl -sO https://obxium.com/img/GHOSTCHAINZ.png
printf "bd07daebcd33265aa06fc78d891fffa030838c342c41a9ab261b13af889c1406 GHOSTCHAINZ.png" | shasum -a 256 --check
If all is good, then the following output will be printed.
You can also check out the image data to get the dimensions, bit depth, and so on.
file GHOSTCHAINZ.png
GHOSTCHAINZ.png: PNG image data, 2160 x 2600, 8-bit/color RGB, non-interlaced
One feature unique so far to MakersPlace is their Proof of Authenticity page for each artwork minted there.
Here is that page for "GHOSTCHAINZ":

This is a nice place to discover more about the NFT, including its token ID, contract information, and more.
The most accurate method to determine the media URL we need to find the stored image is by directly querying the MakersPlace smart contract with the tokenURI
method, and passing in the token ID. In the case of "GHOSTCHAINZ", it is MakersPlace token ID 21566. Reading the contract tokenURI
method on Etherscan for the token returns this URL string:
ipfs://ipfs/QmSBimtMYWGXG5pLh8wbrndgMW2s47JmqhXeFFVUHGD7eS
This is a native IPFS URL, but if you do not have the necessary IPFS tooling installed to use it directly, you can use a translating server to get the data with HTTPS instead, by altering the URL to this value.
https://ipfs.io/ipfs/QmSBimtMYWGXG5pLh8wbrndgMW2s47JmqhXeFFVUHGD7eS
Now download the file and print it to behold the magical MakersPlace metadata for "GHOSTCHAINZ".
curl -s https://ipfs.io/ipfs/QmSBimtMYWGXG5pLh8wbrndgMW2s47JmqhXeFFVUHGD7eS | jq
{
"title": "GHOSTCHAINZ",
"name": "GHOSTCHAINZ",
"type": "object",
"imageUrl": "https://ipfsgateway.makersplace.com/ipfs/QmXctieQkbujSZamJ66RznzbhQW6xWdDAYZgNymqT8sgSg",
"description": "This is another ghost of the simulation.\r\n\r\nIt moves in fields of gold dragging ethereal chains to dip and drip until from the other side of the code emerges the finest in swangin' chainz for entertainment purposes only.",
"attributes": [
{
"trait_type": "Creator",
"value": "obxium"
}
],
"properties": {
"name": {
"type": "string",
"description": "GHOSTCHAINZ"
},
"description": {
"type": "string",
"description": "This is another ghost of the simulation.\r\n\r\nIt moves in fields of gold dragging ethereal chains to dip and drip until from the other side of the code emerges the finest in swangin' chainz for entertainment purposes only."
},
"preview_media_file": {
"type": "string",
"description": "https://ipfsgateway.makersplace.com/ipfs/QmXctieQkbujSZamJ66RznzbhQW6xWdDAYZgNymqT8sgSg"
},
"preview_media_file_type": {
"type": "string",
"description": "jpg"
},
"created_at": {
"type": "datetime",
"description": "2020-05-04T17:09:39.161636+00:00"
},
"total_supply": {
"type": "int",
"description": 1
},
"digital_media_signature_type": {
"type": "string",
"description": "SHA-256"
},
"digital_media_signature": {
"type": "string",
"description": "07d77dc43a1b3a5c3b15a90ecff42fba461492f12a3954261d4c9524507e90e6"
}
}
}
Such meta, much detail. WOW!
The field of most interest to us here is imageUrl
, which contains the original media URL.
Download this URL.
curl -sO https://ipfsgateway.makersplace.com/ipfs/QmXctieQkbujSZamJ66RznzbhQW6xWdDAYZgNymqT8sgSg
Then validate the cryptographic hash of the downloaded file.
printf "bd07daebcd33265aa06fc78d891fffa030838c342c41a9ab261b13af889c1406 QmXctieQkbujSZamJ66RznzbhQW6xWdDAYZgNymqT8sgSg" | shasum -a 256 --check
QmXctieQkbujSZamJ66RznzbhQW6xWdDAYZgNymqT8sgSg: FAILED
shasum: WARNING: 1 computed checksum did NOT match
Oh noes! Something is wrong and the downloaded data from MakersPlace does not match the original art media. Let's validate that we at least have image data before looking into it more.
file QmXctieQkbujSZamJ66RznzbhQW6xWdDAYZgNymqT8sgSg
QmXctieQkbujSZamJ66RznzbhQW6xWdDAYZgNymqT8sgSg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, progressive, precision 8, 831x1000, components 3
Well, this is certainly unexpected! Somehow the data MakersPlace is storing is JPG even though I minted the are with a PNG! This seems like a case of #3, from the introduction, "Artist's original media differs from that stored for the token."
What in the– the data is a JPG now?!

Looking back at the metadata, the imageUrl
value is also present as the value of the description
field under the preview_media_file
field so that's kind of a clue that the preview is a JPEG, but so is the image stored in IPFS?!
I checked numerous other artworks of mine and others, and all still image based art appears to return JPG images always. This probably needs a closer look...and perhaps a clarifying statement from MakersPlace.
UPDATE
I reached out to MakersPlace via Twitter for clarification about what is going on here, and it is apparently part of their strategy to protect the artist's original image. While the original image is not pointed to the token, there is a hash of the original image included in the token metadata as the value of digital_media_signature
.
This is a key differentiator between MakersPlace and most other galleries with respect to how tokens are minted. Here's the relevant Twitter exchange.

SuperRare
My latest 💎 SuperRare artwork "STONED", a commision that I created at the request of the awesome crypto artist Lenara. It deals with the topics of Rai stones and current technology (VR), so money and technology in a consumerist pop culture way, but I digress all to hell...

Here are steps using common system utilities to download the media file, and validate it against the correct published SHA 256 summary.
Download the original file.
curl -sO https://obxium.com/img/STONED-original.png
Check that the file matches our expecatations as PNG data.
file STONED-original.png
The output shows the original PNG image data.
STONED-original.png: PNG image data, 3200 x 2160, 8-bit/color RGB, non-interlaced
Now validate the file against its known SHA 256 summary.
printf "65d4910f7e7318ee3b7b43dd580ad0835c2b546e0006c05794fa1cce47aa884d STONED-original.png" | shasum -a 256 --check
If all is good, then the following output will be printed.
STONED-original.png: OK
Repeat the process with the media file associated with the token. You can actually view source to check the source on SuperRare for the art in question and determine the media storage URL that way.
You can also directly query the smart contract with the tokenURI
method, and passing in the token ID. In the case of "STONED", it is SuperRare token ID 13988. Reading the contract tokenURI
method on Etherscan for the token returns this URL string:
https://ipfs.pixura.io/ipfs/QmZoudcfPUaBLNdgcmv3E3bK7dJ6aAmgz8u7NG3sTQEuGk/metadata.json
Downloading this URL gives you a JSON object with an image
field containing the actual URL to the original media data as shown here.
curl -s https://ipfs.pixura.io/ipfs/QmZoudcfPUaBLNdgcmv3E3bK7dJ6aAmgz8u7NG3sTQEuGk/metadata.json | jq
{
"name": "STONED",
"createdBy": "obxium",
"yearCreated": "2020",
"description": "Virtually visiting underwater rai stones [3200px x 2160px]",
"image": "https://ipfs.pixura.io/ipfs/QmXZ8eBNDMDMU1YbNwnthCUQU3MKrGHZhAuuUsTEszjUHr/STONED.png",
"media": {
"uri": "https://ipfs.pixura.io/ipfs/QmXZ8eBNDMDMU1YbNwnthCUQU3MKrGHZhAuuUsTEszjUHr/STONED.png",
"dimensions": "3200x2160",
"size": "2041210",
"mimeType": "image/png"
},
"tags": [
"rai",
"Yap",
"coin",
"DDF",
"obxium",
"2020"
]
}
Download the file media from the URL found in the image
field.
curl -sO https://ipfs.pixura.io/ipfs/QmXZ8eBNDMDMU1YbNwnthCUQU3MKrGHZhAuuUsTEszjUHr/STONED.png
Validate the file against SHA 256 summary.
printf "65d4910f7e7318ee3b7b43dd580ad0835c2b546e0006c05794fa1cce47aa884d STONED.png" | shasum -a 256 --check
As with the original art, you want to see OK in the output.
STONED.png: OK
Yay, it looks like we are good to go, and have a high degree of confidence that the image associated with my "STONED" NFT exactly matches my original artwork! 😎
Rarible

A recent Rarible artwork entitled "Present Company Excluded."
You can download the original art media and validate it from obxium.com.
curl -sO https://obxium.com/img/PCE.png
Validate the cryptographic hash.
printf "eb5f3082e760dceb00ab77e6cc1262f16612b6482da188f2a54aa7d5ffa9bd47 PCE.png" | shasum -a 256 --check
If all is good, then the following output will be printed.
PCE.png: OK
You can get the token ID from the artwork URL. For example, here is the Rarible URL for this art.
https://app.rarible.com/token/0x60f80121c31a0d46b5279700f9df786054aa5ee5:22866:0xf65db13b5ee031cb0ebba525ef21aa6c586681b3
The Token ID for my art is the string between the two colons, or 22866.
If we use ID with the Rarible smart contract tokenURI
method, we get this string back.
https://ipfs.daonomic.com/ipfs/QmQfHGPL2vTzp7fRDZLSrUC4hDxTk45jC4RqM73z3BGwQh
You can get this URL and parse the JSON data to learn the image
URL.
curl -s https://ipfs.daonomic.com/ipfs/QmQfHGPL2vTzp7fRDZLSrUC4hDxTk45jC4RqM73z3BGwQh | jq
and the response...
{
"name": "Present Company Excluded",
"description": "Worst timeline ever.",
"image": "ipfs://ipfs/QmYsW9BW1iE1Ek1MdbAWcdKo2Yw6hjyDJkHJZVn2j2rkA8",
"external_url": "https://app.rarible.com/token/0x60f80121c31a0d46b5279700f9df786054aa5ee5:22866",
"attributes": []
}
Finally, as with the other examples, get the image URL and validate it.
curl -s https://ipfs.io/ipfs/QmYsW9BW1iE1Ek1MdbAWcdKo2Yw6hjyDJkHJZVn2j2rkA8
printf "eb5f3082e760dceb00ab77e6cc1262f16612b6482da188f2a54aa7d5ffa9bd47 QmYsW9BW1iE1Ek1MdbAWcdKo2Yw6hjyDJkHJZVn2j2rkA8" | shasum -a 256 --check
It checks out, so Rarible appears to store correct data at least in this case, too. 🥃
QmYsW9BW1iE1Ek1MdbAWcdKo2Yw6hjyDJkHJZVn2j2rkA8: OK
Hopefully this was useful information to you and did not bake your mind too hard.
Enjoy and keep on making art!
– obxium ⓞ
We hope you enjoyed Ghost Palette with obxium; if so, make sure to share this with a friend and donate to Alpacawhal.
Tell them Alpacawhal sent you...