Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/src/specs/validateTitle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export const extractTitle = (directPath: string, tree: Parent) => {
(node) => node.tagName === 'h1'
) as Parent[];

if (titleNodes.length > 1)
if (titleNodes.length == 0) {
throw new Error(
'The ENSIP title must be specified as a h1 (#) heading.'
);
} else if (titleNodes.length > 1)
throw new TracedError(
'More then one h1 (#) heading found, please use h2 (##) or h3 (###) headings',
directPath,
Expand Down
152 changes: 152 additions & 0 deletions ensips/24.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
title: Arbitrary Data Resolution
description: A standardized interface for resolving arbitrary bytes data
contributors:
- premm.eth
- raffy.eth
- clowes.eth
ensip:
created: "2025-09-25"
status: draft
---

# ENSIP-24: Arbitrary Data Resolution

## 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](https://creativecommons.org/publicdomain/zero/1.0/).

[ERC-165]: https://eips.ethereum.org/EIPS/eip-165