ERC-7972:Universal Compliance Router: Integrating DIDs and OnchainID for Modular RWA Compliance

Discussion topic for ERC-7972, RWA’s: Universal compliance router for RWA’s(security tokens) · ERC-7972: Universal Compliance Router for RWA's by deepanshu179 · Pull Request #1087 · ethereum/ERCs · GitHub

Summary:
The Universal Compliance Router (ERC-7972) is a smart contract middleware that enables modular, jurisdiction-aware and can be extended to check kyc level etc. for tokenized Real-World Assets (RWAs). It integrates seamlessly with:

  • DIDs (Decentralized Identifiers) for universal identity resolution
  • OnchainID for verified, on-chain identity and compliance metadata
  • Soulbound Tokens (SBTs) to represent non-transferable credentials like KYC, accreditation, or residency
  • On-chain compliance modules (e.g., KYC/AML, investor limits, blacklists)

Key Benefits:

  • Plug-and-play compliance
  • Interoperability: Works across ecosystems and identity providers
  • Dynamic enforcement: Uses staticcall to run checks without altering state

This router acts as the trust layer for compliant RWA tokenization, enabling secure, scalable, and globally interoperable asset issuance and transfer

Motivation:

Tokenized RWAs such as real estate, equities, and bonds must comply with jurisdictional regulations (e.g., KYC/AML, accredited investor status). Hardcoding compliance logic into token contracts is inflexible and non-scalable. This ERC proposes a universal router that:

  • Supports modular compliance logic
  • Enables jurisdiction-specific enforcement
  • Allows dynamic updates to compliance rules
  • Promotes reuse of compliance modules across ecosystems

Note: The interface and implementation can be extended to include KYC levels and other KYC/AML checks as parameters in the isCompliant function. The core idea is to allow the registration of compliance frameworks or sub-proxies within the Universal Compliance Router, each exposing a unified callable selector for a specific category of compliance contracts.The registered sub-router/proxy or compliance contract counts in the Universal Compliance Router should be optimized to reduce the computational cost associated with looping operations.

Rationale:

On-chain compliance solutions for real-world assets (RWAs) are currently fragmented across different protocols and implementations. Each project often creates its own logic for enforcing regulatory requirements like KYC, AML, or jurisdictional restrictions. This siloed development leads to:

  • Lack of interoperability between compliance mechanisms across protocols
  • Duplication of effort in building and auditing similar compliance logic
  • Barriers to composability, preventing seamless integration between token contracts, marketplaces, and identity providers

The UniversalComplianceRouter consolidates these disparate efforts by introducing a standardized, modular interface to manage and invoke compliance checks. Instead of enforcing rules in isolation, this router allows smart contracts to delegate compliance verification to dynamically registered modules, each responsible for a specific rule set (e.g., “US Accredited Investor”, “EU Retail AML”, etc.).

By standardizing how compliance modules are invoked—using staticcall to ensure safety and composability—this router enables:

  • Composable compliance checks that can be reused across assets and jurisdictions
  • Dynamic rule updates without upgrading token contracts or duplicating logic
  • Shared infrastructure that fosters cooperation between identity providers, compliance vendors, and token issuers

This approach promotes network effects within EVM ecosystems, allowing compliant actors to interoperate with one another through a common compliance routing standard. As adoption grows, so does the value and utility of each registered module, creating a compounding benefit for the broader on-chain RWA ecosystem.

Update Log

  • 2025-06-16: review
  • 2025-06-16: ERC-7972 number assigned

External Reviews

None as of 2025-06-16.

Outstanding Issues

None as of 2025-06-16.

Interface

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title IUniversalComplianceRouter
/// @notice Interface for a modular compliance router supporting general and jurisdiction-aware compliance contracts
interface IUniversalComplianceRouter {
    /// @notice Registers a general compliance contract
    /// @param contractAddress The address of the compliance contract
    /// @param selector The function selector (e.g., isCompliant(address))
    function registerGeneralComplianceContract(address contractAddress, bytes4 selector) external;
    /// @notice Removes a compliance contract from the registry
    /// @param contractAddress The address of the contract to remove
    function removeComplianceContract(address contractAddress) external;
    /// @notice Checks if a user is compliant with any general compliance contract
    /// @param user The address of the user to check
    /// @return True if compliant with any general contract, false otherwise
    function isCompliant(address user) external view returns (bool);
    /// @notice Checks if a user is compliant with any jurisdiction-aware compliance contract
    /// @param user The address of the user
    /// @param jurisdiction The jurisdiction identifier (e.g., country code)
    /// @return True if compliant with any jurisdiction-aware contract, false otherwise
    function isCompliant(address user, bytes32 jurisdiction) external view returns (bool);
}

Refrence Implmentation

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title ComplianceRouter
/// @notice A modular router for managing and verifying compliance across general and jurisdiction-aware contracts
contract UniversalComplianceRouter {
    // ========== COMPLIANCE CHECK FUNCTIONS ==========
    /// @notice Checks if a user is compliant across all general compliance contracts
    /// @param user The address of the user to check
    /// @return True if compliant with any general contract, false otherwise
    function isCompliant(address user) external view returns (bool) {
        for (uint i = 0; i < generalComplianceContractList.length; i++) {
            address target = generalComplianceContractList[i];
            bytes4 selector = generalComplianceContracts[target];
            (bool success, bytes memory result) = target.staticcall(
                abi.encodeWithSelector(selector, user)
            );
            if (success && result.length == 32 && abi.decode(result, (bool))) {
                return true;
            }
        }
        return false;
    }
    /// @notice Checks if a user is compliant for a specific jurisdiction
    /// @param user The address of the user
    /// @param jurisdiction The jurisdiction identifier (e.g., country code)
    /// @return True if compliant with any jurisdiction-aware contract, false otherwise
    function isCompliant(address user, bytes32 jurisdiction) external view returns (bool) {
        for (uint i = 0; i < jurisdictionAwareContractList.length; i++) {
            address target = jurisdictionAwareContractList[i];
            bytes4 selector = jurisdictionAwareComplianceContracts[target];
            (bool success, bytes memory result) = target.staticcall(
                abi.encodeWithSelector(selector, user, jurisdiction)
            );
            if (success && result.length == 32 && abi.decode(result, (bool))) {
                return true;
            }
        }
        return false;
    }
}