This is designed to be a minimum standard to allow innovation in this model while providing a shared framework to help wallets and explorers. I hope future standards will find this one extensible and build for compatibility. I am compiling a list of compatible extensions and plan to keep the list below up to date.
I plan to provide a reference implementation. At this stage I am seeking feedback, especially from those who have experience building these or their tooling.
It might be good to have the default reference implementation not to use slot zero based mapping.
Since if an implementation contract used a mapping in slot zero, you could get a situation where everything works in testing, but has the potential to break in the worst possible way under attack by giving the attacker delegatecall powers.
I’m familiar with pagination as a strategy for serving large arrays. I’d like to avoid it if possible to simplify the API.
The selectors query is currently the least modular one and something I hope improves before the final version of this ERC. Approaches like ERC-8153 facilitate modularity by expecting facets to supply their own selectors.
I also considered a bitmap design but that seems prohibitively large for 4byte.
Are you familiar with some other RPC limits besides gas and timeout? There is an EIP to make memory gas linear instead of quadratic and that’s the main problem justifying pagination as far as I know.
32768 selectors fit in 1 MB. If we switched to a packed design, it would increase this capacity eightfold. If memory gas becomes linear, the main gas hurdle would be SLOAD, encouraging EXTCODECOPY-based solutions not currently possible in Solidity.
This is a valid concern, and the ERC already discusses defensive storage patterns. I was planning to demonstrate the first such pattern (“single shared proxy storage layout”) in the reference implementation, but you have changed my mind. Storage namespaces are more foolproof.
Have you considered using sequential indexes to map to selectors? The idea is that we have a structure uint32 index => bytes4 selector next to bytes4 selector => (address facet, uint32 index). We could add a state variable to track the largest value of the index or use a binary search method. This allows external indexers to build different analysis strategies while maintaining the elegance of the function interface.
This is a good idea but I don’t want to prescribe a particular data structure because I want to encompass many possible implementations. An index (and also pagination) would force indexing onto systems that are better without indexes, such as ERC-8153. ERC-8153 does pagination by facet, which is a great design. They would be able to implement selectors() with a combination of facetAddresses() and facetFunctionSelectors(address). If they had to support selector(uint32), it would be a linear operation calling exportSelectors(). For ERC-8153 to optimize selector(uint32), they would have to maintain a separate index just for this method.
I have pushed a reference implementation for the ERC. It uses a solady-style namespace storage layout and manages the selectors in a list. It’s not especially performant because my main goal was to minimize assembly. I’ll iterate on it with some documentation on this branch and keep them synced.