For the complete documentation index, see llms.txt. This page is also available as Markdown.

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

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.

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