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

06. Register an investor for an asset

Registering an investor attributes an address to an identity for one asset. It is a local registration, made by an agent of the Issuer in the asset's own registry. Most holders do not need it: an investor that links its wallets to its ONCHAINID, or holds in the ONCHAINID itself, is recognized by every asset without any registration. An agent registers an address that cannot link itself and has to hold the asset: a custody account, a contract that cannot sign, an omnibus wallet.

At a glance

Triggered by

Registry Agent

Actors

Registry Agent, Issuer, Investor, Platform

Contracts

TREXRegistry, IdentityRegistryStorage, IdentityFactory (read only)

Preconditions

The token suite is deployed and commissioned (see 01 and 25); the caller holds AGENT in the asset's domain; the Investor has an ONCHAINID (see 02); the address has no local registration yet; the Issuer has verified, under its own process, that the address belongs to the Investor

Result

For this asset, and for every asset that shares its identity storage, the address resolves to the Investor's identity

Actors and roles

Actor
Role in this flow
What they hold

Registry Agent

Submits the registration. Typically the same operator as the Token Agent.

AGENT on the TREXRegistry

Issuer

Decides that the address is to be attributed to the Investor for its asset. Does not transact in this flow.

Its own verification of who controls the address

Investor

Is the subject of the registration. Does not transact.

An ONCHAINID

Platform

Runs the reads and submits with the Registry Agent's key.

The Registry Agent's signing key, or a key authorized for it

Sequence

Step by step

Step 1. Check the address

Who: Platform, on behalf of the Registry Agent

Call: Off-chain, using read calls

Before the call: The Issuer has established that the address belongs to the Investor.

What happens: The Platform reads TREXRegistry.isLocallyRegistered(address), which must be false. It reads TREXRegistry.identity(address) to see what the address resolves to before the registration. If it already returns the Investor's identity, the address is linked globally and no registration is needed. If it returns another identity, the registration overrides the global link for this asset: the Platform reports it to the Issuer before going further. It reads IdentityFactory.identityTypeOf(identity), which is not zero for an identity the factory created. It also reads Token.balanceOf(address): registering an address that already holds tokens changes the identity those tokens are attributed to.

What changes: Nothing on-chain.

Step 2. Register the address

Who: Registry Agent (AGENT)

Call: TREXRegistry.registerIdentity(userAddress, identity, country)

Before the call: Step 1 passed. The country argument is required by the standard interface and is ignored. Pass zero.

What the contract checks:

  1. The caller holds AGENT in the asset's domain. Otherwise the call reverts with AccessManagedUnauthorized.

  2. The identity storage accepts the registry as a writer: the registry holds IRS_WRITER in the storage's domain, which commissioning grants. Otherwise AccessManagedUnauthorized, naming the registry.

  3. Neither the address nor the identity is zero. Otherwise ZeroAddress.

  4. The address has no local registration in the storage. Otherwise AddressAlreadyStored.

The contract does not check that the identity was created by the IdentityFactory, that it holds any claim, or that the address is controlled by its owner. Those are the Issuer's responsibility. The recommendation is to register only identities created by the IdentityFactory: an identity from another source is unlikely to be accepted elsewhere in the ecosystem.

What changes: The storage records the address under the identity and emits IdentityStored(address, identity). When the address is linked globally to a different identity, it also emits IdentityOverridden(address, globalIdentity, localIdentity). The registry emits IdentityRegistered(address, identity). From now on, for this asset, the address resolves to the registered identity.

Step 3. Confirm the effect

Who: Platform

Call: Off-chain, using read calls

Before the call: Step 2 is confirmed.

What happens: The Platform reads TREXRegistry.identity(address), which returns the registered identity, and TREXRegistry.isVerified(address), which is true only if that identity holds a valid claim from a trusted Claim Issuer for every required topic.

What changes: Nothing on-chain.

