> 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/guides/issuer/11.-add-a-compliance-rule.md).

# 11. Add a compliance rule

Adding a compliance rule binds a module to the asset's compliance and configures it. From that moment the module takes part in every movement: a rule module limits the amount that may move, a spender module decides who may execute for a holder, a tracker module records what happened. The Compliance Owner, usually the Issuer, does this at launch and whenever the terms of the asset require a new rule.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                                                             |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Triggered by**  | Compliance Owner                                                                                                                                                                                                                                                                                                                                            |
| **Actors**        | Compliance Owner, Issuer, Investor, Platform                                                                                                                                                                                                                                                                                                                |
| **Contracts**     | `ModularCompliance`, the compliance module                                                                                                                                                                                                                                                                                                                  |
| **Preconditions** | The token suite is deployed (see [01](/guides/issuer/01.-deploy-a-token-suite.md)); the caller holds `OWNER` in the asset's domain (see [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)); the module is deployed: a shared module governed by Network governance, or an instance the Issuer deployed itself; fewer than 25 modules are bound |
| **Result**        | The module is bound to the compliance, configured, and consulted on every movement of the asset                                                                                                                                                                                                                                                             |

## Actors and roles

| Actor            | Role in this flow                                                                                           | What they hold                                                 |
| ---------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| Compliance Owner | Binds and configures the module.                                                                            | `OWNER` on the `ModularCompliance`                             |
| Issuer           | Decides which rule the asset needs and with which settings. Usually the same party as the Compliance Owner. | The terms of the asset                                         |
| Investor         | Is affected: the rule applies to its next movement. Does not transact.                                      | A position, or none yet                                        |
| Platform         | Encodes the configuration calls, runs the reads and submits with the Compliance Owner's key.                | The Compliance Owner's signing key, or a key authorized for it |

## Sequence

```mermaid
sequenceDiagram
    participant Platform
    participant Owner as Compliance Owner
    participant MC as ModularCompliance
    participant Module as Compliance module

    Platform->>MC: isModuleBound(module), getModules() (read)
    Platform->>Module: moduleTypes(), name() (read)
    Platform->>Owner: checks passed, submit
    Owner->>MC: addAndSetModule(module, interactions)
    MC->>MC: caller holds OWNER, at most 5 interactions
    MC->>MC: module not zero, fewer than 25 bound, not already bound
    MC->>Module: isPlugAndPlay(), canComplianceBind(MC)
    MC->>Module: moduleTypes()
    MC->>Module: bindCompliance(MC)
    MC-->>Platform: ModuleTypesRecorded, ModuleAdded
    MC->>Module: each configuration call
    MC-->>Platform: ModuleInteraction for each call
```

## Step by step

### Step 1. Check the module and prepare its settings

**Who:** Platform, on behalf of the Compliance Owner

**Call:** Off-chain, using read calls

**Before the call:** The Issuer has chosen the module and its settings.

**What happens:** The Platform reads `ModularCompliance.isModuleBound(module)`, which must be false, and `ModularCompliance.getModules()`, whose length must be below 25. It reads `Module.name()` and `Module.moduleTypes()` to confirm what the module is and which of `RULE`, `SPENDER` and `TRACKER` it declares. It encodes each configuration call the module needs, for example `setMaxBalance(max)` for the `MaxBalancePerIdentityModule`.

For an asset that already has holders, the Platform also evaluates the effect of the rule on existing positions. A rule does not move or remove anything. It limits the next movements. With a cap, an identity already above the cap keeps its position, can send, and can no longer receive.

**What changes:** Nothing on-chain.

### Step 2. Bind and configure the module

**Who:** Compliance Owner (`OWNER`)

**Call:** `ModularCompliance.addAndSetModule(module, interactions)`, where `interactions` is the list of encoded configuration calls. A module that needs no settings is bound with `ModularCompliance.addModule(module)`.

**Before the call:** Step 1 passed.

**What the contract checks:**

1. The caller holds `OWNER` in the asset's domain. Otherwise the call reverts with `AccessManagedUnauthorized`.
2. There are at most five configuration calls. Otherwise `ArraySizeLimited`.
3. The module is not the zero address. Otherwise `ZeroAddress`.
4. Fewer than 25 modules are bound. Otherwise `MaxModulesReached`.
5. The module is not already bound. Otherwise `ModuleAlreadyBound`.
6. The module accepts this compliance: it is plug and play, or `canComplianceBind` returns true. Otherwise `ComplianceNotSuitableForBindingToModule`.
7. The module declares at least one type, and each type once. Otherwise `ModuleHasNoType` or `DuplicateModuleType`.
8. The module itself accepts the binding: it was not already bound to this compliance on its side. Otherwise `ComplianceAlreadyBound`.
9. Each configuration call succeeds. Otherwise the whole transaction reverts with the module's error.

**What changes:** The compliance adds the module to its set and to the list of each type it declares. The module records the binding. The compliance emits `ModuleTypesRecorded(module, types)` and `ModuleAdded(module)`, then `ModuleInteraction(module, callData)` for each configuration call. The module emits its own events, for example `MaxBalanceSet`. From the next movement, the module is called for what it declared.

### Step 3. Change the settings later

Optional, and later in the life of the asset.

**Who:** Compliance Owner (`OWNER`)

