ERC-7508: Dynamic On-Chain Token Attributes Repository
Dynamic on-chain storage of token attributes in a public-good repository.
Abstract
The Public On-Chain Non-Fungible Token Attributes Repository standard provides the ability for ERC-721 and ERC-1155 compatible tokens to store their attributes on-chain available to any external smart contract interacting with them.
This proposal introduces the ability to assign attributes to NFTs in a public non-gated repository smart contract that is accessible at the same address in all of the networks. The repository smart contract is designed to be a common-good repository, meaning that it can be used by any ERC-721 or ERC-1155 compatible token.
Motivation
With NFTs being a widespread form of tokens in the Ethereum ecosystem and being used for a variety of use cases, it is time to standardize additional utility for them. Having the ability to store token's attributes on chain allows for greater utility of tokens as it fosters cross-collection interactivity and provides perpetual store of attributes.
This ERC introduces new utilities for ERC-721 and ERC-1155 based tokens in the following areas:
Cross-Collection Interactivity
Storing attributes on-chain in a predictable format allows for cross-collection interactivity. This means that the attributes of a token can be used by any external smart contract without the need for the token to be aware of the external smart contract.
For example, a token can represent a game character with its set of attributes and can be used in an unrelated game with the same stats without the need for retrieving these attributes from an off-chain source. This ensures that the data the game is using is legitimate and not tampered with in order to gain an advantage.
Perpetual Store of Attributes
Standardized on-chain token attributes allow for their perpetual storage.
With off-chain attributes storage, the attributes are only available as long as the off-chain storage is available. If the storage is taken down, the attributes are lost. With on-chain attributes storage, the attributes are available as long as the blockchain is available. This increases the value of the token as it ensures that the attributes are available for as long as the token exists.
Token Evolution
On-Chain storage of token attributes allows for the token to evolve over time. Owner's actions can impact the attributes of the token. Since the attributes are stored on chain, the smart contract has the ability to modify the attribute once certain thresholds are met. This allows for token to become more interactive and reflect owner's dedication and effort.
Dynamic State Tracking
On-Chain storage of token attributes allows for dynamic state tracking. The attributes can be used to track the state of the token and its owner. This allows for the token to be used in a variety of use cases. One such use case is supply chains; the token can represent a product and its attributes can be used to track the state of the product as it transitions from pending, shipped, delivered, etc.
Specification
Interface
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Schema
In addition to the interface we propose that collection owers SHOULD be able to set the schema for the attributes of their collection. We distinguish between 2 types: token attributes and collection attributes. The latter are the attributes that are shared by all tokens in the collection, they can be retrieved and set by using the max uint256 value as tokenId.
For each attribute we specificy the following fields: name, description, type, display_name, display_type, decimals, min_value, max_value, conditional_value, modifiers and multi_storage. Only name and type are required. For more details on the fields, please refer to the [JSON schema] avaliable collection-metadata-schema.json
, with an example available at collection-metadata-example.json
.
The reasoning for conditional_value, modifiers and multi_storage will be discussed in the rationale section.
Message format for presigned attribute
The message to be signed by the setter
in order for the attribute setting to be submitted by someone else is formatted as follows:
The values passed when generating the message to be signed are:
DOMAIN_SEPARATOR
- The domain separator of the Attribute repository smart contractMETHOD_TYPEHASH
- The typehash of the method being called. The supported values, depending on the method are:SET_UINT_ATTRIBUTE_TYPEHASH
- Used for setting uint attributesSET_STRING_ATTRIBUTE_TYPEHASH
- Used for setting string attributesSET_BOOL_ATTRIBUTE_TYPEHASH
- Used for setting bool attributesSET_BYTES_ATTRIBUTE_TYPEHASH
- Used for setting bytes attributesSET_ADDRESS_ATTRIBUTE_TYPEHASH
- Used for setting address attributes
collection
- Address of the collection containing the token receiving the attributetokenId
- ID of the token receiving the attributekey
- The attribute keyvalue
- The attribute value of the appropriate typedeadline
- UNIX timestamp of the deadline for the signature to be submitted. The signed message submitted after the deadline MUST be rejected
The DOMAIN_SEPARATOR
is generated as follows:
The SET_UINT_ATTRIBUTE_TYPEHASH
is generated as follows:
The SET_STRING_ATTRIBUTE_TYPEHASH
is generated as follows:
The SET_BOOL_ATTRIBUTE_TYPEHASH
is generated as follows:
The SET_BYTES_ATTRIBUTE_TYPEHASH
is generated as follows:
The SET_ADDRESS_ATTRIBUTE_TYPEHASH
is generated as follows:
Each chain, that the Attributes repository smart contract is deployed in, will have a different DOMAIN_SEPARATOR
value due to chain IDs being different.
Pre-determined address of the Attributes repository
The address of the Emotable repository smart contract is designed to resemble the function it serves. It starts with 0xA77B75
which is the abstract representation of ATTBTS
. The address is TBD.
Rationale
Designing the proposal, we considered the following questions:
- Should we refer to the values stored by the repository as propertiers or attributes?
Historically values defining characteristics of tokens have been called properties, but have evolved in to being called attributes. Referring to the dictionary, the property is defined as a quality or characteristic that something has, and the attribute is defined as a quality or feature of somebody/something. We felt that using the term attribute fits better and decided to use it. - Should the proposal specify access control?
Designing the proposal, we had two options: either to include the access control within the specification of the proposal or to leave the access control up to the implementers that desire to use the attributes repository. While considering this we also had to consider the usability and compatibility aspects of the repository.
On one hand, including access control narrows down the freedom of implementation and requires the implementers to configure it before being able to use the repository. On the other hand, leaving access control up to implementers requires dedicated design of attributes access control within their smart contracts, increasing their size, complexity and deployment costs.
Another important thing to note is that including access control in the proposal makes it compatible with collections existing prior to the deployment of the repository and thus powers backwards-compatibility. - Should the proposal establish an attributes extension or a common-good repository?
Initially we set out to create an attributes extension to be used with any ERC-721 compliant tokens. However, we realized that the proposal would be more useful if it was a common-good repository of token attributes. This way, the tokens that can utilize it are not only the new ones but also the old ones that have been around since before the proposal.
An additional benefit of this course-correction is the compatibility with ERC-1155 tokens. - Should we include only single-action operations, only multi-action operations, or both?
We've considered including only single-action operations, where the user is only able to assign a single attribute to a single token, but we decided to include both single-action and multi-action operations. This way, the users can choose whether they want to assign an attribute to a single token or on multiple tokens at once.
This decision was made for the long-term viability of the proposal. Based on the gas cost of the network and the number of tokens in the collection, the user can choose the most cost-effective way of attribute assigning. - Should we add the ability to assign attributes on someone else's behalf?
While we did not intend to add this as part of the proposal when drafting it, we realized that it would be a useful feature for it. This way, the users can assign attributes on behalf of someone else, for example, if they are not able to do it themselves or if the attribute is earned through an off-chain activity. - How do we ensure that attribute assignment on someone else's behalf is legitimate?
We could add delegates to the proposal; when a user delegates their right to assign attributes to someone else, but having the ability to do so opens up the possibility for abuse and improper setting of attributes.
Using ECDSA signatures, we can ensure that the user has given their consent to assign attribute on their behalf. This way, the user can sign a message with the parameters of the attribute and the signature can be submitted by someone else. - Should we add chain ID as a parameter when assigning attribute to a token?
We decided against this as we feel that additional parameter would rarely be used and would add additional cost to the attribute assignment transactions. If the collection smart contract wants to utilize on-chain token attributes, it requires the reactions to be recorded on the same chain. Marketplaces and wallets integrating this proposal will rely on attributes to reside in the same chain as well, because if chain ID parameter was supported this would mean that they would need to query the repository smart contract on all of the chains the repository is deployed in order to get the attributes of a given token.
Additionally, if the collection creator wants users to record their reactions on a different chain, they can still direct the users to do just that. The repository does not validate the existence of the token being reacted to (except in an instace where the attribute can be modified by the token's owner), which in theory means that you can assign an attribute to non-existent token or to a token that does not exist yet. - How should we reduce the cost of string usage in the repository?
One fo the main issues we were dealing with while designing the proposal is the cost of string usage. We considered using bytes instead of strings, but decided against it as it would require the users to encode and decode the strings themselves.
The solution for reducing the cost was to use a string indices. This means that the cost of setting a new string attribute or key will only be paid by the first user to do so. The subsequent users will only pay the cost of setting the index of the string attribute or key.
We also extended this gas-saving approach to be applicable across the entire repository. This means that if the string was already set by one collection, any other collection using the same string will not have to pay the cost of setting the string again.
Backwards Compatibility
The Attributes repository standard is fully compatible with ERC-721 and ERC-1155 and with the robust tooling available for implementations of ERC-721 as well as with the existing ERC-721 infrastructure.
Test Cases
Tests are included in attributesRepository.ts
.
To run them in terminal, you can use the following commands:
Reference Implementation
Security Considerations
The proposal does not envision handling any form of assets from the user, so the assets should not be at risk when interacting with an Attributes repository.
The ability to use ECDSA signatures to set attributes on someone else's behalf introduces the risk of a replay attack, which the format of the message to be signed guards against. The DOMAIN_SEPARATOR
used in the message to be signed is unique to the repository smart contract of the chain it is deployed on. This means that the signature is invalid on any other chain and the attributes repositories deployed on them should revert the operation if a replay attack is attempted.
Another thing to consider is the ability of presigned message reuse. Since the message includes the signature validity deadline, the message can be reused any number of times before the deadline is reached. The proposal only allows for a single value for a given key to be set, so the presigned message can not be abused to further modify the attribute value. However, if the service using the repository relies on the ability to revert or modify the attribute after certain actions, a valid presigned message can be used to re-assign the attribute of the token. We suggest that the services using the repository in cnjunction with presigned messages use deadlines that invalidate presigned messages after a reasonalby short period of time.
Caution is advised when dealing with non-audited contracts.
Copyright
Copyright and related rights waived via CC0.