EIP.tools

EIP.tools

⚠️ DraftStandards Track: ERC

ERC-8035: MultiTrust Credential (MTC)

Non-transferable reputation credentials with metrics, events, and policy masks, keeping Personally Identifiable Information (PII) off-chain.

Authors
Created2025-09-19
Discussion Linkhttps://ethereum-magicians.org/t/erc-8035-multitrust-credential-mtc-core/25526
Requires
Referenced by

Markdown

https://raw.githubusercontent.com/YutaHoshino/ERCs...

EIP-GPT summary

Contents
AbstractMotivationSpecificationERC-165 Compliance (normative)TermsEvents (normative)Data Structures (normative ABI)Compare Mask Domain (normative)Interface (normative)Revocation Semantics (normative)Non-Transferability & One-Token-Per-Subject (normative)State-Change Authorization (normative)Function Behavior & Failure Conditions (normative, minimum set)Policy Mask (informative)Expiration (optional, normative)Access Control (normative)Token Transferability (normative)On-chain Data Minimization (normative)Replay and Substitution Resistance (normative)Governance and Change Control (normative)RationaleBackwards CompatibilitySecurity ConsiderationsCopyright

Abstract

A MultiTrustCredential (MTC) is an on-chain credential record bound to an address, represented as a non-transferable token, that allows authorized issuers to publish and update typed metrics (plain values and/or cryptographic commitments) used to express reputation, attestations, and policy eligibility with optional zero-knowledge presentations.

This proposal specifies a minimal, implementation-agnostic ERC interface matching the MultiTrustCredential reference. It standardizes schema registration, role-gated mint/update/revoke, slashing, and validity reads for verifiable credentials (VC) aligned reputation metrics, while keeping personally identifiable information (PII) off-chain.

Motivation

Applications need portable, privacy-preserving ways to check eligibility, reputation, or compliance across dApps and chains. Current solutions rely on bespoke contracts or token-based SBTs, which limit interoperability and indexing. MTC defines canonical events, read APIs, and policy masks so wallets, SDKs, and indexers can integrate reputation data with stable semantics.

Specification

The key words “MUST”, “MUST NOT”, “SHOULD”, and “MAY” are to be interpreted as described in RFC 2119 and RFC 8174.

ERC-165 Compliance (normative)

Implementations of this standard MUST implement ERC-165 and report the interface id for IMultiTrustCredentialCore. The interface id is the XOR of all function selectors defined in the normative interface. Implementations MUST NOT change function signatures in a way that alters the interface id.

Terms

  • Subject: Address whose reputation is recorded (one credential token per subject).
  • Metric Schema: Typed identifier (bytes32) describing a reputation dimension.
  • Mask: Bitmask policy that constrains which comparison operators are allowed (e.g., greater-than, less-than, equal; including their “or-equal” variants).
  • Leaf Commitment: On-chain commitment (e.g., Merkle/poseidon/keccak) to off-chain VC payloads.

Events (normative)

event MetricRegistered(bytes32 indexed id, string label, bytes32 role, uint8 mask);
event MetricUpdated(uint256 indexed tokenId, bytes32 indexed metricId, uint32 newValue, uint256 leafFull);
event MetricRevoked(uint256 indexed tokenId, bytes32 indexed metricId, uint32 prevValue, uint256 prevLeaf);
event Slash(uint256 indexed tokenId, bytes32 indexed metricId, uint32 penalty);
event CompareMaskChanged(bytes32 indexed id, uint8 oldMask, uint8 newMask, address indexed editor);
/// @dev OPTIONAL: Implementations that integrate a ZK verifier MAY emit this.
event VerifierSet(address verifier);
event MaskFrozenSet(bytes32 id, bool frozen);

Note: The event parameter name role corresponds to the roleName argument in registerMetric.

Data Structures (normative ABI)

/**
* @dev Stored metric (per tokenId, metricId).
* - `value`     : current numeric value (or placeholder if commitment-only)
* - `leafFull`  : anchor/root commitment used by verification (e.g., Merkle root / circuit anchor; circuit-dependent)
* - `timestamp` : last update time (seconds)
* - `expiresAt` : deadline of this metric. 0 indicates no expiration date.
*/
struct Metric { uint32 value; uint256 leafFull; uint32 timestamp; uint32 expiresAt; }

