For the complete documentation index, see llms.txt. This page is also available as Markdown.

05. Issue a claim to an investor

Issuing a claim records, on an investor's identity, that a Claim Issuer vouches for a fact about it: identity verified, accredited, resident of a jurisdiction. The Claim Issuer verifies the investor off-chain, signs the claim, and the claim is added to the investor's ONCHAINID. From then on, every asset that trusts that issuer for that topic counts the claim. This is what makes an investor eligible.

At a glance

Triggered by

Claim Issuer

Actors

Claim Issuer, Investor, Identity Owner, Platform

Contracts

The ONCHAINID of the Investor, the ONCHAINID of the Claim Issuer, ERC734Validator, IdentityFactory and ReputationRegistry (read only, during the check of the issuer)

Preconditions

The Investor has an ONCHAINID (see 02); the Claim Issuer has an ONCHAINID of type CLAIM_ISSUER with a CLAIM_SIGNER key; the Claim Issuer has verified the Investor under its own process; either the Claim Issuer has standing in the network, or the Investor's identity has granted it a claim key, or the Investor adds the claim itself

Result

The Investor's identity holds a valid claim from the Claim Issuer for the topic

Actors and roles

Actor
Role in this flow
What they hold

Claim Issuer

Verifies the Investor, signs the claim and, in the usual path, adds it to the Investor's identity.

An ONCHAINID of type CLAIM_ISSUER. A CLAIM_SIGNER key to sign. A MANAGEMENT key to submit

Investor

Is the subject of the claim. Provides its documents off-chain.

An ONCHAINID

Identity Owner

The Investor in its capacity as controller of the identity. Transacts only when it adds the claim itself.

A CLAIM_SIGNER or CLAIM_ADDER key on its identity

Platform

Carries the verification session, builds the claim and relays the calls.

Whatever keys the Claim Issuer or the Investor has authorized

Sequence

Step by step

Step 1. Verify the Investor

Who: Claim Issuer

Call: Off-chain

Before the call: The Investor has given the address of its ONCHAINID and the documents the verification requires. When the Investor comes with a wallet that has no identity yet, the Claim Issuer creates one for that wallet first, with the wallet as its only manager (02).

What happens: The Claim Issuer verifies the Investor under its own process and regulation. It confirms that the person it verified controls the identity it will attest. The result of the verification, and the documents, stay with the Claim Issuer.

What changes: Nothing on-chain.

Step 2. Build and sign the claim

Who: Claim Issuer (CLAIM_SIGNER key)

Call: Off-chain, using read calls

Before the call: Step 1 concluded that the fact can be attested.

What happens: The Claim Issuer builds the claim.

Field
Value

topic

The claim topic agreed with the Issuers that rely on it, for example <KYC topic>. Not zero.

scheme, uri

How the claim is to be read, and where supporting material is.

data.issuedAt

The time of signing. Not zero.

data.validUntil

The time after which the claim expires. Zero for no expiry.

data.metadataHash

getMetadataHash(scheme, uri), read on the ERC734Validator, the module whose address Identity.registryModule() returns. It binds the scheme and the URI to the signature.

data.payload

The content for the topic: a hash or an encoded value, not raw personal data.

It reads getClaimHash(identity, topic, data) on its own ONCHAINID, which returns the ERC-712 digest of the claim in the issuer's signing domain, and signs that digest with a key that holds CLAIM_SIGNER on its identity. The signature field of the claim carries the signer and its signature together.

What changes: Nothing on-chain.

Step 3. Add the claim to the identity

There are two ways. The first is the usual one.

Who: Claim Issuer (MANAGEMENT key of its identity)

Call: addClaimTo(topic, scheme, signature, data, uri, identity), called on the Claim Issuer's ONCHAINID

Before the call: Step 2 is done. The call is made directly on the issuer's identity by the key holder.

What the contract checks:

  1. The caller holds MANAGEMENT on the Claim Issuer's identity. Otherwise the call reverts with SenderDoesNotHaveManagementKey.

  2. The claim is valid for this issuer: inside its validity window, not revoked, signed by a CLAIM_SIGNER key of the issuer, with a signature that verifies. Otherwise InvalidClaim.

  3. On the Investor's identity: the Claim Issuer's identity holds a CLAIM_SIGNER or CLAIM_ADDER key there. If it does not: its identity is of type CLAIM_ISSUER in the IdentityFactory, and its reputation is at least the claim-add threshold. Otherwise IdentityNotClaimIssuerType or ReputationBelowClaimAddThreshold.

  4. The topic is not zero. Otherwise InvalidClaimTopic.

  5. The signature, the payload and the URI are within their size limits. Otherwise ClaimSignatureTooLong, ClaimPayloadTooLong or ClaimUriTooLong.

  6. data.metadataHash matches the scheme and the URI. Otherwise ClaimMetadataMismatch.

  7. The issuer confirms the claim: isClaimValid(identity, topic, signature, data) returns true. Otherwise InvalidClaim.

What changes: The Investor's identity stores the claim under claimId = keccak256(abi.encode(issuer, topic)). If the identity already held a claim from this issuer for this topic, it is replaced. The ERC734Validator emits ClaimAdded, or ClaimChanged for a replacement, with the identity, the claim and the caller, and ClaimAddedTo for the write made by the issuer.

