> 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/12.-remove-a-compliance-rule.md).

# 12. Remove a compliance rule

Removing a compliance rule unbinds a module from the asset's compliance. From that moment the module is no longer consulted, and the limit it imposed no longer applies. The Compliance Owner does this when the terms of the asset change, when a rule is replaced by another, or when a module misbehaves. A forced removal exists for a module that refuses to be unbound.

## At a glance

|                   |                                                                                                                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Triggered by**  | Compliance Owner                                                                                                                                                                                                   |
| **Actors**        | Compliance Owner, Issuer, Investor, Platform                                                                                                                                                                       |
| **Contracts**     | `ModularCompliance`, the compliance module                                                                                                                                                                         |
| **Preconditions** | The module is bound to the compliance (see [11](/guides/issuer/11.-add-a-compliance-rule.md)); the caller holds `OWNER` in the asset's domain (see [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)) |
| **Result**        | The module is no longer bound, its settings for this compliance are discarded, and it is no longer called on any movement                                                                                          |

## Actors and roles

| Actor            | Role in this flow                                                                        | What they hold                                                 |
| ---------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| Compliance Owner | Unbinds the module.                                                                      | `OWNER` on the `ModularCompliance`                             |
| Issuer           | Decides that the rule no longer applies. Usually the same party as the Compliance Owner. | The terms of the asset                                         |
| Investor         | Is affected: movements the rule limited become possible. Does not transact.              | A position                                                     |
| Platform         | 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), getModulesByType(type) (read)
    Platform->>Owner: checks passed, submit
    Owner->>MC: removeModule(module)
    MC->>MC: caller holds OWNER, module is bound
    MC->>MC: remove the module from every list
    MC->>Module: unbindCompliance(MC)
    alt the module unbinds
        Module-->>MC: binding and settings discarded
        MC-->>Platform: ModuleRemoved(module)
    else the module reverts
        MC-->>Owner: the removal reverts, the module stays bound
        Owner->>MC: forceRemoveModule(module)
        MC->>MC: remove the module from every list, no call to the module
        MC-->>Platform: ModuleForceRemoved(module)
    end
```

## Step by step

### Step 1. Check what the removal changes

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

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

**Before the call:** The Issuer has decided to remove the rule.

**What happens:** The Platform reads `ModularCompliance.isModuleBound(module)`, which must be true, and `ModularCompliance.getModulesByType(type)` for each type, to see what remains bound. Removing the last `RULE` module leaves movements limited by eligibility alone. Removing the last `SPENDER` module makes every spender acceptable. If the rule is being replaced, the Platform prepares the binding of the new module so that both can go in one transaction.

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

### Step 2. Remove the module

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

**Call:** `ModularCompliance.removeModule(module)`

**Before the call:** Step 1 is done.

**What the contract checks:**

1. The caller holds `OWNER` in the asset's domain. Otherwise the call reverts with `AccessManagedUnauthorized`.
2. The module is not the zero address. Otherwise `ZeroAddress`.
3. The module is bound. Otherwise `ModuleNotBound`.
4. The module accepts to unbind. If its `unbindCompliance` reverts, the whole removal reverts and the module stays bound.

**What changes:** The compliance removes the module from its set and from the list of every type. The module discards its binding and its settings for this compliance. The compliance emits `ModuleRemoved(module)` and the module emits `ComplianceUnbound(compliance)`. From the next movement, the module is not called.

### Step 3. Force the removal

Only when step 2 reverts because of the module.

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

**Call:** `ModularCompliance.forceRemoveModule(module)`

**Before the call:** `removeModule` reverted with an error raised by the module, or the module reverts on every call.

**What the contract checks:**

1. The caller holds `OWNER`. Otherwise `AccessManagedUnauthorized`.
2. The module is not zero and is bound. Otherwise `ZeroAddress` or `ModuleNotBound`.

**What changes:** The compliance removes the module from its set and from the list of every type, without calling the module. It emits `ModuleForceRemoved(module)` and no `ModuleRemoved`. The module still records a binding to this compliance on its side, so the same module address cannot be bound to this compliance again. A new deployment of the module can.

## State after the flow

* `ModularCompliance.isModuleBound(module)` is false and `ModularCompliance.getModules()` no longer includes the module.
* `ModularCompliance.canTransfer(from, to, amount)` no longer reflects the rule.
* After a normal removal, the module holds no settings for this compliance.
* No balance and no position has changed. The compliance ledger is kept by the compliance itself and is unaffected.

## 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 `ModuleNotBound`                                       | The module is not in the set (step 2, check 3; step 3, check 2)       | Read `isModuleBound` first. Nothing to remove                                        |
| `removeModule` reverts with an error from the module                | The module's `unbindCompliance` reverts (step 2, check 4)             | `forceRemoveModule`                                                                  |
| `removeModule` runs out of gas                                      | The transaction did not carry enough gas for the call into the module | Send with more gas. If the module consumes all of it, `forceRemoveModule`            |
| Binding the same module again reverts with `ComplianceAlreadyBound` | The module was force-removed and still holds its binding              | Bind a new deployment of the module                                                  |
| A module bound again has lost its settings                          | Settings belong to a binding and are discarded when it ends           | Configure the module again. Use `addAndSetModule`                                    |
| After removing a spender module, any approved spender can transfer  | It was the last `SPENDER` module                                      | Intended. Bind the replacement in the same transaction if a policy is still required |

## Common misunderstandings

> **Wrong:** "Removing a rule and adding it back is a way to reset or refresh it."
>
> **Right:** A removal discards the module's settings for this compliance. A module bound again starts empty: a cap is unset, a list has no entry. To make the compliance re-read the types of an upgraded module, use `resyncModuleTypes`. To change settings, use `callModuleFunction`.

> **Wrong:** "A faulty module can block the asset."
>
> **Right:** A module that reverts when told of a movement does block movements while it is bound. It cannot stay bound against the Compliance Owner's will: `forceRemoveModule` removes it without calling it.

> **Wrong:** "Removing a rule recalculates positions."
>
> **Right:** Positions are kept by the compliance, not by modules. Removing a module changes which questions are asked before a movement. It does not touch the ledger.

> **Wrong:** "Force removal is the same as removal, only faster."
>
> **Right:** A forced removal leaves the module's own record of the binding in place. The same module address can never be bound to this compliance again. It is a remedy for a module that misbehaves, not the normal path.

## Integration notes

* When replacing a rule, remove the old module and bind the new one in the same transaction, so that there is no interval without the rule.
* Index `ModuleRemoved` and `ModuleForceRemoved` separately. A forced removal signals a module that could not be unbound normally.
* After a forced removal, record that the module address is unusable for this compliance.
* Read `ModularCompliance.getModulesByType(type)` after the removal to confirm which rules, spender policies and trackers remain.
* Movements authorized on satellites before the removal keep the amount range they were issued with. The removal applies to validations issued afterward.

## Related flows

* [11 Add a compliance rule](/guides/issuer/11.-add-a-compliance-rule.md) : the inverse operation
* [26 Authorize spenders](/guides/issuer/26.-authorize-spenders.md) : spender modules and their lists
* [27 Upgrade a suite](/guides/issuer/27.-upgrade-a-suite.md) : after a module is upgraded, resync its types
* [10 Transfer between investors](/guides/positions/10.-transfer-between-investors.md) : where rules are 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/12.-remove-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.
