Identity Verification
Reference for the DALP Identity Verification compliance module, including recipient claim checks, RPN expression configuration, trusted issuer lookup, and failure behavior.
The Identity Verification compliance module checks the recipient of a regulated token operation before the operation executes. It evaluates a token-specific claim expression against the recipient's OnchainID. A claim counts only when the topic exists, the recipient wallet resolves to an accepted identity, the wallet is not marked as lost, and a trusted issuer validates the claim signature and data.
Use this page when you need to configure a recipient claim rule, review the trusted issuer path, or diagnose a RecipientNotVerified failure.
Before you configure the module
You need these inputs before the expression can pass at runtime:
- claim topic IDs that exist in the topic scheme registry
- an accepted recipient identity for each wallet that should receive the token
- claims on the recipient OnchainID for the required topics
- issuer contracts registered as trusted issuers for those topics
- recovered or unmarked wallets for recipients whose previous wallet was marked as lost
- a policy decision for negative topics such as sanctions
Require KYC and AML for recipients
Configure the module with a postfix expression that requires both claim topics:
const expression = [
{ nodeType: 0, value: KYC_TOPIC_ID },
{ nodeType: 0, value: AML_TOPIC_ID },
{ nodeType: 1, value: 0n },
];At transfer time the module resolves the recipient wallet through the token identity registry and evaluates the expression. The transfer can continue only when the recipient's OnchainID has valid claims for both topics from issuers trusted for those topics.
Runtime flow
- The token's compliance engine calls the Identity Verification module during
canTransfer. - The module reads the token's identity registry from the token contract.
- The registry verifies the
toaddress against the configured expression. - Each
TOPICnode checks the recipient's OnchainID for claims with that topic. - The trusted issuers registry returns issuers trusted for the topic and recipient identity.
- The claim passes only when the claim issuer matches a trusted issuer and the issuer contract reports the signature and data as valid.
- If the expression evaluates to
false, the module reverts withRecipientNotVerified.
The module checks the recipient address. It does not evaluate the sender. Sender-side restrictions belong in other modules or in explicit token and role controls.

