---
eip: 8226
title: Regulated Agent Mandate
description: A compliance delegation layer for AI agents operating on tokenized regulated assets.
author: Ludovico Rossi (@ludovicor-eth) <ludovico@brickken.com>, Dario Lo Buglio (@xaler5) <dario@brickken.com>, Thamer Dridi (@thamerdridi) <thamer@brickken.com>, Nabil El Alami Khalifi (@nabil-brickken) <nabil@brickken.com>
discussions-to: https://ethereum-magicians.org/t/erc-8226-regulated-agent-mandate/28208
status: Draft
type: Standards Track
category: ERC
created: 2026-04-12
requires: 165
---

## Abstract

This standard defines a compliance delegation layer for AI agents operating on tokenized regulated assets. It specifies how a verified principal can delegate scoped, time-bounded, and financially capped authority to an on-chain agent, and how regulated token contracts verify mandate validity through a pre-transfer compliance hook before executing transfers.

Regulated Agent Mandate Standard, or RAMS, is agnostic to the agent identity system, the token standard, and the token compliance framework. It is designed to work with any trustless agent identity system that maps wallet addresses to agent identifiers (such as [ERC-8004](./eip-8004.md)), any token standard ([ERC-20](./eip-20.md), [ERC-721](./eip-721.md), [ERC-1155](./eip-1155.md)), and any regulated token standard that implements a pre-transfer compliance check (such as [ERC-7943](./eip-7943.md) or [ERC-3643](./eip-3643.md)).

## Motivation

The market for tokenized real-world assets is entering a phase of institutional adoption. Platforms operating under regulatory frameworks are beginning to support programmable, agent-driven portfolio management on regulated instruments. AI agents that can autonomously execute securities transactions are no longer theoretical — they are being built now, without a standard that makes their operation legally defensible.

An agent purchasing a tokenized fund unit on behalf of an investor must satisfy three conditions that no existing standard addresses jointly:

1. The principal on whose behalf the agent acts must be a verified, Know Your Customer (KYC)-cleared legal identity, not merely an Ethereum address.
1. The mandate granted to the agent must be legally traceable, time-bounded, and financially capped, analogous to a power of attorney in traditional finance.
1. The asset contract must verify mandate validity atomically at the point of transfer, without relying on off-chain coordination.

Compliance frameworks such as [ERC-7943](./eip-7943.md) and investor eligibility standards such as [ERC-3643](./eip-3643.md) govern who may hold or transact a regulated token, but neither defines an agent delegation model. Trustless agent identity standards such as [ERC-8004](./eip-8004.md) provide agent discovery and trust signals but no mandate framework. RAMS defines the delegation interface, the compliance provider model, and the integration pattern with regulated token contracts.

The compliance responsibilities across the three layers are as follows:

| Layer | Responsibility | Standard |
|---|---|---|
| Token compliance | Investor eligibility on this specific asset | Token compliance framework (e.g., [ERC-7943](./eip-7943.md), [ERC-3643](./eip-3643.md)) |
| Mandate compliance | Agent authority from this principal for this scope | This ERC |
| Agent identity | Agent exists and is registered | Agent registry (e.g., [ERC-8004](./eip-8004.md)) |

## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHOULD", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). All implementations MUST implement [ERC-165](./eip-165.md).

RAMS defines two interfaces that MUST be deployed as separate contracts:

| Interface | Role | Deployed by |
|---|---|---|
| `IComplianceProvider` | Verifies principal eligibility (identity + compliance) | Compliance operator or platform |
| `IAgentMandate` | Mandate lifecycle, execution recording, freeze, principal resolution, and views | RAMS registry operator |

RAMS-aware regulated token contracts consult the RAMS registry inside their existing pre-transfer compliance hook.

### `IComplianceProvider`

`IComplianceProvider` is implemented by a third-party compliance operator or platform — for example, a KYC provider or an on-chain identity registry adapter — and deployed independently of the RAMS registry. Its address is supplied by the principal at mandate grant time via the `complianceProvider` field of `IAgentMandate.grantMandate`. A single `IComplianceProvider` instance MAY serve multiple mandates across multiple principals.

The compliance provider manages principal eligibility: granting, revoking, and checking whether a principal is eligible for a given scope. Identity verification is a subset of compliance checking. A compliance provider that declares a principal eligible has implicitly verified that the underlying identity is valid.

