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

ModularCompliance

ModularCompliance is the second gate of an asset and its ledger of positions. It answers the token before a movement, records the movement after it, routes each question to the modules the Issuer bound, and keeps what every identity owns over all its addresses and all chains. The concepts are in Modular compliance and The compliance ledger.

The functions that issue and settle cross-chain validations are part of the same contract. They are listed in Transfer validation.

At a glance

Source

contracts/compliance/modular/ModularCompliance.sol, with ComplianceLedger.sol and TransferValidation.sol, on the standard base ERC3643Compliance

Interfaces

IModularCompliance, IERC3643Compliance, IComplianceLedger, ITransferValidation, ISettlementHandler

Deployed as

A beacon proxy per asset, bound to one token

Limits

25 modules. 5 setting calls in one addAndSetModule

Called by the token

Function
Access
Behavior

canTransfer(_from, _to, _amount)

Anyone, read

True when the amount is at most the smallest amount any RULE module allows. True with no RULE module. While a rule is bound, false when the sender or the recipient resolves to no identity

canSpenderCall(_spender, _from, _to, _value)

Anyone, read

True when every SPENDER module accepts the spender. True with no SPENDER module

transferred(_from, _to, _amount)

The bound token

Moves the amount between the positions of the two identities, then calls afterTransfer on every TRACKER module. Reverts with ZeroValue, ZeroAddress

created(_to, _amount)

The bound token

The same for a mint

destroyed(_from, _amount)

The bound token

The same for a burn

handleSettlement(originChainKey, notification)

The bound token

Applies a settlement notification from a satellite: see Transfer validation

A call from another address than the bound token reverts with AddressNotATokenBoundToComplianceContract.

Token binding

Function
Access
Reverts with

bindToken(_token)

The token itself while no token is bound, or OWNER

OnlyOwnerOrTokenCanCall, ZeroAddress, TokenCirculating when the token has supply. Emits TokenBound

unbindToken(_token)

The token, or OWNER

OnlyOwnerOrTokenCanCall, ZeroAddress, TokenNotBound. Emits TokenUnbound

getTokenBound(), isTokenBound(_token)

Anyone, read

init(tokenAddress, accessManagerAddress, modules, moduleSettings)

Initializer, called once by the factory

ZeroAddress, InvalidCompliancePattern when there are more settings than modules, and the errors of addModule

Modules

Function
Access
Reverts with

addModule(_module)

OWNER

ZeroAddress, MaxModulesReached, ModuleAlreadyBound, ComplianceNotSuitableForBindingToModule, ModuleHasNoType, DuplicateModuleType. Emits ModuleTypesRecorded and ModuleAdded

addAndSetModule(_module, _interactions)

OWNER

ArraySizeLimited above five calls, then the errors of addModule and of each setting call. Emits ModuleInteraction for each call

callModuleFunction(callData, _module)

OWNER

ModuleNotBound, and any revert of the module. The module sees the compliance as the caller. Emits ModuleInteraction

removeModule(_module)

OWNER

ZeroAddress, ModuleNotBound, and any revert of the module's unbindCompliance. Emits ModuleRemoved

forceRemoveModule(_module)

OWNER

ZeroAddress, ModuleNotBound. Makes no call to the module. Emits ModuleForceRemoved and no ModuleRemoved. The module keeps its record of the binding, so the same module address cannot be bound to this compliance again

resyncModuleTypes(_module)

OWNER

ModuleNotBound, ModuleHasNoType, DuplicateModuleType. Reads moduleTypes() again after an upgrade of the module. Emits ModuleTypesRecorded

isModuleBound(_module), getModules(), getModulesByType(moduleType)

Anyone, read

Guides: 11 Add a compliance rule, 12 Remove a compliance rule, 26 Authorize spenders.

The ledger

Function
Access
Returns or does

positionOf(identity)

Anyone, read

What the identity owns, over all its addresses and all chains

pendingInOf(identity)

Anyone, read

What open validations promise to the identity

pendingOutOf(identity)

Anyone, read

What open validations promise out of it

ownerOf(wallet)

Anyone, read

The identity the ledger credits for an address of the reference chain. It can lag the registry until the next movement through that address

positionGap()

Anyone, read

An int256: the amounts the ledger could not attribute to an identity. Zero in a consistent ledger

fixPosition(from, to, amount)

OWNER

Moves amount of position from one identity to another. The zero address stands for the gap. Reverts with FromAndToAreTheSame, InsufficientPosition. Emits PositionFixed

A normal movement changes positions without an event. The ledger emits only on exceptions: WalletOwnerChanged, PositionUnresolved, PositionUnderflow.

Events

Event
Emitted when

TokenBound(address _token), TokenUnbound(address _token)

The compliance is bound to its token, unbound

ModuleAdded(address indexed module), ModuleRemoved, ModuleForceRemoved (same argument)

A module is bound, removed, removed without being called

ModuleTypesRecorded(address indexed module, IModule.ModuleType[] moduleTypes)

A module is filed under its types

ModuleInteraction(address indexed target, bytes data)

A setting of a module is changed through the compliance

WalletOwnerChanged(address indexed wallet, address indexed previousIdentity, address indexed identity, uint256 amount)

The ledger moves the balance of an address to the identity the registry now names

PositionUnresolved(bytes32 indexed wallet, uint256 amount)

One side of a movement resolves to no identity. The amount is added to the gap on the receiving side, subtracted on the sending side

PositionUnderflow(address indexed identity, uint256 missing)

A debit exceeds the position: the position goes to zero and missing is subtracted from the gap

PositionFixed(address indexed from, address indexed to, uint256 amount)

fixPosition

The events of validations are listed in Transfer validation.

Last updated