Two-Layer Policy Model

architecture agents

Off-chain MAWS policy checks, and the separate on-chain PCL policies the chain enforces.

MAWS checks an agent's policy before it signs, so an AI agent gets immediate feedback. Those MAWS rules — spendingLimit, allowedTargets, status — are stored on-chain as IdentityRegistry metadata, but only MAWS enforces them; the chain does not read them. What the chain enforces is a separate layer, the Programmable Compliance Layer (PCL), configured with its own global and contract policies. Its one per-agent cap reads a different metadata key, TransferLimit.

Architecture

graph LR
  A[Agent Intent] --> B{"Preflight (Off-chain)"}
  B -- Reject --> C[ToolError to LLM]
  B -- Pass --> D[Submit Tx]
  D --> E{"PCL (On-chain)"}
  E -- Reject --> F[Tx Reverted]
  E -- Pass --> G[State Updated]
    classDef evm fill:#0096AA,stroke:#0096AA,color:#fff;
    classDef precompile fill:#FF8C50,stroke:#FF8C50,color:#fff;
    class F evm;
    class A,B,C,D,G evm;
    class E precompile;

The two checks a transfer meets — MAWS's preflight (its own spendingLimit / allowedTargets) and the chain's PCL policies (a separate configuration). Only the second applies to a transaction sent without MAWS.

Layer 1: Off-Chain Preflight

When an agent calls transfer.send, the MCP server's AgentManager reads the agent's policy from the on-chain IdentityRegistry. It simulates the transaction rules locally. If the transfer violates the spendingLimit or allowedTargets, the server immediately returns a ToolError (e.g., POLICY_REJECTED). This saves gas and gives the LLM a chance to adjust its behavior. It is also the only place spendingLimit and allowedTargets are enforced.
const { allowed, reason } = await manager.checkPolicy(onchainId, to, valueWei);
if (!allowed) throw new ToolError("POLICY_REJECTED", reason);

Layer 2: On-Chain PCL

If the preflight passes, MAWS signs and submits the transaction, and the chain's PCL evaluates it — against PCL's OWN policies, not against spendingLimit or allowedTargets, which no chain module reads. On Maroo Testnet the global policy (read it with IPcl.globalPolicies()) caps every sender without an EAS attestation (KYC) at 2,000,000 OKRW per transaction and 10,000,000 OKRW per UTC day. An agent wallet's transfers count toward its owner's daily total, so it takes the owner's attestation to lift both caps; the wallet's own attestation lifts only the per-transaction cap. A transaction sent with the agent wallet's key outside MAWS meets these PCL policies but none of the MAWS rules. The one per-agent cap the chain can enforce is the TransferLimit metadata (an abi.encode(uint256) value in atokrw), read by AGENT_OKRW_TRANSFER_LIMIT_POLICY only where that policy is configured — see pcl-template-agent-okrw-transfer-limit-policy.
ESC
Type to search