Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC: Event-Driven NFT Utilities #353

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
256 changes: 256 additions & 0 deletions ERCS/erc-7672.md
@@ -0,0 +1,256 @@
---
eip: 7672
title: Event-Driven NFT Utilities
description: The event-driven mechanism to facilitate distinct utilities of an NFT across multiple applications.
author: William Hong (@williamh-zx), William Hong <william@zeroxtech.com>, Martin Yan <martin@zeroxtech.com>, Saeid Yazdinejad <saeid@zeroxtech.com>, David Wang <david@zeroxtech.com>
discussions-to: https://ethereum-magicians.org/t/erc-7672-event-driven-nft-utilities/19449
status: Draft
type: Standards Track
category: ERC
created: 2024-03-06
---

## Abstract

This proposal introduces a standard for enabling dynamic **_status changes_** of a Non-Fungible Token (NFT) in different applications. There are 2 main features introduced:

1. Instead of modifying the NFT metadata directly on-chain, this approach employs an event-driven mechanism that represents **status changes** through the emission of on-chain events.
2. Instead of limiting an NFT to a specific application, this approach facilitates distinct utilities of an NFT (even an existing one like a Doodle) across multiple applications.

Any application following this standard effectively possesses a chain of events where each event corresponds to a distinct application status. Note that such an event contains only **status changes** to the application, which may be a single-bit change of an NFT status like "Health Point +1" for "Doodle #9999" within a specific application.

Therefore, an NFT will have a distinct representation in each supporting application (e.g. a mighty hero in one game; an ordinary pet in a social app; or a ticket with changing access levels in an event platform), corresponding to its unique NFT utility within this application. This standard supports efficient management of application-NFT statuses and NFT interoperability across various applications, significantly broadening NFT utilities.

## Motivation

The rapid growth of the NFT ecosystem has unveiled a multitude of new opportunities for creators, collectors, and developers. However, this growth has also highlighted significant limitations in the current standards for NFTs, particularly regarding their dynamic interaction with decentralized applications (dApps). Presently, NFTs are often static and confined to the context in which they were created. This lack of flexibility limits the potential use cases and the overall utilities of NFTs within the broader blockchain ecosystem.

Several key challenges motivate this proposal:

1. **Static Nature of NFTs:** Once minted, the metadata of most NFTs, including their attributes and utilities, remains unchanged. This rigidity prevents NFTs from evolving over time or interacting dynamically with various dApps.
2. **Application-Specific Limitations:** NFTs are frequently designed and utilized within specific applications or ecosystems. This approach restricts the NFT's utility and value to its original context, hindering cross-application interoperability and innovation.
3. **Efficiency and Scalability Concerns:** Directly modifying NFT metadata on-chain for status updates is inefficient and can lead to increased transaction costs and scalability issues. This approach is not sustainable for applications requiring frequent updates.

This proposal aims to address these challenges by introducing a standard for dynamic status changes of NFTs in different applications. By leveraging an event-driven mechanism for signaling status updates and facilitating NFT utilities across multiple applications, this proposal seeks to enhance the usability, efficiency, interoperability, and overall utilities of NFTs within the Ethereum ecosystem. The introduction of a flexible and standardized approach for dynamic application-NFT interactions promises to unlock new possibilities for creators, developers, and users, paving the way for innovative applications and use cases that extend beyond the current limitations.

## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

### Overview

The Event-Driven NFT Utilities Standard streamlines the integration and update process of NFT statuses within diverse applications. This section outlines the key functionalities and flow of the system as follows:

![Event Driven Utilities Standard](../assets/erc-7672/diagram.png)

