Proof of Integrity
We publicly introduced the concept of Proof of Integrity along with our first release of PSA Graded Pokémon cards as Connected Collectibles. In short, a Proof of Integrity acts like a tamper-proof seal and establishes that every Courtyard NFT is provably linked to a unique matching physical item stored in our vaults. If someone tries to alter the NFT's fingerprint (i.e. its identity), the Proof of Integrity will show that the token is corrupted.
Every item that is stored at Courtyard is identified by an accurate, human readable description and a unique ID. While the description allows anyone to quickly identify the item visually, the unique ID brings further differentiation when we store multiple copies of the same item (ex: a few pairs of collectible sneakers of the same model, colorway and size). The accurate description of the asset and the unique ID are used together to form the fingerprint of the asset, which is both unique and human readable at the same time:
Nike | Dunk Low X UNION | Pistachio | US M 8 | CYXSNKS_DJ9649-401_USM8-001
Before storing an asset, we always take a picture of it and make sure that the unique ID that differentiates it from other similar items is shown in the picture, and we include a link to that picture in the metadata of the NFT, along with the 3D rendering:

IRL picture of the vaulted item

NFT 3D rendering
Because the fingerprint of an item has an arbitrary length, we encode it using a cryptographic function called
keccak256
, which is widely used on the Ethereum blockchain as a standard way to securely encode sensitive data. The result of this encoding is what we call the Proof of Integrity. It is a string starting with 0x
, followed by 64 hexadecimal characters that uniquely represents the corresponding physical item:0xa094eb968cc1b7222174abd778317cfdc3a60e7ad0d9a3a27b145d0c5d6c4a5b
The on-chain function that is used to calculate the Proof of Integrity is the following:
1
/**
2
* @dev Generates a Proof Of Integrity as the keccak256 hash of a {fingerprint} and a {salt} value.
3
* - the fingerprint is a unique, human readable description of an item.
4
* - the salt value is a random number used to bring some further functionalities, like creating NFTs that can provably exist but remain "unrevealed" until the salt value is made public.
5
*/
6
function generateProofOfIntegrity(string memory fingerprint, uint256 salt) public pure returns (bytes32) {
7
return keccak256(abi.encodePacked(fingerprint, salt));
8
}
A useful property of the
keccak256
function is that if the fingerprint changes by a single character, the resulting Proof of Integrity would be a completely different value, that has nothing to do with the original one. This is how we can tell if a fingerprint has been altered.A useful property of the
keccak256
function is that if the fingerprint changes by a single character, the resulting Proof of Integrity would be a completely different value, that has nothing to do with the original one. This is how we can tell if a fingerprint has been altered.Example:
Fingerprint | Proof of Integrity (using salt = 0) |
---|---|
Nike | Dunk Low X UNION | Pistachio | US M 8 | CYXSNKS_DJ9649-401_USM8-001 | 0xa094eb968cc1b7222174abd778317cfdc3a60e7ad0d9a3a27b145d0c5d6c4a5b |
nike | Dunk Low X UNION | Pistachio | US M 8 | CYXSNKS_DJ9649-401_USM8-001 | 0x55f4a3ad958d68384f9c7505dc1b994548067556d68c29ca4b375d761d6a8100 |
Once we obtained the Proof of Integrity of an item, we can use it to generate the corresponding NFT. We create the NFT using the Proof of Integrity as its token ID.
But wait... aren't token IDs on Ethereum numbers, and not hexadecimal strings?
Exactly! And this is where something special happens. We already established that a Proof of Integrity is a unique hexadecimal value of 64 characters. However, hexadecimal values are nothing more than numbers encoded in a way to save space on a computer. And as such, to every hexadecimal value corresponds an actual decimal number. For instance, the following hexadecimal value:
0xa094eb968cc1b7222174abd778317cfdc3a60e7ad0d9a3a27b145d0c5d6c4a5b
corresponds to the very long number:
72633175108116729587988855287683041143289251328703106236432533250301397584475
We store that number on-chain and use as the token ID of the NFT.
The tokenId and the Proof of Integrity are the same thing, just written differently. An NFT's token ID corresponds exactly to a Proof of Integrity that identifies a unique physical item stored at Courtyard, and vice-versa.
On the blockchain side, it is well established that the token ID of an NFT, once recorded on-chain, is there to stay forever, unchanged. However, we cannot say as much of the metadata of that NFT, which may or may not be stored in decentralized storage, and may or may not be subject to evolve over-time, either because the creators of the NFT want to regularly provide a richer experience to the holder of the NFT, or because a malicious actor came and was able to alter the metadata and completely change the nature of the NFT.
The Proof of Integrity is Courtyard's way of making the NFT really tamperproof by using the 3 following properties:
- Once an NFT's token ID is recorded on-chain, it will never change.
- A Proof of Integrity of an NFT is exactly the same thing as its token ID, and so it will never change either.
- The unique, human readable fingerprint of a physical asset stored with Courtyard corresponds exactly to the Proof of Integrity, and only that fingerprint corresponds to it.
Together, these 3 properties ensure that if the metadata changes as the result of a malicious attack to alter the identity of the underlying physical asset, the seal of integrity will be "broken", meaning that the Proof of integrity will invalidate the NFT.
Last modified 1mo ago