tzip-16.md 24.3 KB
Newer Older
1
---
2
tzip: 016
3 4 5 6 7 8 9 10 11
title: Contract Metadata
status: Work In Progress
type: Interface
author: Seb Mondet <seb@mondet.org>
created: 2020-06-30
---

## Abstract

12 13 14 15 16 17 18
Contract metadata provides information that is not directly used for a
contract's operation, whether about the contract's code (e.g. its interface,
versioning) or the off-chain meaning of its contents (e.g. an artwork
corresponding to an NFT). Currently, Tezos smart contracts lack a standard way
to access such important data, fragmenting access to useful information that is
needed for a scalabe integration experience by wallets, explorers, and
applications.
19

20
To address this need and ease the integration, discoverability, and querying of
21
Tezos smart contracts, we propose TZIP-016.  TZIP-016 is a standard for encoding
22 23
access to such smart contract metadata in JSON format either on-chain using
tezos-storage or off-chain using IPFS or HTTP(S).
24

25
TZIP-016 defines:
26

27 28 29
- A basic structure to find _some_ metadata in a contract's storage.
- An URI scheme to find data: on-chain (contract storage) or off-chain
  (web-services or IPFS).
30 31 32
- An extensible JSON format (JSON-Schema) to describe the metadata, it contains
  among other things:
    - provenance and authorship information,
33 34 35
    - references to other standards implemented,
    - off-chain “views” (Michelson functions to query the contract), and
    - custom extensions.
36
- optional entrypoints to validate metadata information.
37 38 39
 
The standard is meant to be extended/specialized by other TZIPs, for instance by
adding fields to the JSON format of the metadata or imposing certain off-chain
40
views. We intend to extend existing token APIs specified in TZIP-012 and TZIP-007 
41
with such metadata and off-chain views using TZIP-016.
42

43 44 45 46 47 48 49 50 51 52
## Table Of Contents

