Metadata in SmartPy
SmartPy is an intuitive and powerful smart contract development platform for Tezos.
TQ Tezos has introduced, in https://medium.com/tqtezos/contract-metadata-on-tezos-e4c692e2f6ba, the metadata standard for Tezos smart contracts TZip16.
Such metadata can be read and queried with the TZComet explorer.
SmartPy has implemented a few elements to support this initiative: a simple yet very effective way to compute metadata files and a way to easily link to off-chain metadata.
The SmartPy explorer https://SmartPy.io/explorer.html automatically links to TZComet when metadata is found in a contract storage.
The features described in this document are visible on the Metadata template https://smartpy.io/ide?template=metadata.py.
Metadata on Tezos are still a work in progress. We will add new improvements in the future.
Creation of metadata files
init_metadata
To create metadata files, we can use self.init_metadata
in the __init__
section.
class MyContract(sp.Contract):
def __init__(self, ...):
self.init_metadata("meta_0", {"foo": "bar"})
self.init_metadata("meta_1", {"a" : 12, "c" : True)
...
It takes a name and a Python expression and creates a JSON of the right form.
With the SmartPy CLI, it creates corresponding files. In https://SmartPy.io/ide, it adds new metadata tabs inside the contract output.
Off-chain views
Metadata can contain off-chain views as described in the TZip16 standard.
They have at most one parameter and can contain doc-strings.
They are introduced by doing:
@sp.offchain_view
def get_x(self, params):
"""blah blah ' fdsfds " """
sp.result(sp.record(a = self.data.x, b = 12 + params)) @sp.offchain_view
def get_storage(self):
sp.result(self.data.x) @sp.offchain_view
def get_cst(self):
"""42"""
sp.result(42)
Meta-programming, typing and metadata
When we write such a command in a contract:
self.init_metadata("a_name", expr)
the Python expression expr
is split into:
Python dictionaries
They are not supposed to be properly typed and are mapped to JSON dictionnaries.
Python lists
They are not supposed to be properly typed and are mapped to JSON lists.
Off-chain views
They are typed by SmartPy and are converted to the expected structure from TZip-16 standard.
Anything else
Anything else is converted to a SmartPy expression. It is then properly typed and converted to JSON as expected.
A rather complete example
class MyContract(sp.Contract):
def __init__(self, **kargs):
self.init_metadata("meta_0", {"foo": "bar"})
meta_1 = {"some": "string",
"some_dict" : {1, 2, 3},
"a": sp.key("tz1..."),
"foo" : (),
"views" : [self.get_x,
self.get_cst,
self.get_storage],
"f": lambda x : x + 2
}
self.init_metadata("meta_1", meta_1)
self.init(**kargs) @sp.entry_point
def incr(self):
self.data.x += 1 @sp.entry_point
def change_metadata(self, params):
self.data.metadata[""] = params @sp.offchain_view
def get_x(self, params):
"""blah blah ' fdsfds " """
sp.result(sp.record(a = self.data.x, b = 12 + params)) @sp.offchain_view
def get_storage(self):
sp.result(self.data.x) @sp.offchain_view
def get_cst(self):
"""42"""
sp.result(42) ...
Linking of metadata files
It is possible to embed metadata files in a smart contract’s storage but it’s rather inefficient. It’s better to link to such a file by, e.g., storing this file on IPFS.
This is supported by SmartPy as well by including a field metadata
of a proper type by doing:
metadata = sp.metadata_of_url(
"ipfs://QmWDcp3BpBjvu8uJYxVqb7JLfr1pcyXsL97Cfkt3y1758o")
A Real Example
Here is a live example on TZComet: KT1CUB3CP67JwhG5JcBVbVX3zq744theGQh5.