/**
* @dev Input for single mint.
* - `uri` is set as tokenURI on first mint for the address.
*/
struct MetricInput  { bytes32 metricId; uint32 value; uint256 leafFull; string uri; uint32 expiresAt; }

/**
* @dev Input for an update.
*/
struct MetricUpdate { bytes32 metricId; uint32 newValue; uint256 leafFull; uint32 expiresAt; }

/**
* @dev Batch mint item; creates token if absent and sets tokenURI on first write.
*/
struct MintItem { address to; bytes32 metricId; uint32 value; uint256 leafFull; string uri; uint32 expiresAt; }

/**
* @dev Batch update item for an existing token.
*/
struct UpdateItem { uint256 tokenId; bytes32 metricId; uint32 newValue; uint256 leafFull; uint32 expiresAt; }

Compare Mask Domain (normative)

Valid mask values are limited to the bitwise subset of { GT = 1, LT = 2, EQ = 4 } (where the 3 least-significant bits represent {GT, LT, EQ} in that order).

Examples:

  • 0b000 (= 0): no comparison operators allowed (KYC-only / no numeric comparison).
  • 0b110 (= 6): {LT | EQ} allowed (≤).
  • 0b111 (= 7): {GT | LT | EQ} allowed (any comparison: >, <, = / i.e., no restriction).

Implementations MUST revert if a mask contains undefined bits. After setMaskFrozen(id, true) for a given id, subsequent setCompareMask(id, …) MUST revert.

Interface (normative)

pragma solidity ^0.8.22;

interface IMultiTrustCredentialCore {
    /* Schema & Policy */
    function registerMetric(
        bytes32 id,
        string  calldata label,
        bytes32 roleName,
        bool    commitment,
        uint8   mask
    ) external;

    function setCompareMask(bytes32 id, uint8 mask) external;
    function setMaskFrozen(bytes32 id, bool frozen) external;

    /* Mint & Update */
    function mint(address to, MetricInput calldata data) external;
    function mintBatch(MintItem[] calldata arr) external;
    function updateMetric(uint256 tokenId, MetricUpdate calldata upd) external;
    function updateMetricBatch(UpdateItem[] calldata arr) external;
    function revokeMetric(uint256 tokenId, bytes32 metricId) external;
    function slash(address offender, bytes32 metricId, uint32 penalty) external;

    /* Read API */
    function getMetric(uint256 tokenId, bytes32 metricId)
        external view returns (uint32 value, uint256 leafFull, uint32 timestamp);

    function tokenIdOf(address subject) external pure returns (uint256);
}

Revocation Semantics (normative)

After a metric is revoked for (tokenId, metricId), getMetric(tokenId, metricId) MUST revert.

Non-Transferability & One-Token-Per-Subject (normative)

Implementations MUST enforce non-transferability of the credential token. There MUST NOT exist more than one credential token per subject. If a deterministic mapping is used, tokenId SHOULD equal uint256(uint160(subject)), and tokenIdOf(subject) MUST return that value.

State-Change Authorization (normative)

All state-changing functions (registerMetric, setCompareMask, setMaskFrozen, mint, mintBatch, updateMetric, updateMetricBatch, revokeMetric, slash) MUST be restricted by implementation-defined roles/policies.

Function Behavior & Failure Conditions (normative, minimum set)

  • registerMetric MUST revert if id is already registered (unless the implementation explicitly supports updates) and MUST revert if mask contains undefined bits.
  • setCompareMask MUST revert if id is unknown or if mask contains undefined bits.
  • setMaskFrozen(id, true) freezes the policy for id; subsequent setCompareMask(id, …) MUST revert.
  • mint MUST revert if a credential token already exists for to, or if data.metricId is not registered, or if the caller lacks the required role.
  • updateMetric / updateMetricBatch MUST revert if the metric schema is not registered, or if the caller lacks the required role. If the implementation supports expiration, it MUST apply the rules described in “Expiration (optional, normative)” consistently.
  • revokeMetric MUST revert if the metric is not present or if the caller lacks the required role.
  • slash(offender, …) MUST target the existing credential holder; implementations SHOULD derive tokenId via tokenIdOf(offender) and MUST revert if no token exists for offender.

