Abstract
This ERC defines a standard interface for associating documents with a token contract and for notifying off-chain systems when those documents change. Documents can represent legal agreements, offering materials, disclosures, or other issuer-provided references needed for security token operations.
Motivation
Security tokens commonly represent assets with legal rights and obligations that depend on external documents. Wallets, exchanges, custodians, and compliance tools need a predictable way to discover those documents and track updates.
Without a standard, each implementation exposes different storage and retrieval methods, increasing integration cost and operational risk. A common interface allows ecosystem participants to read and monitor document metadata consistently.
Within security token frameworks, the document management component highlights that security tokens usually have associated documentation such as offering documents and legend details. The ability to set, remove, and retrieve these documents, with events emitted on those actions, allows investors and integrators to remain up to date.
This ERC intentionally does not define an on-chain mechanism for investors to attest they have read or agreed to any document.
Although originally designed for security tokens built on ERC-20 as part of a broader real-world asset token suite, this interface is not restricted to that context. It can be adopted by any token standard, including ERC-721 non-fungible tokens and ERC-1155 multi-tokens, as well as by decentralized applications, vaults, and any other on-chain product that requires structured document management.
Historically, this proposal was authored as part of a broader security token standards suite that had not yet been merged in this repository when this proposal was added.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", and "MAY" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Implementations MUST support querying and subscribing to updates on any relevant documentation for the security.
A document entry is identified by a name (bytes32) and stores:
- A URI (
string) pointing to the document location. - A content hash (
bytes32) for integrity checks. - A last-modified timestamp (
uint256) set when the entry is written.
Interface
/// @title IERC1643 Document Management
interface IERC1643 {
/// @notice Emitted when a document is created or updated.
/// @param name Identifier of the document.
/// @param uri Document location.
/// @param documentHash Hash of the document contents.
event DocumentUpdated(bytes32 indexed name, string uri, bytes32 documentHash);
/// @notice Emitted when a document is removed.
/// @param name Identifier of the document.
/// @param uri Document location at the time of removal.
/// @param documentHash Hash of the document contents at the time of removal.
event DocumentRemoved(bytes32 indexed name, string uri, bytes32 documentHash);
/// @notice Reverts when `setDocument` is called with `name == bytes32(0)`.
error ERC1643InvalidName();
/// @notice Reverts when `removeDocument` is called for a missing document.
error ERC1643MissingDocument();
/// @notice Creates or updates a document entry.
/// @dev MUST emit `DocumentUpdated` on success.
/// @param name Identifier of the document.
/// @param uri Document location.
/// @param documentHash Hash of the document contents.
function setDocument(bytes32 name, string calldata uri, bytes32 documentHash) external;
/// @notice Removes an existing document entry.
/// @dev MUST emit `DocumentRemoved` on success.
/// @param name Identifier of the document to remove.
function removeDocument(bytes32 name) external;
/// @notice Returns metadata for a document identified by `name`.
/// @param name Identifier of the document.
/// @return uri Document location.
/// @return documentHash Hash of the document contents.
/// @return lastModified Last update timestamp.
function getDocument(bytes32 name)
external
view
returns (string memory uri, bytes32 documentHash, uint256 lastModified);
/// @notice Returns all document names currently tracked by the contract.
/// @return documentNames Names of all documents that are currently set.
function getAllDocuments() external view returns (bytes32[] memory documentNames);
}Interface Detection (ERC-165)
Implementations SHOULD support ERC-165 interface detection.
When ERC-165 is implemented, supportsInterface SHOULD return true for type(IERC1643).interfaceId and for the ERC-165 interface id.
Function Requirements
-
getDocument:- MUST return the latest values for the provided document name.
- MUST return empty values when the entry does not exist (
"",bytes32(0),0). - MUST NOT revert solely because the entry does not exist.
- Implementations SHOULD ensure that a stored entry always has a non-zero
lastModified, so thatlastModified == 0identifies an absent entry. SinceurianddocumentHashMAY both be empty for a stored document, they cannot distinguish an absent entry from one stored with empty metadata, andlastModifiedis the only value that can. Integrators should treat this as advisory rather than guaranteed when interacting with deployments predating this guidance.
-
setDocument:- MUST create a new entry when
nameis not present. - MUST overwrite the existing entry when
namealready exists. - MUST update the stored last-modified timestamp.
- MUST emit
DocumentUpdatedafter state changes. - MUST revert if the update cannot be persisted.
- SHOULD revert when
name == bytes32(0)to avoid ambiguous/default-key usage. urianddocumentHashMAY be empty (""andbytes32(0)), depending on issuer workflow and document lifecycle stage.- Implementations MAY decide to reject empty
uriand/or emptydocumentHashbased on policy requirements. - Implementations SHOULD use the custom error defined in the interface (
ERC1643InvalidName()) when rejectingname == bytes32(0). - Implementations MAY use different error names/signatures than those shown in this specification.
- MUST create a new entry when
-
removeDocument:- MUST remove the entry identified by
name. - MUST emit
DocumentRemovedwith the removed metadata. - MUST revert if removal cannot be completed.
- Implementations SHOULD use the custom error defined in the interface (
ERC1643MissingDocument()) when the named document does not exist. - Implementations MAY use different error names/signatures than those shown in this specification.
- MUST remove the entry identified by
-
getAllDocuments:- MUST include every document name added by
setDocumentand not removed byremoveDocument. - MUST NOT include removed document names.
- The order of the returned names is unspecified. Removing a document MAY change the position of unrelated names, so consumers MUST NOT rely on a stable ordering, and MUST NOT treat a change in position as a change to a document.
- MUST include every document name added by
Rationale
- The standard uses
bytes32names to keep keys compact and deterministic, while leaving naming conventions to implementations. A URI-based pointer is used instead of on-chain document storage to avoid high gas costs and to support existing off-chain document systems. - Including a document hash enables clients to verify that fetched off-chain content matches issuer-published metadata.
- Emitting update and removal events supports indexing and near-real-time monitoring without repeated full-state polling.
- While a human-readable document title cannot always be represented directly in
bytes32without hashing or canonicalization,bytes32remains practical for on-chain identifiers because fixed-size values can be compared directly (a == b). By contrast,stringcomparisons generally require hashing (for example,keccak256(bytes(s))), which increases contract code size and gas usage when repeated comparisons are needed on-chain, such as locating and removing a document name from an array.
Backwards Compatibility
This ERC is additive and does not alter base token transfer semantics. It can be implemented alongside existing token standards and permissioning systems without changing their core behavior.
Test Cases
Implementations should verify at least the following:
- Adding a new document and reading it through
getDocument. - Reading a name that was never set, confirming
getDocumentreturns empty values ("",bytes32(0),0) and does not revert. - Updating an existing document and validating changed URI/hash/timestamp.
- Removing a document and ensuring it is no longer returned by
getAllDocuments. - Emission of
DocumentUpdatedon create/update andDocumentRemovedon delete. - Enumeration consistency after multiple add/update/remove operations.
Reference Implementation
The interface is provided in the reference interface. A reusable abstract module implementing the full interface is provided in the reference module.
Example integrations attaching the module to ERC-20 and ERC-721 tokens are provided in the ERC-20 example and the ERC-721 example.
These examples use the OpenZeppelin library and restrict document mutation to the contract owner. They are provided for educational purposes only and have not been audited.
The module maintains:
- Mapping from
bytes32name to document metadata. - Array/set for enumeration of active names.
- Index tracking to support O(1) removals from the enumeration set.
Security Considerations
- Document URIs may reference mutable off-chain content. Consumers are strongly encouraged to verify content using the published
documentHashand trusted retrieval channels. - Implementations should protect
setDocumentandremoveDocumentwith appropriate authorization, otherwise unauthorized actors can modify legal or operational references. - Applications should treat event streams as advisory and reconcile against on-chain state when correctness is critical.
getAllDocumentsreturns the entire set of names in a single call, and its response grows with the number of stored documents. It is the only enumeration path defined here, so a contract holding a large document set may produce a call that exceeds the gas limit aneth_callprovider applies, leaving no standard way to enumerate. Implementations expecting large sets should consider exposing an additional paginated accessor alongside this interface.- Document names may not always fit cleanly into
bytes32, especially for long legal titles. Implementations should avoid lossy truncation of human-readable names; using a deterministic hash-based identifier (for example, the document content hash or a hash of a canonical full title) as thebytes32name is a safer alternative. - The custom errors
ERC1643InvalidName()andERC1643MissingDocument()are defined in this interface but were absent from the earlier draft text of this proposal. Older implementations may not define these errors and may instead revert with strings or implementation-specific error patterns. Integrators should not assume all ERC-1643 contracts expose identical revert data. - ERC-165 interface detection was also not part of the earlier ERC-1643 draft text. Older implementations may not expose
supportsInterfacefor ERC-165 orIERC1643, so integrators should treat ERC-165 support as optional when interacting with legacy deployments.
Copyright
Copyright and related rights waived via CC0.