Implementations MUST return structured data sufficient for regulatory audit. A binary oracle is not conformant: reason codes and expiry timestamps are required for any credible compliance trail. The compliance provider MUST verify that `identityRef` resolves to a valid, unrevoked attestation at `checkPrincipal` invocation time and MUST return `eligible == false` if it does not.

```solidity
interface IComplianceProvider {
    enum ReasonCode {
        COMPLIANT,             // 0
        KYC_EXPIRED,           // 1
        AML_FLAG,              // 2
        NOT_ACCREDITED,        // 3
        NOT_QUALIFIED,         // 4
        JURISDICTION_BLOCKED,  // 5
        IDENTITY_NOT_FOUND,    // 6
        ATTESTATION_REVOKED,   // 7
        OTHER                  // 8
    }

    /// @notice Emitted when a principal is granted eligibility for a scope.
    event PrincipalGranted(
        address indexed principal,
        bytes32 indexed scopeHash,
        bytes32 identityRef
    );

    /// @notice Emitted when a previously eligible principal is revoked.
    event PrincipalRevoked(
        address indexed principal,
        bytes32 indexed scopeHash,
        bytes32 identityRef,
        ReasonCode reason
    );

    /// @notice Grants eligibility to a principal for a given scope.
    /// @param principal The on-chain address of the principal.
    /// @param identityRef An off-chain identity reference (e.g., keccak256 of a Decentralized Identifier (DID) or attestation ID).
    /// @param scopeHash The keccak256 hash of the off-chain scope document.
    function grantPrincipal(address principal, bytes32 identityRef, bytes32 scopeHash) external;

    /// @notice Revokes a principal's eligibility for a given scope.
    /// @param principal The on-chain address of the principal.
    /// @param scopeHash The keccak256 hash of the off-chain scope document.
    /// @param reason The reason for revocation.
    function revokePrincipal(address principal, bytes32 scopeHash, ReasonCode reason) external;

    /// @notice Returns eligibility of a principal for a given scope.
    /// @param principal The on-chain address of the principal.
    /// @param identityRef An off-chain identity reference (e.g., keccak256 of a Decentralized Identifier (DID) or attestation ID).
    /// @param scopeHash The keccak256 hash of the off-chain scope document.
    /// @return eligible True if the principal is compliant for this scope.
    /// @return reason Reason code. MUST be COMPLIANT when eligible is true.
    /// @return expiresAt Unix timestamp after which this result MUST be re-checked. 0 means no expiry.
    function checkPrincipal(address principal, bytes32 identityRef, bytes32 scopeHash)
        external view returns (bool eligible, ReasonCode reason, uint48 expiresAt);
}
```

An `IComplianceProvider` implementation MAY delegate identity verification to on-chain identity standards, Ethereum Attestation Service (EAS) attestations, or any other identity backend. The interface is agnostic to the source.

### `IAgentMandate`

`IAgentMandate` is implemented by the RAMS registry, a single contract deployed by a registry operator (e.g., a platform or a regulated entity acting as operator). Principals interact with this contract to grant, extend, and revoke mandates. Regulated token contracts interact with this contract inside their pre-transfer compliance hook to verify mandate validity and record executions.

