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.
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
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
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
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.
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.
Last updated