Alternative: the Investor adds the claim.

Who: Identity Owner (CLAIM_SIGNER or CLAIM_ADDER key of the Investor's identity)

Call: addClaim(topic, scheme, issuer, signature, data, uri), called on the Investor's ONCHAINID

Before the call: The Claim Issuer has sent the signed claim to the Investor.

What the contract checks: The caller holds CLAIM_SIGNER or CLAIM_ADDER on the identity. Otherwise SenderDoesNotHaveClaimSignerKey. Then checks 4 to 7 above.

What changes: The same.

Step 4. Confirm the effect

Who: Platform

Call: Off-chain, using read calls

Before the call: Step 3 is confirmed.

What happens: The Platform reads getClaim(claimId) on the Investor's identity, and isClaimValid(identity, topic, signature, data) on the Claim Issuer's identity, which must return true. For an asset the Investor wants to hold, it reads TREXRegistry.isVerified(address) for one of the Investor's addresses.

What changes: Nothing on-chain.

State after the flow

  • The Investor's identity holds one claim from this issuer for this topic, readable with getClaim(claimId) and listed by getClaimIdsByTopic(topic).

  • The Claim Issuer answers isClaimValid with true for that claim, until it expires, is revoked, or its signing key is withdrawn.

  • Every asset that requires the topic and trusts this issuer for it counts the claim, for every address of the identity, on every chain.

  • No asset was touched. No registry of any asset changed.

  • No personal data is on-chain.

Failure modes

Symptom
Cause
Fix

Reverts with SenderDoesNotHaveManagementKey

addClaimTo was called by a key that does not hold MANAGEMENT on the issuer's identity (step 3, check 1)

Submit with a MANAGEMENT key. The signing key signs, it does not submit

Reverts with InvalidClaim

The claim does not validate: wrong digest, signer without CLAIM_SIGNER, issuedAt zero or in the future, expired, or already revoked (step 3, checks 2 and 7)

Read getClaimStatus on the ERC734Validator, with the issuer's identity as the caller of the read, to see which. Sign a fresh claim

Reverts with IdentityNotClaimIssuerType

The issuer has no claim key on the Investor's identity and is not a CLAIM_ISSUER identity of the factory (step 3, check 3)

Have the Investor add the claim itself, or grant the issuer a claim key

Reverts with ReputationBelowClaimAddThreshold

The issuer's reputation is below the network threshold (step 3, check 3)

Same. Or have the REPUTATION_MANAGER review the issuer

Reverts with ClaimMetadataMismatch

metadataHash was not computed from the scheme and the URI submitted (step 3, check 6)

Compute it with ERC734Validator.getMetadataHash(scheme, uri)

Reverts with InvalidClaimTopic

The topic is zero (step 3, check 4)

Use the agreed topic

Reverts with a size error

The signature, the payload or the URI exceeds 2,048 bytes (step 3, check 5)

Put a hash in the payload, not the data

Reverts with SenderDoesNotHaveClaimSignerKey

In the alternative path, the caller holds neither CLAIM_SIGNER nor CLAIM_ADDER on the Investor's identity

Use a key that does, or a MANAGEMENT key

The call is refused when sent as a user operation

addClaim and addClaimTo require a direct call from the key holder

Call the identity directly

The claim is on the identity and the Investor is still not eligible

The asset does not trust this issuer for this topic, or another required topic is missing

See 04 and 03

Common misunderstandings

Wrong: "The protocol issues the KYC." Or: "The Issuer issues the claim."

Right: A Claim Issuer signs claims. The protocol stores them and checks them. The Issuer of an asset decides which Claim Issuers it trusts and for which topics. It can run its own Claim Issuer, which is then a separate role with its own identity.

Wrong: "A claim is issued for a token."

Right: A claim is about an identity, not about an asset. One claim serves every asset that trusts its issuer for its topic. An investor verified once is not verified again per asset.

Wrong: "A claim is issued to a wallet."

Right: A claim is added to the ONCHAINID. Every address of the identity shares it: the identity's own address and every linked wallet, on every chain.

Wrong: "The claim contains the investor's data."

Right: The claim contains a topic, the issuer, a validity window, a signature and a payload that is a hash or an encoded value. The documents stay off-chain with the Claim Issuer.

Integration notes

  • Read getClaimHash(identity, topic, data) from the issuer's identity to obtain the digest to sign. Do not rebuild the typed data by hand.

  • Set validUntil to the date after which the verification has to be renewed. Expiry needs no transaction, and the investor stops being eligible at that moment.

  • Re-issuing for the same topic replaces the previous claim from the same issuer. Renewal is this same flow.

  • Keep the signing key and the submitting key apart. A signing service holds CLAIM_SIGNER. Submission and revocation take MANAGEMENT.

  • Store, off-chain, the digest of each claim issued with the verification record. It is what revokeClaimByDigest takes.

  • Index ClaimAdded, ClaimChanged and ClaimRemoved on the ERC734Validator, filtered by identity or by issuer.

  • An Investor without a gas token receives its claims through the first path. The Claim Issuer pays.

  • A Claim Issuer with standing needs no approval from the Investor to add a claim. This is by design: asking for an acceptance on every claim would add a step to every onboarding. The gate is upstream, on who can be a Claim Issuer at all. The Investor can remove a claim from its identity afterward.

Last updated