> 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/reference/token-suite/compliance-modules.md).

# Compliance modules

A compliance module is a contract that implements one rule of an asset for `ModularCompliance`. This page lists the module interface, the base contract every shipped module inherits, and the three modules of the repository. How to write one is in [Write a compliance module](/build/write-a-compliance-module.md).

### At a glance

|                 |                                                                                                                                                   |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Source**      | `contracts/compliance/modular/modules/`: `IModule.sol`, `AbstractModuleUpgradeable.sol`, `ModuleProxy.sol`, and one file per module               |
| **Deployed as** | One implementation behind one `ModuleProxy` (ERC-1967, UUPS). One deployment serves every compliance bound to it, with separate settings for each |
| **Settings**    | Changed by the Compliance Owner through `ModularCompliance.callModuleFunction`, never by a direct call                                            |
| **Upgrade**     | `upgradeToAndCall`, restricted by the access manager given to `initialize`                                                                        |

### `IModule`

| Function                                                       | Called by                                                         | Purpose                                                                                                                     |
| -------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `moduleTypes()`                                                | The compliance, when the module is bound or resynced              | The types the module declares: `RULE`, `SPENDER`, `TRACKER`, in the order of the enum `ModuleType`. At least one, each once |
| `allowedAmount(ctx)`                                           | The compliance, on every `RULE` module. Open to any caller        | The largest amount the rule allows for this movement: `type(uint256).max` for no limit, 0 for a refusal                     |
| `moduleCheckSpender(ctx)`                                      | The compliance, on every `SPENDER` module                         | Whether `ctx.spender` may execute the movement for the sender                                                               |
| `afterTransfer(ctx)`                                           | The compliance, on every `TRACKER` module, after the ledger moved | Records what the module needs                                                                                               |
| `bindCompliance(_compliance)`, `unbindCompliance(_compliance)` | The compliance concerned                                          | Open and close a binding                                                                                                    |
| `isComplianceBound(_compliance)`                               | Anyone, read                                                      | Whether a compliance is bound                                                                                               |
| `isPlugAndPlay()`, `canComplianceBind(_compliance)`            | The compliance, before binding                                    | A module binds when the first is true, or the second is true for that compliance                                            |
| `name()`                                                       | Anyone, read                                                      | The name of the module, reported by `UtilityChecker`                                                                        |

#### `TransferContext`

| Field                        | Type      | Content                                                                                         |
| ---------------------------- | --------- | ----------------------------------------------------------------------------------------------- |
| `compliance`                 | `address` | The compliance asking                                                                           |
| `fromIdentity`, `toIdentity` | `address` | The ONCHAINID of each side. Zero on the absent side of a mint or a burn                         |
| `fromWallet`, `toWallet`     | `bytes32` | The key of each wallet: an address padded on the left, or the `keccak256` of a satellite wallet |
| `amountMin`, `amountMax`     | `uint256` | Inclusive bounds. Equal for an exact amount. A range at the issuance of a validation            |
| `isIssuance`                 | `bool`    | True while a validation is being issued                                                         |
| `spender`                    | `bytes`   | Who executes for the sender, as an ERC-7930 address. Filled for `moduleCheckSpender` only       |

### `AbstractModuleUpgradeable`

| Function                         | Access                                        | Reverts with                                                                                                                                                                                          |
| -------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bindCompliance(_compliance)`    | The compliance being bound, through the proxy | `ZeroAddress`, `OnlyComplianceContractCanCall`, `ComplianceAlreadyBound`. Emits `ComplianceBound`                                                                                                     |
| `unbindCompliance(_compliance)`  | The bound compliance, through the proxy       | `OnlyBoundComplianceCanCall`, `ZeroAddress`, `OnlyComplianceContractCanCall`, `ModuleStillBound` while the compliance still lists the module. Increments the bind nonce and emits `ComplianceUnbound` |
| `getNonce(_compliance)`          | Anyone, read                                  | The bind nonce of a compliance: 0 at first, plus one at each unbind. Settings are stored under it, so an unbind discards them                                                                         |
| `isComplianceBound(_compliance)` | Anyone, read                                  |                                                                                                                                                                                                       |
| `allowedAmount(ctx)`             | Default                                       | Returns the maximum                                                                                                                                                                                   |
| `moduleCheckSpender(ctx)`        | Default                                       | Returns true                                                                                                                                                                                          |
| `afterTransfer(ctx)`             | A bound compliance                            | Does nothing. Reverts with `OnlyBoundComplianceCanCall` for another caller                                                                                                                            |
| `multicall(bytes[])`             | As the calls it carries                       | Several settings in one `callModuleFunction`                                                                                                                                                          |

The modifier `onlyComplianceCall` protects every setter: the caller must be a bound compliance, and the setter writes under that compliance. Events: `ComplianceBound(address indexed compliance)`, `ComplianceUnbound(address indexed compliance)`.

### Shipped modules

Each of the three modules has `initialize(accessManagerAddress)`, which reverts with `ZeroAddress`, returns true from `isPlugAndPlay()`, and binds to any compliance.

#### `MaxBalancePerIdentityModule`

|             |                                                                                                                                                                                                                                    |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Type**    | `RULE`                                                                                                                                                                                                                             |
| **Rule**    | One identity owns at most the cap, over every wallet and every chain. The rule counts the position of the recipient plus what open validations promise to it. A burn and a movement between two wallets of one identity are exempt |
| **Setting** | `setMaxBalance(_max)`, through the compliance. Emits `MaxBalanceSet(address indexed compliance, uint256 maxBalance)`. An unset cap is zero and refuses every acquisition                                                           |
| **Read**    | `maxBalanceOf(_compliance)`                                                                                                                                                                                                        |

#### `SpenderVerificationModule`

|             |                                                                                                                       |
| ----------- | --------------------------------------------------------------------------------------------------------------------- |
| **Type**    | `SPENDER`                                                                                                             |
| **Rule**    | The spender must be eligible for the asset: `TREXRegistry.isWalletVerified(ctx.spender)` on the registry of the token |
| **Setting** | None                                                                                                                  |

#### `SpenderWhitelistModule`

|              |                                                                                                                                                                                                   |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Type**     | `SPENDER`                                                                                                                                                                                         |
| **Rule**     | The spender must be on the list of named operators of the compliance. The list starts empty, so every spender is refused until one is listed                                                      |
| **Settings** | `allowSpender(_spender)`, which reverts with `SpenderAlreadyAllowed`, and `disallowSpender(_spender)`, which reverts with `SpenderNotListed`. `_spender` is an ERC-7930 address in canonical form |
| **Read**     | `isSpenderAllowed(_compliance, _spender)`                                                                                                                                                         |
| **Events**   | `SpenderAllowed(address indexed _compliance, bytes32 indexed _spenderKey, bytes _spender)`, `SpenderDisallowed` (same arguments)                                                                  |

Guide: [26 Authorize spenders](/guides/issuer/26.-authorize-spenders.md).

> **Trust statement.** Whoever can upgrade a module can change its rule for every compliance bound to it. An Issuer that wants sole control over a rule deploys its own instance of the module, under its own access manager.


---

# 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/reference/token-suite/compliance-modules.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.
