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
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
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
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:
The caller holds
OWNERin the asset's domain. Otherwise the call reverts withAccessManagedUnauthorized.The module is not the zero address. Otherwise
ZeroAddress.The module is bound. Otherwise
ModuleNotBound.The module accepts to unbind. If its
unbindCompliancereverts, 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:
The caller holds
OWNER. OtherwiseAccessManagedUnauthorized.The module is not zero and is bound. Otherwise
ZeroAddressorModuleNotBound.
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 andModularCompliance.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
Reverts with AccessManagedUnauthorized
The caller does not hold OWNER in the asset's domain (check 1)
Grant the role: 25
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, usecallModuleFunction.
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:
forceRemoveModuleremoves 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
ModuleRemovedandModuleForceRemovedseparately. 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 : the inverse operation
26 Authorize spenders : spender modules and their lists
27 Upgrade a suite : after a module is upgraded, resync its types
10 Transfer between investors : where rules are evaluated
Last updated