Configuration fields
| Field | Type | Required | Meaning |
|---|---|---|---|
expression | ExpressionNode[] | Yes | Postfix expression evaluated against recipient claims. |
nodeType | 0, 1, 2, 3 | Yes | Operation for one expression node: 0 TOPIC, 1 AND, 2 OR, 3 NOT. |
value | uint256 | For TOPIC | Claim topic ID for TOPIC; ignored for operators and normally set to 0. |
The expression can contain up to 32 nodes. A non-empty expression must leave exactly one boolean value on the stack after evaluation. Topic ID 0 is invalid. An empty expression skips claim-topic evaluation, but the recipient wallet still needs a registered identity and must not be marked as lost.
Expression nodes
| Node type | Stack behavior | Validation rule |
|---|---|---|
TOPIC | Pushes true when the recipient identity has a valid trusted claim for the topic. | value must be a non-zero topic ID and the topic must exist in the topic scheme registry at verification time. |
AND | Pops two values and pushes true only when both are true. | Requires two operands already on the stack. |
OR | Pops two values and pushes true when at least one operand is true. | Requires two operands already on the stack. |
NOT | Pops one value and pushes its inverse. | Requires one operand already on the stack. |
Postfix notation removes parentheses from the on-chain config. For example, (KYC AND AML) OR ACCREDITED becomes:
[
{ nodeType: 0, value: KYC_TOPIC_ID },
{ nodeType: 0, value: AML_TOPIC_ID },
{ nodeType: 1, value: 0n },
{ nodeType: 0, value: ACCREDITED_TOPIC_ID },
{ nodeType: 2, value: 0n },
];Common expression patterns
| Requirement | Postfix expression | Effect |
|---|---|---|
| KYC and AML | [KYC, AML, AND] | Recipient needs both claims. |
| Accredited investors only | [ACCREDITED] | Recipient needs the accredited investor claim. |
| Corporate entity or fully screened individual | [CONTRACT, KYC, AML, AND, OR] | Contract identities pass. Individuals need KYC and AML claims. |
| KYC and not sanctioned | [KYC, SANCTIONED, NOT, AND] | Recipient needs KYC and must not have the sanctioned topic. Use this only when the sanctioned topic is actively maintained, because a missing negative claim evaluates as false before NOT turns it into true. |
Trusted issuer path
A claim topic is necessary but not sufficient. The verification path also checks trust:
| Registry or contract | Role in verification |
|---|---|
| Topic scheme registry | Confirms the claim topic is registered. Unknown topics fail verification. |
| Token identity registry | Resolves the recipient wallet to its OnchainID and evaluates the expression. |
| Recipient OnchainID | Stores ERC-735 claims and returns claim IDs by topic. |
| Trusted issuers registry | Returns issuers trusted for the topic and identity being checked. |
| Claim issuer contract | Confirms the claim signature and data are valid for the identity and topic. |
When operators remove an issuer from the trusted issuers registry, existing claims from that issuer stop satisfying this module. Those claims can still exist on the identity contract. Transfer verification will not accept them.
Production checks
Before enabling a token policy in production, verify the whole path with the same topic IDs and issuers the token will use:
- Confirm each topic ID exists in the topic scheme registry.
- Confirm the recipient wallet has an accepted identity and is not marked as lost.
- Confirm the recipient OnchainID contains the required claim IDs by topic.
- Confirm each claim issuer is trusted for the topic and identity being checked.
- Run a transfer preflight with a recipient that should pass and a recipient that should fail.
ClaimSource and provider events
ClaimSource handlers connect provider verdicts to the on-chain claim system. Provider verdict events use the canonical claim data format providerKind:providerEventId:state, where the provider event ID and verdict state must match the event fields. An approved verdict issues a claim. A rejected verdict revokes the existing claim for that provider, subject, and topic. under_review and action_required verdicts record the event without an on-chain claim effect.
ClaimSource events are serialized per provider and mapped identity, so repeated events for the same DALP identity are applied in order. Unmapped provider events are recorded as unmapped and do not issue claims. Monitoring alerts revoke an existing claim only when their severity meets the provider topic's revocation threshold.
Failure modes
| Failure | Trigger | Result | Operator response |
|---|---|---|---|
RecipientNotVerified | The expression evaluates to false for the recipient. | Transfer reverts before execution. | Check the recipient identity registration, claims, topic IDs, and trusted issuer registrations. |
ExpressionTooComplex | Configuration has more than 32 nodes. | Configuration reverts. | Split the policy or simplify the expression. |
InvalidTopicIdZeroNotAllowed | A TOPIC node uses topic ID 0. | Configuration reverts. | Register or resolve the correct claim topic ID. |
NotOperationRequiresOneOperand | NOT appears before an operand. | Configuration reverts. | Reorder the expression into valid postfix notation. |
AndOrOperationRequiresTwoOperands | AND or OR appears before two operands. | Configuration reverts. | Add the missing topic node or reorder the expression. |
InvalidExpressionMustEvaluateToOneResult | The expression leaves zero or multiple stack values. | Configuration reverts. | Ensure the expression reduces to one boolean result. |
| Unknown topic at runtime | A topic is not present in the topic scheme registry. | Topic check returns false. | Confirm the topic exists and the module config uses the current topic ID. |
| No trusted issuer for topic | No issuer is trusted for the required topic and identity. | Topic check returns false. | Register the issuer for the topic or use a claim from an already trusted issuer. |
Boundaries
- The module verifies identity claims. It does not perform KYC, KYB, AML, sanctions, or accreditation checks itself.
- The module uses on-chain claim artifacts. Keep raw verification evidence, documents, and provider payloads off-chain.
- The module checks recipient eligibility. It does not evaluate the sender address.
- The module does not replace country restrictions, transfer approvals, supply limits, timelocks, custody approvals, or role-based administration.
- Forced transfers bypass normal compliance checks under ERC-3643. Use them only as controlled servicing operations.
See also
- Identity and Compliance for the full identity registry and OnchainID architecture
- Public chain privacy boundaries for what identity data is visible on-chain
- Compliance Overview for module architecture and regulatory templates
- Identity Lists for explicit allowlist and blocklist controls
- Transfer Approval and TimeLock for modules that reuse RPN expressions for exemptions
Address Block List
Block specific wallet addresses from sending or receiving an asset. Includes configuration, transfer behavior, and identity-list boundaries.
Supply & Investor Limits
TokenSupplyLimit and InvestorCount modules for time-based and rolling supply caps with currency conversion, plus unique holder limits with per-country granularity.