Skip to content

Conversation

@clowestab
Copy link
Contributor

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

It's development has been motivated by the the requirements of ERC-7828: Interoperable Names using ENS.

This specification is simple yet incredibly flexible. It allows for the resolution of anything using an ENS name.

@adraffy
Copy link
Member

adraffy commented Oct 21, 2025

A consequence of this design is that you can implement a resolver that has all of the traditional profiles, but only uses data() underneath.

This greatly simplifies crosschain resolvers, since there's only 1 storage mechanism, and profiles are just last-mile logic for function → key and data → result.

eg.

keyForAddr(coinType) := "addr:${coinType}"
     keyForText(key) := "text:${key}"

          addr(node) := data(node, keyForAddr(60))
addr(node, coinType) := data(node, keyForAddr(coinType)) // ignored: ENSIP-19 default logic
     text(node, key) := data(node, keyForText(key))

          setAddr(node, value) := setData(node, keyForAddr(60), abi.encodePacked(value)
setAddr(node, coinType, value) := setData(node, keyForAddr(coinType), value)
     setText(node, key, value) := setData(node, keyForText(key), bytes(value)

IMO, this reveals there's a further abstraction for resolvers, where we separate the data translation (frontend) from the data storage (backend), but that's another discussion 😄.

@clowestab
Copy link
Contributor Author

A consequence of this design is that you can implement a resolver that has all of the traditional profiles, but only uses data() underneath.

This greatly simplifies crosschain resolvers, since there's only 1 storage mechanism, and profiles are just last-mile logic for function → key and data → result.

eg.

keyForAddr(coinType) := "addr:${coinType}"
     keyForText(key) := "text:${key}"

          addr(node) := data(node, keyForAddr(60))
addr(node, coinType) := data(node, keyForAddr(coinType)) // ignored: ENSIP-19 default logic
     text(node, key) := data(node, keyForText(key))

          setAddr(node, value) := setData(node, keyForAddr(60), abi.encodePacked(value)
setAddr(node, coinType, value) := setData(node, keyForAddr(coinType), value)
     setText(node, key, value) := setData(node, keyForText(key), bytes(value)

IMO, this reveals there's a further abstraction for resolvers, where we separate the data translation (frontend) from the data storage (backend), but that's another discussion 😄.

Agreed. In hindsight, these abstractions would have been implemented from the get go. Stacking complexity is not going to incentivize developers to build on top of ENS.

Copy link
Member

@Arachnid Arachnid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fully supportive of this; a bytes equivalent to the text accessor is a good idea.

supportedDataKeys is also a good idea; perhaps consider adding supportedTextKeys to this standard as optional as well for completeness?

@nxt3d
Copy link

nxt3d commented Oct 24, 2025

This should be updated to:

event DataChanged(
    bytes32 node, 
    string key,
    string indexed keyHash, 
    bytes indexed dataHash
);

Remove: => The keyHash and dataHash values are the keccak256 hashes of the key and the data being set.

@clowestab
Copy link
Contributor Author

supportedDataKeys is also a good idea; perhaps consider adding supportedTextKeys to this standard as optional as well for completeness?

As an extension of ENSIP-5 for text-record discoverability? I think this should be added somewhere, but I don't think this ENSIP is naturally where implementors would look, and thus it might get overlooked.

This should be updated to:

event DataChanged(
    bytes32 node, 
    string key,
    string indexed keyHash, 
    bytes indexed dataHash
);

Remove: => The keyHash and dataHash values are the keccak256 hashes of the key and the data being set.

It was intentionally written as it is to enforce the explicit keccak256 hashing by the user - I don't like Solidity magic.

It also allows for the implementor to calculate the hashes off chain if they like, which could have non-negligible gas savings if storing large bytes at scale.

Copy link
Contributor

@stevieraykatz stevieraykatz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This specification just makes sense and I fully support its inclusion. Instead of attempting to solve each unique new resolution challenge with a unique ensip and resolver profile, I'm a huge fan of this generic storage approach.

The only gap I see is a community-accepted standardization of storage keys. Perhaps this spec is not the place to record the set but some structure should be established for convention's sake. Perhaps a 4byte analog to start?

@clowestab
Copy link
Contributor Author

I've made some small changes based on discussions with @adraffy and @nxt3d.

  • Changes to make the event match that used in ITextResolver
  • Small clarifcations.
  • Some small formatting changes.

@stevieraykatz The Interoperable Addressing standards outline some standard keys for that use case. As more 'standard' keys are defined I do see value in creating a database somewhere that outlines them as well as linking to further explainers on their usage and associated specs.

@adraffy adraffy merged commit b6dfd97 into ensdomains:master Nov 7, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants