Contract metadata
INFO
Contract metadata should not be confused with token metadata.
Contract metadata provides details about the contract itself and its off-chain views, like the back cover of a book describing its content and author. A range of tools such as wallets, block explorers, and dApps use contract metadata to display useful information to users, such as a name, description, and version number.
Creating metadata
Contract metadata is structured in key-value pairs that follow the TZIP-16 standard. When you create FA2 contracts, be sure to read the standard to know the information that should be in the metadata.
Here is an example of contract metadata:
{
"name": "MyContract",
"version": "1.0.0",
"description": "This implements FA2 (TZIP-012) using SmartPy.",
"interfaces": ["TZIP-012", "TZIP-016"],
"authors": ["SmartPy <https://smartpy.io>"],
"homepage": "https://smartpy.io/ide?template=fa2_lib.py",
"source": {
"tools": ["SmartPy"],
"location": "https://gitlab.com/SmartPy/smartpy/-/raw/master/python/templates/fa2_lib.py"
},
"permissions": {
"receiver": "owner-no-hook",
"sender": "owner-no-hook",
"operator": "owner-or-operator-transfer"
}
}
Uploading metadata
In most cases, contract metadata is stored as a separate JSON file on the InterPlanetary File System (IPFS) or any HTTPS-accessible URI and linked into the contract's metadata
big-map. Many contract creators use IPFS to store metadata in a decentralized way. This decentralization ensures that the file content cannot be altered without changing its hash and thus the URL, providing a form of content integrity. Moreover, anyone can host an IPFS node to re-upload the content if necessary, ensuring resilience against single points of failure.
SmartPy provides tools to upload metadata to IPFS automatically; see Creating and publishing metadata.
TIP
If you upload your metadata file on IPFS, remember to 'pin' it. Pinning is the process of marking a particular file or data block to prevent it from being garbage collected and removed from your IPFS storage. You should also keep a backup version of your file elsewhere, because you may need to re-upload it again later.
Linking the contract to the metadata
Contracts store a link to their metadata in a big-map named metadata
that has a single key: the empty string ""
. The value is a link to the metadata JSON file. For example, if the file is on IPFS, the URI is in the form "ipfs://<hash>"
, where <hash>
is the IPFS hash of the file.
The example below shows how to upload contract metadata to IPFS and put the URI in the contract storage. For more information, see Creating and publishing metadata.
# Build contract metadata content
contract_metadata = sp.create_tzip16_metadata(
name="My contract",
description="This is a contract using SmartPy.",
version="1.0.0",
license_name="CC-BY-SA",
license_details="Creative Commons Attribution Share Alike license 4.0 https://creativecommons.org/licenses/by/4.0/",
interfaces=["TZIP-012", "TZIP-016"],
authors=["SmartPy <https://smartpy.io/#contact>"],
homepage="https://smartpy.io/ide?template=fa2_lib_fungible.py",
# Optionally, upload the source code to IPFS and add the URI here
source_uri=None,
offchain_views=contract.get_offchain_views(),
)
# Upload the contract metadata to IPFS and get the URI
metadata_uri = sp.pin_on_ipfs(contract_metadata)
# Create the metadata big map based on the IPFS URI
contract_metadata = sp.scenario_utils.metadata_of_url(metadata_uri)
# Update the scenario instance with the new metadata
contract.data.metadata = contract_metadata
You can also upload the contract metadata yourself and manually provide a URI with IPFS, HTTPS, or some other protocol, as in this example:
import smartpy as sp
from smartpy.templates import fa2_lib as fa2
def bytes_of_string(s):
return sp.bytes("0x" + s.encode('utf-8').hex())
@sp.add_test()
def test():
sc = sp.test_scenario("NFT", fa2.main)
contract_metadata_IPFS = sp.big_map({
"" : bytes_of_string(
"ipfs://QmPChd2hVbrJ6bfo3WBcTW4iZnpHm8TEzWkLHmLpXhF68A")
})
c1 = fa2.main.Nft(contract_metadata_IPFS, {}, [])
sc += c1
contract_metadata_HTTPS = sp.big_map({
"" : bytes_of_string(
"https://example.com/contract_metadata.json")
})
c2 = fa2.main.Nft(contract_metadata_HTTPS, {}, [])
sc += c2