Standards
ERC3643 Token for Regulated EXchanges
Understand ERC3643, a regulated exchange token standard with KYC rules, permissioned transfers & enforceable compliance for real-world assets.
ERC3643, known as T-REX (Token for Regulated EXchanges) is a permissioned token standard that extends ERC20 with built-in regulatory controls. It enforces compliance requirements such as on-chain identity verification, transfer eligibility checks, and modular rule sets for KYC/AML, accreditation, jurisdiction restrictions, and administrative actions like freezes or pauses.
For RWA protocols, ERC3643 is the canonical standard for issuing regulated security tokens: fractional real estate shares, private credit instruments, private equity funds, or tokenized bonds. By embedding compliance directly into token logic, it ensures that transfers occur only between verified and authorized investors, while retaining blockchain transparency and full auditability.
The standard emerged to bridge a critical gap between traditional financial regulation and permissionless ERC20 tokens. Securities laws require features that ERC20 cannot provide, investor whitelisting, jurisdictional gating, lockup enforcement, recovery flows, and revocation. ERC3643 addresses this with a modular architecture composed of identity registries, compliance modules, and trusted issuers. Issuers can customize or upgrade rules without modifying the core token contract.
Designed to work alongside ERC20, ERC3643 now underpins institutional-grade platforms including Tokeny and Fasanara Capital's tokenized credit products. In modern RWA architectures, it frequently integrates with ERC4626 vaults for compliant share issuance and ERC7540 for asynchronous settlement of illiquid assets, providing a full regulatory and technical foundation for institutional RWA deployments.
The RWA market is still very early and highly fragmented: every platform tends to use its own stack, which makes interoperability and scale hard. ERC-3643 has been created to standardize the contract and identity layer for permissioned tokens so that everyone could build on the same, compliance-ready foundation. That convergence is already happening, with more and more institutional players adopting 3643 as their baseline.
The next step is solving fragmentation across chains and VMs. That’s exactly what i am currently building with the T-REX Network : a shared layer of compliance and control that works across networks while still being enforced by the ERC-3643 framework. I’m convinced that this kind of standards-driven, chain-agnostic architecture is what will unlock large-scale institutional adoption of RWAs.
Use of ERC3643 in RWA Contexts
ERC3643 enables RWAs to transition from static, illiquid assets into compliant, transferable digital securities. By embedding regulatory logic directly into the token contract, it ensures that only verified participants can hold or receive the token, preventing unauthorized transfers and supporting compliant secondary markets under frameworks such as MiFID II, AIFMD, or SEC Reg D/Reg S. Compared to unrestricted ERC20 wrappers, ERC3643 enforces a “whitelisted ecosystem,” sharply reducing legal exposure for issuers while allowing institutional-grade liquidity.
Key Applications in RWA Development
Compliant Tokenization and Issuance Mint tokens representing commercial real estate, private credit, or venture debt exclusively to verified investors using mint() gated by isVerified checks. Supports batch issuance for IPO-style releases and ties easily to off-chain legal documentation via IPFS or similar URIs.
Restricted Transfers and Secondary Liquidity All transfers route through canTransfer, enabling enforcement of rules such as accreditation status, per-investor caps, jurisdiction restrictions, or lockup periods. This allows tokenized bonds or equity units to trade on permissioned DEXs while blocking transfers to ineligible users.
Administrative Controls and Recovery Mechanisms ERC3643 supports partial freezes, emergency halts, and address recovery, critical features when token ownership is linked to legal claims. Issuers can freeze suspicious wallets for AML reasons or migrate ownership if a user loses their private keys.
Modular Compliance for Global RWA Markets Compliance modules can be swapped or extended to reflect local regulatory regimes (EU, US, APAC), integrating with oracles for dynamic updates (for example, sanctions lists or jurisdiction changes). It pairs well with ERC7518 for on-chain identity proofs during issuance or redemption.
Developer Advantages in RWA Builds
- Regulatory Alignment On-chain verifiable claims (for example, ERC735/734 identity attestations) streamline KYC while providing transparent, audit-friendly records for regulators.
- Modularity and Upgradability Identity registries and compliance rules are decoupled from the token contract, allowing issuers to evolve with regulatory changes without redeploying the token.
- Gas-Efficient Operations Batch functions (such as
batchTransfer) optimize high-volume investor operations. Pre-checks viacanTransferenable safe off-chain simulation before broadcasting a transaction. - Seamless Interoperability Retains ERC20 compatibility for wallets and DEX tooling while extending neatly into ERC4626 vault architectures or ERC7540 async flows, making it easy to integrate regulated tokens into broader RWA systems.
The following sequence diagram illustrates a compliant transfer within an ERC3643 based RWA token. It captures each validation step from identity verification to compliance checks and final token movement, highlighting the multi-contract coordination required for regulated asset transfers.
Core Specification
ERC3643 tokens must implement the full ERC20 interface, with transfer and transferFrom overridden to enforce compliance checks. The standard follows a validator architecture, where every transfer is routed through identity and compliance registries before execution. A fundamental requirement is that all token holders possess on-chain identities; addresses that are not verified cannot receive or hold the token.
Key Definitions
- On-Chain Identity An ERC735 identity contract that stores cryptographically signed claims (for example, KYC/AML verification) issued by trusted entities. These claims form the basis for compliance checks.
- Compliance Module A pluggable rules engine that validates each transfer. It can enforce caps, jurisdiction restrictions, lockups, or any custom rule set needed for regulated assets.
- Agent A privileged role defined through ERC173, responsible for administrative functions such as freezing accounts, forcing transfers, or updating registries.
- Claim Topics Standardized identifiers (for example, topic
100for KYC verification) registered viaIClaimTopicsRegistry, ensuring consistent claim semantics across issuers and tokens. - Free Balance The holder’s spendable balance, calculated as
balanceOfminus any frozen portion. Used in partial freeze scenarios. - Forced Transfer An administrative override that allows the agent to execute transfers irrespective of compliance rules,typically for regulatory enforcement, court orders, or recovery operations.
ERC3643 also supports global pauses and account-level (full or partial) freezes, enabling issuers to respond quickly to compliance events, audits, or regulatory directives while maintaining transparent on-chain auditability.
The ERC3643 standard defines a comprehensive set of interfaces to support its modular architecture. These interfaces separate identity management, compliance validation, and token logic, allowing issuers to update or replace components without redeploying the token itself. Presented below are the full ABI-compliant interfaces from the EIP, organized by module and accompanied by developer notes highlighting their relevance for RWA implementations.
IAgentRole
Manages administrative authority for regulated operations.
1interface IAgentRole {
2 // events
3 event AgentAdded(address indexed _agent);
4 event AgentRemoved(address indexed _agent);
5
6 // functions
7 // setters
8 function addAgent(address _agent) external;
9 function removeAgent(address _agent) external;
10
11 // getters
12 function isAgent(address _agent) external view returns (bool);
13}Delegate issuer or compliance-officer roles across multiple custodians or service providers. Always pair with multisigs to avoid unilateral freezes or forced transfers.
IERC3643 (Core Token Interface)
Primary ERC20 extension enabling identity-gated and rules-gated transfers.
1interface IERC3643 is IERC20 {
2 // events
3 event UpdatedTokenInformation(
4 string _newName,
5 string _newSymbol,
6 uint8 _newDecimals,
7 string _newVersion,
8 address _newOnchainID
9 );
10 event IdentityRegistryAdded(address indexed _identityRegistry);
11 event ComplianceAdded(address indexed _compliance);
12 event RecoverySuccess(
13 address _lostWallet,
14 address _newWallet,
15 address _investorOnchainID
16 );
17 event AddressFrozen(
18 address indexed _userAddress,
19 bool indexed _isFrozen,
20 address indexed _owner
21 );
22 event TokensFrozen(address indexed _userAddress, uint256 _amount);
23 event TokensUnfrozen(address indexed _userAddress, uint256 _amount);
24 event Paused(address _userAddress);
25 event Unpaused(address _userAddress);
26
27 // functions
28 // getters
29 function onchainID() external view returns (address);
30 function version() external view returns (string memory);
31 function identityRegistry() external view returns (IIdentityRegistry);
32 function compliance() external view returns (ICompliance);
33 function paused() external view returns (bool);
34 function isFrozen(address _userAddress) external view returns (bool);
35 function getFrozenTokens(address _userAddress) external view returns (uint256);
36
37 // setters
38 function setName(string calldata _name) external;
39 function setSymbol(string calldata _symbol) external;
40 function setOnchainID(address _onchainID) external;
41 function pause() external;
42 function unpause() external;
43 function setAddressFrozen(address _userAddress, bool _freeze) external;
44 function freezePartialTokens(address _userAddress, uint256 _amount) external;
45 function unfreezePartialTokens(address _userAddress, uint256 _amount) external;
46 function setIdentityRegistry(address _identityRegistry) external;
47 function setCompliance(address _compliance) external;
48
49 // transfer actions
50 function forcedTransfer(address _from, address _to, uint256 _amount)
51 external
52 returns (bool);
53 function mint(address _to, uint256 _amount) external;
54 function burn(address _userAddress, uint256 _amount) external;
55 function recoveryAddress(
56 address _lostWallet,
57 address _newWallet,
58 address _investorOnchainID
59 ) external returns (bool);
60
61 // batch functions
62 function batchTransfer(
63 address[] calldata _toList,
64 uint256[] calldata _amounts
65 ) external;
66 function batchForcedTransfer(
67 address[] calldata _fromList,
68 address[] calldata _toList,
69 uint256[] calldata _amounts
70 ) external;
71 function batchMint(
72 address[] calldata _toList,
73 uint256[] calldata _amounts
74 ) external;
75 function batchBurn(
76 address[] calldata _userAddresses,
77 uint256[] calldata _amounts
78 ) external;
79 function batchSetAddressFrozen(
80 address[] calldata _userAddresses,
81 bool[] calldata _freeze
82 ) external;
83 function batchFreezePartialTokens(
84 address[] calldata _userAddresses,
85 uint256[] calldata _amounts
86 ) external;
87 function batchUnfreezePartialTokens(
88 address[] calldata _userAddresses,
89 uint256[] calldata _amounts
90 ) external;
91}This is the core of a regulated token. Every mint, burn, transfer, and freeze operation routes through identity and compliance checks, making it suitable for tokenized securities, real-estate shares, or private credit units.
IIdentityRegistry
Tracks investor identities and verification status.
1interface IIdentityRegistry {
2 // events
3 event ClaimTopicsRegistrySet(address indexed claimTopicsRegistry);
4 event IdentityStorageSet(address indexed identityStorage);
5 event TrustedIssuersRegistrySet(address indexed trustedIssuersRegistry);
6 event IdentityRegistered(
7 address indexed investorAddress,
8 IIdentity indexed identity
9 );
10 event IdentityRemoved(
11 address indexed investorAddress,
12 IIdentity indexed identity
13 );
14 event IdentityUpdated(
15 IIdentity indexed oldIdentity,
16 IIdentity indexed newIdentity
17 );
18 event CountryUpdated(address indexed investorAddress, uint16 indexed country);
19
20 // functions
21 // identity registry getters
22 function identityStorage()
23 external
24 view
25 returns (IIdentityRegistryStorage);
26 function issuersRegistry()
27 external
28 view
29 returns (ITrustedIssuersRegistry);
30 function topicsRegistry()
31 external
32 view
33 returns (IClaimTopicsRegistry);
34
35 // identity registry setters
36 function setIdentityRegistryStorage(address _identityRegistryStorage) external;
37 function setClaimTopicsRegistry(address _claimTopicsRegistry) external;
38 function setTrustedIssuersRegistry(address _trustedIssuersRegistry) external;
39
40 // registry actions
41 function registerIdentity(
42 address _userAddress,
43 IIdentity _identity,
44 uint16 _country
45 ) external;
46 function deleteIdentity(address _userAddress) external;
47 function updateCountry(address _userAddress, uint16 _country) external;
48 function updateIdentity(address _userAddress, IIdentity _identity) external;
49 function batchRegisterIdentity(
50 address[] calldata _userAddresses,
51 IIdentity[] calldata _identities,
52 uint16[] calldata _countries
53 ) external;
54
55 // registry consultation
56 function contains(address _userAddress) external view returns (bool);
57 function isVerified(address _userAddress) external view returns (bool);
58 function identity(address _userAddress) external view returns (IIdentity);
59 function investorCountry(address _userAddress) external view returns (uint16);
60}isVerified is the primary gatekeeper for issuance and transfers. Batch registration is crucial for onboarding institutional investor sets (e.g., funds, SPVs, syndicates).
IIdentityRegistryStorage
Stores identity objects and country metadata.
1interface IIdentityRegistryStorage {
2 // events
3 event IdentityStored(
4 address indexed investorAddress,
5 IIdentity indexed identity
6 );
7 event IdentityUnstored(
8 address indexed investorAddress,
9 IIdentity indexed identity
10 );
11 event IdentityModified(
12 IIdentity indexed oldIdentity,
13 IIdentity indexed newIdentity
14 );
15 event CountryModified(
16 address indexed investorAddress,
17 uint16 indexed country
18 );
19 event IdentityRegistryBound(address indexed identityRegistry);
20 event IdentityRegistryUnbound(address indexed identityRegistry);
21
22 // functions
23 function storedIdentity(address _userAddress)
24 external
25 view
26 returns (IIdentity);
27 function storedInvestorCountry(address _userAddress)
28 external
29 view
30 returns (uint16);
31 function addIdentityToStorage(
32 address _userAddress,
33 IIdentity _identity,
34 uint16 _country
35 ) external;
36 function removeIdentityFromStorage(address _userAddress) external;
37 function modifyStoredInvestorCountry(address _userAddress, uint16 _country)
38 external;
39 function modifyStoredIdentity(address _userAddress, IIdentity _identity)
40 external;
41
42 // role setter
43 function bindIdentityRegistry(address _identityRegistry) external;
44 function unbindIdentityRegistry(address _identityRegistry) external;
45
46 // getter for bound IdentityRegistry role
47 function linkedIdentityRegistries()
48 external
49 view
50 returns (address[] memory);
51}Keeps clean separation between identity verification and token logic. Critical for GDPR-aligned or jurisdiction-specific RWA workflows.
ICompliance
External compliance rule engine evaluated during transfers.
1interface ICompliance {
2 // events
3 event TokenBound(address _token);
4 event TokenUnbound(address _token);
5
6 // functions
7 // initialization of the compliance contract
8 function bindToken(address _token) external;
9 function unbindToken(address _token) external;
10
11 // check the parameters of the compliance contract
12 function isTokenBound(address _token) external view returns (bool);
13 function getTokenBound() external view returns (address);
14
15 // compliance check and state update
16 function canTransfer(address _from, address _to, uint256 _amount)
17 external
18 view
19 returns (bool);
20 function transferred(address _from, address _to, uint256 _amount) external;
21 function created(address _to, uint256 _amount) external;
22 function destroyed(address _from, uint256 _amount) external;
23}Ideal for encoding country-specific rules, holding caps, lockups, NAV-based weighting, or sanctions screening. Can be swapped without redeploying the token.
ITrustedIssuersRegistry
Maintains the set of KYC/AML claim issuers.
1interface ITrustedIssuersRegistry {
2 // events
3 event TrustedIssuerAdded(
4 IClaimIssuer indexed trustedIssuer,
5 uint[] claimTopics
6 );
7 event TrustedIssuerRemoved(IClaimIssuer indexed trustedIssuer);
8 event ClaimTopicsUpdated(
9 IClaimIssuer indexed trustedIssuer,
10 uint[] claimTopics
11 );
12
13 // functions
14 // setters
15 function addTrustedIssuer(
16 IClaimIssuer _trustedIssuer,
17 uint[] calldata _claimTopics
18 ) external;
19 function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external;
20 function updateIssuerClaimTopics(
21 IClaimIssuer _trustedIssuer,
22 uint[] calldata _claimTopics
23 ) external;
24
25 // getters
26 function getTrustedIssuers()
27 external
28 view
29 returns (IClaimIssuer[] memory);
30 function isTrustedIssuer(address _issuer) external view returns (bool);
31 function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer)
32 external
33 view
34 returns (uint[] memory);
35 function getTrustedIssuersForClaimTopic(uint256 claimTopic)
36 external
37 view
38 returns (IClaimIssuer[] memory);
39 function hasClaimTopic(address _issuer, uint _claimTopic)
40 external
41 view
42 returns (bool);
43}Central to regulated onboarding. Lets issuers restrict which KYC providers or attestors are considered valid.
IClaimTopicsRegistry
Defines standardized claim types used across the ecosystem.
1interface IClaimTopicsRegistry {
2 // events
3 event ClaimTopicAdded(uint256 indexed claimTopic);
4 event ClaimTopicRemoved(uint256 indexed claimTopic);
5
6 // functions
7 // setters
8 function addClaimTopic(uint256 _claimTopic) external;
9 function removeClaimTopic(uint256 _claimTopic) external;
10
11 // getter
12 function getClaimTopics()
13 external
14 view
15 returns (uint256[] memory);
16}Allows creation of harmonized taxonomy, for example:
- topic
100= KYC verified - topic
101= RWA accredited - topic
200= AML cleared
Detailed Function Breakdown
transfer(address _to, uint256 _amount) Overrides the ERC20 transfer logic by enforcing compliance conditions before any movement of tokens. Checks include: contract not paused, sender not frozen, sufficient free balance, receiver has a verified identity via IIdentityRegistry, and ICompliance.canTransfer returns true. Emits a standard Transfer event.
Forms the basis of compliant peer-to-peer transfers. Errors should be explicit (for example, "Identity not verified" or "Transfer restricted by compliance").
isVerified(address _userAddress) Provided by IIdentityRegistry, this function confirms that a user possesses valid claims issued by trusted authorities. RWA significance: The primary gate for minting and transferring regulated assets. Typically backed by ERC735 identity contracts storing KYC/AML claims.
canTransfer(address _from, address _to, uint256 _amount) Implemented by the ICompliance module, this function checks rule-specific logic such as balance ceilings, jurisdiction restrictions, or lockups. RWA significance: Allows dynamic constraints, including country-based caps or investor eligibility conditions derived from investorCountry.
forcedTransfer(address _from, address _to, uint256 _amount) Bypasses canTransfer and standard restrictions, allowing an Agent (under IAgentRole) to forcibly move tokens between verified users. RWA significance: Required for regulatory enforcement, operational corrections, or legally mandated redemptions. _to must still be verified to maintain compliance integrity.
mint(address _to, uint256 _amount) Creates new tokens and assigns them to a verified address. Triggers created in the Compliance module for state tracking. RWA significance: Used during primary issuance, onboarding rounds, or batch allocations for SPVs, funds, or regulated offerings.
burn(address _userAddress, uint256 _amount) Destroys tokens from a verified holder and calls destroyed in the Compliance module. RWA significance: Used for redemptions tied to off-chain settlement events such as bond maturity, property-sale proceeds, or closed-end fund withdrawals.
recoveryAddress(address _lostWallet, address _newWallet, address _investorOnchainID) Migrates balances and identity linkage from a compromised or lost wallet to a new verified wallet. Emits RecoverySuccess. RWA significance: Critical for institutional custody workflows, allowing key-rotation or recovery without breaking legal ownership.
pause() and unpause() Global controls that halt or resume all token operations. Restricted to Agents or Owners. RWA significance: Essential for emergency responses, auditor-mandated halts, or controlled market suspensions during legal events.
setAddressFrozen(address _userAddress, bool _freeze) and freezePartialTokens(address _userAddress, uint256 _amount) Freeze full balances or specific token amounts for a given user. RWA significance: Supports AML interventions, regulatory flags, sanctions responses, or targeted freezes for suspicious accounts. Batch versions enable mass compliance actions.
batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) Executes multiple transfers atomically, applying the same identity and compliance checks for each movement. RWA significance: Ideal for dividend distributions, coupon payments, or bulk investor allocations in large RWA funds.
Reference Implementation
The ERC3643 Association maintains the official, Solidity implementation of the standard.
Constructor and Setup
1import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
2import "./interfaces/IERC3643.sol";
3import "./IdentityRegistry.sol";
4import "./ModularCompliance.sol";
5
6contract RWAToken is ERC20, IERC3643 {
7 IIdentityRegistry public override identityRegistry;
8 ICompliance public override compliance;
9 bool public override paused;
10 mapping(address => bool) public frozen;
11 mapping(address => uint256) public frozenTokens;
12
13 constructor(
14 string memory name,
15 string memory symbol,
16 address _identityRegistry,
17 address _compliance
18 ) ERC20(name, symbol) {
19 identityRegistry = IIdentityRegistry(_identityRegistry);
20 compliance = ICompliance(_compliance);
21 }
22
23 // ERC165 support
24 function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
25 return super.supportsInterface(interfaceId) || interfaceId == type(IERC3643).interfaceId;
26 }
27}- Identity and compliance modules must be deployed and initialized before deploying the token.
- After deployment, call
bindTokenon the compliance module to finalize the linkage. - The contract uses explicit boolean flags rather than OZ’s
PausableorAccessControl, so teams may need to adapt for governance or multisig-based administration.
transfer (Compliance-Aware Override)
1function transfer(address _to, uint256 _amount) public virtual override returns (bool) {
2 require(!paused, "Paused");
3 require(!frozen[msg.sender] && !frozen[_to], "Frozen wallet");
4 require(_amount <= balanceOf(msg.sender) - frozenTokens[msg.sender], "Insufficient free balance");
5 require(identityRegistry.isVerified(_to), "Invalid identity");
6 require(compliance.canTransfer(msg.sender, _to, _amount), "Compliance failure");
7 _transfer(msg.sender, _to, _amount);
8 compliance.transferred(msg.sender, _to, _amount);
9 return true;
10}This is the heart of the regulated transfer pipeline. Teams often extend this function to integrate:
- oracle-fed jurisdiction updates
- dynamic risk scores
- NAV-based position caps
- investor accreditation expiration checks
All these can be handled via the compliance module for modularity.
mint
1function mint(address _to, uint256 _amount) external override onlyOwner {
2 require(identityRegistry.isVerified(_to), "Invalid identity");
3 _mint(_to, _amount);
4 compliance.created(_to, _amount);
5}Use for primary issuance, investor onboarding rounds, or fund share allocations. For regulated offerings, many teams add:
- EIP-712 signed subscription agreements
- compliance checks tied to offering jurisdictions
- per-investor mint caps
forcedTransfer (Agent-Only)
1function forcedTransfer(address _from, address _to, uint256 _amount) external override onlyAgent returns (bool) {
2 require(identityRegistry.isVerified(_to), "Invalid identity"); // Still checks receiver
3 _transfer(_from, _to, _amount);
4 return true;
5}Enables legally mandated actions such as:
- execution of court orders
- regulatory seizures
- corrections to erroneous transfers
- migration from compromised wallets
Even here, ERC3643 enforces that the receiver must remain verified, ensuring regulatory integrity.
Security Considerations
ERC3643's modular compliance stack is powerful, but it significantly expands the attack surface compared to plain ERC20s.
For RWA systems, where tokens represent enforceable legal claims, these risks are magnified. The following are the most critical vectors to secure.
Claim Forgery & Replay Attacks
ERC3643 relies on ERC735 identity claims signed by trusted issuers. These signatures can be forged, duplicated, or replayed across chains if not properly constrained.
A forged accreditation or KYC claim allows an attacker to mint or receive regulated assets illegally, exposing issuers to regulatory penalties.
Mitigations:
- Require nonces, timestamps, or expiry fields in claim payloads.
- Enforce ECDSA signature recovery only against trusted issuer addresses.
- Use claim-topic scoping so claims can’t be re-interpreted for other purposes.
- Fuzz-test claim validation paths for malformed or borderline signatures.
Centralized Admin / Agent Privileges
Agent roles can trigger forced transfers, freezes, or supply changes. This is essential for regulatory compliance but introduces insider risk.
These tokens represent legal ownership stakes, an abused forced transfer is equivalent to taking someone’s shares or property.
Mitigations:
- Place all
Owner/Agentroles behind multisigs (e.g., 3/5 or 4/7). - Add timelocks for any admin actions that alter balances or identity registries.
- Prefer DAO-based governance for community-owned RWA systems.
- Emit rich events and use monitoring dashboards to detect suspicious admin activity.
Registry Manipulation (Identity & Issuers)
If an attacker alters the TrustedIssuersRegistry or ClaimTopicsRegistry, they can approve fake identities or remove legitimate validators.
The entire compliance boundary collapses if malicious issuers are whitelisted.
Mitigations:
- Restrict registry writes using AccessControl with multisig/DAO owners.
- Log every registry update (
IssuerAdded,TopicsUpdated) and integrate off-chain SIEM/monitoring. - Use upgradeable proxies with pause switches for emergency shutdowns.
Compliance Module Logic Flaws
Custom compliance modules (e.g., volume caps, jurisdiction filters, lockups) run logic for every transfer. Incorrect rules may:
- incorrectly allow illegal transfers
- freeze legitimate users
- create DoS conditions (e.g., all transfers revert)
Mitigations:
- Keep compliance modules small and isolated.
- Unit-test and fuzz-test
canTransfer,transferred,created, anddestroyed. - Use ReentrancyGuard for multi-transfer or batch functions.
- Create reference snapshots for module invariants (e.g., max balance).
Identity Storage Integrity
IIdentityRegistryStorage often serves multiple identity registries or tokens. Misconfiguration or binding errors can corrupt identity mappings.
A broken identity registry can freeze investor accounts or mis-route compliance checks.
Mitigations:
- Ensure tokens bind/unbind correctly to storage via explicit role checks.
- Add invariants: stored identities must equal verified identities for each registry.
- Backup and verify off-chain identity proofs to recover if storage is corrupted.