1. **Application Registration**: Application owners initiate the process by registering their application with the _Event-Driven Utilities Smart Contract_. Post-registration, each application is associated with an "Update Module". The application owner specifies this module by providing a URL pointing to the content of the "Update Module" stored on decentralized storage (e.g., `https://ipfs.io/ipfs/QmeSjS...`). This module sets out the global attributes for the NFTs and the update mechanisms within the application.
2. **NFT Collection Registration**: Upon successful application registration, a unique identifier is assigned to the application. The application owner can then proceed to register multiple NFT collections under this application across different blockchains by providing the respective `chain ID` and `NFT smart contract address`. This registration is performed via a function call to the smart contract, ensuring the NFT collections are recognized for use and updates within the application environment.
3. **Status Updates Through Events**: Once the application and NFT collections are registered, the application owner can initiate status updates. This is done by emitting events that signal status changes within the application for the updated tokens. The bottom of the diagram illustrates the independent update processes for different applications, allowing them to update their respective statuses through events. This design ensures efficiency and that while applications MAY share NFT collections, the status of an NFT is managed separately within each application, maintaining autonomy and non-interference across applications. Effectively, each application possesses a chain of events representing the progression of applicaiton statuses.

### Smart Contract Interface

The interface for our Event-Driven NFT Utilities Standard, which establishes the foundational structure for implementing the proposed standard, is provided below:

```solidity
/**
* @title IEventDrivenUtilities
* Interface for the Event-Driven NFT Utilities Standard
*/
interface IEventDrivenUtilities {
/// MUST be emitted upon the registration of an application.
event AppRegistered(uint256 indexed appId, address indexed appOwnerAddr, string appInfo);
/// MUST be emitted when an NFT collection is registered under an application.
event CollectionRegistered(uint256 indexed appId, uint256 indexed chainId, address indexed collectionAddr);
/// MUST be emitted when an update module is set for an application
event UpdateModuleSet(uint256 indexed appId, string updateModuleUrl);
/// MUST be emitted when the ownership of an application is transferred.
event OwnerTransferred(uint256 indexed appId, address newOwner);
/// MUST be emitted when the information related to an application is changed.
event InfoChanged(uint256 indexed appId, string newInfo);
/// MUST be emitted for any status updates within an application.
event AppStatusUpdated(uint256 indexed appId, string updateUrl);

/// Registers an application, initializing it for dynamic NFT status updates.
function registerApp(string calldata appInfoUrl) external returns (uint256 newAppId);
/// Specifies which NFT collection(s) to support in a registered application.
function registerCollections(uint256 appId, uint256[] calldata chainIds, address[] calldata collectionAddrs) external;
/// Configures an update module for an application, detailing the update mechanism.
function setAppUpdateModule(uint256 appId, string calldata updateModuleUrl) external;
/// OPTIONAL: Transfers the ownership of an application to a new owner.
function transferAppOwner(uint256 appId, address newOwner) external;
/// OPTIONAL: Changes the informational details of an application.
function changeAppInfo(uint256 appId, string calldata newInfo) external;
/// Facilitates a status update within an application, affecting its NFT statuses and thus the application status.
function updateAppStatus(uint256 appId, string calldata updateUrl) external;
}
```

### Sample JSON Schema

To illustrate the kind of content `updateModuleUrl` MAY point to, below is a sample JSON schema. This schema specifies the update mechanism, known as the update module, showcasing a potential implementation:

```json
{
"applicationId": 1,
"applicationName": "GTA V",
"description": "The update module for App 1: GTA V",
"global_attributes": ["HP", "MP", "EXP", "ATK", "DEF", "SPEED"],
"collections": {
"Chain-1_0x0c1AfA2d6D05da2BE8E73CBA2398cf09a530e2B4": [0, 1, 2], // i.e. ["HP", "MP", "EXP"]
"Chain-1_0xAADBA53eB120A1f60064aa4443a71BDf81C8Cc34": [3, 5], // i.e. ["ATK", "SPEED"]
"Chain-42161_0xF0240b006e324D76cC12191DFd22b239927ecC16": [0, 1, 2, 3, 4, 5],
"Chain-56_0xC36e927e9a28dfc80AF62019a2890dDaAe038a63": [0, 1],
"Chain-8453_0x812d249854EaaD98E50e6a3Fe4033C575925E4aC": [2, 4, 5]
}
}
```

The schema illustrates key components such as `applicationId`, `applicationName`, and a list of `global_attributes` that an application might want to update (`HP`, `MP`, `EXP`, etc.). It also demonstrates how an NFT collection, identified by its `chain ID` and `contract address`, can have collection-specific attributes (subset of the `global_attributes`) related to status updates. For example, the key-value pair

