Foundations

What PCL is, why a public chain can still be regulated, and the dual-track model.

Programmable Compliance Layer (PCL)

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 intercepts transactions before they are processed, validating them against a set of global and contract-specific rules. 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.

PCL Precompile component

The PCL Precompile is a stateful precompile at a fixed EVM address that bridges Solidity code to Maroo's compliance engine. It exposes read views (getParams, globalPolicies, contractPolicies, policyTemplate, periodic-volume queries) and admin-guarded writes (registerPolicyTemplate, setGlobalPolicies, changeContractPolicies, and their remove counterparts). It also owns the regulated execution path: deployPclProxy deploys a canonical PCL-wrapped proxy whose hook path calls preCall before, and postCall after, the underlying execution — the only mechanism through which contract-scoped policies fire.

Public, but Regulated mechanism

Maroo is a permissionless chain where anyone can create a wallet. However, it dynamically routes transactions through a Regulated Path or an Open Path based on counterparty identity, transaction size, and asset type.

PCL Dual-Track Transaction Model mechanism

Maroo separates transaction execution into two contexts. In the open track, a call goes directly to a target contract and only the global GlobalPolicyConfig (evaluated at the transaction entry point) applies — no contract-scoped policies fire. In the regulated track, users transact through a PCL-registered proxy address; that proxy's hook path calls IPcl.preCall(...) before the underlying execution and IPcl.postCall(...) after, so any ContractPolicyConfig bound to the proxy is enforced atomically around the call. Contract admins choose the track by publishing either the raw implementation address (open) or the PCL-registered proxy address (regulated) as their user-facing entry point.

Dual-Track Transactions mechanism

Maroo processes transactions through two distinct paths: the Open Path for standard, low-risk activities, and the Regulated Path for high-value or compliant asset transfers. The PCL automatically determines the correct path.

Policy model

How policies are structured, composed, administered, evaluated, and how rejections surface.

PCL Policy Structure component

PCL stores compliance rules as Solidity-defined ABI tuples, not as JSON objects. The hierarchy has three tiers: a PolicyTemplate (the type of rule, registered by the policy admin) is instantiated as a PolicySet (the type ID plus an ABI-encoded parameters blob plus an optional function selector) and bundled into a PolicyConfig (either the global config or a per-contract config). PolicyTemplate itself is metadata-only — it carries templateId, name, and description, so the shape of each template's parameters must come from the template-specific struct in IPcl.sol.

PCL Built-in Policy Templates component

Maroo's PCL ships with five built-in policy templates that cover the most common regulatory and business compliance gates. Each template defines a Solidity parameter struct in IPcl.sol (encoded into PolicySet.policy via abi.encode) and an evaluation rule that runs at the AnteHandler (for GlobalPolicyConfig) or through the PCL proxy hook path (for ContractPolicyConfig). Administrators instantiate a template as a PolicySet and attach it to a config; the on-chain policy admin decides which templates are actually registered on a given network — read IPcl.policyTemplate(templateId) at runtime rather than assuming.

PCL Policy Admin component

The PCL policy admin is a single address stored in the x/pcl module parameters and exposed via IPcl.policyAdmin(). It is the only caller authorized to register or remove policy templates and to set or clear the GlobalPolicyConfig. To ensure the admin can always recover from a misconfigured global policy or denylist that would otherwise reject its own transactions, calls from the admin to a fixed set of PCL control-plane methods bypass PCL policy evaluation entirely at both the pre-execution operations extraction step and the post-execution transfer-scan hook.

PCL Policy Enforcement mechanism

Every Maroo transaction is filtered through PCL before any state-changing work runs. Global policies (a GlobalPolicyConfig set by the policy admin) are evaluated for all transactions ahead of execution. Contract-scoped policies (a ContractPolicyConfig registered against a PCL-wrapped proxy) run through the proxy's preCall / postCall hooks. A rejection at either boundary aborts the transaction with an ABI-encoded PCL ReasonCode (or, for authorization / encoding failures, one of the shared IPrecompile errors) — the same shape whether the rejection happens at RPC-submission time or at on-chain execution, so a single client decoder handles both.

PCL Composite Policies — LogicalPolicy and ForEachPolicy component

