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

Eligibility and compliance: the two gates

Every change of ownership passes two gates before the register records it. The two gates ask different questions, live in different contracts and are configured by different means. These docs never merge them into "compliance checks".

Eligibility
Compliance

Question

May this identity hold this asset?

May this movement happen now?

About

The holder

The movement, and the distribution that results from it

Evaluated on

The recipient's identity

The sender's identity, the recipient's identity, the amount, the spender

Configured with

Required claim topics and trusted Claim Issuers

Compliance modules and their settings

Lives in

TREXRegistry

ModularCompliance and its modules

Entry point

isVerified(address)

canTransfer(from, to, amount)

Changes when

A claim is added, expires or is revoked. The Issuer changes the topics or the trusted issuers.

Positions change. The Issuer changes the rules.

Configured by

The OWNER role on the registry

The OWNER role on the compliance

"Adding an eligibility rule" means changing claim topics or trusted issuers. "Adding a compliance rule" means binding or configuring a module.

Gate 1: eligibility

What the Issuer configures

  • The required claim topics. A claim topic is a number that stands for a fact about a holder: identity verified, accredited, resident of a permitted jurisdiction. The Issuer lists the topics its asset requires with addClaimTopic.

  • A different set per identity type, where needed. With addClaimTopicForIdentityType, the Issuer defines the topics for one type of identity, for example CORPORATE. A set defined for a type replaces the default set for identities of that type. It does not add to it.

  • The trusted Claim Issuers. With addTrustedIssuer(trustedIssuer, claimTopics), the Issuer names a Claim Issuer and the topics it trusts that issuer for.

How isVerified decides

  1. Resolve the address to an identity. A wallet registered for this asset by a Registry Agent resolves to the identity it was registered with. Any other wallet resolves through the global identity registry, to the identity its owner linked it to. An ONCHAINID resolves to itself. An address that resolves to no identity is not verified.

  2. Refuse a revoked wallet. A wallet whose link the Identity Owner has revoked is not verified, even though it still resolves to its identity.

  3. Select the required topics. The registry reads the type of the identity from the IdentityFactory, never from the identity itself. It takes the topics defined for that type if there are any, and the default topics otherwise. If no topic is required, the address is verified.

  4. Look for a valid claim per topic. For each required topic, the registry goes through the Claim Issuers trusted for that topic. For each one it reads the claim that issuer made on the identity for that topic, and asks the issuer whether the claim is valid, with isClaimValid.

  5. Conclude. The address is verified if and only if every required topic has at least one valid claim from a trusted Claim Issuer.

A claim is valid when it is inside its validity window, its digest has not been revoked, the key that signed it holds the CLAIM_SIGNER purpose on the issuer's identity, and the signature verifies.

What follows

  • Eligibility belongs to the identity. Every address of an identity shares it: the identity's own address and every wallet linked to it. An investor is verified once, not once per wallet or per chain.

  • A wallet with no claims behind it is not verified. Linking or registering a wallet proves nothing about eligibility. The claims on the identity do.

  • A claim from an issuer the asset does not trust does not count, however valid it is.

  • Revocation is immediate. When a Claim Issuer revokes a claim, the identity stops being eligible at the next check. Nothing changes in the registry.

  • Expiry is automatic. A claim past its validUntil stops counting without any transaction.

  • Configuration changes apply to everyone at once. Adding a required topic makes every identity without a valid claim for it ineligible. Removing a trusted issuer does the same for every identity that relied on that issuer alone.

  • Losing eligibility does not remove a position, and does not lock it. The gate is checked on the recipient. A holder that becomes ineligible keeps what it holds, can no longer receive, and can still transfer to an eligible recipient. This is by design: the holder was eligible when it received the tokens and is still identified, so it can liquidate its position. An Issuer that wants to block such a holder freezes its tokens or its address. A forced transfer and a redemption remain available too.

Not an allowlist. The registry does not keep a list of approved addresses. It evaluates claims on the identity behind the address, at the moment of each transfer. No address is ever put on a list.

No personal data on-chain. A claim carries a topic, an issuer, a signature, a validity window and a payload. The payload is a hash or an encoded value, not raw personal data.

The eligibility switch

The OWNER of the registry can turn eligibility checks off with disableEligibilityChecks, and back on with enableEligibilityChecks. While they are off, isVerified returns true for every address. This exists for assets that do not restrict their holders. The compliance gate is unaffected, and as long as a rule module is bound, the compliance still refuses a recipient that resolves to no identity.

Gate 2: compliance

What the Issuer configures

The rules of the asset are compliance modules, bound to the ModularCompliance with addModule, or with addAndSetModule to configure a module in the same transaction. A module declares what it is, and the compliance calls it only for that.