```json
"Chain-8453_0x812d249854EaaD98E50e6a3Fe4033C575925E4aC": [2,4,5]
```

specifies the attributes an NFT collection corresponds to within the application based on the `global_attributes` array. The `8453` segment denotes the `chain ID` of the blockchain network (**Base Mainnet** in this case) where the NFT collection's smart contract is deployed. The hexadecimal sequence `0x0c1AfA2d6D05da2BE8E73CBA2398cf09a530e2B4` is the smart contract address for the NFT collection on that specific chain. The array `[2,4,5]` indicates the indices of the `global_attributes` array this NFT collection corresponds to, signifying that the NFTs of this collection corresponds to the attributes `EXP`, `DEF`, and `SPEED` for their dynamic status updates.

This schema is critical for clarifying the expected update mechanism, promoting a consistent and flexible approach to application-NFT status management, and ensuring interoperability within the multi-chain environment.

## Rationale

### Event-Driven Mechanism

The decision to implement an event-driven mechanism for signaling status changes of an NFT is rooted in the pursuit of efficiency, scalability, and interoperability across different applications. Traditional methods of updating NFT metadata or attributes directly on-chain not only are costly in terms of transaction fees but also introduce challenges in terms of scalability, especially for applications that require frequent status updates. By leveraging on-chain events to represent status changes, this approach minimizes the need for direct modifications to the NFT's on-chain metadata, significantly reducing transaction costs and the burden on the blockchain.

Furthermore, the event-driven architecture enhances the flexibility and interoperability of NFTs. It allows for the dynamic representation of an NFT's status across multiple applications without altering its inherent properties. This mechanism supports a broad range of use cases, enabling NFTs to serve different roles and functions in various applications. For example, an NFT could represent a character with evolving attributes in a game, or act as a ticket with changing access levels in a decentralized event platform. The adoption of an event-driven model thus represents a strategic move towards a more vibrant, functional, and interconnected ecosystem for NFTs.

### Update Module

The introduction of the Update Module within the Event-Driven NFT Utilities Standard is a strategic choice aimed at maximizing flexibility and specificity in how NFT statuses are updated across applications. This modular approach allows each application to define its own unique set of rules and mechanisms for status updates, encapsulated within a URL pointing to decentralized storage. This design decision was made to ensure that updates are both transparent and verifiable by the community, adhering to the decentralized ethos of the blockchain space. Choosing decentralized storage for hosting the Update Module ensures that the instructions for status changes are immutable and accessible, fostering trust in the update mechanism. It also provides a scalable solution that can accommodate the diverse needs of different applications without congesting the blockchain with extensive data. This method avoids the limitations of a one-size-fits-all approach, enabling applications to tailor the update logic to their specific requirements and dynamics. The Update Module thereby plays a critical role in the standard, ensuring that NFT status updates are conducted in a consistent, secure, and application-specific manner.

Additionally, for those seeking a more formalized method to implement the update module, `ERC-5185` offers a structured strategy that enables controlled NFT status updates through specific formulas. This method ensures deterministic and limited updates, which are verifiable through on-chain events, thereby making it suitable for applications that desire a methodical approach to NFT status changes. While `ERC-5185` provides an option for scenarios that demand a defined and deterministic update process, this EIP fosters dynamic and application-specific updates for NFTs. Application owners are RECOMMENDED to consider `ERC-5185` for implementing a structured update module.

### Storage Module

The Event-Driven NFT Utilities Standard is designed to be agnostic regarding the choice of decentralized storage platforms, thereby granting developers the autonomy to select the one that best suits their application's requirements. Supported platforms include, but are not limited to, Filecoin, Arweave, ETH Storage, BNB Greenfield, and others that might offer similar decentralized storage solutions. This flexibility ensures that the storage of update modules and related data can be tailored to the specific security, redundancy, and accessibility needs of each application.

### Synergies with Other EIPs

This EIP is strategically crafted to complement and enhance existing EIPs, thereby boosting its utility and fostering greater interoperability across the Ethereum ecosystem. With a focus on a generalized approach, we promote the integration with and adaptation to a wide range of applications: games, social media, event platforms, and beyond. We intend for this EIP to serve as a versatile foundational layer, enabling seamless interaction with other EIPs, such as `EIP-6551`. By highlighting the potential for cooperation and mutual enhancement among EIPs, we demonstrate our dedication to nurturing a unified and expansive ecosystem that capitalizes on the collective progress and innovation within Ethereum.