-   [Introduction](#introduction)
-   [Example Use-Case](#example-use-case)
-   [Definition of The Standard](#definition-of-the-standard)
    -   [Contract Storage](#contract-storage)
    -   [Metadata URIs](#metadata-uris)
    -   [Metadata JSON Format](#metadata-json-format)
    -   [Optional `assertMetadata<hash>` Entrypoints](#optional-assertmetadatahash-entrypoints)
-   [Machine-Readable Specifications](#machine-readable-specifications)
53
-   [How To "Derive" From TZIP-016](#how-to-derive-from-tzip-016)
54 55 56 57 58 59 60 61 62
-   [Known Implementations](#known-implementations)
-   [Rationales / Design Choices](#rationales-design-choices)
-   [Future Work & Extensions](#future-work-extensions)
-   [Copyright](#copyright)

<!-- TOC generation:
pandoc -s -t gfm -i proposals/tzip-16/tzip-16.md -w markdown --toc | head -n 30
-->

63
## Introduction
64

65 66
This document defines a proposal for an interoperable method for encoding access
to off-chain data from a Tezos contract (_KT1 account_).  The method does not
67 68
require a protocol change and seeks to minimize the use and impact of on-chain
storage.
69

J A's avatar
J A committed
70
The goal is to allow smart contract authors, and wallet and indexers
71
to agree on the location and the shape of a contract's metadata.
72

73 74
We define metadata as all information available on the contract that is not
directly used for its operation, and _usually_ only for off-chain consumption.
75 76 77 78 79

This includes authorship/copyright information, descriptions, and
**off-chain-views**.  Off-chain-views are queries (for now REST-API queries or
Michelson functions) that anyone can run off-chain to obtain processed
information on the current storage of a given contract.
80 81 82

## Example Use-Case

83 84 85 86 87
For instance, an FA1.2 (i.e. ERC20-like) token contract may provide, among its
metadata fields, a Michelson function “`get-balance`” which gives an account's
current balance from the storage. The author of the contract can store this
information on IPFS and add only an IPFS-URI to the contract's storage.

88
Conformity to the TZIP-016 (or a derivative) standard allows a wallet
89 90 91 92
implementation to find the IPFS-URI and decode its contents to find the
particular implementation of the `get-balance` off-chain-view in order to
display a user's balance in its interface.

93 94
## Definition of The Standard

95 96 97 98
In this section, we define the various components of the standard for human
consumption (see annexes and reference implementations for more formal and
machine-readable definitions).

99 100
### Contract Storage

101
To provide a TZIP-016-compliant initial access-point to the metadata from a given
102 103
on-chain contract (`KT1...` address) one must include a `%metadata` field in
the contract-storage.
104

105 106 107
The field can be anywhere within the top-level tree of nested pairs forming the
storage type (meaning that it cannot be inside an `or`, an `option`, a `map`,
etc.) and must have the following type:
108 109

```
110
(big_map %metadata string bytes)
111 112 113 114 115
```

At least one value must be present: 

- the one for the empty string key (`""`).
116
- the value must be a URI as specified in the following section which points to
J A's avatar
J A committed
117
  a JSON document as specified further below.
118

119 120 121 122 123 124
Unless otherwise-specified, the encoding of the values must be the direct stream
of bytes of the data being stored. For instance, an URI starting with `http:`
will start with the 5 bytes `0x687474703a` (`h` is `0x68`, `t` is `0x74`,
etc.). There is no implicit conversion to Michelson's binary format (`PACK`) nor
quoting mechanism.

125
### Metadata URIs
126 127 128 129

URIs are used here first to locate metadata contents, but the format may be reused 
in other similar cases for instance in extensions to this specification.

130
See the specification of a URI's generic format:
131 132 133 134
<https://tools.ietf.org/html/rfc3986>

In the context of this specification, valid schemes include:

135 136 137 138 139 140 141
- `http`/`https`: see
  [RFC 7230](https://tools.ietf.org/html/rfc7230#section-2.7).
- `ipfs`: see IPFS URIs
  [specification](https://www.iana.org/assignments/uri-schemes/prov/ipfs), and
  [documentation](https://github.com/ipfs/in-web-browsers/blob/master/ADDRESSING.md#addressing-with-native-url).
- `tezos-storage`: defined in the section below.
- `sha256`: defined in the section right after.
142 143 144

#### The `tezos-storage` URI Scheme

145 146
URIs that point at the storage of a contract, should provide the following
information:
147

148
**Host:**
149

150
- Location of the contract pointed to, with the format `<address>.<network>`.
151 152 153 154
    - Valid addresses are base58check-encoded Tezos contract addresses:
      `KT1...`.
    - Valid networks are base58check-encoded Tezos chain identifiers `Net....`
      or known public network aliases: `mainnet`, `carthagenet`, `delphinet`, …
155
- Example: `KT1QDFEu8JijYbsJqzoXq7mKvfaQQamHD1kX.mainnet` or
156
  `KT1QDFEu8JijYbsJqzoXq7mKvfaQQamHD1kX.NetXNfaaGTuJUGF`
157
- This is all optional, if contract address or network are not provided the
158 159
  defaults are “current” ones within a given context. If only one is present, it
  should be interpreted as a contract address within the current network.
160 161 162
- It is expected that given implementation of a URI resolver may not be able to
  handle every known network or even handle more than one; such cases should
  return/display a proper error message.
163

164 165
**Path:** a string used as key in the `%metadata` big-map of the contract. If
the path starts with a `/` we remove it; only the first “slash” character is
166 167 168 169 170 171 172
removed, if any (*rationale:* when URIs contain a host component, the path
always starts with a `/`, cf.
[section 3.3](https://tools.ietf.org/html/rfc3986#section-3.3) of RFC 3986).
All other `/` characters are forbidden; if one needs to include such a character
for the key in the big-map, it must be Percent-encoded (a.k.a. URL-encoded, cf.
[section 2.1](https://tools.ietf.org/html/rfc3986#section-2.1)), i.e. written as
`%2F`.
173 174 175 176 177 178 179 180

Examples:

- `tezos-storage:hello`: in the current contract fetch the value
  at key `"hello"` from the `%metadata` big-map.
- `tezos-storage://KT1QDFEu8JijYbsJqzoXq7mKvfaQQamHD1kX/foo`: get the value at
  `foo` from the metadata big-map of the contract
  `KT1QDFEu8JijYbsJqzoXq7mKvfaQQamHD1kX` (on the current network).
181 182 183 184 185
- `tezos-storage://KT1QDFEu8JijYbsJqzoXq7mKvfaQQamHD1kX/%2Ffoo`: like the above,
  but the key is `/foo` (first `/` removed from the path, percent-encoded one is
  kept).
- `tezos-storage:hello/world` is invalid, while `tezos-storage:hello%2Fworld` is
  valid (the key is `hello/world`).
186 187 188

#### The `sha256` URI Scheme

189
This is a compound URI, the *host* must be understood as the SHA256 hash in
190
hexadecimal format (preceded by `0x` as in Michelson) of the resource being
191 192
pointed at by the path of the URI (which should be percent-encoded to avoid
ambiguity with `/` characters).
193 194 195

Example:

196
`sha256://0xeaa42ea06b95d7917d22135a630e65352cfd0a721ae88155a1512468a95cb750/https:%2F%2Ftezos.com`
197 198 199 200


### Metadata JSON Format

201
We first define the format in a rather informal way; a JSON-Schema specification
202
is provided as an annex to this document.
203

204 205
The metadata should be a valid JSON object
([STD-90](https://tools.ietf.org/html/std90) /
206 207
[RFC-8259](https://www.rfc-editor.org/info/rfc8259)) with various top-level
fields.
208

209 210
*All* top-level fields are optional, i.e. the empty object `{}` is valid
metadata.
211

212 213
For compatibility, a compliant parser should ignore any extra fields it doesn't
know about.
214 215 216

#### Reserved Fields

217
This standard defines a few top-level fields:
218

219 220 221 222 223 224 225 226 227
`"name"`:

- A single string, free format.
- It is recommended to have “name” strings help identifying the purpose of the
  contract.

`"description"`:

- A single string, free format.
228
- Preferably a set of proper *natural language* paragraphs.
229

230 231 232 233
`"version"`:

- A single string, free format.
- It is recommended to have version strings which attempt at uniquely
234 235
  identifying the exact Michelson contract, or at least its behavior, as a
  precision w.r.t the `name` field.  Of course, none of that can be enforced.
236 237 238

`"license"`:

239 240
- An extensible object `{ "name": <string> , "details" : <string> }`,
  `"details"` being optional.
241
- It is recommended to use _de facto standard_ short names when possible, see
242
  the Debian
243
  [guidelines](https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#license-short-name)
244
  for instance.
245 246 247 248 249 250 251

`"authors"`:

- A list of strings.
- Each author should obey the `"Print Name <'contact'>"`, where the `'contact'`
  string is either an email address, or a web URI.

252 253 254 255 256 257
`"homepage"`:

- A single 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.
258 259 260 261 262 263 264 265 266 267 268 269 270
  
`"source"`:

- An object
 `{ "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.
- The goal is to attempt to provide enough information for interested parties to
  reproduce the Michelson from its source, or at least to inspect it.

271

Seb Mondet's avatar
Seb Mondet committed
272
`"interfaces"`:
273 274 275

- A list of strings.
- Each string should allow the consumer of the metadata to know which interfaces
276
  and behaviors the contract *claims* to obey (other than the obvious TZIP-016).
277
- In the case of standards defined as TZIPs in the present repository, the
278 279
  string should obey the pattern `"TZIP-<number><extras>"` where `<extras>` is
  additional information prefixed with a space character.
280
- Example: an FA2 contract would (at least) have an `"interfaces"` field
281
  containing `["TZIP-012"]` or `["TZIP-012 git 6544de32"]`.
Seb Mondet's avatar
Seb Mondet committed
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
  
`"errors"`:

- A 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). They are either:
    - static: `{ "error": <michelson>, "expansion": <michelson>, ... }`: where
      `<michelson>` is a Michelson expression in JSON (see also the following
      section on off-chain-views). The objects show the correspondences between
      `FAILWITH` values and _expanded_ values.
    - dynamic: `{"view": <view-name>, ... }`: which means that one needs to call
      the off-chain-view `<view-name>` (a reference to a view in the `"views"`
      field below). The view's input type should be the one of the error value,
      and it should return the expanded structure.
    - both objects allow an extra field `"languages": [<lang1>, <lang2>, ...]`:
      a list of natural-language codes to filter-on if possible, the codes
      should be “IETF language tags”
      (cf. [Wikipedia](https://en.wikipedia.org/wiki/IETF_language_tag), and
      [RFC-5646](https://tools.ietf.org/html/rfc5646)).
    - the objects can be redundant (incl. for various languages),
      implementations are expected to find the “best fit” according to their
      own priorities.
306 307 308 309

`"views"`:

- A list of off-chain-view objects, defined in the following section.
310

311 312 313 314 315
Example:

```json
{
  "version": "foo.1.4.2",
316
  "license": { "name": "ISC" },
317
  "authors": [ "Seb Mondet <seb@mondet.org>" ],
318 319 320 321
  "source": {
     "tools": ["SmartPy dev-20201031", "Flextesa 20200921"],
     "location": "https://gitlab.com/smondet/fa2-smartpy/-/blob/c05d8ff0/multi_asset.py"
  },
322
  "interfaces": [ "TZIP-012" ],
323 324 325 326 327 328 329
  "errors":[
    { "error": {"int": "42"}, 
      "expansion": { "string": "You did something wrong"},
      "languages": ["en"] },
    { "error": {"int": "42"}, 
      "expansion": { "bytes": "0x7175656c7175652063686f7365206e276120706173206d61726368c3a9"},
      "languages": ["fr"] },
330
    { "view": "translateStringError" }
331
  ],
332 333 334 335 336 337 338 339
  "views": [
     // ... see below ...
  ]
  // ... potential extensions ...
}
```


340
#### Semantics of Off-chain Views
341

Seb Mondet's avatar
Seb Mondet committed
342
An off-chain view object has at least 2 fields:
343

344
- `"name"`; the canonical name of the query (as in function name,
345 346 347
  e.g. `"get-balance"`).
- `"description"`: a human readable description of the behavior of the view
  (optional field).
348 349 350
- `"implementations"`: a list of implementation objects: usable definitions of
  the views. Each implementation is a one-field object where the field name
  discriminates between various kinds of views. Below, this standard defines 2
351
  of those kinds, `"michelsonStorageView"` and `"restApiQuery"`, further
352
  deriving standards may add new ones.
353 354 355 356 357 358
- `"pure"` (optional, default: `false`): a boolean “tag” advertising that the
  view should be considered *a (pure) function* of the storage of the contract
  and extra parameters passed to the view. I.e., that if no operation changes
  the storage of the contract, one can assume that the view returns always the
  same result for a given parameter. Examples of non-pure views are for instance
  queries which depend on the current time & date.
359 360 361 362 363 364 365

Example:

```json
{
  "name": "get-allowance-for-user",
  "description": "Get the current allowance for a user of the contract.",
366
  "pure": "true",
367
  "implementations": [
368 369
     { "michelsonStorageView" : { /* ,,, see below ... */ } },
     { "restApiQuery" : { /* ,,, see below ... */ } },
370 371 372 373
     // ... potential extensions ...
  ]
}
```
374 375 376

##### Michelson Storage Views

377
The `"michelsonStorageView"` field is a JSON object describing a sequence of
378 379 380 381 382 383 384 385
Michelson instructions to run on a pair formed by a given parameter and the
storage of the contract being queried in order to leave the execution stack with
the queried value.  For this object we define 3 fields and a custom type
`michelson`” (see below) and an extra optional field:

- `"parameter"` (optional): an (annotated) Michelson type of the potential
  external parameters required by the view code; if the field is absent the view
  does not require any external input parameter.
386
- `"returnType"` (required): the type of the result of the view (i.e. for the
387 388 389 390
  value left on the stack); the type can also be annotated.
- `"code"` (required): the Michelson code expression implementing the view.
- `"annotations"`: a list of objects documenting the annotations used in the 3
  above fields. These objects have two string fields `"name"`, the annotation
Julien Hamilton's avatar
Typos  
Julien Hamilton committed
391
string, and a human-readable blob of text `"description"`.
392 393 394
- `"version"` (optional): a string representing the version of Michelson that
  the view is meant to work with; Michelson versions *are* base58check-encoded
  protocol hashes.
395

396 397
The 3 “Michelson” fields have the same format, they are JSON values obeying the
Michelson JSON format of the Tezos protocol (sometimes referred to as
398 399
“Micheline” encoding). Only protocol-level primitives are allowed, the so-called
“Michelson macros” are not).
400

401 402 403 404
Example:

```json
{
405 406 407 408 409 410
  "parameter": {
    "prim": "pair", "args": [
      {"prim": "mutez", "annots": ["%amount"]},
      {"prim": "string", "annots": ["user"]}
    ]
  },
411
  "returnType": {"prim": "nat"},
412 413 414 415 416 417 418 419
  "code": [
    {"prim": "DUP"},
    {"prim": "DIP", "args": [
        [
          {"prim": "CDR"},
          {"prim": "PUSH", "args": [
    // ....
  ],
420
  "annotations": [
421 422
     { "name": "amount", "description": "The number of token in question." },
     { "name": "user", "description": "The token-user being referred to." },
423 424 425 426 427
     // ...
  ]
}
```

428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
There are limitations on the contents of the `"code"` field:

- The following instructions must not be used:
  [AMOUNT](https://michelson.nomadic-labs.com/#instr-AMOUNT),
  [CREATE_CONTRACT](https://michelson.nomadic-labs.com/#instr-CREATE_CONTRACT),
  [SENDER](https://michelson.nomadic-labs.com/#instr-SENDER),
  [SET_DELEGATE](https://michelson.nomadic-labs.com/#instr-SET_DELEGATE),
  [SOURCE](https://michelson.nomadic-labs.com/#instr-SOURCE),
  and
  [TRANSFER_TOKENS](https://michelson.nomadic-labs.com/#instr-TRANSFER_TOKENS)
- The following instructions should be understood, as relative to the contract
  (and block) currently being queried:
  [SELF](https://michelson.nomadic-labs.com/#instr-SELF),
  [BALANCE](https://michelson.nomadic-labs.com/#instr-BALANCE),
  [NOW](https://michelson.nomadic-labs.com/#instr-NOW), and
  [CHAIN_ID](https://michelson.nomadic-labs.com/#instr-CHAIN_ID).
- To simplify adoption by various implementations, in this first version of the
  specification, the instruction
  [SELF](https://michelson.nomadic-labs.com/#instr-SELF) should only be used
  before [ADDRESS](https://michelson.nomadic-labs.com/#instr-ADDRESS) (i.e. only
  as `SELF; ADDRESS` should be used).
- All the above rules should apply to code contained in Michelson _lamdas_ or
  serialized into `bytes` values (with `PACK`) but, as a recommendation, those
  techniques should be avoided in off-chain-views.

453 454
#### Rest API Views

455
The `"restApiQuery"` field is an object describing how to map the view to an
456 457
[Open API](https://github.com/OAI/OpenAPI-Specification) description of a
REST-API.
458

459
- `"specificationUri"` (required): a string giving the location (URI) of the
460
  full Open API specification.
461
- `"baseUri"` (optional): The recommended `"server"` to use.
462 463 464
- `"path"` (required): The API path within the Open API specification that
  implements the view.
- `"method"` (optional, default: `"GET"`): The method used for the view.
465

466 467 468 469
Example:

```json
{
470 471
  "specificationUri": "https://example.com/openapi/my-token",
  "baseUri": "https://example.com/my-token/v2",
472 473 474 475
  "path": "/allowances",
}
```

476
### Optional `assertMetadata<hash>` Entrypoints
477 478 479

For the cases when a batched transaction requires assurances that a (portion of)
the contract metadata has not changed at the time the batch-operation is
480 481 482
included in a block, the contract implementation may provide one or more
`assertMetadata<hash>` entrypoints where `<hash>` is any of the Michelson hash
functions `SHA256`, `BLAKE2B`, or `SHA512`.
483

484
If included, the type of such an entrypoint must be:
485 486 487 488 489 490 491

```
(pair (string %key) (bytes %hash))
```

and behave as follows:

492 493
- If the corresponding hash of the value at key `%key` in the metadata `big_map`
  is equal to `%hash`, then do nothing and succeed.
494 495 496
- If the value is not present, call `FAILWITH` with the string `"NOT_FOUND"`,
- If the value is present but its hash is not equal to `%hash`, call `FAILWITH`
  with either `Unit` or with the correct hash if available.
497

498 499 500 501 502 503 504
## Machine-Readable Specifications

In the present repository, `proposals/tzip-16/metadata-schema.json` is a
JSON-Schema specification of the contents of the “Metadata JSON Format”
described above. A few valid examples are available in the
`proposals/tzip-16/examples/` directory.

505
## How To “Derive” From TZIP-016
506

507
This proposal is meant to be extended and specialized by other standards.  Here
508
are some of the ways one can define TZIP-016 extensions:
509 510 511 512 513 514 515

- Defining new fields in the metadata-JSON.
- Making some of the metadata content mandatory, e.g. requiring the
  `"interfaces"` fields to be present, or requiring some off-chain-views to be
  implemented in Michelson with fixed parameter and return types.
- Using other _keys_ of the `%metadata` big-map for storing particular data.
- Using the metadata-URI definition to locate other pieces of data.
516

517
Other aspects are better proposed as a backwards-compatible changes to TZIP-016.
518 519
For instance, adding a new URI scheme to locate (meta)data can be better handled
within this standard.
520

521 522
See also the future-work section for extensions that are already planned or in
the works.
523

524 525
## Known Implementations

526
The section will reference known implementations of (part of) TZIP-016:
527 528 529 530 531

- The work-in-progress merge-request
  [`smondet/tezos!7`](https://gitlab.com/smondet/tezos/-/merge_requests/7) adds
  support in `tezos-client`: fetching and displaying metadata for any given
  contract as well as “calling” off-chain-views.
532 533 534 535 536 537
- The project [`github.com/tqtezos/TZComet`](https://github.com/tqtezos/TZComet)
  (live at [`tqtezos.github.io/TZComet`](https://tqtezos.github.io/TZComet/),
  previously known as *“Comevitz”*) provides validating metadata URI and JSON
  editors with various examples, a Michelson serialized data analyzer, and a
  (work-in-progress) metadata explorer which can fetch metadata URI and JSON
  contents.
538 539
- The Archetype language implementation has support in the compiler (cf.
  [documentation](https://docs.archetype-lang.org/archetype-language/metadata-tzip-16)).
540

541 542 543 544 545 546
## Rationales / Design Choices

This section keeps track of some of the choices made in the development of the
specification.

- *Use of the contract storage:* Another way of “storing” an URI on-chain could
Seb Mondet's avatar
Seb Mondet committed
547
  have been to use an “operation event” (a contract-call that is only used to
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
  record its payload on the blockchain without keeping anything in the
  contract's storage). This method is the cheapest in gas and storage but it
  requires an indexer for any implementation to be able to do anything. We
  consider this too much of a barrier for adoption. Moreover, we also want to
  leave the option of storing the metadata JSON *on-chain* (strongly requested
  by users).
- *The use of a big-map:* Once the contract-storage solution is assumed, we
  could have left the choice between `(bytes %metadata)` and `(big_map %metadata
  string bytes)`. The former's gain in type-checking/deserialization gas seems
  negligible (we measured to around 250 units) compared to the extra complexity
  of the specification that it would incur.
- *The `string` in `(big_map string bytes)`:* The keys of the big-map are used
  for addressing values, including in the URI definition. The Michelson `string`
  type is a good practical choice for this; it has an obvious and readable
  concrete encoding: the string literal itself.
- *The `bytes` in `(big_map string bytes)`:* Michelson strings are limited in
  the characters one can encode (only ASCII printable and some whitespace), so
  to allow arbitrary values we need to use the `bytes` type.  This includes the
  metadata JSON which, when stored on-chain, requires full UTF-8 capabilities
  (not supported by the `string` type).
- *The use of JSON-Schema instead of JSON_LD or other specifications:* Basic
  JSON(-Schema) was chosen because it is already pervasive in the Tezos
  ecosystem, including in the node RPCs defined in all the Mainnet protocols.


573 574
## Future Work & Extensions

575
A few extensions and improvements of TZIP-016 are already planned or in progress:
576

577
- Augment/upgrade the TZIP-012 and TZIP-007 standards with metadata support.
578 579 580
- Specify *off-chain-events*: events are similar to off-chain-views, but they
  extract knowledge from (the sequence of) contract **calls**, like “balance
  updates”; cf.
581 582 583
  [agora](https://forum.tezosagora.org/t/deriving-fa-token-balance-updates-from-big-map-diff/1972)
  and this
  [article](https://baking-bad.org/blog/2020/08/28/off-chain-events-and-tezos-tokens-indexing/).
584 585 586
- Specify *multi-contract off-chain views* which are off-chain-views defined
  over more than one contract-storages.

587 588 589 590 591
## Copyright

Copyright and related rights waived via
[CC0](https://creativecommons.org/publicdomain/zero/1.0/).