Each mandate references an off-chain scope document (specified in [Scope Document](#scope-document)) via `scopeHash`. The scope document is a content-addressed JSON file that describes, in human-readable form, the actions delegated, asset classes, jurisdictions, and operational notes. The on-chain `MandateScopeParams` struct mirrors the enforceable subset of those fields.

Each `agentId` has at most one active mandate at any given time. This constraint is by design: in regulated markets, each agent-principal relationship requires segregated accounts and independent audit trails. An operator serving multiple principals deploys one agent wallet per principal, each with its own `agentId` and mandate. This mirrors the account segregation requirements common to regulated financial frameworks.

Value limits in `MandateScopeParams` are denominated in the base unit of the token at `assetAddress`. If `assetAddress` is `address(0)` (asset-class mandate), limits are denominated in the smallest unit of the currency declared in the off-chain scope document. `uint128` accommodates any practical financial instrument value with 18-decimal precision (up to approximately 3.4 × 10^20 whole tokens). A value of `type(uint128).max` signals "no limit" for both `maxTransactionValue` and `maxCumulativeValue`. Implementations MUST revert if an incoming amount exceeds `type(uint128).max`.

The `EnforcerTier` enum distinguishes platform-initiated from regulator-initiated enforcement. This distinction is legally material: in a regulatory dispute, the audit trail must identify both the enforcer address and the authority tier. Implementations MUST use an access control mechanism that enforces this distinction. Access control for `freezeAgent` and `unfreezeAgent` MUST be enforced by the implementation. Global freeze (`bytes32(0)`) MUST be restricted to `REGULATORY` tier enforcers. Jurisdiction-scoped freeze MAY be executed by either tier. The admin role governing enforcer permissions MUST NOT be the same address as any enforcer.

An approved operator MAY call `revokeMandate` and `extendMandate` on behalf of the principal.

`recordExecution` MUST only be callable by the token at `onChainScope.assetAddress` (asset-specific mandates), or by a contract registered in the RAMS token registry, or by an address with enforcer privileges (asset-class mandates). Arbitrary callers MUST be rejected. `recordExecution` MUST revert if the amount exceeds `maxTransactionValue` (when not set to `type(uint128).max`), or if `cumulativeUsed + amount` exceeds `maxCumulativeValue` (when not set to `type(uint128).max`). `cumulativeUsed` MUST NOT reset on `extendMandate`. A cap reset requires explicit revocation and re-issuance.

`grantMandate` MUST revert if `signature` is empty and `msg.sender != principal`. If `signature` is non-empty, implementations MUST verify the signature against `principal`. `grantMandate` MUST revert if `identityRef` is non-zero and the designated `complianceProvider.checkPrincipal` returns `eligible == false`. `grantMandate` MUST revert if `agentId` already has an active mandate. `extendMandate` MUST revert if `newValidUntil` is less than or equal to the current `validUntil`. Implementations MUST NOT treat `identityRef != bytes32(0)` as proof of eligibility independent of `complianceProvider.checkPrincipal`.

`isActive` and `isActiveForAmount` MUST behave as follows (Solidity pseudocode, illustrative):

```solidity
function isActive(uint256 agentId, address principal) public view returns (bool) {
    Mandate storage m = mandates[agentId][principal];

    if (m.principal == address(0))                                              return false; // mandate does not exist
    if (block.timestamp < m.validFrom || block.timestamp > m.validUntil)        return false; // outside validity window
    if (m.revoked)                                                              return false;
    if (isFrozen(agentId, m.onChainScope.jurisdictionHash))                     return false;
    if (isFrozen(agentId, bytes32(0)))                                          return false; // global freeze

    if (m.complianceProvider != address(0)) {
        (bool eligible, , ) = IComplianceProvider(m.complianceProvider)
            .checkPrincipal(principal, m.identityRef, m.scopeHash);
        if (!eligible)                                                          return false;
    }

    if (m.onChainScope.maxCumulativeValue != type(uint128).max
        && m.cumulativeUsed >= m.onChainScope.maxCumulativeValue)               return false; // cap exhausted

    return true;
}

function isActiveForAmount(uint256 agentId, address principal, uint256 amount) public view returns (bool) {
    if (!isActive(agentId, principal))                                          return false;

    Mandate storage m = mandates[agentId][principal];

    if (m.onChainScope.maxTransactionValue != type(uint128).max
        && uint128(amount) > m.onChainScope.maxTransactionValue)                return false;

    if (m.onChainScope.maxCumulativeValue != type(uint128).max
        && m.cumulativeUsed + uint128(amount) > m.onChainScope.maxCumulativeValue) return false;

    return true;
}
```

```solidity
interface IAgentMandate is IERC165 {

    struct MandateScopeParams {
        uint128 maxTransactionValue;
        uint128 maxCumulativeValue;
        address assetAddress;
        bytes32 jurisdictionHash;
    }

    struct Mandate {
        address            principal;
        bytes32            identityRef;
        bytes32            scopeHash;
        address            complianceProvider;
        MandateScopeParams onChainScope;
        uint48             validFrom;
        uint48             validUntil;
        uint128            cumulativeUsed;
        bool               revoked;
    }

    enum EnforcerTier { PLATFORM, REGULATORY }

    /// @notice Emitted when a mandate is granted to an agent.
    event MandateGranted(
        uint256 indexed agentId,
        address indexed principal,
        address indexed complianceProvider,
        bytes32 scopeHash,
        uint48 validFrom,
        uint48 validUntil
    );

    /// @notice Emitted when a mandate is revoked.
    event MandateRevoked(
        uint256 indexed agentId,
        address indexed principal,
        address indexed revokedBy
    );

    /// @notice Emitted when a mandate's validity is extended.
    event MandateExtended(
        uint256 indexed agentId,
        address indexed principal,
        uint48 newValidUntil
    );

    /// @notice Emitted when an operator approval is set or revoked.
    event OperatorSet(
        address indexed principal,
        address indexed operator,
        bool approved
    );

    /// @notice Emitted when an agent executes a transfer recorded by a RAMS-aware token.
    event ExecutionRecorded(
        uint256 indexed agentId,
        address indexed principal,
        uint256 amount,
        uint128 cumulativeUsed
    );

    /// @notice Emitted when an agent is frozen for a jurisdiction or globally.
    /// @dev jurisdictionHash of bytes32(0) indicates a global freeze. Global freeze is REGULATORY tier only.
    event AgentFrozen(
        uint256 indexed agentId,
        bytes32 indexed jurisdictionHash,
        address indexed enforcer,
        EnforcerTier tier
    );

    /// @notice Emitted when a freeze is lifted.
    event AgentUnfrozen(
        uint256 indexed agentId,
        bytes32 indexed jurisdictionHash,
        address indexed enforcer
    );

    /// @notice Grants a mandate to the specified agent on behalf of a principal.
    /// @param agentId The unique agent identifier.
    /// @param principal The address of the principal granting the mandate.
    /// @param identityRef Off-chain identity reference for the principal. MUST be non-zero for regulated asset mandates.
    /// @param scopeHash keccak256 of the off-chain scope document stored on IPFS or equivalent.
    /// @param onChainScope Structured on-chain scope parameters.
    /// @param complianceProvider Address of an IComplianceProvider. May be address(0) to skip compliance checks.
    /// @param validFrom Unix timestamp from which the mandate is active.
    /// @param validUntil Unix timestamp after which the mandate expires.
    /// @param signature Optional signature by the principal. If empty, msg.sender MUST equal principal.
    function grantMandate(
        uint256 agentId,
        address principal,
        bytes32 identityRef,
        bytes32 scopeHash,
        MandateScopeParams calldata onChainScope,
        address complianceProvider,
        uint48 validFrom,
        uint48 validUntil,
        bytes calldata signature
    ) external;

    /// @notice Revokes the active mandate for the given agent and principal.
    /// @dev Callable by the principal or an approved operator.
    /// @param agentId The unique agent identifier.
    /// @param principal The address of the principal whose mandate is revoked.
    function revokeMandate(uint256 agentId, address principal) external;

    /// @notice Extends the validity of an existing mandate without resetting cumulativeUsed.
    /// @dev Callable by the principal or an approved operator.
    /// @param agentId The unique agent identifier.
    /// @param principal The address of the principal whose mandate is extended.
    /// @param newValidUntil New expiry timestamp. MUST be greater than the current validUntil.
    function extendMandate(uint256 agentId, address principal, uint48 newValidUntil) external;

    /// @notice Freezes an agent for a given jurisdiction, or globally if jurisdictionHash is bytes32(0).
    /// @dev Global freeze is restricted to REGULATORY tier enforcers.
    /// @param agentId The unique agent identifier.
    /// @param jurisdictionHash keccak256 of the ISO 3166-1 alpha-2 jurisdiction code, or bytes32(0) for global.
    function freezeAgent(uint256 agentId, bytes32 jurisdictionHash) external;

    /// @notice Lifts a freeze for a given agent and jurisdiction.
    /// @param agentId The unique agent identifier.
    /// @param jurisdictionHash The jurisdiction hash of the freeze to lift, or bytes32(0) for global.
    function unfreezeAgent(uint256 agentId, bytes32 jurisdictionHash) external;

    /// @notice Sets or revokes operator approval for msg.sender.
    /// @param operator The address being approved or revoked.
    /// @param approved True to approve, false to revoke.
    function setOperator(address operator, bool approved) external;

    /// @notice Records an agent-initiated execution. Called by RAMS-aware regulated tokens inside their pre-transfer hook.
    /// @param agentId The unique agent identifier.
    /// @param principal The principal on whose behalf the transfer is executed.
    /// @param amount The transfer amount in the token's base unit.
    function recordExecution(uint256 agentId, address principal, uint256 amount) external;

    /// @notice Returns the principal address of the sole active mandate for the given agent.
    /// @param agentId The unique agent identifier.
    /// @return The principal address.
    function getActivePrincipal(uint256 agentId) external view returns (address);

    /// @notice Returns true if the mandate for the given agent and principal is currently active.
    /// @param agentId The unique agent identifier.
    /// @param principal The principal address.
    /// @return True if the mandate is active.
    function isActive(uint256 agentId, address principal) external view returns (bool);

    /// @notice Returns true if the mandate is active and the given amount is within all defined limits.
    /// @param agentId The unique agent identifier.
    /// @param principal The principal address.
    /// @param amount The transfer amount to check, in the token's base unit.
    /// @return True if the mandate is active and the amount is within limits.
    function isActiveForAmount(uint256 agentId, address principal, uint256 amount) external view returns (bool);

    /// @notice Returns the full Mandate struct for the given agent and principal.
    /// @param agentId The unique agent identifier.
    /// @param principal The principal address.
    /// @return The Mandate struct.
    function getMandate(uint256 agentId, address principal) external view returns (Mandate memory);

    /// @notice Returns true if the operator is approved for the given principal.
    /// @param principal The principal address.
    /// @param operator The operator address.
    /// @return True if approved.
    function isOperator(address principal, address operator) external view returns (bool);

    /// @notice Returns true if the agent is frozen for the given jurisdiction.
    /// @param agentId The unique agent identifier.
    /// @param jurisdictionHash The jurisdiction hash, or bytes32(0) to check the global freeze.
    /// @return True if frozen.
    function isFrozen(uint256 agentId, bytes32 jurisdictionHash) external view returns (bool);

}
```

### Integration with Regulated Token Contracts

Regulated token contracts that implement a pre-transfer compliance hook (such as `canTransfer` in [ERC-7943](./eip-7943.md) or transfer restrictions in [ERC-3643](./eip-3643.md)) can integrate with RAMS to detect agent-initiated transfers and enforce mandate validity. Agents call standard transfer functions ([ERC-20](./eip-20.md) `transfer`/`transferFrom`, [ERC-721](./eip-721.md) `safeTransferFrom`, [ERC-1155](./eip-1155.md) `safeTransferFrom`/`safeBatchTransferFrom`), no token-side changes are needed. The pseudocode below uses [ERC-20](./eip-20.md) as an example; the same pattern applies to [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) tokens.

The token MUST be able to resolve a wallet address to an agent identifier. An agent registry (such as [ERC-8004](./eip-8004.md)) MAY provide this mapping via a `getAgentByWallet(sender)` lookup. Alternatively, RAMS implementations MAY maintain a separate `agentWallet => agentId` mapping populated at `grantMandate` time.

When the sender is identified as an agent, the token resolves the principal by calling `ramsRegistry.getActivePrincipal(agentId)` and then executes a dual compliance check: first, the token's own investor eligibility check on the principal; second, the RAMS mandate validity (agent authority from this principal for this scope). Both conditions MUST hold for the transfer to proceed. The token's compliance module is never bypassed. When an agent initiates a transfer, the token applies its investor eligibility checks to the principal address, not the agent wallet. RAMS adds a second compliance layer but does not replace or override the first. The token issuer retains full sovereignty over who may hold or transact their instrument.

The asset address MUST also be verified: if `onChainScope.assetAddress` is not `address(0)`, it MUST equal `address(this)`. If this check fails, the transfer MUST be rejected.

```solidity
// Pseudocode: canTransfer for RAMS-aware ERC-7943 tokens.

function canTransfer(address from, address to, uint256 amount)
    public view returns (bool)
{
    uint256 agentId = agentRegistry.getAgentByWallet(from);

    if (agentId != 0) {
        address principal = ramsRegistry.getActivePrincipal(agentId);

        require(canSend(principal), ERC7943CannotTransfer(principal, to, amount));
        require(canReceive(to), ERC7943CannotTransfer(principal, to, amount));

        if (!ramsRegistry.isActiveForAmount(agentId, principal, amount)) return false;

        Mandate memory mandate = ramsRegistry.getMandate(agentId, principal);
        if (mandate.onChainScope.assetAddress != address(0) &&
            mandate.onChainScope.assetAddress != address(this)) return false;

        return true;
    }

    require(canSend(from), ERC7943CannotTransfer(from, to, amount));
    require(canReceive(to), ERC7943CannotTransfer(from, to, amount));
    return true;
}
```

```solidity
// Pseudocode: transfer recording RAMS execution.

function transfer(address to, uint256 amount) public returns (bool) {
    require(canTransfer(msg.sender, to, amount), ERC7943CannotTransfer(msg.sender, to, amount));

    uint256 agentId = agentRegistry.getAgentByWallet(msg.sender);
    if (agentId != 0) {
        address principal = ramsRegistry.getActivePrincipal(agentId);
        ramsRegistry.recordExecution(agentId, principal, amount);
        emit AgentOperationDetected(agentId, principal, amount);
    }

    return super.transfer(to, amount);
}
```

RAMS-aware tokens SHOULD emit the following event for every agent-initiated transfer:

```solidity
event AgentOperationDetected(
    uint256 indexed agentId,
    address indexed principal,
    uint256 amount
);
```

### Scope Document

The off-chain JSON referenced by `scopeHash` MUST be stored on IPFS or equivalent content-addressed storage. The `notes` field SHOULD describe in human-readable language: (a) the actions delegated, (b) operational limitations not captured by structured fields, and (c) the regulatory context.

Implementations MUST verify `onChainScope` internal consistency at `grantMandate` time. Off-chain tooling and the `grantMandate` caller MUST verify that `onChainScope` values are consistent with the scope document referenced by `scopeHash` before submission. Discrepancies discovered post-grant MUST be resolved via revocation.

```json
{
    "type": "urn:eip:RAMS:scope:v1",
    "actions": ["agent.action.buy", "agent.action.sell", "agent.action.transfer"],
    "assetClasses": ["Security Token", "Debt Token"],
    "tokenAddress": "0x...",
    "maxTransactionValue": "500000",
    "maxCumulativeValue": "2000000",
    "valueCurrency": "USD",
    "jurisdictions": ["EU", "AE-DU", "CH"],
    "complianceProviderRef": "eip155:1:0x...",
    "notes": "Agent is authorized to execute secondary market purchases of tokenized real estate securities on behalf of the principal, up to the stated transaction and cumulative limits, within the listed jurisdictions. No leveraged or derivative transactions are authorized."
}
```

## Rationale

RAMS is defined as a separate ERC rather than an extension of any specific agent identity or token compliance standard because mandate delegation is a distinct concern. Agent identity standards (such as [ERC-8004](./eip-8004.md)) handle discovery and trust signals. Token compliance frameworks (such as [ERC-7943](./eip-7943.md) or [ERC-3643](./eip-3643.md)) handle investor eligibility. RAMS handles the delegation of authority from a principal to an agent. Coupling RAMS to a specific identity or compliance standard would limit its applicability across the fragmented regulated token ecosystem.

A single `IComplianceProvider` interface is used rather than separate identity and compliance interfaces because identity verification is a logical subset of compliance checking. A compliance provider that declares a principal eligible has already verified that the underlying identity is valid and unrevoked. Splitting these into separate interfaces forces consumers to make two calls to answer one question, creates inconsistency risk, and doubles the integration surface. A single interface with granular `ReasonCode` values preserves diagnostic specificity without the architectural overhead.

Freeze authority is kept within `IAgentMandate` rather than a separate registry because an enforcer does not exist independently of the mandates it can freeze. Extracting freeze into a separate contract creates an additional deployment, audit surface, and integration point for what is functionally an access control list. The `EnforcerTier` distinction (`PLATFORM` vs `REGULATORY`) is preserved in the events, which is where it matters for audit trails.

One active principal per `agentId` is enforced because in regulated markets every managed account must be segregated. A portfolio manager handling multiple clients operates distinct accounts, each with its own mandate, risk profile, and audit trail. The on-chain equivalent is one agent wallet per principal with its own `agentId`. This mirrors the account segregation requirements common to regulated financial frameworks. Multiple agent wallets are trivially deployable via `CREATE2`.

Agents use standard token functions ([ERC-20](./eip-20.md), [ERC-721](./eip-721.md), [ERC-1155](./eip-1155.md)) rather than agent-prefixed variants because requiring `agentTransfer(to, amount, principal)` on the token leads to interface duplication: every token operation an agent can perform (`transfer`, `transferFrom`, `safeTransferFrom`, `mint`, `burn`, `approve`) would require an `agent*` variant. This bloats the token interface, increases the audit surface, and forces every RAMS-aware token to implement a parallel function set. The principal is resolved from the RAMS registry via `getActivePrincipal(agentId)` inside the token's existing pre-transfer compliance hook, requiring no new functions on the token.

The pre-transfer hook executes a dual compliance check because a RAMS-aware token must first apply its own investor eligibility checks to the principal and then verify the RAMS mandate. These are two different questions answered by two different layers: the token issuer defines who may hold or transact their instrument, and RAMS defines who may delegate to an agent and under what constraints. Both conditions must hold, preserving full issuer sovereignty.

Value limits are denominated in token base units rather than fiat because denominating in fiat requires an on-chain FX oracle, introducing price manipulation risk and liveness dependency. Limits in token base units are deterministic and oracle-free. The off-chain scope document may include `valueCurrency` for readability; this field has no enforcement role on-chain.

`cumulativeUsed` does not reset on `extendMandate` because a mandate represents a single delegation agreement. Extending validity does not constitute a new agreement. A cap reset requires explicit revocation and re-issuance, preserving audit trail integrity.

Jurisdiction-scoped freeze is supported because a regulatory action in one jurisdiction must not prevent an agent from operating in others. Global freeze (`bytes32(0)`) is reserved for `REGULATORY` enforcers only, reflecting the exceptional nature of a full regulatory halt.

Operator permissions are explicitly scoped so that delegation of authority remains auditable. An operator can revoke or extend a mandate (defensive actions) but cannot grant new mandates (offensive actions that create new legal obligations). This asymmetry reflects the fiduciary principle that a delegate should be able to limit or terminate authority but not expand it without the principal's direct authorization.

RAMS defines its own `IComplianceProvider` interface that is agnostic to the identity verification backend. An implementation MAY use an [ERC-3643](./eip-3643.md)-compatible registry, an [ERC-7943](./eip-7943.md) compliance module, or any other on-chain identity source as the data backend for principal eligibility checks. There is no dependency on any specific token compliance or identity standard.

## Backwards Compatibility

RAMS introduces no changes to any existing standard.

## Reference Implementation

<!-- TODO: reference implementation -->

## Security Considerations

`identityRef` is a reference, not proof. Eligibility comes from `complianceProvider.checkPrincipal`, not from a non-zero `identityRef` value. If an agent wallet address is also a standard investor address, the pre-transfer agent-detection logic could misidentify it. Agent identity registries should require proof of key control for agent wallet registration and emit a distinct event when an address is registered as an agent wallet.

If `recordExecution` were callable by arbitrary addresses, an attacker could advance `cumulativeUsed` to exhaust the cap and deny service to the legitimate agent. The caller restriction specified in the Specification closes this attack surface. Callers can use `isActiveForAmount` for pre-transaction checks or rely on `recordExecution`'s revert behavior for atomic enforcement.

A compromised compliance provider can declare non-compliant principals as eligible. Principals should only use compliance providers with audited, time-locked upgrade mechanisms. The `REGULATORY` enforcer tier operates independently of compliance provider state, so a captured provider cannot prevent a regulator from halting an agent. If a compliance provider contract becomes non-responsive, all mandates referencing that provider become inoperative because `isActive` calls `complianceProvider.checkPrincipal`, which will revert. This constitutes a systemic denial-of-service risk. Principals should select compliance providers with documented uptime Service Level Agreements (SLAs) and audited fallback mechanisms. Implementations may define a grace period after which a mandate with an unresponsive provider is auto-revoked rather than permanently blocked, provided the revocation is logged for audit.

A transaction can fail at two distinct compliance layers: the token's investor eligibility check on the principal, or the RAMS mandate validity check on the agent. Frontends and autonomous agents should pre-verify both layers before submitting a transaction to enable clear diagnostic reporting.

On-chain contracts cannot read or verify the content of an off-chain IPFS document. If `onChainScope` and the off-chain scope document drift, audit and enforcement diverge. The consistency requirements specified in the Scope Document section close this gap, and post-grant discrepancies are resolved via revocation.

A window exists between a `PrincipalRevoked` event from the compliance provider and enforcement of a freeze on the RAMS registry. High-sensitivity protocols should use an automated freeze relay that monitors `PrincipalRevoked` events and calls `freezeAgent` immediately. A `PLATFORM` enforcer that can execute global freezes is non-conformant per the Specification. The admin-versus-enforcer role separation specified in the Specification prevents self-escalation; without it, a single compromised key could both grant enforcer privileges and execute global freezes.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).