Type
The question it answers
When it is asked

RULE

What is the largest amount you allow for this movement? (allowedAmount)

Before a transfer or a mint. When a cross-chain validation is issued.

SPENDER

May this spender execute this movement for the sender? (moduleCheckSpender)

Before an allowance is spent in transferFrom. When a cross-chain validation names a spender.

TRACKER

Nothing. It is told that a movement happened. (afterTransfer)

After every movement the register records.

How canTransfer decides

  1. No rule, no limit. If no RULE module is bound, the answer is true.

  2. Build the context once. The compliance resolves the identity of the sender and of the recipient and puts them, with the addresses, the amount and the spender, in one TransferContext. Every module receives the same context. A module never resolves an address itself.

  3. Require an identity on each side. A sender or a recipient that resolves to no identity is refused. A rule about distribution is keyed on identities and could not count a holder it cannot name.

  4. Ask every rule for its maximum. Each RULE module returns the largest amount it allows. No limit is the maximum value, a refusal is zero.

  5. Keep the smallest answer. The movement is allowed if its amount does not exceed the smallest answer.

Rules read the state of the asset from the compliance ledger, which the compliance keeps itself: the total position of each identity (positionOf), and what open cross-chain validations promise to it (pendingInOf) and from it (pendingOutOf). A rule therefore reasons about the investor, across all its addresses and all chains, and not about one wallet on one chain.

After the balances move, the compliance updates the positions and calls every TRACKER module.

A movement between two addresses of the same identity changes no position. Rules about distribution do not limit it.

The spender

On transferFrom, a third party moves a holder's tokens. The compliance checks that third party as well. Before the allowance is spent, the token asks the compliance whether the caller may execute this transfer, and every SPENDER module must agree. If one refuses, the call reverts with SpenderNotAllowed and the allowance is untouched.

This closes the gap that would let an intermediary route a transfer in a way the rules are meant to prevent. A direct transfer has no spender and skips this check. When no SPENDER module is bound, every spender is accepted.

Which operations pass which gate

Operation
Pause
Freeze
Eligibility of the recipient
Rules
Spender policy
Authorized by

transfer

Blocks

Blocks

Checked

Checked

No spender

The holder

transferFrom

Blocks

Blocks

Checked

Checked

Checked

An allowance from the holder

identityTransfer

Blocks

Blocks

Checked

Checked

No spender

The identity the from wallet is linked to

mint

Does not block

Not checked

Checked

Checked

No spender

AGENT_MINTER

burn

Does not block

Unfreezes what the burn needs

Not applicable

Not applicable

No spender

AGENT_BURNER

forcedTransfer

Blocks

Overridden

Checked

Not checked. The compliance is notified.

No spender

AGENT_FORCED_TRANSFER

recoveryAddress

Blocks

Carried to the new wallet

Not checked. The position stays with the same identity when the identity of the lost wallet is named, and moves to the named identity otherwise.

Not checked. The compliance is notified.

No spender

AGENT_RECOVERY_ADDRESS

Cross-chain validation

A per-chain issuance pause blocks

Not applicable

Checked at issuance

Checked at issuance: they narrow the amount range

Checked at issuance, if a spender is named

The sender's identity, or AGENT

How to read it:

  • Holder operations pass both gates. There is no path by which a holder moves tokens to an ineligible recipient or past a rule.

  • Agent operations are the servicing operations of the register. A forced transfer executes a decision that overrides the holder: a court order, the correction of an error. It bypasses freezes and rules by design, and it is gated by a dedicated role. It still requires an eligible recipient, and it still tells the compliance, so positions stay correct.

  • The pause halts every movement between holders, including forced transfers and recovery. Mints and burns stay possible while the token is paused.

On a satellite chain

Neither gate runs on a satellite. T-REX Lite has no registry and no rules of its own. The compliance contract linked to it stores the validations it receives and decides nothing. Both gates run on the reference chain when the validation for a movement is issued: the recipient's eligibility, the rules against the global position set, the spender if one is named. T-REX Lite then executes against that validation and nothing else. A movement the gates would refuse receives no validation and never executes.

Statements to avoid

Statement
What is true

"The wallet is whitelisted."

The identity behind the address holds valid claims from trusted Claim Issuers for the required topics.

"The investor has the KYC flag."

Eligibility is not a boolean stored somewhere. It is evaluated from claims, per topic, at each transfer.

"The protocol issued the KYC."

Claim Issuers sign claims. The protocol keeps the registries and checks the claims.

"The transfer passed the compliance checks."

It passed two gates: eligibility, then compliance. Say which one is meant.

"Compliance checks the sender and the receiver."

Rules can depend on the complete distribution of the asset, and the spender is checked on transferFrom.

Last updated