Skip to content

ENSIP-24: Arbitrary Data Resolution

Authors: premm.eth, raffy.eth, clowes.eth

Created: September 25, 2025

Status: draft

Abstract

This ENSIP proposes a new resolver profile for resolving arbitrary bytes data.

Motivation

ENS has seen significant adoption across the blockchain ecosystem, but the current resolver profiles are too restrictive for many emerging applications.

There is a clear need for a richer record type that can store unstructured binary data. This would provide a flexible, gas-efficient mechanism for associating any data with an ENS name. This would enable direct support for resolution of things like:

  • Hashed data commitments for off-chain data verification
  • Interoperable addresses
  • Context data

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

This ENSIP introduces a new interface for the resolution of arbitrary bytes data:

/// @dev Interface selector: `0xecbfada3`
interface IDataResolver {
    /// @notice For a specific `node`, get the data associated with the key, `key`.
    /// @param node The node (namehash) for which data is being fetched.
    /// @param key The key.
    /// @return The associated arbitrary `bytes` data.
    function data(
        bytes32 node,
        string calldata key
    ) external view returns (bytes memory);
}

The ERC-165 identifier for this interface is 0xecbfada3.

The key argument is a string type for simplicity and clarity.

Resolvers implementing this ENSIP MUST emit the following event when data is changed:

/// @notice For a specific `node`, the data associated with a `key` has changed.
event DataChanged(
    bytes32 indexed node, 
    string indexed indexedKey,
    string key, 
    bytes indexed indexedData
);

Resolvers MAY implement the following interface

/// @dev Interface selector: `0x29fb1892`
interface ISupportedDataKeys {
    /// @notice For a specific `node`, get an array of supported data keys.
    /// @param node The node (namehash).
    /// @return The keys for which we have associated data.
    function supportedDataKeys(bytes32 node) external view returns (string[] memory);
}

The ERC-165 identifier for this interface is 0x29fb1892.

If implemented this function MUST return an array of string keys which the resolver MAY store data for.

Rationale

Retrofitting the addr(bytes32 node, uint coinType) function defined in ENSIP-9 as a getter for resolving arbitrary bytes was considered, but whilst cool, its usage is hacky and not semantically clear to developers.

We considered simply storing bytes data as a string within text records but this is inefficient, expensive, and impractical.

The DataChanged event emits both the key and the indexedKey. This allows for efficient event filtering whilst immediately exposing useful key data. indexedData is also emitted to allow data integrity checks whilst avoiding chain bloat.

The optional ISupportedDataKeys interface allows for data key discovery. The intended usage of this interface is to enable data discovery for small, known, and bounded sets of keys without the need for external dependencies.

Example

Below is an illustrative snippet that shows how to set and retrieve arbitrary data:

pragma solidity ^0.8.25;
 
interface IDataResolver {
    event DataChanged(
        bytes32 indexed node, 
        string indexed indexedKey,
        string key, 
        bytes indexed indexedData
    );
 
    function data(
        bytes32 node,
        string calldata key
    ) external view returns (bytes memory);
}
 
contract Resolver is IDataResolver {
    mapping(bytes32 node => mapping(string key => bytes data)) private dataStore;
    
    function data(bytes32 node, string calldata key) external view returns (bytes memory) {
        return dataStore[node][key];
    }
    
    // setData function can be used to set the data (not shown)
}

Set and retrieve arbitrary data:

// Pseudo javascript example
 
// Store arbitrary data
const tx = await resolver.setData(node, "agent-context", "0x0001ABCD...");
await tx.wait();
 
// Retrieve arbitrary data
const result = await resolver.data(node, "agent-context");

Backwards Compatibility

This proposal introduces a new resolver profile and does not affect existing ENS functionality. It introduces no breaking changes.

Security Considerations

None.

Copyright

Copyright and related rights waived via CC0.