ERC-8119: Key Parameters - Standard Format for Parameterized String Keys

This ERC proposes a standard format for parameterized string keys used in EVM key-value storage. It defines a simple convention using a colon and space separator (`: `) to represent variations or instances of metadata types, enabling better interoperability across different implementations.

PR: https://github.com/ethereum/ERCs/pull/1455/

Many EVM-based smart contracts use key-value storage (e.g., Solidity mappings, Vyper hash maps) to store metadata where string keys may need to represent multiple instances or variations of the same metadata type. Without a standardized format, different implementations use inconsistent formats like `“registration-1”`, `“registration:1”`, or `“registration1”`, leading to:

- Interoperability issues between contracts and tooling

- Parsing difficulties for clients and indexers

- Fragmentation in the ecosystem

This standard enables consistent parameterized keys that are both human-readable and easy to parse programmatically. Standards such as ERC-8048 (Onchain Metadata for Token Registries) and ERC-8049 (Contract-Level Onchain Metadata) can leverage this ERC to support parameterized metadata keys.

When string keys include parameters, they **MUST** use a colon and space separator (`: `).

Valid formats:

- `“registration: 1”`

- `“registration: 2”`

- `“user: alice”`

- `“key: value:with:colons”` (colons allowed in parameter, but not `: `)

**Invalid formats:**

- `“registration-1”` (hyphen separator)

- `“registration:1”` (colon without space)

- `“registration1”` (no separator)

The colon and space separator (`: `) was chosen because:

- It improves human readability compared to formats like `key:value` or `key-value`

- It provides a clear, unambiguous separator that is easy to parse programmatically

- It maintains compatibility with existing parsers that support this format

This format was inspired by TOON format (developed by Johann Schopplich), and we acknowledge this preceding work.

1 Like

One potential problem I can see, is that “x: y” is a pattern commonly used by many languages and data formats to indicate that key “x” has value “y”. This is true in JSON, and even in TOON. I don’t know of any language or data format that uses “x: y” as a way to point to attribute “y” in object “x” or member “y” of array “x”.

If human-readability is important, it seems better to follow these norms which others are already familiar with.

The format “x[y]” is widely used to reference “y” as being a key name or index belonging to object/array “x”, and I feel like that would be a much better format for parameterization.

1 Like

Thank for the feedback! Is there a functional difference between x[y] and x: y? If it’s just a style thing, I tend towards keeping x: y because it might have broader appeal beyond just developers? Also, just in general name: Lena reads more naturally than name[Lena] to me. I think a counter example is passing a struct as a parameter to a function, and using key: value syntax for initialization, like in Solidity when calling a function with a struct as a parameter: setPerson(P({name: "Lena", age: 30}))?

1 Like

I think we should focus on developers, because they are the only ones who will be consuming the data at this level :slight_smile:

I believe it is a functional issue to follow established norms and expectations and avoid confusion wherever possible. Key-value pairs are represented by different languages as objects/dictionaries/maps, and so we should choose a pattern that accurately represents that way of thinking.