## Backwards Compatibility

This proposal seeks to be maximally backward-compatible with existing NFT collections. As such, it does not extend the [ERC-721](./erc-721.md) or [ERC-1155](./erc-1155.md) standard.

## Reference Implementation

A fundamental implementation of the EventDrivenUtilities Standard:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IEventDrivenUtilities.sol";

/**
* @title EventDrivenUtilities
* A fundamental implementation of the Event-Driven NFT Utilities Standard.
*/
contract EventDrivenUtilities is IEventDrivenUtilities {
/*
* STATE VARIABLES
*/

// appId iterator
uint256 private _appIdIterator = 0;

// appId => Application owner address
mapping(uint256 => address) public appOwner;
// appId => Application information URL
mapping(uint256 => string) public appInfo;
// appId => Application update module URL
mapping(uint256 => string) public appUpdateModule;
// appId => NFT collection address array
mapping(uint256 => address[]) public appRegisteredCollections;
// appId => NFT collection address => bool
mapping(uint256 => mapping(address => bool)) public appIsCollectionRegistered;

/*
* MODIFIERS
*/

// Modifier that checks if the caller is the owner of the application
modifier onlyAppOwner(uint256 appId) {
require(appOwner[appId] == msg.sender, "Caller is not the owner of this application.");
_;
}

/*
* FUNCTIONS
*/

/**
* @notice Registers an application and assigns a unique appId to it.
* @dev appId can be non-sequential through a deterministic collision-free hash to keep its uniqueness.
* We assign the _appIdIterator here as the appId for simplicity.
* @param appInfoUrl : URL containing information about the app.
* @return newAppId : The assigned appId.
*/
function registerApp(string calldata appInfoUrl) external returns (uint256 newAppId) {
newAppId = ++_appIdIterator;
appOwner[newAppId] = msg.sender;
appInfo[newAppId] = appInfoUrl;
emit AppRegistered(newAppId, msg.sender, appInfoUrl);
}

/**
* @notice Registers an array of NFT collection(s) for an application.
* @param appId : The unique ID of the application.
* @param chainIds : An array of chain ID(s) of the NFT collection(s).
* @param collectionAddrs : An array of contract address(s) of the NFT collection(s).
* @dev May return the number of NFT collections registered.
*/
function registerCollections(uint256 appId, uint256[] calldata chainIds, address[] calldata collectionAddrs)
external
onlyAppOwner(appId)
{
require(chainIds.length == collectionAddrs.length, "Array lengths of Chain ID and collection address mismatch.");
// Register the array of NFT collection(s) to the application one-by-one
for (uint256 i = 0; i < collectionAddrs.length; i++) {
if (!appIsCollectionRegistered[appId][collectionAddrs[i]]) {
appRegisteredCollections[appId].push(collectionAddrs[i]);
appIsCollectionRegistered[appId][collectionAddrs[i]] = true;
emit CollectionRegistered(appId, chainIds[i], collectionAddrs[i]);
}
}
}

/**
* @dev transferAppOwner() and changeAppInfo() can be implemented in a similar way with their events
* @notice Sets the update module for an application.
* @param appId : The unique ID of the application.
* @param updateModuleUrl : URL containing information about the update module.
*/
function setAppUpdateModule(uint256 appId, string calldata updateModuleUrl) external onlyAppOwner(appId) {
appUpdateModule[appId] = updateModuleUrl;
emit UpdateModuleSet(appId, updateModuleUrl);
}

/**
* @notice Emits an update event for an application.
* @param appId : The unique ID of the application.
* @param updateUrl : The URL of the update information.
*/
function updateAppStatus(uint256 appId, string calldata updateUrl) external onlyAppOwner(appId) {
require(bytes(appUpdateModule[appId]).length > 0, "Update module of the application has not been set.");
emit AppStatusUpdated(appId, updateUrl);
}
}
```

## Security Considerations

Needs discussion.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
Binary file added assets/erc-7672/diagram.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.