LogicalPolicy and ForEachPolicy are structural policy templates that wrap other PolicySet entries into a tree instead of carrying their own leaf rule. LogicalPolicy combines its children under an AND (Every) or OR (Any) quantifier; ForEachPolicy takes a single child and applies it once per resolved subject — currently the caller's set of agent owners. Both are subject to a maximum structural depth (MaxDepthExceeded) and to a set of validation errors (LogicalPolicyChildrenEmpty, ForEachChildAbsent, QuantifierUnspecified, ChildSelectorNotEmpty) that reject malformed trees at registration.

PCL ReasonCodes mechanism

Every PCL rejection carries one of the typed Solidity errors declared on IPcl — or, since IPcl now inherits from IPrecompile, one of the shared boundary errors. Wallet and dApp code should decode the revert payload against the IPcl ABI (which transitively includes the IPrecompile errors) and drive UX off the error name plus arguments — never off a free-form string. The codes break into four groups: policy-violation codes (the user's transaction failed a compliance rule), configuration codes (an admin call was malformed or unauthorized), composite / structural codes (a LogicalPolicy or ForEachPolicy combinator rejected), and the inherited boundary errors (invalid address, wrong argument count, unknown method, SDK-level rejections). A small number of failures are still plain-string reverts and are not decodable as typed errors — those are called out explicitly.

Proxy & binding

The execution surface: PCL proxies, pre/post hooks, and contract-admin binding.

Policy templates

The built-in policy templates, one page per template.

DENYLIST_POLICY component

DENYLIST_POLICY is a leaf policy template whose parameter is a list of addresses. PCL evaluates it against every observed non-PCL-managed CALL route captured during execution — both direct calls and nested routes that pass through a PCL-wrapped proxy. Evaluation is call-route based, not transfer based: even a zero-value call that produces no Transfer log will trip the policy if the route touches a denylisted address. When a match is found the transaction reverts with InDenylist(address sender).

VOLUME_POLICY component

Enforces per-transaction min/max amount limits for one or more token denoms. Each transaction is evaluated independently against the limits — no rolling window, no cumulative tracking. For period-based cumulative limits, see PERIODIC_VOLUME_POLICY.

PERIODIC_VOLUME_POLICY component

Tracks cumulative transaction volume per sender per denom over a configured reset period and rejects transactions that would push the running total above the limit. Distinct from VOLUME_POLICY, which checks each transaction independently. Used for daily / monthly transfer caps and Travel-Rule–style thresholds.

EAS_POLICY component

Gates a transaction on whether the sender holds a valid (non-expired, non-revoked) attestation issued under a specific EAS schema. The canonical primitive for KYC, KYB, accreditation, and any credential-based gate. Resolves attestations through the EAS precompile so lookups stay cheap during AnteHandler evaluation.

OKRW_EAS_TRANSFER_LIMIT_POLICY component

OKRW_EAS_TRANSFER_LIMIT_POLICY no longer exists on chain. It was one of the monolithic policy templates that bundled an attestation condition with a cap; upstream removed them so that the same rule is expressed by composing the pieces. Registering or referencing this template id now fails — changeContractPolicies reverts with PolicyNotRegistered(string templateId). This page is kept so that integrations still carrying the id find out what happened and what to write instead.

OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY component

OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY no longer exists on chain. It was one of the monolithic policy templates that bundled an attestation condition with a cap; upstream removed them so that the same rule is expressed by composing the pieces. Registering or referencing this template id now fails — changeContractPolicies reverts with PolicyNotRegistered(string templateId). This page is kept so that integrations still carrying the id find out what happened and what to write instead.

AGENT_OKRW_TRANSFER_LIMIT_POLICY component

Caps a single OKRW transfer originating from an agent wallet using a limit stored as on-chain metadata on the ERC-8004 IdentityRegistry — specifically getMetadata(agentId, "TransferLimit"). The policy struct itself has no configurable fields; the cap is whatever the agent's owner has written into the metadata slot. The evaluator attributes the transfer to the agent both when the agent is the direct msg.sender and, under global-scope policies, when the agent is the initiating principal forwarded by the PCL proxy hook — so a contract that pulls funds from an agent principal still hits the same cap. If the resulting attributed value exceeds the configured TransferLimit, the transaction reverts with ExceededAgentTransferLimit(maxLimit, value).

Regulatory primitives

Chain-level regulatory mechanisms beyond per-transaction policy checks.

ESC
Type to search