ERC-4824 DAO common interface (Common Interfaces for DAOs)
Review · Other · created 2022-02-17
An API standard that lets a DAO expose, through a single daoURI, the standardized locations of its name, members, proposals, activity log, and governance documents. Like tokenURI in ERC-721, it lets any tool read DAO information in the same way.
Why it is needed
Since DAOs (Decentralized Autonomous Organizations) were first mentioned in the Ethereum whitepaper, their definition has remained loose. As a result, a variety of design patterns emerged, but there has been little standardization or interoperability among the frameworks and tools built on top of them. Even basic information, such as who a DAO's members are or what proposals are pending, is published differently by each organization, so discovery services and governance tools must build separate integrations for each DAO. A standard daoURI, similar to tokenURI in ERC-721, would make DAOs easier to find and their contents easier to read. It would allow proposal execution to be simulated in advance and improve interoperability among tools. More consistent data disclosure across the ecosystem is also a prerequisite for future DAO standards.
What to implement
event DAOURIUpdate(address daoAddress, string daoURI);: An event signaling that a DAO's daoURI has changed.function daoURI() external view returns (string memory _daoURI);: Returns a URI pointing to a JSON object that follows the ERC-4824 DAO JSON-LD schema. This JSON is split into four sub-URIs: membersURI, proposalsURI, activityLogURI, and governanceURI.event DAOURIRegistered(address daoAddress);: An event emitted by the indexer contract when it records a DAO's registration.function logRegistration(address daoAddress) external: Can be called by anyone to register a DAO with the indexer. If the target address does not support the IERC4824 interface, it reverts with an ERC4824InterfaceNotSupported error.
In depth
daoURI and the top-level JSON-LD schema
A contract implementing this standard must implement the IERC4824 interface. daoURI() returns a URI pointing to a JSON object that follows the ERC-4824 DAO JSON-LD schema. The top-level JSON includes @context, type, name, description, along with membersURI, proposalsURI, activityLogURI, governanceURI, and contractsURI. governanceURI should typically point to a plain-text file such as .md, while the others should point to JSON following their respective JSON-LD schemas; these JSON documents can be hosted statically or generated dynamically. The content of a sub-level JSON can also be embedded directly within the top-level JSON, in which case the URI suffix should be dropped from the field name (membersURI becomes members). If both the suffixed and unsuffixed fields appear together, the unsuffixed one takes precedence. Conversely, name and description can be replaced with nameURI and descriptionURI, in which case the dereferenced URI must return a JSON-LD object containing an @context field and the original key-value pair. Fields with no value are best omitted rather than set to null.
Direct inheritance and external registry contracts
A DAO can either directly inherit the IERC4824 interface or create a separate external registry contract that follows this standard. Either way, it is best to define an update method with access control logic so that daoURI can be updated efficiently. If an external registry contract is created, it must store the DAO's representative address (usually the address of its main governance contract). Because the standard can be adopted using only an external registry contract without modifying or migrating existing contracts, it can also be applied to DAOs that are already deployed and operating. Existing contracts that do not use the standard are unaffected.
Indexing and precedence rules
When a DAO is created from a 4824-compatible factory, the factory should call the indexer contract during DAO initialization to help with network-wide indexing. If the DAO supports ERC-165, the factory can register it without additional permissions; if not, the factory must first obtain access control permission from the indexer and then directly call logRegistration with the new DAO's address and daoURI. The reference implementation ERC4824Index checks in logRegistration whether the target address supports the IERC4824 interface, and reverts if not. This function can be called by anyone, including the DAO itself. If a daoURI is published both directly on the contract and via a registry factory call, the daoURI and all sub-URIs from the registry factory take precedence, and if there are multiple registrations, the most recent one takes precedence. An on-chain registry contract is considered the most trustworthy way of publishing information, since it most clearly reflects the DAO's active intent.
membersURI: member list
membersURI should point to JSON containing the list of members. Each member is represented by a single id, which can be a CAIP-10 address, a DID address, or another URI identifier. For example, an Ethereum mainnet address would be written as eip155:1:0x1234abcd in CAIP-10, or did:ethr:0x1234abcd as a DID. Membership models vary widely across DAOs. Some, like Gnosis Safe or Tribute, maintain an explicit on-chain set of members. Many others, such as Moloch, Compound, DAOstack, and 1Hive Gardens, base membership on token holding, which requires off-chain indexing to compute the member list. For this reason, the standard requires only an off-chain JSON schema instead of on-chain functionality, favoring flexibility and efficiency, and it also recommends schema extensions for things like active/inactive status or membership tiers.
proposalsURI: proposals and the proposal ID format
proposalsURI should point to JSON containing the list of proposals. For on-chain proposals, the id must take the form CAIP10_ADDRESS + "?proposalId=" + PROPOSAL_COUNTER. PROPOSAL_COUNTER is an arbitrary identifier, such as a uint256 counter or a hash, that is unique within that CAIP-10 address, and this combination makes the proposal ID globally unique. Off-chain proposals can use a similar format, substituting an appropriate URI or URL in place of CAIP10_ADDRESS. Each proposal includes a name, a contentURI that returns the proposal body, a discussionURI that returns a link to discussion such as a forum post or Discord channel, a status field, and a calls array containing execution call data. Because status varies across governance systems and there is no agreed set of values among existing DAOs, it is left as a free text field, and the primary purpose of the calls data is to simulate proposal execution.
activityLogURI and governanceURI
activityLogURI should point to JSON recording interactions between members and proposals. Each activity includes an id, a type, which proposal it relates to (proposal), and which member performed it (member). Examples of activities include drafting and submitting a proposal, voting, and raising objections. governanceURI points not to JSON but typically to a plain text file such as a .md file. Because many membership rights, such as voting rights or the right to ragequit, are realized off-chain through mechanisms like Snapshot votes or Discord access, rather than standardizing or forcing these varied practices on-chain, the standard instead requires publishing documentation that explains what membership means and how proposals work. Services like Etherscan can read this plain text file to help with discovering and understanding a DAO.
contractsURI, spam and impersonation prevention, and organizations that are not DAOs
contractsURI should point to JSON containing the addresses (id), names, and descriptions of contracts belonging to the DAO. This is especially important for DAOs whose governance is spread across multiple contracts or even multiple chains, where several addresses may report the same daoURI. So that indexers can filter out unauthorized addresses impersonating the same daoURI or name, DAOs adopting the standard should publish all relevant contract addresses in contractsURI, including contracts that inherit IERC4824 and any registry contract deployed via a registration factory. Meanwhile, organizations that are not DAOs, or that do not wish to be called DAOs, may also publish a daoURI. In that case, the type field should use a value such as "Organization", "Foundation", "Person", or "Entity" instead of "DAO". Off-chain, aliases of daoURI such as orgURI or entityURI may be used, but when reporting the URI via an on-chain contract or registry, IERC4824's daoURI must be used for indexing purposes.
Why JSON-LD was chosen, and the proposal's current status
JSON-LD was chosen over plain JSON for two reasons: to support representing members through identification methods other than Ethereum addresses, and to remain compatible with future multichain standards. Both cases require context and type information for addresses, which JSON-LD already provides. Considering patterns like subDAOs and DAOs of DAOs seen in large organizations such as Synthetix, as well as L2 and multichain use cases, it is expected that multiple DAOs will point to the same URI and use it as a gateway into data from multiple contracts and services, and JSON-LD makes extending and managing such data easier. This draft was developed at a DAOstar roundtable attended by representatives of all major EVM-based DAO frameworks. However, this proposal is currently in Review status, so its content may still change.
Things to watch out for
- URI update rules are outside the scope of the standard: This standard only defines the interface for the DAO URI; it does not define rules for determining the URI or methods for preparing the data. Implementers must decide how to update data in a way that fits the DAO's governance model and how to keep the data current while minimizing reliance on centralized service providers.
- Caution regarding executable code returned by the URI: Indexers relying on data returned by the URI should be cautious when a DAO returns executable code from the URI. That code may be intended to retrieve up-to-date information about members, proposals, and activity logs, but it could also be used to execute unrelated operations.