Policy Mask (informative)

/**
 * @dev Bit mask for allowed comparison operators in zk checks.
 * GT=1, LT=2, EQ=4 .. combinations allowed (e.g., 1|4).
 */
library CompareMask {
    // Bit flags (base)
    uint16 internal constant GT  = 1 << 0; // 0b0001
    uint16 internal constant LT  = 1 << 1; // 0b0010
    uint16 internal constant EQ  = 1 << 2; // 0b0100
    // Aliases / composites
    uint16 internal constant NONE = 0;           // KYC-only (no compare)
    uint16 internal constant NE   = GT | LT;     // not equal
    uint16 internal constant GTE  = GT | EQ;
    uint16 internal constant LTE  = LT | EQ;
    uint16 internal constant ALL  = GT | LT | EQ;
}

Expiration (optional, normative)

The Metric.expiresAt field is an optional expiry timestamp expressed in seconds since Unix epoch.

If an implementation supports expiration:

  • it MUST interpret expiresAt == 0 as "no expiration".
  • if expiresAt != 0 and block.timestamp > expiresAt, the metric is expired.
  • for expired metrics, getMetric(tokenId, metricId) MUST revert. Note: Implementations that want clients to be able to observe expiresAt on-chain SHOULD provide an auxiliary read method or emit events that include expiresAt, but such extensions are out of scope for this core interface.

Access Control (normative)

Implementations MUST restrict metric write operations (mint/update/revoke/slash) according to the per-metric role identifier (roleName) and MUST restrict administrative operations (e.g., schema registration, policy/mask updates) to authorized roles.

Token Transferability (normative)

Implementations MUST prevent transfers of credential tokens between non-zero addresses. Minting and burning MAY be supported.

On-chain Data Minimization (normative)

Implementations MUST NOT store personally identifiable information (PII) on-chain. Commitment fields (e.g., leafFull) MAY commit to normalized off-chain payloads; if used, the commitment function SHOULD be collision-resistant.

Replay and Substitution Resistance (normative)

Implementations SHOULD bind metric updates to (tokenId, metricId) and SHOULD prevent replay of stale or duplicated commitments/digests according to the implementation’s threat model. Clients SHOULD validate consistency between emitted events and state reads.

Governance and Change Control (normative)

If implementations provide mutable policy parameters (e.g., CompareMask, verifier addresses), they SHOULD support governance controls (roles, timelocks, or equivalent). Implementations MAY provide a “freeze” mechanism for policy parameters after audits.

Rationale

  • Event stability: Canonical events enable uniform indexing across chains.
  • Struct-based ABI: Minimizes app/SDK glue and preserves upgrade flexibility.
  • Mask governance: Freezing masks allows ossifying comparison policies post-audit.

Backwards Compatibility

MTC does not conflict with ERC-721/ERC-1155 and can co-exist with ERC-4973/ERC-5192 when tokens represent badges. This specification does not define transfer or approval events and should not be confused with token transfer standards.

Security Considerations

Role-gated writes. Restricting metric writers per schema limits unauthorized mint/update/revoke/slash operations. Administrative actions (schema registration and policy changes) are high-impact and should be protected with strong access control.

Non-transferability. Preventing transfers preserves the “one credential per subject address” model and avoids secondary-market abuse. If burning is supported, consider how revocation and re-issuance are handled.

Privacy and data minimization. Storing PII on-chain creates irreversible disclosure risk. Prefer commitments to normalized off-chain payloads and use collision-resistant hashing/commitment schemes.

Replay and substitution. Without freshness controls, proofs/commitments may be reused outside their intended context. Consider binding updates and proofs to domain parameters (e.g., chainId/contract, anchor root, epoch/version) and designing issuer/client checks to detect stale or duplicated artifacts.

Governance. Policy parameters (compare masks, verifiers, predicate profiles) affect security posture. Operationally, teams often gate updates behind governance processes and may freeze parameters once audited.

Copyright and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth