This standard defines an on-chain framework for decentralized identity (DID) management, reputation scoring, and
threat intelligence sharing for autonomous AI agents operating across Ethereum protocols. The specification defines
interfaces for agent identity registration, multi-tier verification status, reputation tracking, and collaborative
threat reporting.
Motivation
Autonomous AI agents increasingly interact with smart contracts and decentralized finance (DeFi) protocols. This
presents specific security and coordination challenges:
Identity Fragmentation: AI agents lack standardized identity systems across protocols.
Threat Intelligence Gaps: Exploits and malicious behaviors detected by one protocol are not shared across the
network in real time.
Verification Inconsistencies: No common standard exists to distinguish unverified bots from verified agents.
Cross-Protocol Attack Vectors: Malicious actors can deploy disposable agent addresses to repeat known exploits
against disparate protocols.
This standard addresses these challenges by establishing standardized interfaces for portable agent identity,
verifiable trust scoring, and decentralized threat pattern sharing.
Specification
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.
Definitions
AI Agent: An autonomous or semi-autonomous software system executing on-chain transactions via an Ethereum
address.
Decentralized Identifier (DID): A standardized identifier string formatted as did:agent:<chainId>:<address>.
Verification Tier: A numerical level (0 to 4) representing the verification standard met by the agent:
0: None (unverified default)
1: Bronze (basic identity confirmation)
2: Silver (extended verification and operator attestation)
3: Gold (comprehensive verification and collateral bond)
4: Platinum (independently audited system and continuous monitoring)
Reputation Score: An unsigned integer between 0 and 1000 representing trust in basis points (1000 = 100.0%).
Threat Pattern: A bytes32 cryptographic hash identifying a specific anomalous or malicious behavior pattern.
Interfaces
IAgentIdentityRegistry
Compliant identity registry contracts MUST implement the IAgentIdentityRegistry interface:
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.0;/**
* @title IAgentIdentityRegistry
* @notice Standard interface for AI agent decentralized identity and reputation management.
*/interfaceIAgentIdentityRegistry{structAgentIdentity{stringdid;bytes32publicKey;uint8verificationTier;uint16reputationScore;uint256registrationTime;uint256lastUpdate;uint256verificationExpiry;stringmetadataURI;boolisActive;uint256totalTransactions;uint256successfulTransactions;}structVerificationTier{stringname;stringrequirements;uint16trustMultiplier;uint256verificationFee;uint256validityPeriod;}eventAgentRegistered(stringindexeddid,addressindexedagentAddress,uint8verificationTier,uint16reputationScore,uint256registrationTime);eventAgentUpdated(stringindexeddid,uint16reputationScore,uint8verificationTier,uint256lastUpdate);eventReputationScoreUpdated(stringindexeddid,uint16oldScore,uint16newScore,stringupdateReason);eventVerificationTierUpdated(stringindexeddid,addressindexedagentAddress,uint8oldTier,uint8newTier,uint256verificationExpiry);/**
* @notice Registers a new AI agent identity at tier 0 (unverified).
* @dev Newly registered agents MUST initialize at verificationTier 0. The caller MUST be authorized
* to register the corresponding agent address.
* @param did Decentralized identifier following did:agent:<chainId>:<address>.
* @param publicKey Public key associated with the agent.
* @param metadataURI Off-chain metadata URI (e.g. IPFS).
* @return agentId Assigned unique identifier for the registered agent.
*/functionregisterAgent(stringcalldatadid,bytes32publicKey,stringcalldatametadataURI)externalpayablereturns(uint256agentId);/**
* @notice Updates the reputation score for an agent.
* @dev MUST revert if called by an unauthorized address.
* @param agentAddress Address of the agent whose score is being updated.
* @param newScore New reputation score (0-1000).
* @param updateReason Justification string for the score update.
*/functionupdateReputationScore(addressagentAddress,uint16newScore,stringcalldataupdateReason)external;/**
* @notice Updates the verification tier and expiration of an agent.
* @dev MUST revert if called by an unauthorized address.
* @param agentAddress Address of the agent.
* @param newTier New tier level (0-4).
* @param validityPeriod Duration in seconds until this tier verification expires.
*/functionupdateVerificationTier(addressagentAddress,uint8newTier,uint256validityPeriod)externalpayable;/**
* @notice Returns the effective verification tier of an agent, accounting for active status and expiry.
* @dev MUST return 0 if the agent is inactive (!isActive) OR if block.timestamp > verificationExpiry.
* @param agentAddress Address of the agent.
* @return Effective verification tier level (0-4).
*/functiongetEffectiveTier(addressagentAddress)externalviewreturns(uint8);/**
* @notice Returns the full identity record for a given agent address.
* @param agentAddress Address of the agent.
* @return AgentIdentity struct containing stored registration data.
*/functiongetAgentIdentity(addressagentAddress)externalviewreturns(AgentIdentitymemory);/**
* @notice Resolves an agent address from its DID string.
* @param did Decentralized identifier string.
* @return Address of the corresponding agent.
*/functiongetAgentByDID(stringcalldatadid)externalviewreturns(address);/**
* @notice Queries whether an agent has active, unexpired verification.
* @dev MUST return false if the agent is inactive (!isActive) or if getEffectiveTier <= 0.
* @param agentAddress Address of the agent.
* @return True if the agent is active and holds an unexpired verification tier above 0.
*/functionisVerified(addressagentAddress)externalviewreturns(bool);/**
* @notice Computes an effective trust score for an agent combining reputation and tier.
* @dev MUST return 0 if the agent is inactive (!isActive).
* MUST use getEffectiveTier(agentAddress) — not the stored verificationTier field — for
* any tier-based weighting or multiplier. An expired verification MUST contribute zero
* tier boost to the returned score even if the stored verificationTier field is non-zero.
* @param agentAddress Address of the agent.
* @return Effective score between 0 and 1000 (0 for inactive agents).
*/functioncalculateTrustScore(addressagentAddress)externalviewreturns(uint16);}
IThreatRegistry
Compliant threat intelligence registries MUST implement the IThreatRegistry interface:
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.0;/**
* @title IThreatRegistry
* @notice Standard interface for sharing malicious agent patterns and incident reports.
*/interfaceIThreatRegistry{enumIncidentStatus{ACTIVE,// Active confirmed threat posing immediate risk
RETRACTED,// False positive or reporting error formally retracted
RESOLVED// Remediated historical exploit with verified resolution
}structAttackRecord{addressagentAddress;bytes32patternHash;uint256amountUSDC6;uint256riskMilli;uint8layer;uint256blockedAt;addressreporter;IncidentStatusstatus;}eventAttackBlocked(uint256indexedidx,addressindexedagent,bytes32indexedpattern,uint256amountUSDC6,uint256riskMilli,uint8layer);eventAttackRetracted(uint256indexedincidentId,addressindexedagent,addressindexedreporter,stringreason);eventAttackResolved(uint256indexedincidentId,addressindexedagent,addressindexedresolver,stringremediationURI);eventThreatPatternShared(bytes32indexedpattern,addressindexedreporter,uint256riskScore,stringdescription);/**
* @notice Records an intercepted attack or unauthorized exploit attempt.
* @dev MUST revert if called by an unauthorized address. Emits AttackBlocked.
* @param agent Malicious agent address.
* @param pattern Behavioral pattern hash.
* @param amountUSDC6 Associated transaction volume in 6-decimal units.
* @param riskMilli Calculated risk score scaled by 1000 (0-1000).
* @param layer Identified attack vector category (1-4).
* @return incidentId Unique identifier assigned to this incident.
*/functionrecordAttack(addressagent,bytes32pattern,uint256amountUSDC6,uint256riskMilli,uint8layer)externalreturns(uint256incidentId);/**
* @notice Retracts an erroneous report (false positive).
* @dev MUST only be callable by original reporter or governance. Emits AttackRetracted.
* @param incidentId ID of the incident to retract.
* @param reason Explanation for the retraction.
*/functionretractAttack(uint256incidentId,stringcalldatareason)external;/**
* @notice Marks an attack as resolved following verified remediation or restitution.
* @dev MUST NOT be callable by the subject agent. Emits AttackResolved.
* @param incidentId ID of the incident resolved.
* @param remediationURI Attestation URI or audit proof of remediation.
*/functionresolveAttack(uint256incidentId,stringcalldataremediationURI)external;/**
* @notice Publishes a recognized threat pattern for cross-protocol intelligence sharing.
* @param pattern Cryptographic hash of the observed threat topology.
* @param riskScore Associated severity rating (0-1000).
* @param description Brief description of the threat vector.
*/functionshareThreatPattern(bytes32pattern,uint256riskScore,stringcalldatadescription)external;/**
* @notice Checks whether an address has active, un-remediated threat incidents.
* @param agent Address to check.
* @return True if at least one ACTIVE incident exists for this address.
*/functionisKnownAttacker(addressagent)externalviewreturns(bool);/**
* @notice Queries whether an address has active threat incidents (alias for isKnownAttacker).
* @param agent Address to check.
* @return True if at least one ACTIVE incident exists for this address.
*/functionhasActiveThreat(addressagent)externalviewreturns(bool);/**
* @notice Returns the full attack record for a given incident ID.
* @param incidentId ID of the incident to query.
* @return AttackRecord struct containing stored incident data.
*/functiongetAttackRecord(uint256incidentId)externalviewreturns(AttackRecordmemory);/**
* @notice Retrieves the historical attack frequency and blocked volume for an agent.
* @param agent Address to query.
* @return attackCount Total number of recorded attack events across all states.
* @return totalBlocked Cumulative value blocked across incidents.
*/functiongetAgentAttackHistory(addressagent)externalviewreturns(uint256attackCount,uint256totalBlocked);/**
* @notice Returns detailed incident breakdown by lifecycle status.
* @param agent Address to query.
* @return active Count of active, un-remediated incidents.
* @return resolved Count of confirmed but remediated incidents.
* @return retracted Count of retracted false-positive reports.
*/functiongetIncidentSummary(addressagent)externalviewreturns(uint256active,uint256resolved,uint256retracted);/**
* @notice Evaluates potential transaction risk against recorded threat patterns.
* @param agent Address initiating the transaction.
* @param amount Transaction amount.
* @param action Identifier of the attempted interaction.
* @return Computed threat risk score (0-1000).
*/functionassessTransactionRisk(addressagent,uint256amount,bytes32action)externalviewreturns(uint256);}
IRiskAssessment (Optional)
Registries supporting pre-execution transaction risk assessment MAY implement IRiskAssessment:
The registerAgent function MUST revert if the supplied did is empty or already registered to a different
address.
Registrations MUST initialize strictly at verificationTier = 0 (unverified default) with verificationExpiry = 0.
registerAgent MUST NOT accept a non-zero tier parameter; promotion to a verified tier (1–4) MUST occur through
a distinct invocation of updateVerificationTier by an authorized verifier.
The contract MUST emit an AgentRegistered event upon successful creation of an identity.
Implementations MUST initialize new agent identities with reputationScore = 500 (representing baseline neutral
standing) unless specified otherwise by local governance.
Verification Tier Lifecycle & Expiration:
Promotions to non-zero verification tiers MUST only be callable through updateVerificationTier by authorized
verifiers, audited verification contracts, or governance.
updateVerificationTier MUST set verificationExpiry = block.timestamp + validityPeriod.
The getEffectiveTier view function MUST evaluate active status and the validity period: if the agent
is inactive (!isActive) OR if block.timestamp > verificationExpiry, the function MUST return 0
regardless of the stored verificationTier.
isVerified MUST return true if and only if the agent is active (isActive == true) AND
getEffectiveTier(agentAddress) > 0. Inactive or expired verifications MUST evaluate to false without
requiring an explicit on-chain downgrade transaction.
Updates to reputation scores (updateReputationScore) or general activity (lastUpdate) MUST NOT renew or extend
verificationExpiry. Conforming to the Identity/Reputation Boundary (T5), verification renewal MUST require an
independent verification action by an authorized verifier.
Reputation Management:
The updateReputationScore function MUST only be callable by authorized entities (such as designated oracle
addresses, audited verification contracts, or governance multi-signatures).
If an unauthorized address calls updateReputationScore, the implementation MUST revert.
The newScore argument MUST NOT exceed 1000. Values above 1000 MUST cause the transaction to revert.
On successful update, the contract MUST emit a ReputationScoreUpdated event.
The calculateTrustScore function MUST return 0 if the agent is inactive (!isActive). For active agents,
it MUST derive any tier-based weighting from getEffectiveTier(agentAddress) rather than reading stored
verificationTier directly.
Threat Reporting & Incident Lifecycle:
The recordAttack function MUST only be callable by authorized security oracles, monitoring contracts, or
whitelisted protocol adapters.
If an unauthorized address calls recordAttack, the call MUST revert.
Implementations MUST persist the attack record with an initial status = IncidentStatus.ACTIVE, assign a unique
incidentId, and increment the agent’s active incident counter.
The implementation MUST emit an AttackBlocked event.
retractAttack MUST only be callable by the original reporting entity or authorized governance to remediate
false-positive findings. When called on an ACTIVE incident, it transitions the record to IncidentStatus.RETRACTED,
emits AttackRetracted, and decrements the agent’s active incident counter.
resolveAttack MUST NOT be callable by the subject agent. It MUST require authorized verifier, auditor, or
governance attestation verifying that the exploit vulnerability has been remediated. It transitions the record
to IncidentStatus.RESOLVED, emits AttackResolved, and decrements the agent’s active incident counter.
isKnownAttacker (and hasActiveThreat) MUST return true if and only if the agent has at least one incident with
status == IncidentStatus.ACTIVE.
Resolving or retracting one incident MUST NOT clear an agent’s active threat flag while any other incident remains
in the ACTIVE state.
Threat Pattern Derivation:
Threat patterns MUST be derived using keccak256 hashing of the interaction parameters:
Ethereum smart contracts lack native floating-point math. Using an integer scale of 0 to 1000 provides fine-grained
reputation measurement (0.1% resolution) without incurring fixed-point arithmetic overhead in consuming DeFi protocols.
Separation of Identity and Threat Registries
Decoupling IAgentIdentityRegistry from IThreatRegistry allows protocols to adopt identity management without
mandating a specific threat database, and vice versa. It also enables modular upgrades where threat detection oracles
can evolve independently of long-lived identity attestations.
Standardized DID Format
Adopting the generic scheme did:agent:<chainId>:<address> ensures cross-chain compatibility without binding the
standard to any proprietary vendor namespace.
Backwards Compatibility
This standard introduces new interfaces and does not conflict with existing token standards or registries. Protocols
can integrate conformant registries alongside existing access-control modules without breaking changes.
Test Cases
An executable Foundry test package covers the T5/T8 verification-expiry
checks. It advances time, compares expired and unverified agents with equal reputation, and covers the exact
expiry boundary. The included registry is a test fixture; the same checks can be adapted to an implementation.
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.0;contractMockIdentityTest{IAgentIdentityRegistrypublicregistry;IThreatRegistrypublicthreatRegistry;functiontestAgentRegistration()public{stringmemorydid="did:agent:1:0x1111111111111111111111111111111111111111";bytes32publicKey=keccak256("agent_public_key");uint256agentId=registry.registerAgent(did,publicKey,"ipfs://QmExample");require(agentId>0,"Registration failed");require(!registry.isVerified(address(this)),"New agent must start unverified");require(registry.getEffectiveTier(address(this))==0,"Initial effective tier must be 0");// Promotion by authorized verifier
registry.updateVerificationTier(address(this),1,30days);require(registry.isVerified(address(this)),"Agent should now be verified");require(registry.getEffectiveTier(address(this))==1,"Effective tier should be 1");}functiontestReputationUpdate()public{addressagent=address(0x2222222222222222222222222222222222222222);registry.updateReputationScore(agent,750,"Verified consistent operations");IAgentIdentityRegistry.AgentIdentitymemoryidentity=registry.getAgentIdentity(agent);require(identity.reputationScore==750,"Reputation score mismatch");}functiontestThreatDetection()public{addressmaliciousAgent=address(0xBAD);bytes32pattern=keccak256("drain_star_attack");uint256incidentId1=threatRegistry.recordAttack(maliciousAgent,pattern,1000000,950,1);require(threatRegistry.isKnownAttacker(maliciousAgent),"Attacker record failed");require(threatRegistry.hasActiveThreat(maliciousAgent),"Active threat check failed");// False positive retraction
threatRegistry.retractAttack(incidentId1,"False positive confirmed by oracle");require(!threatRegistry.isKnownAttacker(maliciousAgent),"Attacker flag should be cleared after retraction");// Independent state invariant (T7): two incidents
uint256incA=threatRegistry.recordAttack(maliciousAgent,pattern,500000,800,1);uint256incB=threatRegistry.recordAttack(maliciousAgent,pattern,500000,800,1);threatRegistry.resolveAttack(incA,"ipfs://audit-remediation-report");require(threatRegistry.isKnownAttacker(maliciousAgent),"Flag must stay true while incB is active");threatRegistry.resolveAttack(incB,"ipfs://audit-remediation-report-2");require(!threatRegistry.isKnownAttacker(maliciousAgent),"Flag cleared once all active incidents resolved");(uint256active,uint256resolved,uint256retracted)=threatRegistry.getIncidentSummary(maliciousAgent);require(active==0&&resolved==2&&retracted==1,"Incident summary count mismatch");}}
Security Considerations
Access Control and Privilege Escalation
The integrity of this standard relies heavily on restricting access to state-modifying functions:
Reputation Manipulation: If updateReputationScore is left unprotected or guarded by an easily compromised key,
an adversary could inflate the reputation score of malicious agents to bypass protocol limits, or defame competitor
agents by setting their score to zero. Registries MUST implement strict access control, such as multi-signature
governance, timelocks, or decentralized consensus among multiple independent reporting nodes.
Denial-of-Service via Malicious Threat Reporting: If recordAttack is publicly callable without verification or
stake, an attacker could falsely label benign addresses as known attackers, causing integrated protocols to block
legitimate users. Registries MUST restrict this function to authenticated monitors or require bonded stakes that are
slashed upon fraudulent reports.
Verification Tier Invariants and Lifecycle Expiration
Allowing identity registrants to self-select verification tiers creates an immediate privilege-escalation vector where
unverified bots claim false trust guarantees. Restricting registerAgent to tier 0 enforces an essential separation of
privileges: permission to register an identity is permissionless or identity-gated, whereas issuing a verification tier
requires specific attestation authority.
Furthermore, static tier records without time bounds permit stale certifications to persist indefinitely if an agent
is abandoned or repurposed. Enforcing an explicit verificationExpiry evaluated dynamically in getEffectiveTier and
isVerified guarantees that verification status reflects current, active attestations without relying on off-chain
parties to trigger costly manual downgrade transactions. In line with the identity/reputation boundary, reputation score
updates MUST NOT modify verificationExpiry.
Implementations MUST apply this expiry consistently across all tier-dependent outputs. Specifically,
calculateTrustScore MUST derive any tier-based weighting from getEffectiveTier(agentAddress) rather than reading
AgentIdentity.verificationTier directly. After a verification expires, the agent MUST lose any multiplier associated
with that tier (e.g., a Gold multiplier for tier 3) even though the stored verificationTier field retains its value.
The agent’s reputationScore field is unaffected; only the tier contribution to the trust score is zeroed. This ensures
that calculateTrustScore is consistent with isVerified and that consumers cannot inadvertently retain the benefit of
an expired certification simply by reading a stale field (conformance test T8).
Sybil Resistance and Tier Thresholds
Because Ethereum accounts can be generated at negligible cost, malicious actors can create arbitrary numbers of tier 0
(unverified) agent identities. Consuming protocols SHOULD NOT grant permissions, borrowing capacity, or reduced
collateral requirements to agents based solely on tier 0 registration. High-trust operations SHOULD require verification
tier 2 (Silver) or higher and evaluate historical transaction depth.
Stale Reputation Risk
An agent with historically high reputation can become compromised or undergo an intentional malicious exit strategy.
Protocols consuming IAgentIdentityRegistry SHOULD inspect the lastUpdate timestamp and reject evaluations whose
attestations exceed an acceptable staleness window.
Replay and Front-Running Attacks
When transaction risk evaluations or threat pattern hashes are broadcast to the public mempool, malicious agents could
monitor incoming reports and attempt to front-run blocking transactions. Implementations SHOULD use private mempools,
commit-reveal schemes, or batch updates when reporting critical threat incidents.
Incident Lifecycle and Remediation Isolation
Without an explicit incident lifecycle, false positives recorded by authorized oracles result in permanent, un-remediable
denial-of-service for benign agents across all integrating DeFi protocols. Defining independent state transitions
(ACTIVE -> RETRACTED and ACTIVE -> RESOLVED) guarantees that:
Honest reporting errors can be safely retracted by reporters without destroying historical audit trails.
Remediated exploits can be distinguished from false positives: protocols with dynamic defense evaluate hasActiveThreat
(active > 0), whereas institutional risk modules can query getIncidentSummary to enforce strict zero-tolerance
policies (active > 0 || resolved > 0).
Subject agents cannot self-resolve their own incidents.
Independent incident accounting prevents resolution of one finding from prematurely lifting blocks while other active
threats remain unaddressed (conformance test T7).
Identity/Reputation Boundary
Identity resolution and reputation assessment MUST remain semantically distinguishable, even when implemented in the
same contract or storage structure:
Reputation updates must not rebind identity: An operation authorized solely to update reputation MUST NOT change
the subject’s identifier binding, controller, or verification key.
Lifecycle separation: Key rotation, controller changes, and revocation MUST follow their separately authorized
identity-lifecycle rules and remain distinguishable from reputation updates in audit records.
No freshness masquerading: Reputation freshness MUST NOT be presented as evidence that an identity binding has
been revalidated. Consumers MUST be able to distinguish identity-binding provenance from reputation-update provenance.
Encoding rules: Identifier comparison MUST follow the applicable identifier and address-encoding rules of each
identifier system; encoding rules of one system MUST NOT be misapplied to another.
No implicit authorization: Reputation scores and verification tiers MUST NOT, by themselves, be interpreted as
authorization for a specific action.
Conformance cases (boundary properties only; “unsupported” is an acceptable explicit answer, and unimplemented
capabilities MUST NOT be marked PASS):
T1: A reputation-only update leaves the subject’s identifier binding, controller, and registration source unchanged.
T2: A principal holding only reputation-write permission cannot modify identity binding.
T3: Supported identity lifecycle operations require their own authorization and a traceable record distinguishable
from reputation updates; unsupported operations are declared unsupported.
T4: Identifier comparison follows each identifier system’s declared encoding rules; distinct subjects are never merged.
T5: A reputation refresh is never presented as a re-validation of the identity binding.
T6: Identification or a high reputation score is never treated as authorization for a privileged action.
T7: Incident state transitions (retraction or remediation) are strictly independent; resolving or retracting
one incident MUST NOT clear an active attacker flag while any other incident remains in the ACTIVE state.
T8: After a verification expiry, a subsequent reputation update MUST NOT restore any tier boost:
calculateTrustScore called after expiry MUST return a score with zero tier contribution, identical to calling it on
an agent with the same reputationScore but verificationTier == 0.
These requirements do not mandate separate contracts, prohibit combined reads, or prescribe a storage layout. Identity/
reputation separation as a concept has prior art (ERC-8004 separates Identity, Reputation and Validation registries;
W3C DID Core); this section claims only the specific constraints and test cases above.