> For the complete documentation index, see [llms.txt](https://docs.t-rex.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.t-rex.network/build/indexing-events-and-supply-accounting.md).

# Indexing: events and supply accounting

This page is for teams that build an indexer, a data product, a cap table or a reporting system on T-REX assets. It lists the events of each contract with their exact signatures, shows how to rebuild balances, supply, holders, positions per identity and cross-chain validations from them, and says where a view has to be read instead.

## Before the tables

* **One suite, five emitters.** `Token`, `TREXRegistry`, `IdentityRegistryStorage` and `ModularCompliance` are four proxies, named by `TREXSuiteDeployed`. The fifth is the suite's access manager, `Token.authority()`. One movement often logs on several of them.
* **Identity events come from shared contracts.** `IdentityFactory`, `ERC734Validator` and `KeyApprovalModule` each serve every identity. An identity emits none of them itself: filter on the indexed identity argument.
* **A satellite wallet is a `bytes` value and a `bytes32` key.** The value is its ERC-7930 interoperable address, in canonical encoding. The key (`walletKey`, `fromKey`, `toKey`) is its `keccak256`. A `chainKey` is the `keccak256` of the chain's ERC-7930 prefix with an empty address, and `ChainRegistered` maps it back.

## Events by contract

### `Token`

| Event                                                                                                                                 | Emitted when                                                            |
| ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `Transfer(address indexed from, address indexed to, uint256 value)`                                                                   | A balance of the reference chain changes                                |
| `Approval(address indexed owner, address indexed spender, uint256 value)`                                                             | `approve`, `permit`                                                     |
| `Paused(address account)`, `Unpaused` (same argument)                                                                                 | Halt, resume. A token is deployed paused                                |
| `UpdatedTokenInformation(string _newName, string _newSymbol, uint8 _newDecimals, string _newVersion, address indexed _newOnchainID)`  | Deployment, change of name, symbol or ONCHAINID                         |
| `IdentityRegistryAdded(address indexed _identityRegistry)`, `ComplianceAdded(address indexed _compliance)`                            | The setters only, not deployment                                        |
| `AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner)`                                         | An address is frozen or unfrozen                                        |
| `TokensFrozen(address indexed _userAddress, uint256 _amount)`, `TokensUnfrozen` (same arguments)                                      | An amount is frozen, released                                           |
| `RecoverySuccess(address indexed _lostWallet, address indexed _newWallet, address indexed _investorOnchainID)`                        | End of a recovery                                                       |
| `ForcedTransfer(address indexed agent)`                                                                                               | After the `Transfer` of a forced transfer                               |
| `IdentityTransfer(address indexed identity, address indexed from, address indexed to, uint256 amount)`                                | After the `Transfer` of an `identityTransfer`                           |
| `ReservedForValidation(bytes32 indexed walletKey, bytes wallet, uint256 amount)`, `ReleasedFromValidation` (same arguments)           | A validation reserves part of a satellite wallet, gives it back         |
| `HeldInTransit(bytes32 indexed fromKey, uint256 indexed validationId, bytes fromWallet, uint256 amount)`                              | A burn is reported before its mint                                      |
| `ReturnedInTransit(bytes32 indexed walletKey, uint256 indexed validationId, bytes wallet, uint256 amount)`                            | A held amount returns to its wallet                                     |
| `BridgedTransfer(bytes32 indexed fromKey, bytes32 indexed toKey, uint256 indexed validationId, bytes from, bytes to, uint256 amount)` | Settlement between two satellite wallets                                |
| `SettledToNative(bytes32 indexed fromKey, address indexed to, uint256 indexed validationId, bytes fromWallet, uint256 amount)`        | Settlement from a satellite wallet to an address of the reference chain |
| `ChainRegistered(bytes32 indexed chainKey, bytes2 chainType, bytes chainReference)`                                                   | First sight of a chain                                                  |
| `RouteSet(bytes32 indexed chainKey, address indexed gateway)`, `PeerSet(bytes32 indexed chainKey, bytes peer)`                        | The gateway, the T-REX Lite address, for a chain                        |
| `TrustedGatewayRegistrySet(address trustedGatewayRegistry)`                                                                           | Once, at deployment                                                     |
| `ValidationRoutePinned(uint256 indexed validationId, bytes32 indexed chainKey, address gateway)`                                      | First dispatch of a validation to a chain                               |
| `ProtocolMessageSent(MessageTypesLib.Message indexed messageType, bytes32 indexed chainKey, bytes32 sendId)`                          | A message leaves for a satellite                                        |
| `ProtocolMessageReceived(MessageTypesLib.Message indexed messageType, bytes32 indexed chainKey, bytes32 receiveId)`                   | A message from a satellite was processed                                |
| `BurnProofReceived(bytes32 indexed originChainKey, bytes burnedWallet, address indexed nativeWallet, uint256 amount)`                 | A burn proof arrives                                                    |

Defined in `EventsLib`, emitted by the delegation and recall paths:

| Event                                                                                         | Emitted when                                          |
| --------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `DelegatedOut(address indexed holder, bytes32 indexed toKey, bytes toWallet, uint256 amount)` | After the `Transfer(holder, 0x0)` of a delegation-out |
| `Recalled(bytes32 indexed fromKey, address indexed holder, bytes fromWallet, uint256 amount)` | After the `Transfer(0x0, holder)` of a recall         |

`MessageTypesLib.Message` is a `uint8` in the ABI: 0 `COMPLIANCE_VALIDATION`, 1 `MINT_INSTRUCTION`, 2 `RECALL_INSTRUCTION`, 3 `SETTLEMENT_NOTIFICATION`, 4 `BURN_PROOF`.

### `TREXRegistry`

| Event                                                                                                                                                                                        | Emitted when                                                                                     |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `IdentityRegistered(address indexed _investorAddress, IIdentity indexed _identity)`, `IdentityRemoved` (same arguments)                                                                      | An address is registered for this asset, its registration deleted                                |
| `IdentityUpdated(IIdentity indexed _oldIdentity, IIdentity indexed _newIdentity)`                                                                                                            | A registration names another identity. The address is in `InvestorIdentityChanged`               |
| `ClaimTopicAdded(uint256 indexed _claimTopic)`, `ClaimTopicRemoved` (same argument)                                                                                                          | A required topic is added, removed                                                               |
| `ClaimTopicAddedForIdentityType(uint256 indexed identityType, uint256 indexed claimTopic)`, `ClaimTopicRemovedForIdentityType` (same arguments)                                              | The same, for one type of identity                                                               |
| `TrustedIssuerAdded(address indexed _trustedIssuer, uint256[] _claimTopics)`, `ClaimTopicsUpdated` (same arguments)                                                                          | A Claim Issuer is trusted for topics, its topics replaced                                        |
| `TrustedIssuerRemoved(address indexed _trustedIssuer)`                                                                                                                                       | It is no longer trusted                                                                          |
| `EligibilityChecksDisabled()`, `EligibilityChecksEnabled()`                                                                                                                                  | Checks switched off, on. The second also at deployment                                           |
| `IdentityStorageSet(address indexed _identityStorage)`, `ClaimTopicsRegistrySet(address indexed _claimTopicsRegistry)`, `TrustedIssuersRegistrySet(address indexed _trustedIssuersRegistry)` | Deployment, and a change of storage for the first. The last two carry the registry's own address |

### `IdentityRegistryStorage`

A storage can serve several registries. Its events then concern every asset wired to it.

| Event                                                                                                                                                                                                    | Emitted when                                                         |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `IdentityStored(address indexed _investorAddress, IIdentity indexed _identity)`, `IdentityUnstored` (same arguments), `IdentityModified(IIdentity indexed _oldIdentity, IIdentity indexed _newIdentity)` | The storage side of the registration events, logged just before them |
| `InvestorIdentityChanged(address indexed investor)`                                                                                                                                                      | With `IdentityModified`: the address                                 |
| `IdentityOverridden(address indexed investor, IIdentity indexed globalIdentity, IIdentity indexed localIdentity)`                                                                                        | A registration names another identity than the address's global link |
| `IdentityOverrideReleased(address indexed investor, IIdentity indexed localIdentity, IIdentity indexed globalIdentity)`                                                                                  | That divergence ends                                                 |
| `IdentityRegistryBound(address indexed _identityRegistry)`, `IdentityRegistryUnbound` (same argument)                                                                                                    | A registry is bound, unbound                                         |

`CountryUpdated` and `CountryModified` are declared by the ERC-3643 interfaces and never emitted: T-REX stores no country.

### `ModularCompliance`

| Event                                                                                                                                                                           | Emitted when                                                                                                                                                                                                           |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TokenBound(address _token)`, `TokenUnbound` (same argument)                                                                                                                    | Bound to its token, at deployment. Unbound                                                                                                                                                                             |
| `ModuleAdded(address indexed module)`, `ModuleRemoved`, `ModuleForceRemoved` (same argument)                                                                                    | A module is bound, removed, removed without being called                                                                                                                                                               |
| `ModuleTypesRecorded(address indexed module, IModule.ModuleType[] moduleTypes)`                                                                                                 | A module is filed under its types                                                                                                                                                                                      |
| `ModuleInteraction(address indexed target, bytes data)`                                                                                                                         | A module setting is changed through the compliance                                                                                                                                                                     |
| `WalletOwnerChanged(address indexed wallet, address indexed previousIdentity, address indexed identity, uint256 amount)`                                                        | The ledger moves an address's balance to the identity the registry now names                                                                                                                                           |
| `PositionUnresolved(bytes32 indexed wallet, uint256 amount)`                                                                                                                    | One side of a movement resolves to no identity. On the receiving side `amount` is added to the gap, on the sending side it is subtracted. `wallet` is the address padded on the left, or the key of a satellite wallet |
| `PositionUnderflow(address indexed identity, uint256 missing)`                                                                                                                  | A debit exceeds the position: the position goes to zero and `missing` is subtracted from the gap                                                                                                                       |
| `PositionFixed(address indexed from, address indexed to, uint256 amount)`                                                                                                       | The `OWNER` moves position. The zero address is the gap                                                                                                                                                                |
| `TransferValidationIssued(uint256 indexed validationId, bytes from, bytes to, bytes spender, uint256 amountMin, uint256 amountMax, uint64 expiry, uint64 reconciliationWindow)` | A validation is issued                                                                                                                                                                                                 |
| `SettlementNotified(bytes32 indexed originChainKey, uint256 indexed validationId, bytes from, bytes to, uint256 amount)`                                                        | A report from a satellite reaches the compliance                                                                                                                                                                       |
| `ValidationLegConfirmed(uint256 indexed validationId, bytes32 indexed chainKey, uint256 amount)`, `ValidationSettled` (same arguments)                                          | The first of two reports is recorded. Every report arrived                                                                                                                                                             |
| `LateReconciliation(uint256 indexed validationId, bytes32 indexed chainKey)`, `ReplayedSettlement` (same arguments)                                                             | A report after the discard. A report for an unknown validation, for a leg already applied, or for a validation already `Resolved`                                                                                      |
| `ValidationDiscarded(uint256 indexed validationId)`                                                                                                                             | The Validation Keeper discards an expired validation                                                                                                                                                                   |
| `ValidationResolved(uint256 indexed validationId, uint256 returnedAmount)`                                                                                                      | The Validation Keeper gives up on a half-reported transfer                                                                                                                                                             |
| `ValidationIssuancePaused(bytes32 indexed chainKey)`, `ValidationIssuanceUnpaused` (same argument)                                                                              | Issuance for a chain stops, resumes                                                                                                                                                                                    |
| `DefaultValidityWindowSet(uint64 duration)`, `ReconciliationWindowSet(bytes32 indexed chainKey, uint64 duration)`                                                               | A window is set                                                                                                                                                                                                        |

A module logs at its own address and serves several compliances: `ComplianceBound(address indexed compliance)`, `ComplianceUnbound` (same argument), and its own settings.

### Factory, versions, roles, gateways

| Emitter                       | Event                                                                                                                                                         | Emitted when                                                                         |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `TREXFactory`                 | `TREXSuiteDeployed(address indexed token, address registry, address irs, address mc, string salt)`                                                            | A suite is deployed                                                                  |
|                               | `IsolatedSuiteDeployed(address indexed token, ITREXImplementationAuthority.SuiteBeacons beacons)`                                                             | After it, for a suite with its own beacons                                           |
|                               | `Deployed(address indexed addr)`                                                                                                                              | Each proxy the factory deploys: access manager, storage, registry, compliance, token |
|                               | `ImplementationAuthoritySet(address implementationAuthority)`, `IdFactorySet(address idFactory)`, `TrustedGatewayRegistrySet(address trustedGatewayRegistry)` | A pointer of the factory is set                                                      |
| `TREXImplementationAuthority` | `VersionPublished(Version version, ITREXImplementationAuthority.SuiteImplementations implementations)`, `SuiteUpgraded` (same arguments)                      | A version is archived. The shared beacons move to it                                 |
|                               | `BeaconsDeployed(ITREXImplementationAuthority.SuiteBeacons beacons)`                                                                                          | Once, at construction                                                                |
| `TREXAccessManager`           | `DomainCreated(uint32 indexed domainId, string name)`, `DomainAssigned(uint32 indexed domainId, address indexed target)`                                      | A role domain is created, a contract assigned to it                                  |
| `TrustedGatewayRegistry`      | `TrustedGatewaySet(address indexed gateway, bool trusted)`                                                                                                    | The network trusts a gateway, or stops                                               |

`Version` is a `uint24`. Both structs are five addresses: token, registry, storage, compliance, access manager.

### `IdentityFactory`

| Event                                                                                                                        | Emitted when                                                     |
| ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `IdentityDeployed(address indexed identity, bytes account, uint256 indexed identityType, address indexed deployer)`          | An identity is created                                           |
| `IdentityTypeRecorded(address indexed identity, uint256 identityType)`                                                       | Same transaction: its type, recorded once                        |
| `AccountLinked(bytes account, address indexed identity, address caller)`, `AccountRevoked` (same arguments)                  | A wallet is linked. A wallet is revoked, for good                |
| `PendingCrossChainLinkProposed(bytes account, address indexed identity, uint256 expiry, address gateway, bytes32 receiveId)` | A wallet of another chain asks for a link. Nothing is linked     |
| `CrossChainLinkConfirmed(bytes account, address indexed identity)`                                                           | The identity accepts. Follows the `AccountLinked` of that wallet |
| `CrossChainLinkRejected(bytes account, address indexed identity, uint256 expiry)`                                            | The identity declines                                            |

Settings events, declared in `IIdentityFactory`: `IdentityTypePolicySet`, `IdentityTypePolicyRemoved`, `IdentityTypeModulesSet`, `TrustedGatewaySet` (another signature than the registry's), `TrustedVerifierSet`, `BeaconInitialized`, `BeaconUpgraded`.

### `ERC734Validator`

It holds the keys and claims of every identity. `Identity.registryModule()` returns its address.

| Event                                                                                                                                                                                                                                        | Emitted when                                                                      |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `KeyAdded(address indexed account, bytes32 indexed keyHash, uint256 indexed purpose, uint256 keyType, bytes signerData, bytes clientData, address caller)`                                                                                   | A key receives a purpose on the identity `account`                                |
| `KeyRemoved(address indexed account, bytes32 indexed keyHash, uint256 indexed purpose, address caller)`                                                                                                                                      | A purpose is removed from a key                                                   |
| `KeyUsed(address indexed account, bytes32 indexed keyHash, bytes32 userOpHash)`                                                                                                                                                              | A user operation of `account` validates against the key                           |
| `ClaimAdded(address indexed identity, bytes32 claimId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, Structs.ClaimData data, string uri, address caller)`, `ClaimChanged`, `ClaimRemoved` (same arguments) | A claim is added to `identity`, replaced, removed                                 |
| `ClaimRevoked(bytes32 indexed digest, address indexed issuer, address caller)`                                                                                                                                                               | The Claim Issuer `issuer` revokes a claim by its digest                           |
| `ClaimAddedTo(address indexed identity, uint256 topic, bytes signature, Structs.ClaimData data, address caller)`                                                                                                                             | A Claim Issuer delivered the claim itself. Follows `ClaimAdded` or `ClaimChanged` |

`Structs.ClaimData` is `(uint256 issuedAt, uint256 validUntil, bytes32 metadataHash, bytes payload)`. `claimId` is `keccak256(abi.encode(issuer, topic))`.

### `KeyApprovalModule`

| Event                                                                                                                                                                 | Emitted when                                         |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `ExecutionRequested(address indexed account, uint256 indexed executionId, address indexed to, uint256 value, bytes data, address proposer)`                           | A key queues a call of the identity `account`        |
| `Approved(address indexed account, uint256 indexed executionId, bool approved, address approver)`                                                                     | The call is approved or rejected                     |
| `Executed(address indexed account, uint256 indexed executionId, address indexed to, uint256 value, bytes data, address executor)`, `ExecutionFailed` (same arguments) | The call ran, reverted                               |
| `QueueInvalidated(address indexed account, uint256 firstValidId)`                                                                                                     | An uninstall voids the requests below `firstValidId` |

## Balances

`balanceOf(address)` is `Transfer` in minus `Transfer` out, with no special handling. What needs care is the meaning of a `Transfer` from or to the zero address.

| Movement                                           | `Transfer`                         | Same transaction, on the token                            | `totalSupply` |
| -------------------------------------------------- | ---------------------------------- | --------------------------------------------------------- | ------------- |
| Mint                                               | `(0x0, to, amount)`                |                                                           | Up            |
| Burn                                               | `(from, 0x0, amount)`              | `TokensUnfrozen` before, if frozen tokens are taken       | Down          |
| Transfer                                           | `(from, to, amount)`               | `IdentityTransfer` after, when the identity ordered it    | Same          |
| Forced transfer                                    | `(from, to, amount)`               | `TokensUnfrozen` before if needed, `ForcedTransfer` after | Same          |
| Recovery                                           | `(lostWallet, newWallet, balance)` | See "Frozen amounts"                                      | Same          |
| Delegation-out                                     | `(holder, 0x0, amount)`            | `DelegatedOut` after                                      | Same          |
| Recall                                             | `(0x0, holder, amount)`            | `Recalled` after                                          | Same          |
| Settlement from a satellite to the reference chain | `(0x0, to, amount)`                | `SettledToNative` after                                   | Same          |
| Transfer between satellite wallets                 | None                               | `BridgedTransfer`                                         | Same          |

A `Transfer` from or to the zero address is a mint or a burn only when no `DelegatedOut`, `Recalled` or `SettledToNative` follows it. `BurnProofReceived` announces a proof. It is never a credit by itself: count a recall from the pair `Transfer(0x0, holder)` and `Recalled` only.

## Supply

`totalSupply()` is the whole issuance: the ERC-20 supply of the reference chain plus `totalBridged`. Only a mint or a burn changes it.

```solidity
function totalSupply() public view override(ERC20Upgradeable, IERC20) returns (uint256) {
    return super.totalSupply() + _tokenStorage().totalBridged;
}
```

`totalBridged()` is the sum of every bridged position, in-transit holds included. `totalInTransit()` is the part of it that no wallet holds, and `inTransitOf(validationId)` the amount held for one validation.

The comment on `totalSupply` states the trade-off: "a supply derived by summing `Transfer` events under-reports by `totalBridged`; `DelegatedOut`, `Recalled` and `SettledToNative` are the reconciliation for anyone deriving it that way, and `totalSupply() - totalBridged()` is the native float." And: "`INV-7` sums the buckets to this figure and asserts that escrow is never there; `INV-8` holds `totalBridged` to the positions, whichever transition moved it."

| Figure                           | From events                                                                                                                                                                                               |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Supply of the reference chain    | `Transfer` from `0x0` minus `Transfer` to `0x0`                                                                                                                                                           |
| `totalBridged`                   | `DelegatedOut` minus `Recalled` minus `SettledToNative`                                                                                                                                                   |
| Bridged position of a wallet key | Up: `DelegatedOut`, `BridgedTransfer` (`toKey`), `ReturnedInTransit`. Down: `Recalled`, `SettledToNative`, `HeldInTransit`, and `BridgedTransfer` (`fromKey`) unless a hold exists for its `validationId` |
| `inTransitOf`                    | Set by `HeldInTransit`. Cleared by `ReturnedInTransit` or by the `BridgedTransfer` of that validation                                                                                                     |
| `reservedOf`                     | `ReservedForValidation` minus `ReleasedFromValidation`                                                                                                                                                    |

Identities to assert at any block, each tested as an [invariant](/security-and-trust-model/invariants.md):

* `totalSupply()` = sum of `balanceOf` + `totalBridged()`
* `totalBridged()` = sum of `bridgedBalanceOf` + `totalInTransit()`
* `getFrozenTokens(a)` is at most `balanceOf(a)`
* `balanceOf` of the token's own address is zero: there is no escrow address
* sum of `positionOf` + `positionGap()` = `totalSupply()`

## Frozen amounts

* **Address freeze.** The last `AddressFrozen` gives the state. `_owner` is the caller the token saw: the access manager when the agent acts through it, the token itself when a recovery carries the flag.
* **Frozen amount.** `TokensFrozen` minus `TokensUnfrozen`. Free is balance minus frozen.
* **Automatic unfreeze.** A burn, a forced transfer and a recovery can take frozen tokens. The token releases what the free balance lacks and emits `TokensUnfrozen` for that shortfall before the `Transfer`. No agent calls an unfreeze.
* **Recovery.** In order: `TokensUnfrozen(lostWallet, frozen)`, `Transfer`, `TokensFrozen(newWallet, frozen)`, then for a frozen lost wallet `AddressFrozen(lostWallet, false, token)` and, unless it is frozen already, `AddressFrozen(newWallet, true, token)`, then `RecoverySuccess`. When they apply, the registry logs the registration of the new wallet first and the removal of the lost one just before `RecoverySuccess`.

## Holders and positions per identity

An address resolves to an identity, for one asset, from two sources:

1. **A registration of the asset**, in its storage: `IdentityStored`, `IdentityModified`, `IdentityUnstored`. It takes precedence. `IdentityOverridden` marks a registration that differs from the global link.
2. **The global record**, on the `IdentityFactory`: `AccountLinked`. An identity's own address resolves to itself from `IdentityDeployed`, with no `AccountLinked`. After `AccountRevoked` the wallet still resolves to its identity, so its tokens keep their owner, and it stops being eligible.

The [compliance ledger](/token-and-register/the-compliance-ledger.md) keeps the position of each identity, and emits nothing when it changes in a normal movement. Its events record the exceptions: `WalletOwnerChanged` when the registration of an address that holds tokens changed (the position follows on the next movement through that address, so `ownerOf(address)` can lag the registry), `PositionUnresolved` and `PositionUnderflow` when an amount goes to the gap, `PositionFixed` when the `OWNER` repairs it.

There is no on-chain list of holders. Collect addresses from `Transfer` and satellite wallets from the `bytes` arguments of the bridged events, then group them by identity. A satellite wallet resolves through the global record only: `AccountLinked`, and it keeps its identity after `AccountRevoked`.

| Figure                            | Read at a pinned block                            | Why not from events                                                       |
| --------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------- |
| Identity of an address            | `TREXRegistry.identity(address)`                  | Two sources and a precedence rule                                         |
| Identity of a satellite wallet    | `TREXRegistry.resolveIdentity(bytes)`             | One source: the global record. A registration of the asset does not apply |
| Position of an identity           | `ModularCompliance.positionOf(identity)`          | No event per change                                                       |
| Pending amounts                   | `pendingInOf(identity)`, `pendingOutOf(identity)` | No event names the identities. `validationOf(validationId)` does          |
| Status of a validation            | `statusOf(validationId)`                          | `Expired` is computed from the clock                                      |
| Eligibility                       | `TREXRegistry.isVerified(address)`                | See "Eligibility over time"                                               |
| Eligibility of a satellite wallet | `TREXRegistry.isWalletVerified(bytes)`            | The same                                                                  |

## Cross-chain validations

The compliance emits the event that enters each status. The token logs the effect on the bridged ledger in the same transaction.

| Status                  | Entered with, on the compliance                          | On the token                                                                                                           |
| ----------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `Pending`               | `TransferValidationIssued`                               | `ReservedForValidation` before it. After it, per satellite: `ValidationRoutePinned`, `ProtocolMessageSent`             |
| `AwaitingMint`          | `ValidationLegConfirmed`                                 | `ReleasedFromValidation`, `HeldInTransit`                                                                              |
| `AwaitingBurn`          | `ValidationLegConfirmed`                                 | Nothing                                                                                                                |
| `Settled`               | `ValidationSettled`                                      | `ReleasedFromValidation` if the wallet was still reserved. Then `BridgedTransfer`, or `Transfer` and `SettledToNative` |
| `Expired`               | No event: `Pending` past `releaseAt`, computed when read |                                                                                                                        |
| `Discarded`             | `ValidationDiscarded`                                    | `ReleasedFromValidation`                                                                                               |
| `DiscardedAwaitingMint` | `ValidationLegConfirmed`, `LateReconciliation`           | `HeldInTransit`                                                                                                        |
| `DiscardedAwaitingBurn` | `ValidationLegConfirmed`, `LateReconciliation`           | Nothing                                                                                                                |
| `LateReconciled`        | `ValidationSettled`, `LateReconciliation`                | As `Settled`, with nothing to release                                                                                  |
| `Resolved`              | `ValidationResolved`                                     | `ReturnedInTransit` if the burn had arrived. Otherwise `ReleasedFromValidation` if the wallet was still reserved       |

* Every report from a satellite logs `SettlementNotified` first and `ProtocolMessageReceived` last. A delivery that reverts logs nothing.
* `ValidationIssuancePaused` is logged with a late report whose amount breaches a rule, and with a resolution where only the mint had arrived.
* A replay logs `SettlementNotified`, `ReplayedSettlement`, then `Paused` on the token. Nothing is applied.

The states are explained in [The dual-state lifecycle](/cross-chain-the-distributed-register/the-dual-state-lifecycle.md), and the keeper's operations in [35 Discard or resolve a validation](/guides/cross-chain/35.-discard-or-resolve-a-validation.md).

## Eligibility over time

Eligibility is not an event. `isVerified` computes it at each check from the identity the address resolves to, its claims, the trusted issuers and the required topics. These events can change the answer:

* On the `ERC734Validator`: `ClaimAdded`, `ClaimChanged`, `ClaimRemoved` for the Investor's identity, `ClaimRevoked` by a Claim Issuer, and `KeyAdded`, `KeyRemoved` on a Claim Issuer, since a claim counts only while the key that signed it is a signing key of its issuer.
* On the `TREXRegistry`: the claim topic and trusted issuer events, `EligibilityChecksDisabled`, `EligibilityChecksEnabled`.
* On the `IdentityFactory` and the storage: `AccountLinked`, `AccountRevoked` and the registration events, which change what an address resolves to.

Two changes emit nothing in the protocol: a claim reaching its `validUntil`, and an attestation revoked on an external system such as EAS. Re-read `isVerified` on a schedule, and before any report that depends on it.

## Upgrades and governance

* **Versions.** `VersionPublished` announces, `SuiteUpgraded` activates for every suite on the shared beacons. An isolated suite upgrades through the beacons listed in its `IsolatedSuiteDeployed`: see [27 Upgrade a suite](/guides/issuer/27.-upgrade-a-suite.md).
* **Rules.** `ModuleAdded`, `ModuleRemoved`, `ModuleForceRemoved`, `ModuleInteraction`.
* **Roles.** The access manager of a suite is `Token.authority()`. It emits OpenZeppelin's `RoleGranted` and `RoleRevoked`: see [25 Commission a suite and grant roles](/guides/issuer/25.-commission-a-suite-and-grant-roles.md).
* **Authority.** Every change of access manager emits OpenZeppelin's `AuthorityUpdated(address authority)` on the contract, at deployment too. `transferOwnership` also emits `OwnershipTransferred(address indexed previousOwner, address indexed newOwner)`. `setAuthority`, the path of `AccessManager.updateAuthority`, emits `AuthorityUpdated` only: index that one.

## Practical notes

* **Discover from the factory.** `TREXSuiteDeployed` gives the four addresses of a suite, and `Token.authority()` its access manager. `TREXRegistry.identityFactory()` gives the `IdentityFactory`.
* **Reorgs and finality are the indexer's concern.** The protocol gives no confirmation signal. Pin each view call to the block of the events it complements.
* **Order by log index.** A companion event is recognized by its position after a `Transfer`.

Checklist:

* Every emitter of a suite is indexed, and the shared identity contracts are filtered by identity.
* Mints and burns exclude the `Transfer` events paired with `DelegatedOut`, `Recalled` or `SettledToNative`.
* Supply is `totalSupply()`, and the five identities are asserted.
* Frozen amounts include the automatic `TokensUnfrozen`.
* Positions, pending amounts, eligibility and `Expired` are read from views.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.t-rex.network/build/indexing-events-and-supply-accounting.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
