This ERC defines interfaces for registries that bind token contracts or token IDs to anchor records representing claims about off-chain assets. Each anchor contains separate commitments to a claimed legal basis and supporting evidence. Bindings distinguish whole-contract scope from token-ID scope, enforce registry-scoped exclusivity, and preserve immutable binding history.
Token-side interfaces allow contracts to declare the same registry and anchor, enabling consumers to verify both sides of a binding. A lifecycle interface defines structured metadata, expiry, re-attestation, and permanent deactivation. An optional recovery interface permits disputed bindings to be invalidated without deleting their historical records.
The resulting records provide durable, registry-scoped binding provenance for consumers that require an auditable lifecycle history.
This ERC does not establish the existence, ownership, legal validity, or value of an off-chain asset.
A token can claim in metadata that it represents an off-chain asset, but that claim does not provide a common interface for another contract to determine:
Deployments subject to institutional or regulatory oversight often need a durable answer to which token tuple was recorded against an asset claim, when the binding was established, and whether it remains current. This ERC makes registry-scoped exclusivity and immutable binding history explicit without asserting that the underlying claim is legally valid or factually correct.
Without a mutually queryable structure, the relationship remains assertion-only tokenization: the token can describe an off-chain asset, but an independent consumer cannot verify the claimed token-to-record relationship through a common interface.
Applications consequently rely on implementation-specific metadata and registries. The same asset claim can be represented differently by each issuer, and consumers cannot inspect a binding through a common interface.
This ERC standardizes the structural relationship between a registry anchor and a token contract or token ID. The guarantee is intentionally limited to one registry instance. Registry operators remain responsible for deciding which claims they accept, and consumers remain responsible for deciding which registries they trust.
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.
An anchor is a registry record containing commitments and metadata for an off-chain asset claim.
A contract binding binds an anchor to an entire token contract. This is appropriate when one contract represents one claimed asset or instrument.
A token-ID binding binds an anchor to one token ID within a token contract.
A binding tuple is (token, bindingScope, tokenId).
A valid binding is a recorded binding that has not been invalidated through
IAssetAnchorRegistryRecovery. Binding validity is distinct from lifecycle
activity.
An active anchor is an anchor that has not been permanently deactivated and whose metadata has not expired.
Implementations MUST use the following scope identifiers:
bytes32 constant BINDING_SCOPE_CONTRACT =
keccak256("ERC-8325:BINDING_SCOPE:CONTRACT");
bytes32 constant BINDING_SCOPE_TOKEN_ID =
keccak256("ERC-8325:BINDING_SCOPE:TOKEN_ID");
For BINDING_SCOPE_CONTRACT, tokenId MUST equal 0 as a canonical unused
value.
For BINDING_SCOPE_TOKEN_ID, every uint256 value is valid, including token ID
0. Token ID 0 MUST NOT be interpreted as a contract-binding sentinel.
Registration metadata MUST be ABI encoded as the following ordered tuple:
struct AnchorMetadata {
bytes32 assetClass;
bytes32 jurisdiction;
uint64 attestationDate;
uint64 expiresAt;
bytes uri;
bytes extensions;
}
The canonical encoding is:
abi.encode(
metadata.assetClass,
metadata.jurisdiction,
metadata.attestationDate,
metadata.expiresAt,
metadata.uri,
metadata.extensions
)
assetClass and jurisdiction MUST NOT be bytes32(0).
attestationDate MUST NOT be 0 and MUST NOT be later than
block.timestamp. expiresAt MUST be later than attestationDate and MUST NOT
be earlier than block.timestamp at registration. uri MUST NOT be empty.
extensions MAY be empty.
assetClass SHOULD be a domain-separated identifier from a documented
taxonomy. When an anchor has one primary country jurisdiction, jurisdiction
SHOULD be a domain-separated identifier derived from its uppercase ISO 3166-1
alpha-2 code.
The URI identifies where a consumer can retrieve material corresponding to the anchor commitments. This ERC does not require a particular URI scheme or guarantee availability.
interface IAssetAnchorRegistry {
struct AnchorRecord {
bytes32 anchorId;
bytes32 legalHash;
bytes32 evidenceHash;
address boundToken;
bytes32 bindingScope;
uint256 boundTokenId;
uint64 registeredAt;
bool active;
}
event AnchorRegistered(
bytes32 indexed anchorId,
bytes32 legalHash,
bytes32 evidenceHash
);
event TokenBound(
bytes32 indexed anchorId,
address indexed token,
bytes32 indexed bindingScope,
uint256 tokenId
);
event AnchorDeactivated(bytes32 indexed anchorId, string reason);
event AnchorReattested(
bytes32 indexed anchorId,
uint64 oldExpiresAt,
uint64 newExpiresAt,
uint64 newAttestationDate
);
function registerAnchor(
bytes32 legalHash,
bytes32 evidenceHash,
bytes calldata metadata
) external returns (bytes32 anchorId);
function bindToken(
bytes32 anchorId,
address token,
bytes32 bindingScope,
uint256 tokenId
) external;
function registerAndBind(
bytes32 legalHash,
bytes32 evidenceHash,
bytes calldata metadata,
address token,
bytes32 bindingScope,
uint256 tokenId
) external returns (bytes32 anchorId);
function getAnchor(bytes32 anchorId)
external
view
returns (AnchorRecord memory);
function isBound(bytes32 anchorId) external view returns (bool);
}
registerAnchor and registerAndBind MUST reject bytes32(0) for
legalHash or evidenceHash.
The anchor identifier MUST be derived as:
anchorId = keccak256(abi.encode(legalHash, evidenceHash));
The same (legalHash, evidenceHash) pair therefore produces the same
anchorId within and across implementations of this ERC. A registry MUST
reject an anchorId that it has already registered.
On successful registration, the registry MUST:
anchorId;registeredAt as uint64(block.timestamp);boundToken to address(0);bindingScope to bytes32(0);boundTokenId to 0;active to true; andAnchorRegistered.The mechanism for authorizing registration is implementation-defined. Implementations MUST document their authorization policy.
bindToken MUST reject an unknown anchor, an inactive or expired anchor, a zero
token address, an unsupported binding scope, and an anchor whose binding fields
have already been set.
Before recording a binding, the registry MUST ensure that no valid anchor is already associated with the same binding tuple. The uniqueness key SHOULD be derived as:
keccak256(abi.encode(token, bindingScope, tokenId))
On successful binding, the registry MUST set boundToken, bindingScope, and
boundTokenId and emit TokenBound. These three historical fields MUST NOT be
modified after they are set, including after deactivation or invalidation.
registerAndBind MUST apply the same registration and binding requirements
atomically.
The mechanism for authorizing binding is implementation-defined. It MUST prevent an unrelated caller from binding another registrar's unbound anchor.
If token exposes anchorRegistry(), the returned address MUST equal the
registry performing the binding. A registry MAY bind a token that does not
implement a token-side interface. Such a record is a registry-side binding only
and does not constitute mutually declared binding under this ERC.
getAnchor MUST return the complete stored record and MUST revert for an
unknown anchorId.
isBound MUST return true when boundToken is not address(0), regardless
of lifecycle activity or recovery invalidation. It MUST return false for a
known but unbound anchor and MUST revert for an unknown anchorId.
Every compliant registry MUST implement the lifecycle interface because anchor activity and expiry are part of the common verification model:
interface IAssetAnchorRegistryLifecycle {
function getMetadata(bytes32 anchorId)
external
view
returns (AnchorMetadata memory);
function registeredBy(bytes32 anchorId)
external
view
returns (address);
function isActive(bytes32 anchorId) external view returns (bool);
function deactivateAnchor(
bytes32 anchorId,
string calldata reason
) external;
function reattest(
bytes32 anchorId,
uint64 newExpiresAt,
uint64 newAttestationDate
) external;
}
getMetadata, registeredBy, and isActive MUST revert for an unknown
anchor.
registeredBy MUST return the address that successfully registered the anchor
and MUST NOT change after registration.
isActive MUST return false if AnchorRecord.active is false or if
block.timestamp > expiresAt. An anchor remains active at the exact
expiresAt timestamp.
deactivateAnchor MUST be restricted to authorized callers, set active to
false, and emit AnchorDeactivated. Manual deactivation is permanent and
MUST NOT be reversed by reattest.
reattest MUST be restricted to an authorized caller and MUST reject a
manually deactivated anchor. newAttestationDate MUST NOT be 0, later than
block.timestamp, or earlier than the existing attestationDate.
newExpiresAt MUST be later than block.timestamp, later than
newAttestationDate, and not earlier than the existing expiresAt. Successful
re-attestation MUST emit AnchorReattested.
Binding recovery is OPTIONAL. A registry that permits disputed bindings to be invalidated MUST implement:
interface IAssetAnchorRegistryRecovery {
event TokenBindingInvalidated(
bytes32 indexed anchorId,
address indexed token,
bytes32 indexed bindingScope,
uint256 tokenId,
bytes32 reasonHash
);
function invalidateTokenBinding(
bytes32 anchorId,
bytes32 reasonHash
) external;
function isBindingValid(bytes32 anchorId)
external
view
returns (bool);
}
invalidateTokenBinding MUST be restricted to an authorized recovery role. It
MUST reject an unknown, unbound, or previously invalidated anchor and a zero
reasonHash.
Successful invalidation MUST:
boundToken, bindingScope, and boundTokenId;isBindingValid(anchorId) return false;AnchorDeactivated if the anchor was active; andTokenBindingInvalidated.An implementation MAY free the binding tuple so that a different anchor can be bound to it. If it does so, the invalidated record MUST remain historically queryable and MUST NOT itself be rebound.
isBindingValid MUST return true only when an anchor is bound and has not
been invalidated. It MUST revert for an unknown anchor.
Whole-contract bindings use:
interface IAssetBoundToken is IERC165 {
function anchorId() external view returns (bytes32);
function anchorRegistry() external view returns (address);
function isAnchorActive() external view returns (bool);
}
Token-ID bindings use:
interface IAssetBoundTokenId is IERC165 {
function anchorIdOf(uint256 tokenId)
external
view
returns (bytes32);
function anchorRegistry() external view returns (address);
function isAnchorActiveFor(uint256 tokenId)
external
view
returns (bool);
}
anchorRegistry MUST remain unchanged after deployment.
For IAssetBoundToken, anchorId MUST remain unchanged after deployment.
For IAssetBoundTokenId, anchorIdOf(tokenId) MUST revert when the token ID is
not bound. Once a nonzero anchor is declared for a token ID, that declaration
MUST NOT change.
isAnchorActive and isAnchorActiveFor MUST reflect lifecycle activity from
the declared registry. Consumers MUST query isBindingValid separately when
the registry implements the recovery interface.
Compliant registries MUST implement ERC-165 and return true
for type(IAssetAnchorRegistry).interfaceId.
Compliant registries MUST return true for
type(IAssetAnchorRegistryLifecycle).interfaceId. Registries implementing
recovery MUST also return true for
type(IAssetAnchorRegistryRecovery).interfaceId.
Compliant tokens MUST return true for the applicable token-side interface ID.
ERC-165 detects interface support. It does not prove correct behavior, transfer enforcement, registrar trustworthiness, or the validity of an off-chain claim.
A consumer treating a binding as mutually declared MUST verify all of the following:
getAnchor(anchorId) returns the expected token, scope, and token ID.isBound(anchorId) returns true.isActive(anchorId) returns true.isBindingValid(anchorId) returns true when recovery is implemented.Failure of any applicable check means the consumer MUST NOT treat the binding as a current mutually declared binding.
A separate registry allows the same binding interface to compose with fungible, non-fungible, multi-token, permissioned, and future token standards. It also provides one inspection surface for applications that do not control the token implementation.
The registry does not create global truth. Consumers select which registry operators and authorization policies they trust.
Off-chain asset structures often distinguish the instrument or legal basis from evidence about the referenced asset. Separate commitments preserve that distinction without assigning universal semantics to either document set.
Deployments that do not need the distinction can commit to two separately defined records. A zero hash is not available as an omission sentinel.
Deriving anchorId from both commitments gives implementations the same
identifier for the same pair of bytes and makes duplicate registration within a
registry unambiguous. It does not deduplicate semantically equivalent documents
with different byte representations.
Using token ID 0 to mean whole-contract binding prevents token ID 0 from
being bound as an actual ERC-721 or
ERC-1155 token. Including an explicit scope makes contract
binding and token-ID-zero binding distinct.
A whole-contract token has no meaningful anchorIdOf query, while a collection
with independently anchored token IDs has no meaningful contract-wide
anchorId. Separate interfaces avoid mandatory functions with misleading or
implementation-specific failure behavior.
A binding is historical identity data. Activity is lifecycle status. Expiry or
deactivation can make an anchor operationally inactive without changing which
token was bound to it. Accordingly, isBound is not an activity check.
Permanent bindings are vulnerable to registrar compromise, key loss, and binding-key squatting. Recovery allows an explicitly trusted administrator to invalidate an operational binding while preserving its history. Deployments that prefer absolute immutability can omit the recovery interface.
Recovery introduces substantial administrative trust and is therefore not part of the minimum registry interface.
Allocation constraints, such as ensuring that fractional token supply does not represent more than a defined share of an asset, depend on the economic and legal structure of the instrument. They are meaningful for some fungible fractional claims but not for a single NFT representing one object or for a registry record that does not express ownership percentages.
The registry therefore standardizes binding identity rather than issuance or allocation rules. Tokens and application-specific contracts remain responsible for enforcing any supply, fraction, or entitlement constraints.
Different deployments require different trust models. A registry may be operated by one accountable issuer, a regulated registrar, a multisignature, a DAO, or a permissionless protocol. Requiring one governance model would exclude otherwise interoperable implementations without making their off-chain claims more truthful.
Consumers select which registries and governance policies they trust. The common interface makes those registries technically inspectable; it does not make them equally trustworthy.
Changing a binding's token, scope, or token ID in place would erase the relationship that consumers previously inspected. This ERC therefore preserves those fields after deactivation and recovery invalidation, allowing auditors and other consumers to determine which tuple was recorded and how its status changed over time.
Field immutability does not mean that a binding remains active or valid. Consumers must evaluate lifecycle activity and, when implemented, recovery validity separately. If an implementation releases an invalidated tuple, the invalidated record remains queryable and a replacement is stored as a separate anchor. The guarantee remains scoped to one registry and does not establish global one-token-to-one-asset uniqueness.
ERC-6956 defines ERC-721 tokens bound one-to-one to physical or digital assets, with operations authorized by oracle attestations of control. This ERC is token-standard-neutral and standardizes a registry record binding, not proof-of-control authorization.
ERC-7929 permanently binds one on-chain token to another and mirrors ownership behavior. This ERC binds token contracts or token IDs to records representing off-chain claims and does not define token-to-token ownership hierarchies.
ERC-6065 defines an ERC-721 extension for tokenized real estate with property identifiers and operating-agreement data. This ERC is not limited to real estate or ERC-721 and does not prescribe asset-specific operations.
ERC-3643 and ERC-7943 define token behavior and compliance-related interfaces. They do not define the registry-scoped token-to-anchor relationship specified here. Tokens implementing either can also implement a token-side interface from this ERC.
This ERC introduces new interfaces and does not change existing token standards. Existing token contracts can be recorded in a registry without modification, but this produces only a registry-side binding.
An existing upgradeable token can add the applicable token-side interface. An immutable token that lacks the interface requires a wrapper, adapter, or new deployment to provide mutually declared binding. Consumers should distinguish a registry-only record from a binding confirmed by both registry and token.
Implementations should test at least the following cases:
tokenId == 0;0;A Solidity reference implementation, unit tests, fuzz tests, invariants, and an independent audit are linked from the discussion referenced in the preamble. The implementation is illustrative; its access-control roles and deployment model are not required by this ERC.
The registry proves only that its own state satisfies the specified structural rules. A malicious or compromised registrar can register false claims with validly formed hashes and metadata. Consumers should evaluate the registry's operator, authorization policy, upgrade authority, and legal context.
Neither a hash nor a mutually declared token binding proves that an off-chain asset exists, that documents are authentic, or that a token conveys a legal right. Those determinations require external verification.
Separate registration and binding create an interval in which an unauthorized
caller may attempt to bind an anchor. Implementations should restrict binding to
authorized parties. Registrars should use registerAndBind when atomicity is
required.
A token can return arbitrary values from the token-side interfaces or falsely claim ERC-165 support. Consumers should verify the registry record independently. Registries that call token contracts during binding should use static calls and handle malformed return data and reverts.
A recovery administrator can invalidate legitimate bindings and, when the
implementation releases binding tuples, enable replacement anchors. Production
deployments should protect recovery authority with a multisignature,
governance, timelock, or equivalent controls. Consumers should monitor
TokenBindingInvalidated and AnchorDeactivated events.
Releasing an invalidated tuple does not change an immutable token-side anchor declaration. A replacement can be mutually declared only when the token already declares the replacement anchor or does not expose a token-side interface.
The registry treats legalHash and evidenceHash as opaque commitments. Two
representations of the same document can produce different hashes. Deployments
requiring interoperable document commitments should define a deterministic
normalization and bundle-hashing procedure.
An on-chain commitment is not useful for verification if the committed material cannot be retrieved. The registry does not guarantee URI persistence or data availability. Deployments should use durable storage and availability policies.
Independent registries or deployments on different chains can register claims about the same off-chain asset. This ERC provides no global uniqueness or cross-registry conflict resolution.
Because anchorId does not include a chain identifier or registry address,
consumers requiring globally scoped identity should use
(chainId, registry, anchorId) rather than anchorId alone. A companion system
that accepts only a bare anchorId can otherwise conflate records from
different registry domains.
Lifecycle status depends on block.timestamp, which block producers can vary
within protocol constraints. Applications should avoid relying on second-level
precision around an expiry boundary.
An upgradeable registry can alter binding behavior after consumers begin relying on it. Upgrades must preserve historical binding fields to remain compliant. Consumers should inspect proxy administration and upgrade policies.
Deployments operating under institutional or regulatory controls should document registration and binding authority, protect recovery and upgrade authority with appropriate multisignature, timelock, governance, or equivalent controls, and monitor all binding and lifecycle events.
Such deployments should also define how contested invalidations are reviewed and how committed records are made available to authorized auditors. Conformance with this ERC provides a common technical interface; it does not establish regulatory status, legal sufficiency, or compliance with any jurisdiction's requirements.
Copyright and related rights waived via CC0.