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
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
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
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
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
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