# ZkRoninsMeta64.sol

{% hint style="info" %}
**Testing phase**

This contract, while *fundamentally* being done, is still being tested by us. We will hold back with its deployment as to not scatter the focus and rush into the *next phase* too soon.
{% endhint %}

ZkRoninsMeta64.sol is the main Base64 metadata contract in the [](https://docs.zkronins.com/contracts/modules/meta64 "mention") module and contains all the logic for enabling, storing and retrieving Base64 encrypted token URIs. This will make it easier to change metadata, for here we construct the (majority of the) JSON scheme on-chain.

With the non-Base64 URI methods we simply point towards an URI that's stored someplace else and therefore making it impossible to change the metadata without having to re-upload a new JSON file. With Base64 changes can be made directly on-chain and it would reflect immediately in the URI.

## A case study

Let's give an example of the power behind leveraging the Base64 format.

{% tabs %}
{% tab title="Code" %}
The following code is a *simplified version* of the return statement in our current ZkRoninsMeta64.sol contract:

<pre class="language-solidity"><code class="lang-solidity">return string.concat(
<strong>    'data:application/json;base64,',
</strong><strong>    Base64.encode(
</strong>        bytes(string.concat(
            '{"name":"', namePrefix, tokenId.toString(), " - ", name,
            '","description":"', description,
            '","image":"', image,
            '","animation_url":"', animation_url,
            '","attributes":[', attributes, ']}'
        )
    ))
);
</code></pre>

Normally, metadata would be returned as a simple URI (`https://example.com/2.json`). But here we create one that is based on values stored inside of the contract itself (variables like *namePrefix, tokenId, name, image, animation\_url, attributes* and *description* represent thes&#x65;*)*.&#x20;

With this method we could for instance change the value of *namePrefix* from `zkRonin #` to `Ronin` which would instantly change the name format for all the ronins on-chain from **zkRonin #10** - Hira Kyoshi to **Ronin 10** - Hira Kyoshi.

This simple change would have been costly with non-Base64 metadata as it can only be done by changing all the .json files, re-uploading them and finally re-linking them to the contract. Whereas here we change only one variable with a single transaction on-chain.

***

This example may seem irrelevant, but it showcases the *sturdiness of non-Base64 URI* versus *the flexibility of Base64 URI* metadata.
{% endtab %}

{% tab title="Result" %}
*(copy and paste this into your address bar to see the metadata for a simple ronin)*

{% code overflow="wrap" %}

```
data:application/json;base64,eyJuYW1lIjoiUm9uaW4gMTAgLSBIaXJhIEt5b3NoaSIsImRlc2NyaXB0aW9uIjoiVGhpcyBpcyBhIHRlc3Qgcm9uaW4uIiwiaW1hZ2UiOiJodHRwczovL3prcm9uaW5zLmNvbS9yb25pbi8wLmpwZyIsImFuaW1hdGlvbl91cmwiOiJodHRwczovL3prcm9uaW5zLmNvbS9yb25pbi8wLmh0bWwiLCJhdHRyaWJ1dGVzIjpbeyJ0cmFpdF90eXBlIjoiQWdlIiwidmFsdWUiOjIwfV19
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Fields

*Name*, *description*, *image*, *animation\_url*, *attributes or individual traits* are all **Fields** we can use to *describe* and create a ronin. In our code we've made it possible to add and remove **Fields**, which is where the dynamic nature of the project really comes into play. Because now we can introduce *new traits* to a character based on whatever mechanism we choose to create. Like e.g. a *Slot Counter*, *Revision Amount* or *Royalty Bonuses* allowing a holder to gain a percentage of made secondary sales (see [royalty-collector](https://docs.zkronins.com/synergy/overview/royalty-collector "mention") for this *very early-stage* concept).

## Breakdown

* **Administration**
  * `getField` returns the value of a field for a specific token ID, slot and fieldName.
  * `getTraits` returns a list of all the available traits. These are basically Fields that *have been labeled* attributes.
  * `setField` sets the value for a field for a specific token ID and slot combination. Can only be called by the admin.
  * `setFields` sets multiple fields for a specific token ID and slot combination.  Can only be called by the admin.
  * `setFieldsMany` sets multiple fields for multiple token ID and slot combinations *or* sets multiple fields for a token ID range, where slot is a fixed value (for example: if we want to populate all the first slots for a large chunk of token IDs). Can only be called by the admin.
  * `addTrait` pushes the name of a field we should consider as a trait to the list of traits. Can only be called by the admin and is only possible if the trait doesn't exist.
  * `addTraits` is the many variant of `addTrait` and skips the traits that have already been added. Can only be called by the admin.
  * `removeTrait` is self-explanatory. Can only be called by the admin and is only possible if the trait exists.
  * `setContractSlots` sets the address to the contract having the storage for the token slots, which in our case happens to be the [nft-erc721](https://docs.zkronins.com/contracts/modules/nft-erc721 "mention") contract. Can only be called by the admin.
  * `setContractMeta64X` sets the address for the [zkroninsmeta64x.sol](https://docs.zkronins.com/contracts/modules/meta64/zkroninsmeta64x.sol "mention") contract. Can only be called by the admin.
* **Metadata functionality**
  * `slotURI` only returns a Base64 URI if the specific token ID and slot has Base64 enabled. This function gathers all necessary [#fields](#fields "mention") and creates a Base64 encoded URI.
  * `slotURIs` returns multiple slotURIs for a given tokenID. The slots that have disabled Base64 will return as an empty string.
  * `tokenURI` only returns a Base64 URI if the specific token ID has Base64 enabled on the currently selected slot (see `selectedSlot` in [metadata](https://docs.zkronins.com/contracts/modules/nft-erc721/metadata "mention")).
  * `tokenURIs` is the many variant for `tokenURI`. Base64 disabled slots return an empty string.
  * `slotFields` is similar to `slotURI` but returns the key-value pairs ¹ stored in a specific slot for a ronin ².
  * `slotFieldsMany` is the *Fields equivalent* of `slotURIs`; meaning it returns key-value pairs ¹ instead of URIs ².
  * `tokenFields` is similar to `tokenURI` but returns the key-value pairs ¹ stored for a ronin ².
  * `tokenFieldsMany` is the *Fields equivalent* of `tokenURIs`; meaning it returns key-value pairs ¹ instead of URIs ².
  * `setReadyBase64` sets whether the Base64 metadata is ready or not. Since we as the contract owners have to populate the contracts with all the metadata, holders shouldn't be able to enable Base64 if it is not deemed ready. Can only be called by the admin.
  * `setReadyBase64Many` is self-explanatory. Can only be called by the admin.
  * `setEnableBase64` enable or disable Base64 metadata on a per token or per slot basis. If the owner chooses to enable it on a per token basis, then every slot will have Base64 enabled by default. This function is guarded by the *upgrade role modifier* ³.
  * `setNamePrefix` sets the prefix for the name of all the NFTs in the collection; see the case study above. Can only be called by the admin.

{% hint style="success" %} <mark style="color:green;">**NOTE:**</mark> *slotURI* is the function where most of the logic lies. Moreover, it's **upgradable**, which will allow us to create more advanced algorithms for the computation of the Base64 URIs (e.g. fully-autonomous metadata; see [zkroninsmeta64x.sol](https://docs.zkronins.com/contracts/modules/meta64/zkroninsmeta64x.sol "mention") for more on this).
{% endhint %}

***

¹ Key-value pairs are useful if we don't necessarily want to create an URI, but just need all the information a ronin is *composed* of.

² The fields getter functions, which only developers might use, will only return correct values if [zkroninsmeta64x.sol](https://docs.zkronins.com/contracts/modules/meta64/zkroninsmeta64x.sol "mention") isn't linked to this contract. This can be noticed by checking whether `contractMeta64X` equals the zero-address (0x0000...0000). If it does link, then we will also have to call the Meta64X's *slotFields*-function for a complete result.

³ Functions with the *upgrade role modifier* can only be called by the [store-upgrade-handler](https://docs.zkronins.com/contracts/modules/upgrade-module/store-upgrade-handler "mention") contract in the [upgrade-module](https://docs.zkronins.com/contracts/modules/upgrade-module "mention") module. This is done to separate *storage* and *logic* for modularity, to improve the contract's upgradability and to circumvent Solidity's immutable nature. See our [Code of Conduct](https://docs.zkronins.com/contracts/code-of-conduct) for more about this.

***

## Transparency and Security

* **No withdraw method**\
  This contract cannot recover funds and shouldn't receive any, so be extra attentive not to accidentally send any funds to this contract.
* **Static and dynamic**\
  An NFT in our collection will be able to have both non-Base64 and Base64 types of metadata with our [token-slots](https://docs.zkronins.com/synergy/overview/token-slots "mention") approach. The current thought process for this is to give owners the possibility to have *static* metadata if they don't want to join the Base64 movement.\
  \
  Allowing both also makes it possible to give owners a way to copy over their Base64 metadata to the [nft-erc721](https://docs.zkronins.com/contracts/modules/nft-erc721 "mention") contract's storage, switch back to non-Base64 and treat their metadata as a static URI.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zkronins.com/contracts/modules/meta64/zkroninsmeta64.sol.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