State after the flow

  • TREXRegistry.isLocallyRegistered(address) is true, and TREXRegistry.contains(address) is true.

  • TREXRegistry.identity(address) returns the registered identity, whatever the global identity registry says for that address.

  • The registration applies to every asset whose registry is bound to the same IdentityRegistryStorage.

  • TREXRegistry.isVerified(address) reflects the claims of the registered identity. The registration has given no eligibility by itself.

  • Nothing changed in the IdentityFactory. The address is not linked to the identity for any other Issuer's asset.

  • If the address held tokens, the compliance attributes them to the registered identity from the next movement through that address.

Failure modes

Symptom
Cause
Fix

Reverts with AccessManagedUnauthorized

The caller does not hold AGENT in the asset's domain (step 2, check 1)

Grant the role: 25

Reverts with AccessManagedUnauthorized, naming the registry

The registry does not hold IRS_WRITER on the identity storage: the suite is not commissioned, or the storage is shared and sits in another domain (step 2, check 2)

Commission the suite: 25

Reverts with ZeroAddress

The address or the identity is zero (step 2, check 3)

Pass both

Reverts with AddressAlreadyStored

The address already has a local registration (step 2, check 4)

To change it, use 17

The address is registered and still cannot receive

The registered identity lacks a valid claim for a required topic

Registration is attribution. Eligibility comes from claims: 05

The address is registered and still cannot receive, with valid claims

The address is a wallet its owner revoked in the global identity registry. A local registration does not reinstate it

Use another address

A registration made for one asset appears on another

Both registries are bound to the same identity storage

Intended for an Issuer's assets that share a storage. Use separate storages to separate them

The registration succeeded and the address is not recognized by another Issuer's asset

A local registration is local to one storage

The other Issuer's agent registers the address in its own storage. An address that can link itself is linked globally by its owner instead: 07

An IdentityOverridden event was emitted

The address is linked globally to a different identity than the one registered

Check that the override is intended. If not, remove it: 18

Common misunderstandings

Wrong: "Every investor has to be registered before it can hold the asset."

Right: No registration is needed for an address that is an ONCHAINID or a wallet its owner linked to one. The registry resolves it through the global identity registry. Registration by an agent is for addresses that cannot link themselves.

Wrong: "An omnibus wallet is registered to the investors behind it."

Right: An omnibus wallet, for example at a centralized exchange, is registered to the identity of the entity that operates it. The register records that entity as the holder. The individual owners behind the omnibus position are in the operator's own books.

Wrong: "Registering the investor whitelists the wallet."

Right: Registration says whose the address is, for this asset. Whether that identity may hold the asset is decided from its claims, at each transfer. A registered address whose identity has no valid claims is not verified.

Wrong: "The agent registers the investor's country."

Right: The registry stores no country. The country argument is ignored. Where an investor resides is attested by a claim on its identity, and any rule about it is a compliance module.

Wrong: "A local registration and a global link are the same record."

Right: They are two records in two contracts. The global link is made by the owner, in the IdentityFactory, for every asset. The local registration is made by an agent, in the asset's identity storage, for the assets that share it. When both exist, the local one takes precedence for those assets.

Integration notes

  • Read identity(address) before registering. If it already returns the right identity, do nothing.

  • Use TREXRegistry.batchRegisterIdentity(addresses, identities, countries) for many addresses. It is gated by the same role and reverts as a whole if one entry fails. The three lists have the same length.

  • Index IdentityStored together with IdentityOverridden. An override logs both, in that order, and the second is what tells an indexer that the asset departs from the global link.

  • The token registers an address itself in one case: the recovery of a lost wallet registers the new address when it resolves nowhere.

  • Do not register an address to give it eligibility. It has no such effect.

  • Keep the Issuer's evidence that the address belongs to the identity. The contract takes the agent's word.

  • An address that can sign, or that can send a message from its own chain, links itself in the global identity registry. Registration by an agent is not the path for it.

Last updated