**Call:** `ModularCompliance.callModuleFunction(callData, module)`

**Before the call:** The module is bound. The Platform has encoded the configuration call.

**What the contract checks:**

1. The caller holds `OWNER`. Otherwise `AccessManagedUnauthorized`.
2. The module is bound to this compliance. Otherwise `ModuleNotBound`.
3. The call succeeds on the module. Otherwise the transaction reverts with the module's error.

**What changes:** The module updates its settings for this compliance. The compliance emits `ModuleInteraction(module, callData)`.

## State after the flow

* `ModularCompliance.isModuleBound(module)` is true and `ModularCompliance.getModules()` includes the module.
* `ModularCompliance.getModulesByType(type)` includes the module for each type it declares.
* The module holds its settings for this compliance. For the per-identity cap, `maxBalanceOf(compliance)` returns the cap.
* `ModularCompliance.canTransfer(from, to, amount)` reflects the new rule from now on.
* No balance and no position has changed.

## Failure modes

| Symptom                                                      | Cause                                                                                         | Fix                                                                                                        |
| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Reverts with `AccessManagedUnauthorized`                     | The caller does not hold `OWNER` in the asset's domain (check 1)                              | Grant the role: [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)                             |
| Reverts with `ArraySizeLimited`                              | More than five configuration calls in `addAndSetModule` (step 2, check 2)                     | Send five, then the rest with `callModuleFunction`                                                         |
| Reverts with `MaxModulesReached`                             | 25 modules are already bound (step 2, check 4)                                                | Remove a module that is no longer needed ([12](/guides/issuer/12.-remove-a-compliance-rule.md))            |
| Reverts with `ModuleAlreadyBound`                            | The module is already in the set (step 2, check 5)                                            | Use `callModuleFunction` to change its settings                                                            |
| Reverts with `ComplianceNotSuitableForBindingToModule`       | The module requires a precondition that this compliance does not meet (step 2, check 6)       | Read the module's documentation and meet the precondition                                                  |
| Reverts with `ModuleHasNoType` or `DuplicateModuleType`      | The module's declaration of types is empty or repeats a type (step 2, check 7)                | The module is faulty. Use a corrected deployment                                                           |
| Reverts with `ComplianceAlreadyBound`                        | The module still holds a binding to this compliance, after a forced removal (step 2, check 8) | Bind a new deployment of the module                                                                        |
| Reverts with `OnlyBoundComplianceCanCall`                    | A configuration function was called on the module directly, not through the compliance        | Use `callModuleFunction`                                                                                   |
| After binding, transfers to holders above a cap revert       | The rule applies to existing positions as well                                                | Intended. Raise the cap, or let those holders reduce their position                                        |
| After binding a spender module, every `transferFrom` reverts | The module starts closed and has no entry yet                                                 | Bind it with its first entries in the same transaction. See [26](/guides/issuer/26.-authorize-spenders.md) |

## Common misunderstandings

> **Wrong:** "A new rule applies to new holders only."
>
> **Right:** A rule is evaluated at each movement against the current state. It applies to every holder from its binding. It does not undo existing positions: it limits what can move next.

> **Wrong:** "The rule checks the wallet's balance."
>
> **Right:** A rule receives the identities of both sides and reads the compliance ledger. A cap per identity counts everything the identity owns: its own address, every linked wallet, every chain, and what open validations already promise to it.

> **Wrong:** "Adding a compliance rule changes who is eligible."
>
> **Right:** Eligibility is the other gate. It is configured in the `TREXRegistry`, with claim topics and trusted issuers. A compliance module limits movements between holders that are already eligible.

> **Wrong:** "The module is the Issuer's contract."
>
> **Right:** A module is one deployment that serves every compliance bound to it, with separate settings for each. The shared modules are deployed and governed by Network governance, which is who can upgrade their code. An Issuer that wants sole control over a rule deploys its own instance of the module.

## Integration notes

* Use `addAndSetModule` for any module that is not usable unconfigured, so that there is no interval between binding and configuration.
* Encode a configuration call as the module's function with its arguments. The compliance forwards it as is.
* Read `ModularCompliance.canTransfer(from, to, amount)` after binding, for a few representative movements, to confirm the rule behaves as intended.
* Index `ModuleAdded`, `ModuleTypesRecorded` and `ModuleInteraction`. `ModuleInteraction` carries the full call, so the settings of every module can be reconstructed from events.
* Settings belong to the binding. A module removed and bound again starts from empty settings.
* A module that is upgraded and changes the types it declares takes effect in this compliance only after `ModularCompliance.resyncModuleTypes(module)`.

## Related flows

* [12 Remove a compliance rule](/guides/issuer/12.-remove-a-compliance-rule.md) : the inverse operation
* [26 Authorize spenders](/guides/issuer/26.-authorize-spenders.md) : binding a spender module with its list
* [01 Deploy a token suite](/guides/issuer/01.-deploy-a-token-suite.md) : modules can be bound at deployment
* [03 Configure required claim topics](/guides/issuer/03.-configure-required-claim-topics.md) : the eligibility gate
* [10 Transfer between investors](/guides/positions/10.-transfer-between-investors.md) : where the rule is evaluated


---

# 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/guides/issuer/11.-add-a-compliance-rule.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.
