TZIP-16 Contract Metadata and Views The @taquito/tzip16
package allows retrieving metadata associated with a smart contract. These metadata can be stored on-chain (tezos-storage) or off-chain (HTTP(S) or IPFS). The package also provides a way to execute the MichelsonStorageView
found in the metadata. More information about the TZIP-16 standard can be found here .
The package can be used as an extension to the well known Taquito contract abstraction.
We first need to create an instance of Tzip16Module
and add it as an extension to our TezosToolkit
Using the default Metadata Provider Using a custom Metadata Provider The constructor of the Tzip16Module
takes an optional MetadataProvider
as a parameter. When none is passed, the default MetadataProvider
of Taquito is instantiated and the default handlers (HttpHandler
, IpfsHandler
, and TezosStorageHandler
) are used.
import { TezosToolkit } from '@taquito/taquito' ;
import { Tzip16Module } from '@taquito/tzip16' ;
const Tezos = new TezosToolkit ( 'rpcUrl' ) ;
Tezos . addExtension ( new Tzip16Module ( ) ) ;
Copy In some cases, we may want to use a customized metadata provider. The constructor of the Tzip16Module
class takes an optional metadata provider as a parameter. This allows to inject a custom metadata provider with custom protocol handlers if desired. For example, if we want to use a different IPFS gateway than the default one, which is ipfs.io
, or if we want to use a different HTTP handler to support authentication or custom headers. Here is an example:
import { Handler , IpfsHttpHandler , TezosStorageHandler , MetadataProvider } from '@taquito/tzip16' ;
const Tezos = new TezosToolkit ( 'rpcUrl' ) ;
const customHandler = new Map < string , Handler > ( [
[ 'ipfs' , new IpfsHttpHandler ( 'gateway.ipfs.io' ) ] ,
[ 'http' , 'customHttpHandler' ] ,
[ 'https' , 'customHttpHandler' ] ,
[ 'tezos-storage' , new TezosStorageHandler ( ) ] ,
] ) ;
const customMetadataProvider = new MetadataProvider ( customHandler ) ;
Tezos . addExtension ( new Tzip16Module ( customMetadataProvider ) ) ;
Copy A list of public gateways is accessible here .
Use the tzip16
function to extend a contract abstraction const contract = await Tezos . contract . at ( 'contractAddress' , tzip16 ) ;
Copy Call the methods of the Tzip16ContractAbstraction
class The namespace tzip16()
need to be specified when calling a method of the Tzip16ContractAbstraction
class:
const metadata = await contract . tzip16 ( ) . getMetadata ( ) ;
const views = await contract . tzip16 ( ) . metadataViews ( ) ;
Copy All other methods of the ContractAbstraction
class can be called as usual on the contract
object.
The getMetadata
method returns an object which contains the URI, the metadata in JSON format, an optional SHA256 hash of the metadata and an optional integrity check result.
A sequence diagram can be found here .
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1V9mi4SiN85aUKjkJGRDDxELSbMSMdBMcy' ;
Tezos . contract
. at ( contractAddress , tzip16 )
. then ( ( contract ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return contract . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1V9mi4SiN85aUKjkJGRDDxELSbMSMdBMcy' ;
Tezos . wallet
. at ( contractAddress , tzip16 )
. then ( ( wallet ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return wallet . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1MJ7wAZ9LBB797zhGJrXByaaUwvLGfe3qz' ;
Tezos . contract
. at ( contractAddress , tzip16 )
. then ( ( contract ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return contract . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1MJ7wAZ9LBB797zhGJrXByaaUwvLGfe3qz' ;
Tezos . wallet
. at ( contractAddress , tzip16 )
. then ( ( wallet ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return wallet . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1JbEzvHn2Y2DjVQ7kgK8H8pxrspG893JsX' ;
Tezos . contract
. at ( contractAddress , tzip16 )
. then ( ( contract ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return contract . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1JbEzvHn2Y2DjVQ7kgK8H8pxrspG893JsX' ;
Tezos . wallet
. at ( contractAddress , tzip16 )
. then ( ( wallet ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return wallet . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1SDtsB4DHdh1QwFNgvsavxDwQJBdimgrcL' ;
Tezos . contract
. at ( contractAddress , tzip16 )
. then ( ( contract ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return contract . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1SDtsB4DHdh1QwFNgvsavxDwQJBdimgrcL' ;
Tezos . wallet
. at ( contractAddress , tzip16 )
. then ( ( wallet ) => {
console . log ( ` Fetching the metadata for ${ contractAddress } ... ` ) ;
return wallet . tzip16 ( ) . getMetadata ( ) ;
} )
. then ( ( metadata ) => {
console . log ( JSON . stringify ( metadata , null , 2 ) ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy A sequence diagram can be found here .
In the next example, we will run a view named someJson
that can be found in the metadata of the contract KT1Vms3NQK8rCQJ6JkimLFtAC9NhpAq9vLqE
. When we inspect those metadata, we can see that this view takes no parameter, has a returnType of bytes and has the following code:
"code":
[
{
"prim": "DROP",
"args": [],
"annots": []
},
{
"prim": "PUSH",
"args": [
{
"prim": "bytes",
"args": [],
"annots": []
},
{
"bytes": "7b2268656c6c6f223a22776f726c64222c226d6f7265223a7b226c6f72656d223a34322c22697073756d223a5b22222c226f6e65222c2232225d7d7d"
}
],
"annots": []
}
]
Copy Try to run the view:
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1XdXkU9piczYpTU8ToAAGJunzFiGCWRvVK' ;
Tezos . contract
. at ( contractAddress , tzip16 )
. then ( ( contract ) => {
console . log ( ` Initialising the views for ${ contractAddress } ... ` ) ;
return contract . tzip16 ( ) . metadataViews ( ) ;
} )
. then ( ( views ) => {
console . log ( ` The following view names were found in the metadata: ${ Object . keys ( views ) } ` ) ;
return views . someJson ( ) . executeView ( ) ;
} )
. then ( ( result ) => {
console . log ( ` Result of the view someJson: ${ result } ` ) ;
console . log ( ` Transform result to char: ${ bytes2Char ( result ) } ` ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1XdXkU9piczYpTU8ToAAGJunzFiGCWRvVK' ;
Tezos . wallet
. at ( contractAddress , tzip16 )
. then ( ( wallet ) => {
console . log ( ` Initialising the views for ${ contractAddress } ... ` ) ;
return wallet . tzip16 ( ) . metadataViews ( ) ;
} )
. then ( ( views ) => {
console . log ( ` The following view names were found in the metadata: ${ Object . keys ( views ) } ` ) ;
return views . someJson ( ) . executeView ( ) ;
} )
. then ( ( result ) => {
console . log ( ` Result of the view someJson: ${ result } ` ) ;
console . log ( ` Transform result to char: ${ bytes2Char ( result ) } ` ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy In the next example, we will run a view named multiply-the-nat-in-storage
that can be found in the metadata of the contract KT19rDkTYg1355Wp1XM5Q23CxuLgRnA3SiGq
. When we inspect those metadata, we can see that this view takes a nat
has a parameter, has a returnType of nat
and has the following instructions: DUP, CDR, CAR, SWAP, CAR, MUL
.
Try to run the view:
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1CVoo3PxuvH3BuBpNTYRDafAQ7aRTfj8bd' ;
Tezos . contract
. at ( contractAddress , tzip16 )
. then ( ( contract ) => {
return contract . storage ( ) . then ( ( storage ) => {
console . log ( ` The nat in the storage of the contract is: ${ storage [ 0 ] } ` ) ;
console . log ( ` Initialising the views for ${ contractAddress } ... ` ) ;
return contract . tzip16 ( ) . metadataViews ( ) ;
} ) ;
} )
. then ( ( views ) => {
console . log ( ` The following view names were found in the metadata: ${ Object . keys ( views ) } ` ) ;
return views [ 'multiply-the-nat-in-storage' ] ( ) . executeView ( 10 ) ;
} )
. then ( ( result ) => {
console . log ( ` Result of the view 'multiply-the-nat-in-storage': ${ result } ` ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
Tezos . addExtension ( new Tzip16Module ( ) ) ;
const contractAddress = 'KT1CVoo3PxuvH3BuBpNTYRDafAQ7aRTfj8bd' ;
Tezos . wallet
. at ( contractAddress , tzip16 )
. then ( ( wallet ) => {
return wallet . storage ( ) . then ( ( storage ) => {
console . log ( ` The nat in the storage of the contract is: ${ storage [ 0 ] } ` ) ;
console . log ( ` Initialising the views for ${ contractAddress } ... ` ) ;
return wallet . tzip16 ( ) . metadataViews ( ) ;
} ) ;
} )
. then ( ( views ) => {
console . log ( ` The following view names were found in the metadata: ${ Object . keys ( views ) } ` ) ;
return views [ 'multiply-the-nat-in-storage' ] ( ) . executeView ( 10 ) ;
} )
. then ( ( result ) => {
console . log ( ` Result of the view 'multiply-the-nat-in-storage': ${ result } ` ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy In the next example we execute the view multiply-the-nat-in-storage
in a custom way:
const contractAddress = 'KT1CVoo3PxuvH3BuBpNTYRDafAQ7aRTfj8bd' ;
Tezos . contract
. at ( contractAddress )
. then ( ( contract ) => {
const view = new MichelsonStorageView (
'test' ,
contract ,
Tezos . rpc ,
new RpcReadAdapter ( Tezos . rpc ) ,
{ prim : 'nat' } ,
[
{ prim : 'DUP' } ,
{ prim : 'CDR' } ,
{ prim : 'CAR' } ,
{ prim : 'SWAP' } ,
{ prim : 'CAR' } ,
{ prim : 'MUL' } ,
] ,
{ prim : 'nat' }
) ;
view . executeView ( 2 ) . then ( ( result ) => {
console . log ( ` Result of the custom view: ${ result } ` ) ;
} ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy
const contractAddress = 'KT1CVoo3PxuvH3BuBpNTYRDafAQ7aRTfj8bd' ;
Tezos . wallet
. at ( contractAddress )
. then ( ( wallet ) => {
const view = new MichelsonStorageView (
'test' ,
wallet ,
Tezos . rpc ,
new RpcReadAdapter ( Tezos . rpc ) ,
{ prim : 'nat' } ,
[
{ prim : 'DUP' } ,
{ prim : 'CDR' } ,
{ prim : 'CAR' } ,
{ prim : 'SWAP' } ,
{ prim : 'CAR' } ,
{ prim : 'MUL' } ,
] ,
{ prim : 'nat' }
) ;
view . executeView ( 2 ) . then ( ( result ) => {
console . log ( ` Result of the custom view: ${ result } ` ) ;
} ) ;
} )
. catch ( ( error ) => console . log ( ` Error: ${ JSON . stringify ( error , null , 2 ) } ` ) ) ;
Copy