Contract metadata
INFO
Do not confuse with token metadata.
This guide is an introduction on contract's metadata and how to deal with them in SmartPy. For the standard description see the TZIP links given in overview.
Introduction
Contract metadata describe the whole contract (e.g. interface, versioning) and the off-chain views. It provides information that is not directly used for a contract's operation.
They are stored in JSON files stored outside of the blockchain and referenced in the contract.
SmartPy gives helpers to generates the JSON files and the reference.
You'll have to upload them outside of the blockchain by yourself. We provide some explanations on how to do it.
Generate the json file in SmartPy
The command to generate the json file is: self.init_metadata(<filename>, <metadata_base>)
.
It generates a file with the name <filename>
.
The content can be seen:
- in the WebIde in the
metadata
tab of the contract - in the files generated by the CLI.
The image below shows how to obtain the metadata artifact from the online IDE.
self.init_metadata
transforms the <metadata_base>
dictionary into the JSON representation. The dictionary should contain the following optional fields ({}
is a valid metadata dictionary).
Key | Type | Description |
---|---|---|
"name" | Python string free format. | Recommended, name of the contract. |
"description" | Python string, free format. | Preferably a set of proper natural language paragraphs. |
"version" | Python string, free format. | It is recommended to have version strings which attempt at uniquely identifying the exact Michelson contract, or at least its behavior, as a precision with regard to the name field. Of course, none of that can be enforced. |
"license" | Python dictionary: { "name": <string> , "details" : <string> } , "details" being optional. | It is recommended to use de facto standard short names when possible, see the Debian guidelines for instance. |
"authors" | Python list of strings. | Each author should obey the "Print Name <'contact'>" string is either an email address, or a web URI., where the 'contact' |
"homepage" | Python string, representing a “web” URL (e.g. HTTPS). | The homepage is for human-consumption, it may be the location of the source of the contract, how to submit issue tickets, or just a more elaborate description. |
"source" | Python dictionary: { "tools": [<string>], "location": <string> } describing the source code which was transformed or generated the Michelson code of the contract. tools" is an informal list of compilers/code-generators/libraries/post-processors used to generate the originated code, if possible with version information. | |
"interfaces" | Python list of strings. Each string should allow the consumer of the metadata to know which interfaces and behaviors the contract claims to obey (other than the obvious TZIP-016). In the case of standards defined as TZIPs in the present repository, the string should obey the pattern "TZIP-<number><extras>" where <extras> is additional information prefixed with a space character. | Example: an FA2 contract would (at least) have an "interfaces" field containing ["TZIP-012"] or ["TZIP-012 git 6544de32"] . |
"errors" | Python list of “error translation” objects, which allow one to interpret error values output by the contract using the FAILWITH instruction. The interpretation is a larger data-structure, for instance, to provide to a wallet-user with a more understandable error message to act on (usually a string or bytes natural language text value). | |
"views" | Python list of off-chain view methods. | List of off-chain views methods to include in the metadata. Their code is compiled to micheline and the name and the method's comment are included. |
Note that there is an extra field for FA2:
Key | Type | Description |
---|---|---|
"permissions" | Python dictionary: { "operator": <operator-policy> , "receiver" : "owner-no-hook", "sender": "owner-no-hook" } where operator-policy is "owner-or-operator-transfer" or "owner-transfer" or "no-transfer". For more info, see policies. |
Example:
import smartpy as sp
class MyContract(sp.Contract):
def __init__(self):
self.init_metadata(
filename="contract_metadata",
metadata_base={
"name": "My contract",
"version": "1.0.0",
"description": "This is a basic contract.",
"interfaces": ["TZIP-012", "TZIP-016"],
"authors": ["SmartPy <https://legacy.smartpy.io/#contact>"],
"homepage": "https://legacy.smartpy.io/",
"source": {
"tools": ["SmartPy"],
"location": "https://gitlab.com/SmartPy/smartpy/",
},
"views": [self.my_offchain_view]
}
)
@sp.offchain_view(pure = True)
def my_offchain_view(self):
"""Description of my off-chain view included in the metadata."""
sp.result(sp.unit)
See contracts#generate-metadata.
This command only generates the file, it doesn't upload it on IPFS, nor reference it in the metadata bigmap.
Uploading the JSON file
The JSON file has to be uploaded somewhere outside the blockchain.
There are two main solutions:
- uploading the file on IPFS.
- uploading it on a regular server/service that exposes the file through HTTPS.
If you don't know which one you want to use, read IPFS VS HTTP.
If you choose HTTP, configure your server or upload your file to a service and make it publicly accessible via a unique URL.
If you choose IPFS, you can read push to IPFS for more information on how to push the JSON file.
Reference the JSON file
In a smart contract, metadata are referenced in a big_map named metadata
. Its type is sp.TBigMap(sp.TString, sp.TBytes)
.
The bigmap must contain at least one value:
- the empty key:
""
- a URI which point to the JSON document.
The values are direct bytes encoding of the content (no sp.pack
).
The valid URI are described in URI schemes.
SmartPy provides a helper that creates a bigmap with an empty key and the encoded version of the URL: sp.utils.metadata_of_url(url)
.
import smartpy as sp
class MyContract(sp.contract):
def __init__(self):
self.init(
metadata=sp.utils.metadata_of_url(
"ipfs://QmRbmXcd2yfNVdgHL7oYWS2yd3tztr2NZiqP2LFuw3voPW"
)
)
This is equivalent to:
import smartpy as sp
class MyContract(sp.contract):
def __init__(self):
self.init(
metadata=sp.bigmap(
{"": sp.utils.bytes_of_string(
"ipfs://QmRbmXcd2yfNVdgHL7oYWS2yd3tztr2NZiqP2LFuw3voPW"
)},
tkey=sp.TString,
tvalue=sp.TBytes
)
)
In FA2
We provide a FA2 library that gives specific helpers.
See FA2 contract metadata for more information.