Programmable Compliance Layer (PCL)

compliance external-dapp

An on-chain framework for defining and enforcing custom transaction rules and regulatory policies.

The Programmable Compliance Layer (PCL) is a core module of the Maroo network that enables the creation and enforcement of compliance policies directly on the blockchain. It validates EVM transactions against chain-wide global rules before and after they execute. It validates them against contract-specific rules during execution, when a call goes through a PCL-registered proxy or reaches a policy-aware precompile such as Privacy. This allows for the implementation of complex regulatory requirements, such as KYC/AML checks, transfer restrictions, and volume limits, without altering the core logic of smart contracts.

Key Features

Global and Contract-Specific Policies

Apply rules across the entire network or target specific smart contracts for granular control.

Template-Based Policy Creation

Use pre-defined, reusable policy templates (e.g., Denylist, Volume Limits, EAS Attestation) to quickly configure complex rules.

EVM-native integration

The chain accepts only EVM transactions. Each one — except the policy admin's own recovery calls to PCL's control-plane methods — is checked against the global policies before it executes and again after it succeeds, so chain-wide compliance is enforced at the chain layer.

Extensible via EAS

Uses the Ethereum Attestation Service (EAS) to gate transactions on on-chain attestations — the basis for KYC, KYB, and credential checks.

Architecture

graph TD
    subgraph TX["Every transaction"]
        A["Tx submitted"] --> B{"Global policies<br/>at submission and before execution"}
        B -- pass --> D["EVM execution"]
        B -- violation --> E["Rejected before execution"]
        D --> R{"Global policies again<br/>after execution"}
        R -- pass --> F["State committed"]
        R -- violation --> S["Tx fails, still mined and charged"]
    end

    subgraph PX["A call into a PCL-wrapped contract"]
        G["Call via PCL proxy"] --> H{"PCL preCall<br/>contract and global policies"}
        H -- pass --> K["Execute contract logic"]
        H -- violation --> L["Revert"]
        K --> T{"PCL postCall"}
        T -- pass --> U["Return result"]
        T -- violation --> L
    end

    subgraph CFG["PCL configuration"]
        M["Policy admin"] -- manages --> N["Policy templates"]
        M -- manages --> O["Global policy config"]
        P["Contract policy admin"] -- manages --> Q["Contract policy config"]
        N --> O
        N --> Q
    end
    classDef evm fill:#0096AA,stroke:#0096AA,color:#fff;
    classDef precompile fill:#FF8C50,stroke:#FF8C50,color:#fff;
    class A,D,E,F,S,G,K,L,U,M,P evm;
    class B,R,H,T,N,O,Q precompile;

PCL runs its global checks in the chain's pre-execution checks, before execution, and again in a post-transaction hook after execution. It integrates into the EVM via a PCL proxy's preCall/postCall hooks for contract-specific checks. All policy configurations are stored on-chain and managed by designated admin accounts.

Policy enforcement flow

PCL validates every transaction against a set of rules at the protocol layer.

Every transaction first goes through the chain's pre-execution checks (the node's JSON-RPC runs the same PCL check when a transaction is submitted). PCL takes part in those checks by running the active Global Policies for the transaction's function selector (and those set for every selector) against the transaction's own call — sender, recipient, value, selector. If any global policy fails at this stage, the transaction is rejected before it executes and pays no gas.

Contract-specific Policies run during execution, and only when a call goes through a PCL-registered proxy or a trusted ERC-4337 EntryPoint calls preCall / postCall for a user operation (the Privacy precompile, a policy-aware precompile, evaluates its own): the proxy calls PCL's preCall before running the implementation and postCall after it. A call made straight to a contract without a PCL proxy is checked only against the global policies, so that call gets no contract-scoped enforcement. After every successful EVM transaction, the chain runs the global policies again over the sender and every call the transaction made; a violation found there fails the transaction — its state is reverted, but it is still mined and its gas is charged. Together — global policies before and after execution, contract-scoped policies on the proxy hook path — these give consistent enforcement across the entire network.
Note: The pre-execution check rejects invalid transactions before they reach the mempool, saving network resources and giving the user immediate feedback.

Policy Templates and Configuration

Instead of defining rules from scratch, PCL uses a template-based system. A PolicyTemplate is a blueprint for a type of rule — its canonical Solidity struct in IPcl.sol declares the parameter layout for instances. Maroo comes with seven built-in templates (EAS_POLICY, DENYLIST_POLICY, VOLUME_POLICY, PERIODIC_VOLUME_POLICY, AGENT_OKRW_TRANSFER_LIMIT_POLICY, LOGICAL_POLICY, FOR_EACH_POLICY).

The PolicyAdmin, a privileged account set in the module's parameters (controllable by governance), is responsible for registering and managing these templates.

To activate a rule, an administrator creates a PolicySet — a { templateId, policy, selector } tuple where policy is the ABI-encoded parameter struct for that template (NOT JSON). For example, a denylist is a PolicySet with templateId = "DENYLIST_POLICY", policy = abi.encode(DenylistPolicy({ addresses: [...] })), and an empty selector (hex"" in Solidity, "0x" in viem), which applies it to all calls. PolicySet objects are then grouped into a GlobalPolicyConfig (chain-wide) or ContractPolicyConfig (per-contract) to be applied.

Next Steps

ESC
Type to search