Imagine if our key-value data was being indexed and passed along to other systems through APIs. In JSON, the proposed system could end up with something like { “bestNumber: 1": 42 }. Although it is possible to understand, it is more confusing than it needs to be and looks like a mistake. If a frontend developer were to put that into a JavaScript object and print to console, it would turn into: bestNumber: 1: 42. This is very ambiguous.

In YAML, TOON, and other related systems where quotes around key names are optional unless the key name includes a space, you would be requiring quotes to be used every time. This reduces efficiency by requiring an additional 2 characters per entry, and increases the chance for operator error.

If we instead used brackets, which is commonly used for objects/dictionaries/maps, the above example would turn into bestNumber[1]: 42. This logically makes sense, because in the majority of programming languages bestNumber[1]would refer to the first entry in an array titled bestNumber, and then we are using the commonly-understood pattern of x: y to show what the value of that entry is. Quotes are never needed, and it should be immediately obvious if something is incomplete after a copy-paste.

In summary, I don’t see any benefits to used the proposed format but I do see many risks of causing developer headaches. If there are some benefits that I’m missing, please highlight them so I can rethink it!

1 Like

:thinking: I think this is a good point. I hadn’t thought about representing this as a pair like this before. What if we use something like <label>/<parameter>?

name/Maria: 42

I am unsure about using <label>[<parameter>], because I feel like the closing square bracket can easily get lost when the parameter is large or spans multiple lines.

For example, a long scene description:

scene/A cinematic wide-angle illustration of a quiet coastal town at dawn, soft pink and blue light reflecting off calm water, wooden fishing boats tied to a weathered pier, mist drifting low across the surface of the harbor. In the background, rolling hills with scattered pine trees fade into the fog. The style is painterly and detailed, with subtle grain and a slightly muted color palette inspired by Studio Ghibli and classic European landscape paintings.

This feels easier to read and more robust than relying on a closing bracket at the end of a long parameter.

With brackets:

scene[A cinematic wide-angle illustration of a quiet coastal town at dawn, soft pink and blue light reflecting off calm water, wooden fishing boats tied to a weathered pier, mist drifting low across the surface of the harbor. In the background, rolling hills with scattered pine trees fade into the fog. The style is painterly and detailed, with subtle grain and a slightly muted color palette inspired by Studio Ghibli and classic European landscape paintings.]

I updated the spec to use “/” as the delimiter.

Sorry for the delay, I was preoccupied with an ETH Global hackathon.

I think this is actually a point in favor of the bracket format.

[ is universally understood to be followed by ], and so for long values we can treat the ]at the end as a sort of soft checksum. If it is not found we can assume the parameter is incomplete (i.e. as the result of data corruption, truncation due to length limits, or a copy-and-paste mistake) and if it is there we will trust the contents are correct.

If / can be understood to delimit the start of the parameter, there is no reason [ can’t fulfill the same roll. [ has the added benefit of being paired with ] and also has the benefit of allowing / to be used in parameter names without needing to be escaped (useful for URIs).

You convinced me before that this doesn’t look great:

bestNumber: 1: 42

My idea was to change from “: “ to “/”

bestNumber/1: 42

With square brackets it looks techie IMO, maybe not in a good way?

bestNumber[1]: 42

I don’t quite understand why this is a top concern. From my understanding, you are proposing a standard format so that computer programmers can build systems that are compatible with one another. Shouldn’t the design be more functional than aesthetic?

If I’ve misunderstood the focus of the ERC and accessibility to non-programmers is a key point, please let me know! :slight_smile:

Is there any functional benefit to [ ] vs /? I see it as just style, and I like / better for readability, and because of the reason that I stated before that it’s easy to get confused by long parameters with the last ].

do we need to specify somewhere in contract which key string was used, maybe its worth of adding some event topics like

event AddKey(string keyLabel)

so we can index all added layers of metadata from off-chain, storing mapping(string => bytes) is good but how to get info about bytes if you dont know string. Function getMetadata(string memory key) is present but there is no way to know which keys were added after constructor.

1 Like

Another thing that would be good is to be able to include the data format, because the base format is bytes, but it’s possible that the end-use format is something different.

event AddKey(string keyLabel, string type)

The types could be standard Solidity types such as bytes, bytes32, int, uint256, etc. There could be a couple of special ones as well, for example raw UTF-8.

1 Like

All right, in the example contract I see mapping(string => bytes), which confused me a bit. I thought it would always be bytes, so yes, something like this might be a good choice.

I just added multi-parameter support(!) using [ ]. When I thought about it more, I realized, why not just do both?

1 Like

I decided to add “:” as well as an alternative to “/”

I think it actually looks ok, even if it is represented as a key/value pair.

It’s possible in JSON for example to do:

“key:param”: 1

1 Like