ERC-5050: Interactive NFTs with Modular Environments
Action messaging and discovery protocol for interactions on and between NFTs
Abstract
This standard defines a broadly applicable action messaging protocol for the transmission of user-initiated actions between tokens. Modular statefulness is achieved with optional state controller contracts (i.e. environments) that manage shared state, and provide arbitration and settlement of the action process.
Motivation
Tokenized item standards such as EIP-721 and EIP-1155 serve as the objects of the Ethereum computing environment. A growing number of projects are seeking to build interactivity and "digital physics" into NFTs, especially in the contexts of gaming and decentralized identity. A standard action messaging protocol will allow this physics layer to be developed in the same open, Ethereum-native way as the objects they operate on.
The messaging protocol outlined defines how an action is initiated and transmitted between tokens and (optional) shared state environments. It is paired with a common interface for defining functionality that allows off-chain services to aggregate and query supported contracts for functionality and interoperability; creating a discoverable, human-readable network of interactive token contracts. Not only can contracts that implement this standard be automatically discovered by such services, their policies for interaction can be as well. This allows clients to easily discover compatible senders and receivers, and allowed actions.
Aggregators can also parse action event logs to derive analytics on new action types, trending/popular/new interactive contracts, which token and state contract pairs users are likely to interact with, and other discovery tools to facilitate interaction.
Benefits
- Make interactive token contracts discoverable and usable by applications
- Create a decentralized "digital physics" layer for gaming and other applications
- Provide developers a simple solution with viable validity guarantees to make dynamic NFTs and other tokens
- Allow for generalized action bridges to transmit actions between chains (enabling actions on L1 assets to be saved to L2s, L1 assets to interact with L2 assets, and L2 actions to be "rolled-up"/finalized on L1).
Specification
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.
Smart contracts implementing this EIP standard MUST implement the EIP-165 supportsInterface function and MUST return the constant value true
if the IERC5050Sender
interface ID 0xc8c6c9f3
and/or the IERC5050Receiver
interface ID 0x1a3f02f4
is passed through the interfaceID
argument (depending on which interface(s) the contract implements).
Action Naming
Actions SHOULD use dot-separation for namespacing (e.g. "spells.cast"
specifies the "cast"
action with namespace "spells"
), and arrow-separation for sequence specification (e.g. "settle>build"
indicating "settle"
must be received before "build"
).
How State Contracts Work
Actions do not require that a state contract be used. Actions can be transmitted from one token contract (Object
) to another, or from a user to a single token contract. In these cases, the sending and receiving contracts each control their own state.
State contracts allow arbitrary senders and receivers to share a user-specified state environment. Each Object
MAY define its own action handling, which MAY include reading from the state contract during, but the action MUST be finalized by the state contract. This means the state contract serves as ground truth.
The intended workflow is for state contracts to define stateful game environments, typically with a custom IState
interface for use by other contracts. Objects
register with state contracts to initialize their state. Then, users commit actions using a specific state contract to make things happen in the game.
The modularity of state contracts allows multiple copies of the same or similar "game environment" to be created and swapped in or out by the client. There are many ways this modularity can be used:
- Aggregator services can analyze action events to determine likely state contracts for a given sender/receiver
- Sender/receiver contracts can require a specific state contract
- Sender/receiver contracts can allow any state contract, but set a default. This is important for NFTs that change their render based on state. This default can also be configurable by the token holder.
- State contracts can be bridges to state contracts on another chain, allowing for L1-verification, L2-storage usage pattern (validate action with layer-1 assets, save on l2 where storage is cheaper).
Example
State Contract FightGame
defines a fighting game environment. Token holders call FightGame.register(contract, tokenId)
to randomly initialize their stats (strength/hp/etc.). An account which holds a registered token A of contract Fighters
, calls Fighters.sendAction(AttackAction)
, specifying token A from Fighters
as the sender, token B from Pacifists
contract as the receiver, and FightGame
as the state contract.
The action is passed to token B, which may handle the action in whatever way it wants before passing the action to the FightGame
state contract. The state contract can verify the stored action hash with the Fighters
contract to validate the action is authentic before updating the stats if the tokens, dealing damage to token B.
Tokens A and B may update their metadata based on stats in the FightGame
state contract, or based on their own stored data updated in response to sending/receiving actions.
Extensions
Interactive
Some contracts may have custom user interfaces that facilitate interaction.
Action Proxies
Action proxies can be used to support backwards compatibility with non-upgradeable contracts, and potentially for cross-chain action bridging.
They can be implemented using a modified version of EIP-1820 that allows EIP-173 contract owners to call setManager()
.
Controllable
Users of this standard may want to allow trusted contracts to control the action process to provide security guarantees, and support action bridging. Controllers step through the action chain, calling each contract individually in sequence.
Contracts that support Controllers SHOULD ignore require/revert statements related to action verification, and MUST NOT pass the action to the next contract in the chain.
Metadata Update
Interactive NFTs are likely to update their metadata in response to certain actions and developers MAY want to implement EIP-4906 event emitters.
Rationale
The critical features of this interactive token standard are that it 1) creates a common way to define, advertise, and conduct object interaction, 2) enables optional, brokered statefulness with useful validity assurances at minimum gas overhead, 3) is easy for developers to implement, and 4) is easy for end-users to use.
Action Names & Selectors
Actions are advertised using human-readable strings, and processed using function selectors (bytes4(keccack256(action_key))
). Human-readable strings allow end-users to easily interpret functionality, while function selectors allow efficient comparison operations on arbitrarily long action keys. This scheme also allows for simple namespacing and sequence specification.
Off-chain services can easily convert the strings to bytes4
selector encoding when interacting with contracts implementing this EIP or parsing SendAction
and ActionReceived
event logs.
Validation
Validation of the initiating contract via a hash of the action data was satisfactory to nearly everyone surveyed and was the most gas efficient verification solution explored. We recognize that this solution does not allow the receiving and state contracts to validate the initiating user
account beyond using tx.origin
, which is vulnerable to phishing attacks.
We considered using a signed message to validate user-intiation, but this approach had two major drawbacks:
- UX users would be required to perform two steps to commit each action (sign the message, and send the transaction)
- Gas performing signature verification is computationally expensive
Most importantly, the consensus among the developers surveyed is that strict user validation is not necessary because the concern is only that malicious initiating contracts will phish users to commit actions with the malicious contract's assets. This protocol treats the initiating contract's token as the prime mover, not the user. Anyone can tweet at Bill Gates. Any token can send an action to another token. Which actions are accepted, and how they are handled is left up to the contracts. High-value actions can be reputation-gated via state contracts, or access-gated with allow/disallow-lists. Controllable
contracts can also be used via trusted controllers as an alternative to action chaining.
Alternatives considered: action transmitted as a signed message, action saved to reusable storage slot on initiating contract
State Contracts
Moving state logic into dedicated, parameterized contracts makes state an action primitive and prevents state management from being obscured within the contracts. Specifically, it allows users to decide which "environment" to commit the action in, and allows the initiating and receiving contracts to share state data without requiring them to communicate.
The specifics of state contract interfaces are outside the scope of this standard, and are intended to be purpose-built for unique interactive environments.
Gas and Complexity (regarding action chaining)
Action handling within each contract can be arbitrarily complex, and there is no way to eliminate the possibility that certain contract interactions will run out of gas. However, developers SHOULD make every effort to minimize gas usage in their action handler methods, and avoid the use of for-loops.
Alternatives considered: multi-request action chains that push-pull from one contract to the next.
Backwards Compatibility
Non-upgradeable, already deployed token contracts will not be compatible with this standard unless a proxy registry extension is used.
Reference Implementation
A reference implementation is included in ../assets/eip-5050
with a simple stateless example ExampleToken2Token.sol
, and a stateful example ExampleStateContract.sol
Security Considerations
The core security consideration of this protocol is action validation. Actions are passed from one contract to another, meaning it is not possible for the receiving contract to natively verify that the caller of the initiating contract matches the action.from
address. One of the most important contributions of this protocol is that it provides an alternative to using signed messages, which require users to perform two operations for every action committed.
As discussed in Validation, this is viable because the initiating contract / token is treated as the prime mover, not the user.
Copyright
Copyright and related rights waived via CC0.