# What is Chainlink ACE? Source: https://docs.chain.link/ace/overview Last Updated: 2026-10-05 **Chainlink Automated Compliance Engine (ACE)** is a compliance layer for EVM smart contracts. It enforces rules — transfer limits, identity checks, sanctions screening, and more — at transaction time, without embedding compliance logic in your application code. Rules can be added, updated, or removed through the ACE Platform — your application contract doesn't need to change. ACE also monitors tokens that are already deployed. [Active Monitoring](/ace/active-monitoring/overview) screens the addresses you choose on a schedule and acts onchain when their risk changes, without changing the token's contract. ## What problems does ACE solve? Building compliant applications on the blockchain requires handling: - **Dynamic policy enforcement** that evolves with regulations — without redeploying your core application contracts. - **Identity verification** across chains, without fragmented credentials that force users to re-verify on every chain. - **Trusted external data** (KYC providers, sanctions lists, price feeds, Proof of Reserves, etc.) delivered onchain to inform compliance decisions. ## Who is ACE for? ACE serves three primary audiences: - **Token issuers and asset managers** — Enforce compliance rules (transfer limits, investor checks, sanctions screening) on your smart contracts. ACE separates these rules from your application code, so you can update them as regulations change — without redeploying. If your token is already deployed and you cannot change its contract, Active Monitoring reacts to risk changes on it. - **Identity verification (IDV) providers** — Issue cross-chain credentials (KYC, AML, accredited investor status) that your clients can verify on any EVM chain from a single issuance. No need for users to re-verify on every network. - **Compliance and legal teams** — Monitor and manage compliance rules through a visual interface — no code required. Every policy decision is recorded onchain and queryable through the Reporting API, providing a complete audit trail. ## What is ACE? ACE has two layers: **onchain smart contracts** that enforce compliance rules on the blockchain, and the **ACE Platform** that lets you manage those contracts through a UI and APIs. ### Onchain contracts ACE is built on two sets of smart contracts: - **[Policy Management](/ace/concepts/policy-management)** — A dynamic engine that enforces compliance rules on your smart contracts. A **PolicyEngine** evaluates a chain of modular **policies** every time a protected function is called. Policies can be added, removed, or reordered without changing your contract. - **[Cross-Chain Identity](/ace/concepts/cross-chain-identity)** — A portable identity system for EVM chains. A **Cross-Chain Identifier (CCID)** links all of a user's wallet addresses across chains to a single identity. Credentials like KYC or AML status are attached to the CCID once and verified everywhere. These contracts execute automatically at transaction time. For a detailed view of the architecture, see [ACE Architecture](/ace/concepts/architecture). ### ACE Platform The [ACE Platform](/ace/concepts/key-terms#ace-platform) provides four managers. Policy Manager and Identity Manager operate on the contracts above: - **Policy Manager** (UI + API) — Configure and deploy compliance rules for your smart contracts. - **Identity Manager** (UI + API) — Manage cross-chain identities and issue credentials. - **Reporting Manager** (API) — Query policy run history, transaction data, and onchain state. - **Active Monitoring** (UI + API) — Screen addresses with TRM and enforce actions on tokens you already run. It uses your existing contracts instead of the ACE contracts above. ACE is currently in Beta. See [Beta Scope](/ace/beta-scope) for the current scope and supported features. ## Preventive and continuous compliance ACE enforces compliance in two ways, and you can use either or both: - **Preventive**: Policy Manager and Identity Manager block a transaction while it executes. Your contract integrates with ACE, and a transaction that breaks a policy reverts. - **Continuous**: Active Monitoring screens a watchlist of addresses with TRM on a schedule, applies your rule when an address's risk level changes, and calls an existing function on your token, such as a freeze or a blocklist entry. Your contract does not change. Preventive enforcement acts immediately but needs a contract that integrates with ACE. Continuous monitoring works on tokens already deployed, but acts at the next screening run, after the risk change. See [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous) to choose, or [What is Active Monitoring?](/ace/active-monitoring/overview) to learn more. ## Key features - **Modular policies** — Each compliance rule is a self-contained module. Chain them together to build sophisticated rulesets; add or remove individual rules without affecting others. You can also register your own [custom policies](/ace/concepts/key-terms#custom-policy). - **Update without redeploying** — When regulations change, update your compliance rules through the ACE Platform. Your core smart contract stays untouched. - **Cross-chain identity** — Verify a user's identity once; the credential is valid across every EVM chain. No re-verification needed when users operate on a new network. Share registries across organizations through [access grants](/ace/concepts/key-terms#access-grant). - **Credential data validation** — Go beyond attestation-only checks. Attach Data Validators to inspect credential contents (for example, jurisdiction codes) and enforce granular, data-level rules. - **Privacy-preserving** — Sensitive user data stays offchain. Only verification results (credentials) are recorded onchain, typically as hashes or minimal references. - **Auditable and transparent** — Every policy evaluation is recorded onchain and queryable through the Reporting API, giving compliance teams a complete audit trail. - **Ready-to-use policy library** — Pre-built modules for common scenarios: allowlists, blocklists, volume limits, time restrictions, role-based access, Proof of Reserves minting caps, identity checks, and [grouped identity validation](/ace/reference/policy-library/grouped-identity-validator-policy). - **Offchain risk screening** — Screen transaction participants with TRM Wallet Screening through a managed CRE workflow, with permits delivered onchain. See [Off-Chain Policy Execution](/ace/concepts/off-chain-policies). - **Continuous monitoring** — Screen the addresses you choose with TRM on a schedule, and react to risk changes on deployed tokens by recording, flagging for review, or enforcing an onchain action. See [Active Monitoring](/ace/active-monitoring/overview). - **Flexible signing models** — Choose between delegated signing (Chainlink signs on your behalf) and self-signing (you sign with your own keys) at onboarding. See [Signing & Ownership Model](/ace/concepts/signing-ownership). ## How it works: a real-world example Here's how ACE components work together. Imagine **Emma** (an institutional investor) wants to buy **$50,000** of a **tokenized bond** on a DEX. ![Image](/images/ace/overview-example.png) ### The compliance journey, step by step 1. **Transaction initiated** — Emma submits her buy order on the DEX. Before executing, the DEX's smart contract calls the **PolicyEngine** to validate the transaction. 2. **Credential check executes** — The PolicyEngine runs the credential check, which uses the **Cross-Chain Identity** component to verify Emma has the required credentials (KYC verified, accredited investor). The same identity and credentials would be valid even if Emma were using a different wallet address on a different EVM chain. 3. **Volume limit executes** — The engine runs the volume limit policy, which tracks Emma's trading volume over time and confirms the $50,000 trade is within her daily limit. 4. **Transaction approved** — With all policies passing, the PolicyEngine allows the transaction to proceed. The DEX executes the trade and Emma receives her tokenized bonds. The power of this model is that if regulations change tomorrow, the DEX's owners could add a new policy (for example, a time-of-day restriction) without redeploying or altering the main DEX contract. If any policy check had failed, the PolicyEngine would have reverted the transaction, preventing a non-compliant trade. ## Where to go next? ### Understand ACE Start here regardless of your role: 1. **[ACE Architecture](/ace/concepts/architecture)** — System components and how they connect. 2. **[Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous)** — The two ways ACE enforces compliance. 3. **[Key Terms](/ace/concepts/key-terms)** — ACE-specific terminology. 4. **[Policy Management](/ace/concepts/policy-management)** — How compliance rules work. 5. **[Cross-Chain Identity](/ace/concepts/cross-chain-identity)** — How identity and credentials work. ### Build with ACE - **[Policy Manager](/ace/concepts/policy-management)** — Configure and manage compliance policies for your smart contracts. - **[Identity Manager](/ace/concepts/cross-chain-identity)** — Manage cross-chain identities and credentials. - **[API Reference](/ace/reference/apis)** — Use the Coordinator, Evaluation, and Reporting APIs programmatically. - **[Policy Library](/ace/reference/policy-library)** — All pre-built policies with configuration details. - **[Policy Ordering & Composition](/ace/concepts/policy-ordering)** — How to compose effective rulesets. - **[Making Your Contract ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible)** — Integration guide for developers adding ACE to new or existing smart contracts. - **[Active Monitoring](/ace/active-monitoring/quick-start)** — Screen addresses and act on tokens you already run. - **[Signing & Ownership Model](/ace/concepts/signing-ownership)** — Understand delegated and self-signing models. --- # Beta Scope Source: https://docs.chain.link/ace/beta-scope Last Updated: 2026-10-07 ACE Beta is an early-access release for testing and integration on supported mainnet and testnet networks. The limitations listed below are all areas of active development. Each will be addressed as ACE progresses toward general availability. ## Supported networks ACE Beta is available on selected [mainnet and testnet networks](/ace/supported-networks). Additional networks may be added before general availability (GA). ## Custom extractors are registered by you, custom mappers are not available ACE Beta provides a library of [pre-built, audited policies](/ace/reference/policy-library) (allowlists, volume limits, role-based access control, pause controls, and more) and pre-built extractors for **ERC-20**, **ERC-3643**, and **CCIP-AdvancedPoolHooks** function signatures. You can also register your own [custom policies](/ace/guides/policy-manager/custom-policies) and [custom extractors](/ace/guides/policy-manager/contracts/custom-contract-types): - **Custom contract types**: You can declare contract types with your own function ABIs via the Coordinator API, so contracts beyond ERC-20 and ERC-3643 (vaults, lending pools, custom token variants) are first-class citizens in the platform. - **Custom extractors**: You write and deploy your own extractor contract (implementing `IExtractor`), then register it via the Coordinator API with its supported function signatures and per-chain deployment addresses. The platform does not write extractors for you: pre-built extractors cover ERC-20, ERC-3643, and CCIP-AdvancedPoolHooks signatures. - **Custom mappers**: Deploying mapper contracts that transform or combine extracted parameters before they reach a policy is **not available** through the platform during Beta. The built-in ERC-20 and ERC-3643 contract types get the full managed experience (policy configuration, reporting, and monitoring) through the Platform UI and Coordinator API, with extractors attached automatically. The built-in `CCIP-AdvancedPoolHooks` type covers the `preflightCheck` and `postflightCheck` hook functions; see [Protect CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools). Custom contract types and extractors are managed through the Coordinator API. See [Custom Contract Types & Extractors](/ace/guides/policy-manager/contracts/custom-contract-types) for the custom flow. ## Self-deployed contracts are not visible in the platform Contracts deployed outside the ACE Platform — for example, via Foundry scripts or direct factory calls — will not appear in the UI or API responses. The ACE Platform only tracks and manages contracts it deploys. Self-deployed PolicyEngines, policies, registries, and extractors function onchain but will not appear in the UI, API responses, or reporting dashboards. To use the full managed experience (UI dashboards, Reporting API queries, policy management), deploy contracts through the Platform UI or Coordinator API. ## Credential data validation is a curated catalog In addition to attestation-based checks (verifying whether a credential exists), the ACE Platform supports **credential data validation** — inspecting the contents of a credential's `credentialData` field for more granular checks. You link a data schema to a credential type, issue credentials that carry data, and attach a **Data Validator** to a policy's credential source to enforce rules on that data. Data Validators are a **curated catalog maintained by Chainlink**, not something organizations deploy themselves. This is **by design**: curating the available validators and their data schemas ensures no personally identifiable information (PII) are used. The first available validator supports **jurisdiction control** using [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes; Chainlink may add further validators over time. Bringing your own Data Validator is not offered — this is a permanent design choice, not a Beta limitation. To get started, see [Managing Data Validators](/ace/guides/policy-manager/manage-data-validators). For the conceptual explanation of attestation-only vs. Credential Data Validator checks, see [Cross-Chain Identity — Credential data and privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy). ## Signing model is chosen at onboarding ACE supports two signing models: **delegated signing** (Chainlink signs and executes transactions on your behalf) and **self-signing** (you sign operations yourself using the [CRE Connect SDK](https://github.com/smartcontractkit/crec-sdk)). Your organization chooses its signing model during onboarding. In both models, you retain full ownership of your contracts through the [CRE Connect Wallet](/ace/concepts/signing-ownership). See [Signing & Ownership Model](/ace/concepts/signing-ownership) for details on how each model works. ## Managed offchain risk policies are limited during Beta ACE Beta provides a managed `wallet_risk_scoring` policy that screens transaction participants with TRM Wallet Screening and delivers approved permits onchain through a managed CRE workflow. > **CAUTION: MVP feature** > > Managed offchain risk policies and the Evaluation API are an MVP. Their interfaces and capabilities can change during > Beta. Contact your Chainlink representative before using this feature and for help with setup. The following limitations apply: - **TRM access required** — Your organization must have a TRM Labs account with Wallet Screening API access and provide its own API credential through CRE Vault DON. - **One policy type** — `wallet_risk_scoring` is the only managed offchain policy available. Custom offchain integrations require assistance from Chainlink. - **One active policy per organization** — Archive the existing offchain policy before creating another. - **Ten addresses per evaluation** — A workflow execution can screen at most ten unique wallet addresses. - **Fixed permit lifetime and usage** — Managed permits are single-use and do not expire. These values are not configurable in the current release. - **Extractor-dependent protection** — Permit parameters must correspond to supported extractor outputs and exactly match the values extracted from the eventual onchain transaction. - **CRE quotas apply** — Evaluations are subject to current [CRE Service Quotas](https://docs.chain.link/cre/service-quotas), including the HTTP trigger rate limit. See [Offchain Policies](/ace/guides/policy-manager/offchain-policies) for an overview, [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) to configure wallet screening, and [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) to integrate evaluations into an application. ## Active Monitoring is limited during Beta Active Monitoring screens a watchlist with TRM and acts on tokens you already run. See [How Active Monitoring Works](/ace/active-monitoring/concepts/how-it-works). The following limitations apply: - **TRM access required**: Your organization needs a TRM Labs account with Wallet Screening API access. You store the key in the Platform, in **General settings > API access**. This key is separate from the credential of managed offchain risk policies. - **TRM is the only provider**: Active Monitoring has no other screening provider. - **Watchlist**: - You cannot remove an address after you add it. - The Platform UI accepts up to 100 rows per CSV upload. - **One monitoring rule per token**: A rule is immutable. To change it, delete it and create a new one. - **Monitored tokens are fixed**: You cannot edit or remove a monitored token after you register it. - **Enforcement functions have conditions**: A function must be a write function with named inputs, no array or tuple inputs, and at least one input. - **Screening interval**: The Platform UI offers 6, 12, or 24 hours. The API accepts 1 to 168 hours. - **Networks**: Active Monitoring is available on the [supported networks](/ace/supported-networks) where your organization has a created CRE Connect Wallet. To get started, see the [Active Monitoring Quick Start](/ace/active-monitoring/quick-start). --- # Supported Networks Source: https://docs.chain.link/ace/supported-networks Last Updated: 2026-10-05 ACE Beta is available on the following networks. > **NOTE: Mainnet access** > > Mainnet networks are not enabled for all ACE Beta organizations by default. Contact your Chainlink contact to request > mainnet access. Testnet networks are available once ACE is enabled for your organization. > **NOTE: Active Monitoring** > > [Active Monitoring](/ace/active-monitoring/overview) uses these networks. You can register a token on a network only > if your organization has a created [CRE Connect > Wallet](/ace/getting-started/account-setup#4-set-up-cre-connect-wallets) there. ## Mainnet networks Network Chain ID Chain Selector Ethereum Mainnet `1` `5009297550715157269` Arbitrum Mainnet `42161` `4949039107694359620` Avalanche Mainnet `43114` `6433500567565415381` Base Mainnet `8453` `15971525489660198786` Polygon Mainnet `137` `4051577828743386545` ## Testnet networks Network Chain ID Chain Selector Ethereum Sepolia `11155111` `16015286601757825753` Arbitrum Sepolia `421614` `3478487238524512106` Avalanche Fuji `43113` `14767482510784806043` Base Sepolia `84532` `10344971235874465080` Polygon Amoy `80002` `16281711391670634445` > **NOTE: About chain selectors** > > A **chain selector** is Chainlink's unique numeric identifier for each blockchain network. Unlike chain IDs, chain > selectors are globally unique across all supported networks. The Coordinator API uses chain selectors — not chain IDs > — in all multi-chain operations such as creating policy engines, registering targets, or deploying protections. --- # Release Notes Source: https://docs.chain.link/ace/release-notes Last Updated: 2026-10-07 ## October 7, 2026: Active Monitoring ACE adds a continuous mode of compliance: **Active Monitoring**. It screens the addresses you choose with TRM on a schedule, applies a rule you define when an address's risk changes, and acts onchain on tokens you already run, without changing their contracts. Policy Manager and Identity Manager stay preventive: they block a transaction while it executes. See [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous). ### What's new - **Monitored tokens**: Register a token and its associated contracts from their ABI, with the enforcement functions Active Monitoring may call. One configuration covers every network of the token. See [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens). - **Monitoring rules**: Map each TRM risk level to a response: Silently log, Flag for review, or Enforce one or more onchain actions, with an optional token balance condition. See [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules). - **Watchlist and screening**: Add addresses from a CSV file or an identity registry, store your TRM API key, and choose the screening interval. See [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist) and [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening). - **Decisions log**: Review every decision with the TRM result, the matched rule, and the operation status, and resolve or enforce flagged decisions. See [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions). - **Coordinator API**: The new Active Monitoring endpoints cover these operations, except resolving and enforcing flagged decisions, which are available in the Platform UI only. See [Active Monitoring API](/ace/active-monitoring/reference/api). For the current limits, see [Beta Scope](/ace/beta-scope#active-monitoring-is-limited-during-beta). ## September 14, 2026: Custom contracts This release makes any ACE-compatible contract a first-class citizen in the platform, not just ERC-20 and ERC-3643 tokens. ### What's new - **Custom contract types**: Declare contract types with your own function ABIs via the new `/contract-types` Coordinator API endpoints. Custom types appear alongside the built-in contract types (ERC-20, ERC-3643, and [CCIP-AdvancedPoolHooks](/ace/guides/policy-manager/ccip-token-pools)) and can be attached to policy engines and targets with `contract_type_ids`. See [Custom Contract Types & Extractors](/ace/guides/policy-manager/contracts/custom-contract-types). - **Custom extractors**: Write and deploy your own extractor contract (implementing `IExtractor` from the [chainlink-ace](https://github.com/smartcontractkit/chainlink-ace) repository) and register it via `POST /extractors` with its supported function signatures and per-chain deployment addresses. Pre-built extractors cover ERC-20, ERC-3643, and CCIP-AdvancedPoolHooks signatures; custom extractors extend coverage to any other function signature. - **Contract types on engines and targets**: `POST/PUT /policy-engines` and `POST/PUT /targets` accept `contract_type_ids`, and engine, target, and extractor responses now include their associated contract types (`contract_types`, `assigned_contract_types`). - **ERC-3643 extractor coverage fix**: `ERC3643MintBurnExtractor` now covers `burn(address,uint256)` only; `mint(address,uint256)` is covered by `ComplianceTokenMintBurnExtractor`. If you protect `mint` on an ERC-3643 token, attach `ComplianceTokenMintBurnExtractor`. ## July 17, 2026: ACE Beta+ ACE Beta+ builds on ACE Beta with new compliance capabilities. This release is still a Beta — see [Beta Scope](/ace/beta-scope) for the current scope and limitations. ### What's new - **Custom policies** — You can now write, deploy, and register your own policy contract and use it like a pre-built library policy. Custom policy implementations are scoped to your organization. See [Custom Policies](/ace/guides/policy-manager/custom-policies). - **Grouped identity validation** — The new [GroupedIdentityValidatorPolicy](/ace/reference/policy-library/grouped-identity-validator-policy) applies different credential requirements to different accounts. It routes each account to a group — by credential attestation or by credential data (for example, jurisdiction) — then validates the account against that group's requirements. Use it to enforce, for example, one rule set for individuals and another for businesses, or different rules per jurisdiction, within a single policy. - **External registries** — Organizations can now share registries with each other. A registry owner grants another organization read access, and the grantee can use the shared registry's identities and credentials as a credential source in its own policies — without re-issuing credentials. Access is read-only for the recipient and revocable at any time. See [External Registries](/ace/guides/identity-manager/external-registries). - **Credential data validation** — Credentials are no longer limited to attestation-only. You can now link a [data schema](/ace/guides/identity-manager/manage-credential-types#typed-credentials-with-data-schemas) to a credential type, issue credentials that carry structured data, and attach a [Data Validator](/ace/guides/policy-manager/manage-data-validators) to a policy's credential source to enforce rules on that data. The first use case is **jurisdiction control** using a pre-built AllowDenyList Data Validator with [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes. See [Credential data and privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy). - **Self-signing model** — Organizations can now choose between **delegated signing** (Chainlink signs on your behalf) and **self-signing** (you sign operations yourself) at onboarding. With self-signing, ACE creates unsigned draft operations that you poll, sign (EIP-712), and submit using the [CRE Connect SDK](https://github.com/smartcontractkit/crec-sdk). Both models use the same CRE Connect Wallet and all platform capabilities work identically. See [Signing & Ownership Model](/ace/concepts/signing-ownership). - **Managed offchain risk policies (MVP)** — Screen transaction participants with [TRM Wallet Screening](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening) before allowing a protected onchain action. Configure global and category-specific risk thresholds, request evaluations through the new Evaluation API, and receive single-use permits delivered onchain by a managed CRE workflow. Grant evaluation access to other organizations so they can request permits against your target contracts. This feature is an MVP; contact your Chainlink representative for help with setup. See [Offchain Policies](/ace/guides/policy-manager/offchain-policies), [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies), [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits), and [Granting Evaluation Access](/ace/guides/policy-manager/offchain-policies/grant-evaluation-access). ## May 26, 2026: Mainnet support ### What's new - **Mainnet deployments** — ACE Beta now supports mainnet on Ethereum, Arbitrum, Avalanche, Base, and Polygon, in addition to existing testnets. Mainnet access is not enabled for all organizations by default — contact your Chainlink contact to request it. See [Supported Networks](/ace/supported-networks) for chain IDs and chain selectors. ### Other improvements - Policy Engine creation is now available in the Platform UI under **Compliance > Policy Manager** (previously API-only). See [Managing Policy Engines](/ace/guides/policy-manager/manage-engines#create-a-policy-engine). ## April 15, 2026: ACE Beta (Private Release) Chainlink ACE Beta is now available to a first set of selected participants as a **private release**. This initial release provides early access to the ACE platform. ### What's included ACE Beta ships with three core components: - **Policy Manager** — Create policy engines, register target contracts, configure policy instances from the pre-built library, and enforce compliance rules on-chain. - **Identity Manager** — Set up identity registries, register on-chain identities, define credential types, and issue verifiable credentials for use in identity-based policies. - **Reporting Manager** — Query on-chain policy configurations, identity states, and transaction history via a read-only API to support compliance verification and auditing workflows. All three components support multi-chain deployments across all [supported testnets](/ace/supported-networks) and are accessible via the [Chainlink Platform UI](https://app.chain.link) and the Coordinator and Reporting APIs, once Chainlink provisions your organization with ACE Beta access. ### Scope and limitations This is a testnet-only release. For full details on what is supported, known constraints, and planned additions, see [Beta Scope](/ace/beta-scope). --- # ACE Architecture Source: https://docs.chain.link/ace/concepts/architecture Last Updated: 2026-10-05 ACE has two layers: **onchain smart contracts** that enforce compliance rules on the blockchain, and the **[ACE Platform](/ace/concepts/key-terms#ace-platform)** (UI and APIs) that lets you manage them. Under the hood, Chainlink infrastructure connects the two — routing your platform actions to the blockchain and indexing onchain events back into the Reporting API. This page gives a bird's-eye view of how all the pieces fit together. ## Two ways ACE enforces compliance ACE enforces compliance in two ways, which use the same Platform and the same CRE Connect Wallet but different components: - **Preventive enforcement** uses the onchain contracts below. A protected function calls a PolicyEngine during the transaction, and the transaction reverts if a policy rejects it. - **Continuous monitoring** is [Active Monitoring](/ace/active-monitoring/overview). It does not use the ACE contracts. The ACE Platform screens addresses with TRM on a schedule, reads balances and executes operations on your existing token through CRE Connect, and records each decision. See [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous) to choose between them. ## System overview The following diagram shows the complete ACE architecture, from the ACE Platform down to the onchain contracts. ![Image](/images/ace/architecture-system-overview.png) The **ACE Platform** is everything you interact with: the **Platform UI**, the **Coordinator API** (to manage ACE resources), and the **Reporting API** (to query what happened onchain). The UI calls the Coordinator API under the hood, so both paths converge. When you manage ACE (create policies, register identities, etc.), the Coordinator API routes your request through **CRE Connect**, which executes the blockchain transaction via your organization's **[CRE Connect Wallet](/ace/concepts/signing-ownership)**. The CRE Connect Wallet owns all your ACE contracts and verifies that only authorized operators can act on them. In the other direction, when policies run onchain, the contracts emit events. **Chainlink's indexing infrastructure** continuously monitors these events, indexes the data, and makes it available through the **Reporting API** — giving you a queryable view of all policy run activity, transaction history, and onchain state. The sections below explain each layer in detail. ## ACE managers ACE Beta provides four managers. Policy Manager and Identity Manager abstract away the complexity of managing ACE onchain contracts. Active Monitoring operates on tokens you already run. ### Policy Manager The Policy Manager lets you create, configure, and deploy onchain compliance rules for your smart contracts. You can browse available policy types (allowlist, volume limits, role-based access control, etc.), create policy instances with per-network configuration, and attach them to specific function selectors on your protected contracts. The Policy Manager operates on the **Policy Management** onchain contracts: it deploys and configures PolicyEngine instances, policy contracts, and extractors on your behalf. See the [Policy Manager guides](/ace/guides/policy-manager/manage-engines) or the [Coordinator API reference](/api/ace/coordinator/docs) to get started. ### Identity Manager The Identity Manager lets you manage cross-chain identities and credentials. You can create identity and credential registries, register wallet addresses to CCIDs, define credential types, and issue credentials to users. The Identity Manager operates on the **Cross-Chain Identity** onchain contracts: it writes to IdentityRegistry and CredentialRegistry instances on your behalf. See the [Identity Manager guides](/ace/guides/identity-manager/manage-identities) or the [Coordinator API reference](/api/ace/coordinator/docs) to get started. ### Reporting Manager The Reporting Manager gives you read-only access to onchain state and transaction history. You can query policy engines and their configurations, look up identities and credentials, and view policy run transactions with filtering by network, target contract, and time range. The Reporting Manager exposes data through the **Reporting API**. Under the hood, Chainlink's indexing infrastructure monitors onchain events (such as `PolicyRunComplete`) emitted by your PolicyEngines and indexes the data so it can be queried through the API. See the [API Overview](/ace/reference/apis) for available endpoints. ### Active Monitoring Active Monitoring screens the wallet addresses you choose with TRM Wallet Screening, applies a monitoring rule to each change of risk level, and acts on tokens that are already deployed. A rule can record a decision, flag it for a person to review, or enforce an action: Active Monitoring calls an enforcement function that you registered on your token, through your CRE Connect Wallet. Unlike the other managers, Active Monitoring does not deploy or configure ACE contracts. It needs the ABI of your token, its addresses, and an onchain role for your CRE Connect Wallet. Decisions and operation history are available in the Platform and through the Coordinator API. See [How Active Monitoring Works](/ace/active-monitoring/concepts/how-it-works). > **NOTE: UI and API** > > Everything you can do in the Policy Manager and Identity Manager UIs is also available through the Coordinator API. > The Reporting Manager is currently API-only. ## Onchain contracts The onchain layer consists of two sets of smart contracts that enforce compliance rules and manage identities directly on the blockchain. ### Policy Management contracts Policy Management separates compliance logic from your application code. The core components are: - **PolicyProtected** — An abstract contract your application inherits from, hooking protected functions into the policy system. - **PolicyEngine** — The central orchestrator that holds the registry of all policies and executes them in order. - **Policies** — Self-contained contracts that each enforce a single rule. ACE ships with a [library of pre-built policies](/ace/reference/policy-library). - **Extractors** — Helper contracts that parse transaction calldata into structured parameters for policies. For a detailed explanation of how these components interact, see [Policy Management](/ace/concepts/policy-management). #### How policy execution works When an end user calls a protected function, the `runPolicy` modifier hands control to the PolicyEngine. This check is transparent to the end user — they simply call the contract function as normal. The engine runs each attached policy in order. Each policy returns **Reject** (transaction reverts), **Allow** (transaction approved, remaining policies skipped), or **Continue** (defer to the next policy). If all policies return Continue, the engine applies a configurable default result. Because ordering determines which policies actually execute, restrictive policies (like a credential check) should come before permissive ones (like an admin bypass). For details on policy outcomes and ordering strategies, see [Policy Ordering & Composition](/ace/concepts/policy-ordering). ### Cross-Chain Identity contracts Cross-Chain Identity provides a unified identity and credential system that works across all EVM chains. The core components are: - **IdentityRegistry** — Maps wallet addresses to Cross-Chain Identifiers (CCIDs), linking multiple addresses across chains to a single identity. - **CredentialRegistry** — Stores credentials (KYC, AML, accredited investor, custom types) linked to CCIDs, each with a type identifier, expiration, and optional data. - **CredentialRegistryIdentityValidatorPolicy** — A pre-built policy that resolves a caller's address to a CCID and validates their credentials at transaction time. The registries themselves are protected by a PolicyEngine, ensuring that only authorized [Credential Issuers](/ace/concepts/key-terms#credential-issuer) can register identities and issue credentials. For a detailed explanation of identities, credentials, and credential sources, see [Cross-Chain Identity](/ace/concepts/cross-chain-identity). #### Privacy model No personally identifiable information (PII) is stored onchain. The Cross-Chain Identity system is designed so that: - Real-world identity verification happens **offchain** with the Credential Issuer. - Only the verification **result** (a credential linked to a CCID) is recorded onchain. - Credential data stored onchain is typically a hash or minimal reference, not the underlying PII. The primary privacy consideration is that CCID-to-address mappings are publicly readable onchain, which means anyone can see which addresses belong to the same identity. For applications where this is a concern, multiple CCIDs can be issued per user while maintaining the correlation in a secure offchain system. ## Chainlink infrastructure ACE relies on Chainlink's infrastructure for both writing to and reading from the blockchain. ### How ACE executes actions (write path) Every ACE management action — whether triggered from the UI or the Coordinator API — is executed via **CRE Connect**: 1. You trigger an action from the UI or API. 2. The **Coordinator API** receives the request and sends it to **CRE Connect**. 3. CRE Connect prepares the blockchain transaction and routes it through your organization's **CRE Connect Wallet** onchain. 4. The CRE Connect Wallet verifies authorization and executes the operation on the target contract. How the transaction gets signed depends on your organization's signing model: with **delegated signing**, Chainlink signs the operation automatically. With **self-signing**, ACE creates an unsigned draft operation that you poll, sign with your own key, and submit using the [CRE Connect SDK](https://github.com/smartcontractkit/crec-sdk). In both cases, the CRE Connect Wallet remains the execution gateway. See [Signing & Ownership Model](/ace/concepts/signing-ownership) for details. Active Monitoring uses the same write path for the operations it creates. It also reads balances through CRE Connect: the Platform asks CRE Connect to call `balanceOf(address)` on your token, and records the result with the decision. ### How ACE observes onchain activity (read path) When a policy runs onchain, the PolicyEngine emits a `PolicyRunComplete` event. Chainlink's indexing infrastructure continuously monitors these events across all managed PolicyEngines and indexes the data. The **Reporting API** then serves this indexed data, giving you a queryable view of all policy run activity. ![Image](/images/ace/architecture-onchain-activity.png) This means you can query transaction history and policy run results through the Reporting Manager without running your own blockchain indexer — Chainlink handles the event monitoring and indexing automatically. --- # Preventive and Continuous Compliance Source: https://docs.chain.link/ace/concepts/preventive-vs-continuous Last Updated: 2026-10-05 ACE enforces compliance in two ways: it can block a transaction while it executes, and it can keep watching addresses and act after their risk changes. This page explains the two modes, so you can choose the one that fits your token, or use both. ## Two modes **Preventive enforcement** runs inside the transaction. A protected function asks a PolicyEngine to evaluate your policies, and the transaction reverts if a policy rejects it. It is provided by [Policy Management](/ace/concepts/policy-management) and, for identity checks, [Cross-Chain Identity](/ace/concepts/cross-chain-identity). Your contract must integrate with ACE: it inherits `PolicyProtected` or is upgraded to do so. **Continuous monitoring** runs outside the transaction. [Active Monitoring](/ace/active-monitoring/overview) screens a watchlist of addresses against TRM on a schedule, applies a rule you define when an address's risk level changes, and calls an existing function on your token, such as a freeze or a blocklist entry. Your contract does not change. | | Preventive (Policy Manager, Identity Manager) | Continuous (Active Monitoring) | | :----------------------------------- | :------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | | **When it acts** | While the transaction executes | After a screening run detects a risk change | | **What it does** | Reverts a transaction that breaks a policy | Calls an admin function on your token, flags a decision for review, or records it | | **Contract changes** | Yes: `PolicyProtected` or an upgrade | None: you provide the ABI and grant an onchain role | | **Works on tokens already deployed** | Only if you can upgrade them or place a contract in front | Yes, if the token has admin functions you can grant a role for | | **Data it uses** | Transaction data, onchain state, credentials, and optionally a permit from an offchain check | TRM risk levels of the addresses on your watchlist, and balances | | **Timing** | Immediate: no violating transaction gets through | Bounded by your screening interval: a change is seen at the next run | | **Evidence** | `PolicyRunComplete` events and the [Reporting API](/ace/concepts/reporting) | The Decisions log, the TRM result, and the operation history | ## What each mode cannot do A preventive policy cannot act on a token whose contract you cannot change, and it cannot react to something that happens outside a transaction, such as an address that is added to a sanctions list after it received tokens. Continuous monitoring cannot stop a transaction. Between a risk change at TRM and the next screening run, an address can still transact. You reduce the window with a shorter screening interval. ## Choose a mode | Your situation | Use | | :---------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------- | | You build a contract, or can upgrade it, and must block non-compliant transactions | [Policy Manager](/ace/getting-started/policy-manager) | | You must check who can hold or transfer a token, based on KYC or accreditation | [Identity Manager](/ace/getting-started/identity-manager) with Policy Manager | | Your token is already deployed and cannot be changed, and you want to react when a holder becomes high risk | [Active Monitoring](/ace/active-monitoring/quick-start) | | You need both blocking at transaction time and a reaction to holders whose risk changes later | Both | The two modes complement each other. Preventive policies stop a violating transaction at the moment it happens. Active Monitoring covers the holders who were compliant when they received the token and changed risk level afterward. ## TRM in each mode ACE uses TRM Wallet Screening in two separate features. They use separate credentials and separate guides: | | Managed offchain risk policy (preventive) | Active Monitoring (continuous) | | :---------------------- | :-------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- | | **When TRM is called** | Once per transaction request, before the user submits it | On a schedule, for every address on the watchlist | | **Result** | A permit that the transaction consumes onchain | A decision that follows your rule | | **Where the key lives** | In the Vault DON, uploaded with the CRE CLI | In the Platform, under **General settings > API access** | | **Guide** | [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) | [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening) | ## What both modes share Both modes use your organization's onboarding, your [CRE Connect Wallet](/ace/concepts/signing-ownership) on each network, the same [signing models](/ace/concepts/signing-ownership), the [Coordinator API](/ace/reference/apis), and the ACE Platform. You can adopt Active Monitoring without using Policy Manager or Identity Manager. ## Next steps - [What is Active Monitoring?](/ace/active-monitoring/overview): the continuous mode in detail. - [Policy Management](/ace/concepts/policy-management): the preventive mode in detail. - [Getting Started with ACE](/ace/getting-started): choose where to begin. --- # Key Terms and Concepts Source: https://docs.chain.link/ace/concepts/key-terms Last Updated: 2026-10-07 This glossary defines the key terms used throughout the ACE documentation. Use it as a quick reference when reading other pages. ### Access grant A cross-organization link that gives another organization read access to one of your resources. For registries, an access grant lets a **grantee** organization read a **grantor**'s registry and use it as a credential source. Grants are `active` or `revoked`, and only the resource owner can create or revoke them. Learn more in [External Registries](/ace/guides/identity-manager/external-registries). ### ACE Platform The complete user-facing layer of ACE, consisting of the Platform UI ([app.chain.link](https://app.chain.link)), the Coordinator API (create, configure, and deploy ACE resources), the Evaluation API (request managed offchain permits), and the Reporting API (query onchain state and transaction history). Everything you can do in the UI is also available through the Coordinator API. ### AML (Anti-Money Laundering) A set of laws, regulations, and procedures designed to prevent criminals from disguising illegally obtained funds as legitimate income. ### CCID (Cross-Chain Identifier) A 32-byte identifier that uniquely represents an entity across multiple EVM blockchains. A CCID links one or more wallet addresses — potentially on different chains — to a single identity. Credentials like KYC or AML status are attached to the CCID, not to individual addresses, making them portable across chains. Learn more in [Cross-Chain Identity](/ace/concepts/cross-chain-identity). ### Composability The ability to combine modular components in a flexible manner. In ACE, composability means you can chain multiple policies together on a single function, use Policy Management with or without Cross-Chain Identity, and reuse the same policies across different contracts and functions. ### Context parameter A `bytes` field passed through the policy execution flow, used to supply arbitrary transaction-specific data to policies. Common uses include offchain signatures, Merkle proofs for allowlist verification, and dynamic risk parameters. Learn more in [Policy Management Concepts](/ace/concepts/policy-management). ### Coordinator API The management API for creating, configuring, and deploying ACE resources (policy engines, policies, identities, credentials). Part of the [ACE Platform](#ace-platform). Everything available in the [Platform UI](#platform-ui) is also available through this API. See the [interactive API reference](/api/ace/coordinator/docs) for details. ### CRE Connect Chainlink infrastructure that routes [ACE Platform](#ace-platform) actions to the blockchain. When you trigger an action (deploy a policy, register an identity), CRE Connect prepares and executes the blockchain transaction through your [CRE Connect Wallet](#cre-connect-wallet). ### CRE Connect Wallet A dedicated onchain smart contract wallet deployed for your organization on each network where you use ACE. Your wallet owns the CRE Connect Wallet, and the CRE Connect Wallet owns all your ACE contracts (policy engines, registries, policies). Chainlink is registered as an authorized operator, allowed to execute operations on your behalf but unable to change ownership or authorization settings. Learn more in [Signing & Ownership Model](/ace/concepts/signing-ownership). ### Credential A verifiable attribute linked to a [CCID](#ccid-cross-chain-identifier), such as KYC verification, AML clearance, or accredited investor status. Credentials are stored in a [Credential Registry](#credential-registry) and can be validated onchain without revealing sensitive information. Only hashes or minimal references are stored on the blockchain — the actual PII stays offchain. ### Credential Data Validator An optional onchain contract (also called a **Data Validator**) configured on a [Credential Source](#credential-source) that inspects the contents of a credential's `credentialData` field. This enables decisions based on what's inside a credential (for example, a jurisdiction code), not just whether the credential exists. ACE provides a pre-built AllowDenyList Data Validator for jurisdiction control; when no validator is configured, a source is attestation-only. Learn more in [Managing Data Validators](/ace/guides/policy-manager/manage-data-validators) and [Cross-Chain Identity — Credential data and privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy). ### Credential Issuer A trusted offchain entity (for example, a KYC/AML provider) authorized to perform real-world identity verification, generate CCIDs, and register the resulting credentials onchain. The Credential Issuer's write access to the registries is governed by policies in the [PolicyEngine](#policy-engine), ensuring only authorized issuers can create identities and issue credentials. ### Credential Registry An onchain contract that manages the lifecycle of credentials linked to CCIDs, including registration, validation, renewal, and removal. Each credential record includes a credential type identifier, an expiration timestamp, and optional credential data (typically a hash for privacy). ### Credential Source An onchain data structure that tells the [Identity Validator](#identity-validator) which [Identity Registry](#identity-registry) and [Credential Registry](#credential-registry) to trust for a given credential type. A single source can handle multiple credential types. Sources are configured when setting up the `CredentialRegistryIdentityValidatorPolicy`. ### Credential Type Identifier A `bytes32` value that denotes the type of credential (for example, KYC, AML, or accredited investor). Identifiers are generated using `keccak256` of a namespaced string: `keccak256("common.kyc")` for standard types, or `keccak256("com.yourapp.custom")` for application-specific types. The `common.` prefix is reserved for standard credential types like `common.kyc`, `common.aml`, `common.kyb`, and `common.accredited`. ### Custom policy A policy implemented by your own contract (rather than the [pre-built library](/ace/reference/policy-library)) and registered with the ACE Platform. Custom policy implementations are scoped to your organization and, once registered, are used exactly like library policies. Learn more in [Custom Policies](/ace/guides/policy-manager/custom-policies). ### Data Schema A reusable definition of the shape and format of a credential's data, linked to a [Credential Type](#credential-type-identifier) so that credentials issued against it carry validated structured data (for example, [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes). Data schemas make a credential type *typed* rather than attestation-only. Learn more in [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types#typed-credentials-with-data-schemas). ### Default result The [PolicyEngine](#policy-engine)'s fallback decision when every policy in a [policy chain](#policy-chain) returns Continue and none makes a final Allow or Reject decision. The default result can be set to either allow or reject, and it is configured per target contract via the `desired_default_allow` field. Learn more in [Policy Ordering & Composition](/ace/concepts/policy-ordering#the-default-result). ### Delegated signing One of two signing models available in ACE. Chainlink signs and executes blockchain transactions on your behalf via the [CRE Connect Wallet](#cre-connect-wallet). You retain full ownership of all contracts. The signing model is chosen during onboarding. Learn more in [Signing & Ownership Model](/ace/concepts/signing-ownership). ### DON (Decentralized Oracle Network) A network of independent Chainlink oracle nodes that reach consensus on offchain computations and deliver certified results onchain. The `CertifiedActionDONValidatorPolicy` uses a DON to validate that a transaction has been approved through an offchain workflow before allowing it to proceed. ### ERC-20 A widely used Ethereum token standard defining rules for fungible tokens. ACE provides a reference implementation (`ComplianceTokenERC20`) that adds policy-protected transfers, minting, and burning to a standard ERC-20. ### ERC-165 An Ethereum standard that enables contracts to declare the interfaces they implement. ACE contracts use ERC-165 for interface detection during policy registration and validation. ### ERC-3643 A regulated token standard (also known as T-REX) designed for securities and permissioned tokens. ACE provides a reference implementation (`ComplianceTokenERC3643`) that replaces the canonical T-REX identity (ONCHAINID) and compliance (ModularCompliance) systems with ACE equivalents. Learn more in [Building an ERC-3643 Compliance Token](/ace/guides/policy-manager/contracts/erc3643-token). ### Evaluation API The MVP runtime API for requesting and monitoring managed offchain policy evaluations. An application submits a transaction intent, receives a deterministic permit ID, and polls until the permit is ready onchain or the evaluation is rejected or fails. Contact your Chainlink representative for help with setup, and see [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) and the [interactive API reference](/api/ace/evaluation/docs). ### External registry A registry owned by another organization that has been shared with yours through an [access grant](#access-grant). From the grantee's perspective it appears with `access_type: "granted"` and is read-only — you can reference its identities and credentials as a credential source but cannot write to it. Learn more in [External Registries](/ace/guides/identity-manager/external-registries). ### Extractor A helper contract that parses raw transaction calldata for a specific function signature and decodes it into a list of named parameters. The [PolicyEngine](#policy-engine) uses extractors to provide each policy with the specific parameters it needs. For example, an `ERC20TransferExtractor` parses `transfer(address,uint256)` calls into `from`, `to`, and `amount` parameters. ### Identity Manager The ACE Beta product for managing cross-chain identities and credentials via the platform UI or API. Identity Manager operates on the [Cross-Chain Identity](/ace/concepts/cross-chain-identity) onchain contracts (Identity Registry and Credential Registry). ### Identity Registry An onchain contract that maintains mappings between wallet addresses and [CCIDs](#ccid-cross-chain-identifier). Each address maps to exactly one CCID, though a single CCID can be associated with multiple addresses across multiple chains. ### Identity Validator An onchain contract (typically the `CredentialRegistryIdentityValidatorPolicy`) that verifies whether a given account meets a set of credential requirements. When a user calls a protected function, the Identity Validator resolves the user's address to a CCID, then checks whether that CCID holds the required credentials from trusted [Credential Sources](#credential-source). ### KYC (Know Your Customer) A compliance process requiring financial institutions to verify the identity of their clients. In ACE, KYC is represented as a credential type (`common.kyc`) attached to a user's [CCID](#ccid-cross-chain-identifier) after offchain verification by a [Credential Issuer](#credential-issuer). ### Managed offchain policy An MVP offchain compliance rule delivered as a managed service. You configure the rule and attach it to a target function; Chainlink manages the CRE workflow, external provider call, [CADV](/ace/reference/policy-library/certified-action-don-validator-policy) deployment, and onchain permit delivery. ACE Beta provides the `wallet_risk_scoring` policy for TRM Wallet Screening. Contact your Chainlink representative for help with setup, and see [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies). ### Mapper An optional helper contract used to transform or combine parameters extracted from transaction data before passing them to a policy. Mappers are needed only for advanced scenarios — for example, calculating a USD value from a token amount and price before passing it to a volume policy. In most cases, the PolicyEngine's built-in name-based parameter mapping is sufficient. ### Permit An onchain authorization for one specific transaction intent. A managed offchain permit binds the caller, target, function selector, and ordered extractor parameters. The DON publishes it to a [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) before the user submits the protected transaction. Managed wallet risk permits are single-use and do not expire in the current Beta release. ### PII (Personally Identifiable Information) Information that can identify an individual, such as a name, address, or national ID number. ACE's [Cross-Chain Identity](/ace/concepts/cross-chain-identity) system avoids storing PII onchain, using hashed references instead to preserve privacy. ### Platform UI The web interface at [app.chain.link](https://app.chain.link) for managing ACE resources visually — deploying policy engines, configuring policies, registering identities, and issuing credentials. Part of the [ACE Platform](#ace-platform). Everything available in the UI is also available through the [Coordinator API](#coordinator-api). ### Policy A self-contained onchain contract that holds a single compliance rule. Each policy implements a `run()` function that receives parameters and returns a verdict: `Allowed` (final approval), `Continue` (defer to the next policy), or reverts with `PolicyRejected` (final rejection). Policies can optionally implement a `postRun()` function for state changes after a check passes (for example, incrementing a volume counter). See the [Policy Library](/ace/reference/policy-library) for all pre-built policies. ### Policy chain The ordered sequence of policies attached to a specific function selector on a target contract. The [PolicyEngine](#policy-engine) executes policies in chain order; each policy's result (Reject, Allow, or Continue) determines whether subsequent policies run. Learn more in [Policy Ordering & Composition](/ace/concepts/policy-ordering). ### Policy Engine The central onchain orchestrator. The PolicyEngine holds the registry of all policies attached to a protected contract's function selectors. When a protected function is called, the PolicyEngine calls the relevant [Extractor](#extractor) to parse the transaction data, then executes each attached policy in order. A single PolicyEngine can manage policies for multiple contracts and functions. ### Policy implementation The reusable template (contract code + configuration schema) that a [policy](#policy) instance is created from. Implementations are either pre-built [library](/ace/reference/policy-library) types (global) or [custom](#custom-policy) types registered by an organization (org-scoped). See [Managing Policies](/ace/guides/policy-manager/manage-policies#policy-implementations-vs-policy-instances). ### Policy Management The onchain framework for defining, executing, and managing dynamic compliance rules. It consists of [PolicyProtected](#policyprotected) contracts (the hook), the [PolicyEngine](#policy-engine) (the orchestrator), [Policies](#policy) (the rules), and [Extractors](#extractor) (the data parsers). Learn more in [Policy Management Concepts](/ace/concepts/policy-management). ### Policy Manager The ACE Beta product for managing policy engines, policy instances, extractors, and protected contracts via the platform UI or API. Policy Manager operates on the [Policy Management](/ace/concepts/policy-management) onchain contracts. ### PolicyFactory An onchain factory contract that deploys [policy](#policy) instances by cloning a policy implementation. When you create a policy instance, the PolicyFactory clones the implementation, initializes it, and verifies the implementation supports the `IPolicy` interface via ERC-165. ### PolicyProtected An abstract contract that your application inherits from. It provides the `runPolicy` modifier, which acts as the hook into the policy system. When a user calls a function decorated with `runPolicy`, the modifier intercepts the call and asks the [PolicyEngine](#policy-engine) to evaluate all attached policies before allowing execution to proceed. ### Proof of Reserve (PoR) A Chainlink data feed that reports the real-world reserves backing a tokenized asset. The `SecureMintPolicy` uses a PoR feed to verify that minting new tokens will not exceed proven reserves. Learn more in the [SecureMintPolicy reference](/ace/reference/policy-library/secure-mint-policy). ### Protected function A function on your smart contract that is guarded by the `runPolicy` modifier. When called, the modifier intercepts execution and routes it through the [PolicyEngine](#policy-engine) for policy evaluation before allowing the function body to proceed. ### Reporting API The API for querying onchain state, transaction history, and policy run results. Chainlink's indexing infrastructure monitors onchain events emitted by your PolicyEngines and makes the data available through this API. Part of the [ACE Platform](#ace-platform). See the [interactive API reference](/api/ace/reporting/docs) for details. ### Reporting Manager The ACE Beta product for querying onchain state, transaction history, and policy run results. Currently available via API only. Reporting Manager exposes data from the Reporting API, which indexes onchain events and state. ### Self-signing One of two signing models available in ACE. ACE prepares unsigned draft operations; you poll, sign (EIP-712), and submit them using the [CRE Connect SDK](https://github.com/smartcontractkit/crec-sdk). You retain full ownership of all contracts. The signing model is chosen during onboarding. Learn more in [Signing & Ownership Model](/ace/concepts/signing-ownership). ### Trusted Verifier See [Credential Issuer](#credential-issuer). A trusted verifier is an offchain entity authorized to conduct external checks (KYC, AML, document verification) and register the resulting credentials onchain. ## Active Monitoring terms The following terms are specific to [Active Monitoring](/ace/active-monitoring/overview). Some reuse words from Policy Management, such as decision and operation, with a different meaning. ### Active Monitoring The ACE capability that screens the addresses on a [watchlist](#watchlist) with TRM on a schedule, applies a [monitoring rule](#monitoring-rule) to each change of risk level, and acts on tokens that are already deployed, without changing their contracts. It is the continuous counterpart of the preventive Policy Manager and Identity Manager. Learn more in [What is Active Monitoring?](/ace/active-monitoring/overview) and [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous). ### Associated contract A contract registered with a [monitored token](#monitored-token), other than the token itself, on which an enforced action can run. A separate blocklist contract is a common example. Learn more in [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens). ### Balance condition The option **Only execute if the address holds a balance on this token** on an [enforced action](#enforced-action). Active Monitoring reads the balance of the screened address with `balanceOf(address)` and runs the action only if the balance is greater than zero. Learn more in [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules#the-balance-condition). ### Decision The record that Active Monitoring keeps each time a [monitoring rule](#monitoring-rule) matches a [risk event](#risk-event) for an address on one network. The **Decisions log** lists decisions. A decision holds the TRM result, the rule that matched, the outcome, and the operation. It is unrelated to a policy run in Policy Management. Learn more in [Decisions, Review, and Audit Trail](/ace/active-monitoring/concepts/decisions-and-audit). ### Enforced action One call to an [enforcement function](#enforcement-function), defined in the Enforce [response](#response) of a monitoring rule. It names the contract, the function, and a [parameter mapping](#parameter-mapping-active-monitoring) for each argument. Learn more in [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules#enforced-actions). ### Enforcement function A write function on your token, or on an [associated contract](#associated-contract), that Active Monitoring calls when a rule enforces an action, such as a freeze or a blocklist function. You select it when you register the contract. It must have named inputs, no array or tuple inputs, and at least one input. Learn more in [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens#contract-abi-and-functions). ### Monitored token A token contract registered with Active Monitoring, optionally with associated contracts. A monitored token has one monitoring rule. It is not a target: a target is a contract protected by a PolicyEngine. Learn more in [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens). ### Monitoring rule The rule of a monitored token that maps each TRM risk level to a [response](#response). A monitoring rule cannot be edited: you delete it and create a new one. It is not a policy. Learn more in [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules). ### Operation An onchain call that CRE Connect executes through your CRE Connect Wallet. Active Monitoring creates one operation for each enforced action. Its status moves from **Submitted** to **Success** or **Failed**, with **Pending signature** under self-signing. See [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values#operation-statuses). ### Parameter mapping (Active Monitoring) In Active Monitoring, the choice of a value for each argument of an enforcement function: **Screened address**, **Balance**, or a constant. It is unrelated to the extractor and mapper pattern of Policy Management. See [Mapper](#mapper) and [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules#parameter-mapping). ### Response What a monitoring rule does for a risk level: **Silently log** records the decision, **Flag** waits for a person to review it, and **Enforce** creates an operation for each enforced action. Learn more in [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules#risk-levels-and-responses). ### Risk event A change in the TRM [risk level](#risk-level-trm-risk-score) of a watchlist address, or its first screening. A risk event is what a monitoring rule reacts to: each rule that matches creates a [decision](#decision). An address that stays at the same level raises no new event. Learn more in [Watchlist and Screening](/ace/active-monitoring/concepts/screening#when-a-result-becomes-a-risk-event). ### Risk level (TRM risk score) The highest risk level that TRM reports for an address: **Severe** (score 15), **High** (10), **Medium** (5), **Low** (1), or **Unknown** (0). A monitoring rule maps each level to a response. See [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values#trm-risk-levels). ### Screening interval The number of hours between two Active Monitoring screening runs. The Platform UI offers 6, 12, and 24 hours. Learn more in [Watchlist and Screening](/ace/active-monitoring/concepts/screening#schedule). ### TRM API key Your TRM Labs API key, which Active Monitoring uses to screen the watchlist. You store it in **General settings > API access**. It is different from your ACE organization API key, and from the credential used by managed offchain policies. See [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening). ### Watchlist The list of wallet addresses that Active Monitoring screens with TRM. Learn more in [Watchlist and Screening](/ace/active-monitoring/concepts/screening). --- # Signing and Ownership Model Source: https://docs.chain.link/ace/concepts/signing-ownership Last Updated: 2026-10-05 Every ACE management action — deploying a policy engine, registering an identity, issuing a credential — is a blockchain transaction. Blockchain transactions require a signer: someone who authorizes the operation onchain. ACE supports two signing models: **delegated signing** and **self-signing**. Your organization chooses its signing model during onboarding. ## Signing models | | Delegated signing | Self-signing | | :--------------------- | :---------------------------------------------------------------------------- | :---------------------------------------------------------------- | | **Who signs** | Chainlink signs transactions on your behalf | ACE creates unsigned operations; you sign them with your own keys | | **Contract ownership** | You retain full ownership of all contracts | You retain full ownership of all contracts | | **What you manage** | Nothing — Chainlink handles transaction signing, orchestration, and execution | Your own signing keys — ACE handles orchestration and execution | | **Best for** | Teams that want a fully managed experience | Teams that require direct control over transaction authorization | ## Delegated signing In the delegated model, ACE uses a **delegated trust** approach centered around a dedicated onchain account called a **CRE Connect Wallet** (technically referred to as an SVA — Signature Verifying Account). This account acts as a gateway between Chainlink's infrastructure and your contracts. ### How it works When your organization onboards onto ACE, the system deploys a CRE Connect Wallet for you on every network you require. Here is the key principle: - **Your wallet owns the CRE Connect Wallet.** - **The CRE Connect Wallet owns all your ACE contracts** (policy engines, registries, policies, etc.). - **Chainlink is registered as an authorized operator** on your CRE Connect Wallet — allowed to execute operations, but nothing more. This means you indirectly own every contract that ACE deploys for your organization, through the CRE Connect Wallet. ### Permissions The CRE Connect Wallet enforces strict permission boundaries between you and Chainlink: | Action | You (client) | Chainlink | | :--------------------------------------------------------- | :----------- | :---------- | | **Change ownership** of the CRE Connect Wallet | Allowed | Not allowed | | **Manage authorized signers** (add/remove who can operate) | Allowed | Not allowed | | **Execute operations** on your contracts | Allowed | Allowed | Chainlink can only execute operations (deploy contracts, configure policies, register identities, etc.) — it cannot change who owns the account or who is authorized to sign. ### What happens during setup When your organization is onboarded onto ACE: 1. You provide your wallet address to the ACE platform. 2. Chainlink creates an internal signing key dedicated to your organization. This key is managed entirely by Chainlink — you never see or interact with it. 3. ACE deploys a CRE Connect Wallet onchain, sets your wallet as its owner, and registers Chainlink's signing key as an authorized operator — all in a single deployment step. 4. ACE deploys all application contracts (policy engines, registries, etc.) and assigns ownership to your CRE Connect Wallet. Once setup is complete, the permission boundaries described above take effect: only you can change ownership or manage authorized signers. Chainlink can only execute operations. ![Image](/images/ace/signing-model-sva-setup.png) ### What happens during operations When you trigger an action — whether from the ACE platform UI or the API: 1. You perform an action (e.g., "deploy a new policy instance on Ethereum"). 2. ACE prepares the corresponding blockchain transaction and signs it using Chainlink's internal signing key for your organization. 3. ACE sends the signed transaction to your CRE Connect Wallet. 4. The CRE Connect Wallet verifies that the signer is in its list of authorized operators. 5. If authorized, the CRE Connect Wallet executes the operation on the target contract. During day-to-day operations, you never interact with a blockchain wallet or sign a transaction. ACE handles the signing and execution, while your CRE Connect Wallet enforces that only authorized operators can act. ![Image](/images/ace/signing-model-sva-operations.png) ### Your safety net You are always in control. Because your wallet owns the CRE Connect Wallet, you can at any time: - **Interact with the CRE Connect Wallet directly** — bypassing ACE entirely. - **Remove Chainlink as an authorized signer** — immediately revoking ACE's ability to execute operations on your contracts. - **Add other signers** — granting operation rights to your own keys or third parties. Revoking Chainlink's access does not affect your contract ownership. Your contracts remain yours, managed through your CRE Connect Wallet. You would simply take over operational responsibility. ## Self-signing With self-signing, you sign operations using your own keys before they are executed through your CRE Connect Wallet. ACE still orchestrates the process — preparing the operation, routing it through the platform, and tracking it — but the final approval and signing authority is yours. The CRE Connect Wallet remains the execution gateway: your signed operations flow through it the same way Chainlink-signed operations do in the delegated model, so all platform capabilities (UI, APIs, reporting) work the same regardless of which signing model you use. ### How it works The ownership model is the same as delegated signing: your wallet owns the CRE Connect Wallet, and the CRE Connect Wallet owns all your ACE contracts. The difference is in **who signs each operation**. When you trigger an action — whether from the ACE Platform UI or the API: 1. You perform an action (e.g., "deploy a new policy instance on Ethereum"). 2. ACE prepares the corresponding blockchain transaction and creates an **unsigned draft operation** with status `pending_signature`. 3. You poll for pending operations using the [CRE Connect SDK](https://github.com/smartcontractkit/crec-sdk) and retrieve the draft. 4. You sign the operation using your own key (EIP-712 typed data signing). 5. You submit the signed operation back through the CRE Connect SDK. 6. The CRE Connect Wallet verifies your signature and executes the operation onchain. ### Polling and signing with the CRE Connect SDK The [CRE Connect SDK](https://github.com/smartcontractkit/crec-sdk) is a Go client library that provides all the tools needed for the self-signing workflow: - **List pending operations** — Poll for unsigned draft operations waiting for your signature. - **Hash operations** — Compute the EIP-712 digest for an operation locally. - **Sign and submit** — Sign the digest with your key and finalize the draft in a single call. - **Cancel operations** — Reject a draft operation if needed. The SDK supports multiple signer backends: local ECDSA keys, AWS KMS, HashiCorp Vault Transit, Privy, and Fireblocks. See the [CRE Connect SDK repository](https://github.com/smartcontractkit/crec-sdk) for installation, configuration, and detailed usage. ### Your safety net The same safety net applies as with delegated signing. Because your wallet owns the CRE Connect Wallet, you can at any time interact with it directly, add or remove authorized signers, or take over operational responsibility entirely. ## Signing in Active Monitoring [Active Monitoring](/ace/active-monitoring/overview) creates an operation each time a monitoring rule enforces an action, or a person enforces an action on a flagged decision. The operation follows your organization's signing model and runs through your CRE Connect Wallet, like any other ACE operation. | | Delegated signing | Self-signing | | :---------------------- | :---------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | | **Who signs** | Chainlink signs and executes the operation on your behalf. | You sign the operation with your own key. | | **Operation statuses** | Submitted, Sending, Executing, Success or Failed | Submitted, Sending, **Pending signature**, Executing, Success or Failed | | **Automatic execution** | Yes. An Enforce response executes as soon as a risk event matches, within your rule and onchain role. | No. An Enforce response prepares the operation, and your signer signs it before it executes. | In both models, the CRE Connect Wallet needs the onchain role that each enforcement function requires. The role is the limit of what an operation can do. See [Enforcement and Security Model](/ace/active-monitoring/concepts/enforcement-and-security). With self-signing, find and sign pending Active Monitoring operations the same way as other pending operations, with the [CRE Connect SDK](https://github.com/smartcontractkit/crec-sdk). --- # Security Model Source: https://docs.chain.link/ace/concepts/security Last Updated: 2026-10-05 This page covers the governance and operational security principles behind how ACE protects onchain assets and identity data — the controls, ordering guarantees, and privacy properties that administrators, auditors, and compliance teams should understand. For implementation-level security guidance (trust boundaries for policies and extractors, context handling, view function requirements), see [Security Considerations for Smart Contracts](/ace/guides/policy-manager/contracts/security-considerations). ## Policy administration is a critical control The ability to add, remove, or reorder policies in a PolicyEngine is the most sensitive administrative power in ACE. An actor who gains control over these functions can effectively disable or bypass all compliance rules for every contract connected to that engine. In ACE Beta, policy administration is handled through the [ACE Platform](/ace/concepts/key-terms#ace-platform) — the Coordinator API and Platform UI — which means configuration changes go through the platform's authentication and authorization layer. The underlying onchain contracts enforce that only the [CRE Connect Wallet](/ace/concepts/key-terms#cre-connect-wallet) (which the platform operates on your behalf) can call administrative functions like `addPolicy`, `removePolicy`, `setExtractor`, and `setDefaultAllow`. ## Policy execution order matters Policies execute in a strict, sequential order — the order they were added to the PolicyEngine for a given function selector. This order has direct security implications because of how the three policy outcomes interact: - **Reject** halts execution immediately and reverts the transaction. No subsequent policies run. - **Allow** approves the transaction immediately and **bypasses all subsequent policies**. - **Continue** passes the decision to the next policy in the chain. Because Allow skips everything after it, a permissive policy placed too early in the chain can inadvertently bypass critical security checks. For example, if an admin bypass policy is placed before a sanctions check, an admin address would never be screened. **Best practice:** Order restrictive policies (sanctions screening, denylist checks) before permissive ones (admin bypass, authorized sender lists). This ensures that hard blocks cannot be circumvented by an early Allow. For a detailed guide on ordering strategies, see [Policy Ordering & Composition](/ace/concepts/policy-ordering). ## Registry governance The IdentityRegistry and CredentialRegistry are not protected by simple access-control lists. Instead, their administrative functions — `registerIdentity`, `registerCredential`, `removeCredential`, and others — are themselves protected by a PolicyEngine. This means: - Only addresses authorized by the registry's PolicyEngine can modify identity or credential data. - The same policy model that protects application contracts also protects the identity infrastructure. - Authorization can be as simple as an allowlist of Credential Issuers, or as sophisticated as a multi-policy chain with role checks and volume limits on issuance. This design ensures that credential issuance is governed by explicit, auditable rules rather than hardcoded access controls. In ACE Beta, the [Identity Manager](/ace/concepts/key-terms#identity-manager) handles registry governance through the platform — users in your organization can create registries, register identities, define credential types, and issue credentials, and the platform's CRE Connect Wallet executes these operations onchain. ## Privacy guarantees ACE is designed so that no personally identifiable information (PII) is stored onchain: - **Credential data** is arbitrary `bytes` — typically a hash of offchain data or a non-sensitive reference. The system never requires raw PII to be written to the blockchain. - **CCID-to-address mappings** are publicly readable onchain. This is by design — it enables cross-chain verification — but it means anyone can see which addresses share the same identity. For applications where this transparency is a concern, multiple CCIDs per user can be used to limit correlation across domains. See [Cross-Chain Identity: Privacy and correlation](/ace/concepts/cross-chain-identity#privacy-and-correlation) for details. - **Credential type identifiers** are hashed (`keccak256`) but use known namespaced strings (e.g., `common.kyc`), so standard types are effectively public knowledge. ## Active Monitoring controls [Active Monitoring](/ace/active-monitoring/overview) acts on tokens you already run, so its controls differ from the controls above. They sit on your token and on your organization: - **The onchain role is the authority.** Active Monitoring can call only the functions you registered, and only if your CRE Connect Wallet holds the role on your contract. You can revoke the role at any time. - **Registered functions only.** Active Monitoring builds a call only from the ABI functions you selected, so it cannot construct a call to any other function. - **Review before action.** The Flag response sends a decision to a person. Resolving or enforcing a flagged decision is recorded with the person who did it. - **Credentials stay out of the interface.** The TRM API key is stored encrypted and never shown again. See [Enforcement and Security Model](/ace/active-monitoring/concepts/enforcement-and-security) for the full model. --- # Policy Management Source: https://docs.chain.link/ace/concepts/policy-management Last Updated: 2026-10-05 This page explains in depth how ACE's Policy Management system works — the design rationale, the execution model, and how the components interact. For a high-level overview of the components themselves (PolicyProtected, PolicyEngine, Policies, Extractors), see the [Architecture page](/ace/concepts/architecture#policy-management-contracts). ## Why separate compliance from business logic? Hardcoding compliance rules directly into a smart contract makes your application rigid and difficult to maintain. Every time a regulation changes or a new rule is required, you face a contract upgrade or redeployment — a costly, risky process that requires re-auditing. Policy Management solves this by separating your application's core logic from its compliance rules. Your contract handles what it was built to do (transfers, minting, trading), while a separate layer of modular policies handles the compliance checks. Policies can be added, removed, reordered, or reconfigured through the [Policy Manager](/ace/concepts/key-terms#policy-manager) without ever touching your application contract. This separation provides: - **Adaptability** — Respond to regulatory changes by updating policies, not your core contract. - **Auditability** — Each policy is a small, focused contract that can be reviewed and audited independently. - **Composability** — Chain multiple policies on the same function to build sophisticated rulesets from simple building blocks. - **Reusability** — The same policy contract can protect functions across multiple contracts and chains. ## How policy chains work When a user calls a protected function, the `runPolicy` modifier intercepts the call and hands control to the PolicyEngine. The engine runs each attached policy in order — a chain of responsibility where each policy's result determines what happens next. ![Image](/images/ace/policy-management-policy-chains.png) In this diagram, the user calls a protected function on your contract. The `runPolicy` modifier forwards the call to the PolicyEngine, which begins executing the policy chain. Policy 1 runs first and returns **Continue** — its check passed, but the decision is deferred. The engine moves to Policy 2, which can produce one of three outcomes: - **Reject** — The policy reverts with `PolicyRejected` and a reason. The entire transaction reverts immediately. - **Allow** — The policy approves the transaction. Execution succeeds without checking any further policies. - **Continue** — The check passed but no final decision was made. If no policies remain, the engine applies its configurable **default result** (allow or reject). > **NOTE: Any policy can short-circuit the chain** > > The diagram above shows Policy 1 returning Continue, but any policy in the chain can also return Reject or Allow. If > Policy 1 had rejected, the transaction would revert immediately and Policy 2 would never run. If Policy 1 had returned > Allow, the transaction would succeed immediately and Policy 2 would also be skipped. This is why policy ordering is > critical — a policy that makes a final decision prevents all subsequent policies from executing. ## The policy execution flow The overview above shows the decision logic. Under the hood, the PolicyEngine does more work before and after each policy runs: it extracts named parameters from the raw calldata and maps the right subset to each policy. This section covers the full execution flow. 1. **Invocation** — The `runPolicy` modifier calls `PolicyEngine.run()` with a payload containing the function selector, caller address, calldata, and optional context. 2. **Extraction** — The PolicyEngine calls the registered Extractor for that function selector. The Extractor parses the raw calldata and returns a list of named parameters (e.g., `to`, `value` for an ERC-20 transfer). 3. **Parameter mapping** — For each policy in the chain, the engine maps the extracted parameters to the subset that policy needs. This mapping works by name: when a policy is added to a function selector, you specify which parameter names it requires (for example, a sanctions policy might need `from` and `to`, while a volume limit needs only `amount`). The engine provides only those parameters to each policy. See [Worked example: ERC-20 transfer](#worked-example-erc-20-transfer) for a concrete walkthrough. 4. **Policy execution** — The engine calls each policy's `run()` function in order, passing the mapped parameters and context. 5. **Result processing** — Based on each policy's response, the engine decides whether to continue, allow, or reject. ![Image](/images/ace/policy-management-execution-flow.png) ### Post-run hooks After a policy returns **Allow** or **Continue**, the engine calls that policy's optional `postRun()` function. This hook is for state changes that depend on the transaction being approved — for example, the [VolumeRatePolicy](/ace/reference/policy-library/volume-rate-policy) uses `postRun()` to increment a cumulative volume counter for the current time period. Most policies leave `postRun()` empty. It only matters for policies that need to track state across transactions. If a policy **rejects**, its `postRun()` is never called — the entire transaction reverts. ## Policy outcomes in detail Each policy's `run()` function produces one of three outcomes. Understanding them — and their interaction with `postRun()` — is essential for designing effective policy chains. ### Reject The policy reverts with `PolicyRejected` and a descriptive reason. This is a **final** decision: the entire transaction reverts immediately, no subsequent policies run, and the policy's `postRun()` is **not** called. Use Reject for hard blocks: sanctions screening, unauthorized senders, expired credentials. ### Allow The policy returns `Allowed`. This is also a **final** decision: all subsequent policies in the chain are **skipped**. The policy's `postRun()` **is** called before the transaction proceeds. Use Allow sparingly — it acts as a bypass. A common pattern is a BypassPolicy at the start of the chain that allows admin addresses to skip all subsequent checks. > **CAUTION: Allow bypasses everything after it** > > Because Allow skips all subsequent policies, place permissive policies (like admin bypass) with extreme care. A > misplaced Allow can inadvertently bypass critical security checks. ### Continue The policy's check passed, but the decision is deferred to the next policy. The policy's `postRun()` **is** called, and the engine moves to the next policy in the chain. If the last policy in the chain returns Continue and no policy has given a final verdict, the PolicyEngine applies its **default result**. The default can be configured to either allow or reject — this is set per target contract and can also be set globally for the engine. Most policies return Continue. This is what makes composability work: each policy handles one concern and passes control forward. ## Policy composition The real power of Policy Management emerges when you chain multiple policies on the same function. Each policy handles one concern, and together they form a comprehensive ruleset: - A **sanctions check** rejects flagged addresses. - A **credential check** verifies the caller holds a valid KYC credential. - A **volume limit** enforces a daily transfer cap. - A **pause control** lets an administrator halt the function in an emergency. By composing these independent checks into a single chain, you build a comprehensive ruleset from simple, auditable building blocks — and you can adjust any single rule without affecting the others. Policies execute in their configured order. Because Allow and Reject are both final decisions that skip remaining policies, the order you place them in determines which checks actually execute. Different use cases call for different orderings — for example, placing a bypass policy first lets admins skip all checks, while placing a credential check first ensures every caller is verified. For a detailed guide on ordering strategies, see [Policy Ordering & Composition](/ace/concepts/policy-ordering). ## The Extractor and Mapper pattern A key design principle is the separation between **parsing data** and **enforcing rules**. Extractors handle parsing; Policies handle rules. This means policies don't need to know how to decode raw calldata — they receive clean, named parameters. ### The default flow For most use cases, the process is straightforward: 1. **One Extractor per function selector** — An Extractor is registered for a specific function signature (e.g., `transfer(address,uint256)`). It parses the calldata and returns all relevant parameters as a named list (e.g., `to` and `value`). 2. **Name-based mapping** — When you add a policy to a function selector, you specify which parameter names that policy needs. The PolicyEngine's built-in mapper automatically provides the right subset to each policy. 3. **Multiple policies, one extraction** — The Extractor runs once per transaction, and the engine distributes the parameters to each policy by name. This keeps gas costs efficient. For example, an ERC-20 transfer might have an Extractor that produces `to` and `value`. A sanctions policy might only need `to`, while a volume limit policy only needs `value`. Each gets exactly what it asks for. > **NOTE: Extractors: pre-built and custom** > > The platform provides pre-built extractors for ERC-20, ERC-3643, and `CCIP-AdvancedPoolHooks` function signatures. See > [Protect CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools) for the CCIP integration. For other > contract types, write and deploy your own extractor contract (implementing `IExtractor`) and register it through the > Coordinator API. See [Custom Contract Types & Extractors](/ace/guides/policy-manager/contracts/custom-contract-types) > for details. ### Worked example: ERC-20 transfer Consider a protected `transfer(address from, address to, uint256 amount)` function with two policies attached: a [RejectPolicy](/ace/reference/policy-library/reject-policy) for sanctions screening and a [VolumeRatePolicy](/ace/reference/policy-library/volume-rate-policy) for daily transfer limits. ![Image](/images/ace/policy-management-extractor-mapper-example.png) Here is what happens step by step: 1. **Extraction** — The registered Extractor decodes the raw calldata and produces three named parameters: `from`, `to`, and `amount`. 2. **Mapping for the RejectPolicy** — The RejectPolicy was configured to receive `from` and `to`. The engine provides both addresses to the policy. 3. **Mapping for the VolumeRatePolicy** — The VolumeRatePolicy was configured to receive `amount`. The engine provides only the transfer size. 4. **Policy execution** — Each policy receives exactly the parameters it was mapped to, and nothing else. The RejectPolicy sees two addresses; the VolumeRatePolicy sees one `uint256`. The parameters a policy receives are determined by the mapper configuration — not by the policy itself. A RejectPolicy configured to receive only `to` would check only the recipient; configured to receive both `from` and `to`, it checks both. > **TIP: Why check all mapped addresses?** > > Checking every address delivered by the mapper is a deliberate security choice. If a sanctions policy only checked the > recipient but not the sender, a sanctioned address could still initiate transfers freely. By mapping both `from` and > `to` to the policy, you ensure neither participant can be a sanctioned entity. ### Custom Mappers In rare cases, name-based mapping isn't enough — you need to **transform or combine** parameters before a policy can use them. This is where a custom Mapper comes in. A Mapper sits between the Extractor and a specific policy. It takes extracted parameters as input, transforms them, and returns the result for that policy. For example, a policy that enforces a USD volume limit might need a `usdValue` parameter, but the Extractor only provides `tokenAmount`. A custom Mapper could multiply `tokenAmount` by a price feed value to produce `usdValue`. Mappers are set per policy using `setPolicyMapper` and override the default name-based mapping for that policy only. > **NOTE: Custom mappers are not available** > > Custom Mappers are not available through the ACE Platform during Beta. The platform uses the default name-based > parameter mapping for all policies. See [Beta > Scope](/ace/beta-scope#custom-extractors-are-registered-by-you-custom-mappers-are-not-available) for details. ## The context parameter Throughout the policy execution flow, a `bytes` field called **context** is passed to every policy's `run()` and `postRun()` functions. This is a flexible data channel for passing arbitrary, transaction-specific information that isn't part of the protected function's arguments. ### Common use cases - **Offchain signatures** — A user signs a message offchain (e.g., approving a high-value transaction), and the front end passes the signature in the context. A policy decodes and verifies it. - **Merkle proofs** — To check membership in a large offchain allowlist, the caller provides a Merkle proof in the context. The policy verifies it against a stored root. - **Dynamic risk parameters** — An integrator passes in offchain risk scores or session data, allowing policies to make context-aware decisions. > **NOTE: Context is a developer-level feature** > > The context parameter is handled entirely in your contract's Solidity code. It is not configurable or visible in the > ACE Platform UI or API. ### Two methods for passing context The `PolicyProtected` contract supports two approaches: **Direct argument (recommended for custom functions)** — If you control the function signature, add a `bytes calldata context` parameter and use the `runPolicyWithContext(context)` modifier. This is the cleanest and most gas-efficient approach. **Two-step method (for standard interfaces)** — When protecting a function with a fixed signature (like an ERC-20 `transfer`), the caller first calls `setContext(bytes)` on your contract and then calls the protected function in the same transaction. The `runPolicy` modifier retrieves and clears the stored context automatically. > **CAUTION: Context must be consumed atomically** > > When using the two-step method, always set and consume the context in the same atomic transaction. Context is stored > per sender — if it isn't consumed immediately, stale context could be reused by a subsequent call. --- # Policy Ordering and Composition Source: https://docs.chain.link/ace/concepts/policy-ordering Last Updated: 2026-03-31 Policies attached to a function execute sequentially, and the order they run in determines the security and behavior of your compliance ruleset. This page covers why ordering matters, how to manage the policy chain, and best practices for composing effective policy chains. For a detailed explanation of the execution model and component interactions, see [Policy Management](/ace/concepts/policy-management). ## Why order matters Each policy in the chain produces one of three outcomes: - **Reject** — The policy reverts with `PolicyRejected`. The transaction reverts immediately and no subsequent policies run. - **Allow** — The policy returns `Allowed`. The transaction is approved immediately and all subsequent policies are **skipped**. - **Continue** — The policy's check passed, but the decision is deferred to the next policy in the chain. Both Reject and Allow are **terminal** — they end the chain. This means the position of each policy directly controls which policies actually execute. For a detailed breakdown of each outcome and its interaction with `postRun()`, see [Policy outcomes in detail](/ace/concepts/policy-management#policy-outcomes-in-detail). ### Ordering example Consider three policies attached to a token's `transfer` function: a [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) (verifies KYC credentials), a [MaxPolicy](/ace/reference/policy-library/max-policy) (caps individual transfers), and a [BypassPolicy](/ace/reference/policy-library/bypass-policy) (allows admins to skip all checks). **Ordering A — BypassPolicy first:** ![Image](/images/ace/policy-ordering-example1.png) **Ordering B — Restrictive checks first:** ![Image](/images/ace/policy-ordering-example2.png) In **Ordering A**, an admin skips both the credential check and the transfer cap. In **Ordering B**, every address — including admins — must pass the credential and transfer-limit checks first. The BypassPolicy only applies after the critical checks have passed, so admins are still subject to the same compliance and risk controls as everyone else. ### How the engine evaluates a chain This sequence diagram shows the full evaluation flow when the PolicyEngine processes a chain of policies: ![Image](/images/ace/policy-ordering-policy-engine.png) ## The default result If every policy in the chain returns Continue and none makes a final Allow or Reject decision, the PolicyEngine applies a configurable **default result**. The default can be set to either **allow** or **reject**, and it is configured per target contract via the `desired_default_allow` field. - **`true` (default)** — The transaction is allowed. Use this when policies act as blockers and everything else should pass through. - **`false`** — The transaction is rejected. Use this for allowlist-style enforcement where only explicitly approved transactions proceed. This matters when all your policies use the Continue pattern (which is the most common approach for composable policy chains). If the default is set to reject but you intended all-Continue to mean "all checks passed," your transactions will revert unexpectedly. > **CAUTION: Verify your default result** > > When using policies that return Continue, make sure the default result is configured correctly for each target > contract. A mismatch between your policy design and the default result can silently block or allow transactions. To view or change the default result for a target contract, see [Managing Targets — Default allow behavior](/ace/guides/policy-manager/manage-targets#default-allow-behavior). ## Managing the policy chain Policies are managed per **target contract** and per **function selector** — each protected function on each contract has its own independent policy chain. During ACE Beta, policy chain management is handled through the [ACE Platform](/ace/concepts/key-terms#ace-platform) — the Platform UI or Coordinator API: - **Adding a policy** — When you attach a policy to a protected function, it is appended to the end of the existing chain by default. You can specify a position to insert it at a specific index instead, shifting existing policies to the right. - **Removing a policy** — Removing a policy shifts the remaining policies to maintain their relative order. - **Reordering** — To move a policy to a different position, remove it and re-add it at the desired index. - **Viewing the chain** — Use the Platform UI or API to see the current policy chain for any protected function, listed in execution order. ## Best practices ### Order restrictive checks first Place policies that reject unauthorized or dangerous transactions at the beginning of the chain. This ensures critical security checks (sanctions screening, credential verification) cannot be bypassed by an earlier Allow result. A recommended ordering pattern: 1. **Security checks** — Sanctions screening, credential verification, pause controls 2. **Business logic** — Volume limits, rate limits, time-based restrictions 3. **Permissive overrides** — Admin bypass or emergency override policies (if needed) ### Use Allow sparingly Policies that return Allow skip every subsequent policy in the chain. This is powerful but dangerous if misplaced. Reserve Allow for deliberate bypass scenarios (like an admin override), and place these policies after the checks they are intended to bypass — not before. ### Keep chains short Each policy in the chain costs gas. While the PolicyEngine is designed for efficiency (extractors run once and parameters are mapped per policy), long chains increase transaction costs. Group related checks into a single policy where practical, and avoid redundant policies. ### Review ordering after changes Any time you add, remove, or reorder a policy, review the full chain to confirm the new ordering matches your intent. A single misplaced policy can create a gap in your compliance coverage. For additional security guidance around trust boundaries, external call risks, and context handling, see [Security Considerations](/ace/guides/policy-manager/contracts/security-considerations). --- # Cross-Chain Identity Source: https://docs.chain.link/ace/concepts/cross-chain-identity Last Updated: 2026-07-17 This page explains in depth how ACE's Cross-Chain Identity system works — the CCID model, the credential lifecycle, and how applications validate identities at runtime. For a high-level overview of the components themselves (IdentityRegistry, CredentialRegistry, CredentialRegistryIdentityValidatorPolicy), see the [Architecture page](/ace/concepts/architecture#cross-chain-identity-contracts). ## Why cross-chain identity? Users in the blockchain ecosystem often juggle multiple identities across different networks. A KYC credential issued on Ethereum has no meaning on Arbitrum or Base without complex bridging. This fragmentation creates real problems: - **Duplicated verification** — Users must re-verify their identity on every chain, increasing cost and friction. - **Inconsistent compliance** — Applications on different chains cannot easily verify each other's identity data. - **Reduced interoperability** — Each chain operates its own siloed identity system. - **Increased attack surface** — Multiple identity systems mean multiple points of failure. Cross-Chain Identity solves this with a single framework: a universal identifier that links all of a user's addresses across all EVM chains to one identity, with credentials issued once and verified everywhere. ## The Cross-Chain Identifier (CCID) A **CCID** is a `bytes32` value that represents a single identity across all EVM blockchains. Each chain maintains a local mapping between wallet addresses and CCIDs through an IdentityRegistry. One CCID can be linked to multiple addresses, even across different chains. ![Image](/images/ace/ccid-overview.png) This means a credential attached to a CCID (for example, a KYC verification) is valid for all of that user's addresses on all chains — without re-issuance. ### Generation and uniqueness CCIDs are generated offchain by the [Credential Issuer](/ace/concepts/key-terms#credential-issuer) (the IDV provider) and then registered onchain. The system does not prescribe a specific generation method — it can be random or deterministic — but any approach must ensure uniqueness or collision resistance within the application domain. ### Privacy and correlation Because CCID-to-address mappings are publicly readable onchain, anyone can see which addresses belong to the same identity. This is by design: it enables cross-chain verification. However, for applications where this transparency is a privacy concern, mitigations are available: - **Multiple CCIDs per user** — Issue separate CCIDs for the same user in different application domains. Maintain the correlation in a secure offchain system while presenting separate identities onchain. - **Scoped or temporary identifiers** — Use time-limited or single-use identifiers to reduce the long-term correlation footprint. ## The registry model Cross-Chain Identity uses two complementary onchain registries: ### IdentityRegistry Maps wallet addresses to CCIDs. Each address maps to exactly one CCID, though a single CCID can be associated with multiple addresses across multiple chains. The Credential Issuer (IDV provider) registers these mappings after completing offchain identity verification. ### CredentialRegistry Manages the lifecycle of credentials linked to CCIDs. Each credential record includes: - A **credential type identifier** — what kind of credential it is (e.g., KYC, AML) - An **expiration timestamp** — when the credential expires - Optional **credential data** — typically a hash or minimal reference (never PII) The Credential Issuer registers credentials after verifying the user offchain. Credentials can be renewed or removed as the user's status changes. ### Registry governance Both registries are themselves protected by a PolicyEngine. This means write access (registering identities, issuing credentials, revoking them) is governed by policies — only authorized Credential Issuers can modify the data. The [Identity Manager](/ace/concepts/key-terms#identity-manager) handles this through the ACE Platform. ### Sharing registries across organizations A registry owner can grant another organization **read access** to a registry, so that organization can reference the same identities and credentials in its own policies without re-issuing them. Access is read-only for the recipient and revocable at any time. This is how a protocol reuses a provider's registry directly. See [External Registries](/ace/guides/identity-manager/external-registries). ## Credential type identifiers Every credential has a **type identifier**: a `bytes32` value generated by hashing a namespaced string. ``` keccak256("namespace.requirement_name") ``` ### Standard types Standard credential types use the reserved `common.` prefix: | Identifier | Purpose | | :------------------ | :------------------------------------------ | | `common.kyc` | Identity has passed KYC checks | | `common.kyb` | Identity has passed KYB checks | | `common.aml` | Identity is not flagged by AML requirements | | `common.accredited` | Identity is a qualified accredited investor | ### Custom types Applications can define custom credential types using their own namespace. Custom types must not use the `common.` prefix — this reserved namespace ensures future standard types can be added without conflicts. ``` keccak256("com.yourapp.level.gold") ``` ## Credential Sources A **Credential Source** is the configuration that tells the `CredentialRegistryIdentityValidatorPolicy` which registries to trust for a given credential type. Each source maps: - One or more **credential type identifiers** (what to check) - An **IdentityRegistry** (where to look up the CCID) - A **CredentialRegistry** (where to validate the credential) - An optional **Credential Data Validator** (for additional data checks) There are two ways to use multiple sources, and they can be combined: ### Different sources for different credential types A protocol might trust KYC credentials from Provider A and AML credentials from Provider B, each with their own registries. Each credential type points to the source that handles it. ![Image](/images/ace/ccid-credential-sources.png) ### Multiple sources for the same credential type Applications can also register several sources for the **same** credential type — for example, trusting KYC credentials from Provider A, Provider B, and Provider C. When defining a credential requirement, the application specifies how many sources must successfully validate the credential before the requirement passes. If only one successful validation is needed, any single trusted provider is sufficient. If two or more are required, the user must hold valid credentials from multiple independent providers. This is useful for higher-assurance scenarios where corroboration from several IDV providers is desired. ![Image](/images/ace/ccid-multiple-sources-same-credential.png) For the full configuration details — including how to set the number of required validations per requirement — see the [CredentialRegistryIdentityValidatorPolicy reference](/ace/reference/policy-library/credential-registry-identity-validator-policy). ## Credential data and privacy A credential in the CredentialRegistry can optionally include associated `bytes` of data. This data is meant to be minimal — a hash, an offchain reference, or a non-sensitive classification code. It must never contain personally identifiable information (PII). > **CAUTION: Never store PII onchain** > > Credential data stored onchain should be a hash or minimal reference to offchain data, not the underlying personal > information. Without proper hashing, it may be possible to infer sensitive information, such as correlating onchain > credential expirations with real-world document expiration dates. ### Attestation-only vs. Credential Data Validator The system supports two levels of credential verification: **Attestation-only** is the default mode. The policy asks a single binary question: *does this credential exist for this identity?* The answer is yes or no. The `credentialData` field is ignored entirely. This is sufficient for many compliance scenarios — for example, verifying that a user holds a `common.kyc` credential before allowing a transfer. **Credential Data Validator** adds a second layer. When a `ICredentialDataValidator` contract is configured on a Credential Source, the policy first checks that the credential exists, then passes the `credentialData` bytes to the validator contract for an additional custom check. The validator reads the non-PII data and returns `true` or `false`. ACE provides a pre-built **AllowDenyList Data Validator** whose first use case is jurisdiction control using [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes. See [Managing Data Validators](/ace/guides/policy-manager/manage-data-validators) to configure one. This enables decisions based on *what's inside* the credential, not just whether it exists. For example: - A Credential Issuer verifies an investor's accreditation offchain and stores a **tier code** in `credentialData` (e.g., `1` = retail, `2` = accredited, `3` = institutional). This is a classification, not PII. - A Credential Data Validator contract reads the tier code and returns `true` only if the tier meets the minimum required for a particular asset — for instance, "only accredited or above can trade this security token." - With attestation-only, this distinction is impossible: the policy can only confirm the credential exists, not differentiate between tiers. | | Attestation-only | With Credential Data Validator | | :------------------------- | :--------------------------- | :--------------------------------------------------------------- | | **Question answered** | Does the credential exist? | Does the credential exist AND does its data pass a custom check? | | **`credentialData` usage** | Ignored | Read and validated by a custom contract | | **Use cases** | KYC yes/no, sanctions yes/no | Investor tiers, jurisdiction codes, risk scores | > **NOTE: Configuring data validation** > > Credential Data Validators are configurable through the ACE Platform. To issue credentials that carry data, link a > data schema to your credential type (see [Managing Credential > Types](/ace/guides/identity-manager/manage-credential-types#typed-credentials-with-data-schemas)); to enforce rules on > that data at transaction time, attach a Data Validator to your policy's credential source (see [Managing Data > Validators](/ace/guides/policy-manager/manage-data-validators)). Attestation-only remains the default when no data > validator is configured. ## Credential lifecycle The end-to-end lifecycle of a credential, from real-world verification to onchain validation: ### 1. Verification request The end user requests verification from a Credential Issuer (IDV provider) — for example, submitting documents for KYC. ### 2. Offchain verification The Credential Issuer conducts offchain checks: document verification, identity validation, AML screening, and any other required checks. If successful, the issuer generates a CCID for the user. ### 3. Identity registration The Credential Issuer registers the user's identity onchain by calling `registerIdentity()` on the IdentityRegistry, mapping the user's wallet address to the generated CCID. This is done through the [Identity Manager](/ace/concepts/key-terms#identity-manager) or the Coordinator API. ### 4. Credential issuance The Credential Issuer registers the credential onchain by calling `registerCredential()` on the CredentialRegistry, linking the credential type, expiration, and optional data to the user's CCID. ### 5. Runtime validation When the user calls a protected function on an application contract, the PolicyEngine executes the `CredentialRegistryIdentityValidatorPolicy`. This policy: 1. Resolves the caller's address to a CCID via the IdentityRegistry. 2. Checks whether the CCID holds the required credentials from trusted sources via the CredentialRegistry. 3. Optionally validates credential data through a Credential Data Validator. 4. Returns the result to the PolicyEngine, which allows or rejects the transaction. ![Image](/images/ace/ccid-runtime-validation.png) After initial issuance, the Credential Issuer is no longer involved in day-to-day validation. The onchain registries and policy handle everything at runtime. ### Credential expiration and revocation Credentials are not permanent. The Credential Issuer sets an expiration timestamp at issuance, and the system will not consider a credential valid once it has expired. If a user's status changes (e.g., their KYC lapses or is flagged), the Credential Issuer can revoke the credential immediately through the Identity Manager or API. ## Design rationale ### Why offchain validation? Many credential formats contain personally identifiable information. Storing this data onchain is neither practical nor desirable. Instead, the Credential Issuer performs verification offchain and publishes only minimal references onchain, preserving privacy while supporting a wide range of credential formats. ### Why CCID instead of per-address credentials? By decoupling credentials from individual addresses and linking them to a cross-chain identifier, applications gain a unified identity reference across all EVM networks. A credential issued once is valid for all of a user's addresses on every chain — no re-issuance, no bridging. ### Why a modular architecture? Separate registries and validators allow: - **Flexibility** — Applications choose which components they need. - **Interoperability** — Different applications can share the same registries and accept credentials from the same issuers. - **Independence** — Individual components can be upgraded without affecting others. - **Ecosystem compatibility** — Common interfaces enable ecosystem-wide credential portability. --- # Reporting Manager Source: https://docs.chain.link/ace/concepts/reporting Last Updated: 2026-10-05 This page explains what the Reporting Manager provides, why it matters for compliance, and how the data pipeline works. For a high-level overview of how the Reporting Manager fits into the ACE architecture, see the [Architecture page](/ace/concepts/architecture#reporting-manager). > **NOTE: Active Monitoring decisions** > > The Reporting Manager covers policy runs, identities, and credentials. The **Decisions log** of [Active > Monitoring](/ace/active-monitoring/overview) is separate: it is available in the Platform and through the Coordinator > API. See [Decisions, Review, and Audit Trail](/ace/active-monitoring/concepts/decisions-and-audit). ## Why reporting matters Enforcing compliance onchain is half the equation. The other half is **proving** it. Regulators, auditors, and internal compliance teams need verifiable evidence that the right rules were in place and applied correctly at the time each transaction occurred. ACE records every policy evaluation as an onchain event. The Reporting Manager indexes these events and makes them queryable through the **Reporting API**, giving you a complete, tamper-evident audit trail without running your own blockchain indexer. Typical use cases include: - **Regulatory audits** — Demonstrate that specific compliance rules were active and enforced during a given period. - **Incident investigation** — Trace exactly which policies evaluated a transaction, what parameters were extracted, and what the outcome was. - **Ongoing monitoring** — Track policy run activity across contracts, chains, and time ranges to identify anomalies or confirm expected behavior. - **Configuration review** — Verify what policies, identities, and credentials were in effect at a specific point in time. ## What you can query The Reporting API exposes four resource types, each representing a different facet of your compliance data. ### Transactions Every transaction that triggers at least one policy engine evaluation emits a `PolicyRunComplete` event onchain. The Reporting Manager indexes these events and makes them available as **transaction records** — the core of your audit trail. Each transaction record includes: - **Full transaction metadata** — chain, block number, timestamp, sender, recipient, and gas details. Only successful transactions are indexed, since the `PolicyRunComplete` event is only persisted on-chain when the transaction succeeds. - **Policy run details** — for each policy engine evaluation within the transaction: the target contract, the function called, the extractor used, each policy that ran (address, name, version, configuration state), the extracted parameters, any context data, and the engine/target default behaviors. You can filter transactions by: - Chain - Time range (from/to timestamps) - Sender or recipient address - Target contract address - Function selector - Specific policy address - Policy engine address This means you can answer questions like "show me every transfer on contract X that was evaluated by the sanctions policy between March 1 and March 31." ### Policies Query deployed policy instances — their configuration state, ownership, version history, and which engine they belong to. Use this to verify what rules were in place and how they were configured. Each policy record includes the chain, contract address, owner, name, description, engine address, version number, configuration state (when requested), and the time range during which that version was effective. ### Targets Query protected contracts and their full policy configuration — which engines are attached, what methods are protected, what extractors and policies are configured per method, and what the default behaviors are. This gives you a complete snapshot of "what compliance rules protect this contract and how are they wired up." ### Identities Look up identity records by wallet address, identity registry, credential type, credential registry, or CCID. Each identity includes all registry memberships (which registries, which chains, which wallet addresses are mapped). Set `include_credential_details=true` to include each identity's credentials in the response. Each credential record contains the credential type identifier, the issuing credential registry, issuance and expiration timestamps, credential data, and full on-chain provenance (block number, transaction hash). This lets you answer questions like "does this wallet address belong to a registered identity?" or "which identities have a KYC credential issued by this credential registry?" ## Point-in-time queries The Policies, Identities, and Targets endpoints each accept a required **`as_of`** timestamp parameter. This lets you reconstruct the state of your compliance system at any historical moment: - **What policies were active** on a contract on a specific date? - **What credentials** did an identity hold at the time of a transaction? - **What protections** were configured on a target contract last quarter? Point-in-time queries are critical for regulatory investigations where you need to prove not just that compliance rules exist *today*, but that they were in place *when a specific event occurred*. The `as_of` parameter returns the version of each resource that was effective at the specified time, including resources that have since been updated or removed. The Transactions endpoint uses `from` and `to` time range filters instead, since transactions are discrete events rather than stateful resources. ## How data flows The Reporting Manager does not read directly from the blockchain. Instead, Chainlink's indexing infrastructure continuously monitors onchain events emitted by your managed PolicyEngines and indexes the data into a queryable store. The pipeline works as follows: 1. A transaction triggers a protected function on your contract. 2. The PolicyEngine evaluates the policy chain and emits a `PolicyRunComplete` event with full details. 3. Chainlink's indexing infrastructure detects the event and indexes the transaction data, policy configurations, identity lookups, and credential checks. 4. The indexed data becomes available through the Reporting API. This happens automatically for all contracts deployed through the ACE Platform. You do not need to run your own indexer, event listener, or database. > **NOTE: Managed contracts only** > > The Reporting Manager only tracks contracts deployed through the ACE Platform (UI or Coordinator API). Self-deployed > PolicyEngines and policies function onchain but do not appear in Reporting API responses. See [Beta > Scope](/ace/beta-scope) for details. ## Beta scope During Beta, the Reporting Manager is **API-only** — there is no reporting UI. You interact with it exclusively through the Reporting API. The API provides read-only access. All compliance configuration changes (deploying policies, registering identities, issuing credentials) are done through the [Coordinator API](/api/ace/coordinator/docs) or [Platform UI](https://app.chain.link). ## Next steps - **[Interactive API Reference](/api/ace/reporting/docs)** — Try API calls directly in the browser with full request/response schemas. - **[Architecture](/ace/concepts/architecture#how-ace-observes-onchain-activity-read-path)** — How the indexing pipeline connects onchain events to the Reporting API. --- # Off-Chain Policy Execution Source: https://docs.chain.link/ace/concepts/off-chain-policies Last Updated: 2026-10-05 ACE policies are not limited to on-chain logic. Off-chain policy execution lets you enforce compliance rules that depend on data or systems outside the blockchain — your internal compliance engine, third-party risk APIs, or any custom business logic. The checks happen off-chain before the transaction, and the result is delivered on-chain as a cryptographic **permit** that authorizes the action. ACE provides two ways to use offchain policies: - **Managed wallet risk screening (MVP)** — An out-of-the-box policy that screens wallet addresses with TRM Wallet Screening. You configure the risk thresholds and call the ACE Evaluation API; Chainlink manages the CRE workflow and onchain permit delivery. - **Custom offchain integrations** — An advanced model for internal compliance systems, custom policy endpoints, and other business logic. These integrations require infrastructure hosted by your organization and assistance from Chainlink during Beta. > **TIP: Start with managed wallet screening** > > To configure the managed TRM policy, see [Managing Offchain Risk Policies > (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies). To request permits from an application, > see [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits). > **NOTE: Active Monitoring also uses TRM** > > Managed wallet risk screening calls TRM once for each transaction request and delivers a permit. [Active > Monitoring](/ace/active-monitoring/overview) calls TRM on a schedule for a watchlist of addresses and acts on risk > changes. The two features use separate credentials and separate guides. See [Preventive and Continuous > Compliance](/ace/concepts/preventive-vs-continuous#trm-in-each-mode). > **CAUTION: Managed service is an MVP** > > Managed offchain risk policies and the Evaluation API are an MVP. Their interfaces and capabilities can change during > Beta. Contact your Chainlink representative before using the managed service and for help with setup. ## Why off-chain policies? On-chain policies evaluate data that is already available in the transaction calldata or on the blockchain. But many compliance requirements depend on information that only exists off-chain: - **Risk and sanctions databases** — screen wallet addresses against external risk intelligence providers - **Internal compliance systems** — connect to proprietary risk models, approval workflows, or internal rule engines - **Custom business logic** — any HTTP-accessible data source or decision service your compliance process requires Off-chain policy execution bridges this gap by running these checks through the Chainlink Decentralized Oracle Network (DON) and delivering the result on-chain. ## Managed wallet risk screening A protected function that requires off-chain verification will revert if the caller does not have a valid permit. Your application must obtain a permit from the Chainlink DON before the user can call the function. ![Managed offchain risk screening flow: an application requests an evaluation from ACE, a managed CRE workflow screens addresses with TRM, an approved permit is stored by the CADV contract, and the protected transaction consumes it.](/images/ace/offchain-risk-screening-flow.svg) 1. Your application calls the ACE Evaluation API with the transaction's caller, target, function, chain, and permit parameters. 2. ACE triggers a managed CRE workflow. The workflow selects the configured addresses and calls TRM Wallet Screening using the credential your organization stores in Vault DON. 3. The workflow compares TRM's results with the policy's global risk threshold, unknown-risk setting, and category-specific thresholds. 4. If an address is rejected, the evaluation becomes `rejected` and no permit is created. 5. If the evaluation passes, the workflow delivers a permit through the Keystone Forwarder. The [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) stores it onchain. 6. Your application polls the Evaluation API. When the status becomes `ready`, it submits the protected transaction. 7. The onchain policy matches the action to the stored permit and consumes it after the call succeeds. Each managed permit is scoped to a specific caller, target, function, and set of extracted parameters. In the current Beta release, permits are single-use and do not expire. ## Custom offchain integrations The custom model supports compliance requirements beyond the managed TRM policy. A Chainlink DON workflow can call a policy endpoint hosted by your organization, which can query internal systems or other external services and return an Allow or Deny decision. ![Custom offchain policy execution flow: an application requests an evaluation from the Chainlink DON, which calls a policy endpoint hosted by the user before delivering an approved permit onchain.](/images/ace/offchain-engine/simplified-offchain-engine-permit.png) Your application and policy endpoint can be the same server — they represent different roles in the flow, not necessarily separate deployments. 1. Your application sends an action request describing the transaction and any context required by the custom policies. 2. A DON workflow evaluates its policy pipeline and calls your policy endpoint where required. 3. Your endpoint runs your business logic and returns an Allow or Deny decision. 4. If every policy passes, the DON generates and delivers a permit onchain. 5. Your application waits for confirmation and then submits the protected transaction. Custom integrations are designed with Chainlink during Beta. Contact your Chainlink representative to discuss workflow design, endpoint authentication, permit lifetime, usage limits, and operational requirements. ## What custom integrations can connect to The custom offchain policy model is open-ended. Built-in rules can run directly inside the DON workflow, and your external policy endpoints can integrate with systems reachable over HTTP, including: - Third-party compliance APIs (wallet risk scoring, sanctions screening, AML checks) - Internal compliance engines and proprietary rule sets - Multi-step approval workflows involving multiple parties - Any combination of the above in a single evaluation pipeline Policies in the pipeline are evaluated sequentially — the first policy to deny stops execution and no permit is generated. This flexibility means off-chain policies can enforce compliance requirements that would be impossible to implement purely on-chain. ## On-chain enforcement The on-chain side of off-chain policy execution is handled by the [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) contract. This policy is attached to your protected functions like any other policy in the chain. At transaction time, it checks whether a valid permit has been delivered by the DON — it does not re-run the off-chain logic. The permit serves as cryptographic proof that the off-chain checks passed. From the policy engine's perspective, the CertifiedActionDONValidatorPolicy is just another policy in the evaluation chain. It can be combined with on-chain policies (allowlists, volume limits, pause toggles, etc.) in any order. ## Related pages - [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) — configure managed TRM wallet screening - [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) — integrate the Evaluation API into an application - [Policy Management](/ace/concepts/policy-management) — how policy chains and evaluation work - [Architecture](/ace/concepts/architecture) — how the on-chain and off-chain layers connect - [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) — the on-chain contract reference - [Policy Library](/ace/reference/policy-library) — all available policy implementations --- # Getting Started with ACE Source: https://docs.chain.link/ace/getting-started Last Updated: 2026-10-05 ACE enforces compliance in two ways. Start with what you need: - **Block non-compliant transactions** in a contract you build or can upgrade: use the preventive managers, Policy Manager and Identity Manager. - **Monitor addresses and react when their risk changes** on a token you already run, without changing its contract: use Active Monitoring. - **Both**: the two modes complement each other and share the same setup. See [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous) for the differences. ACE offers four managers. Choose the path that matches what you need to do: | Manager | What it does | Get started | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | **Policy Manager** | Attach and configure compliance policies on smart contracts — volume limits, allowlists, RBAC, identity-based checks, and more | [Policy Manager Quick Start](/ace/getting-started/policy-manager) | | **Identity Manager** | Manage identity registries, register cross-chain identities (CCIDs), and issue credentials such as Proof of Identity or accreditation attestations | [Identity Manager Quick Start](/ace/getting-started/identity-manager) | | **Active Monitoring** | Screen the addresses you choose with TRM on a schedule, and record, flag, or enforce an onchain action when their risk changes, on tokens you already run | [Active Monitoring Quick Start](/ace/active-monitoring/quick-start) | | **Reporting Manager** | Query on-chain transaction history, policy configurations, and identity states via a read-only API for compliance verification | [Reporting concepts](/ace/concepts/reporting) — [API Reference](/api/ace/reporting/docs) | ### Shared first step All managers require **account setup** first — creating your organization, sharing your Org ID for enablement, generating an API key, and setting up [CRE Connect Wallets](/ace/concepts/key-terms#cre-connect-wallet): - [Account Setup](/ace/getting-started/account-setup) — the four steps every ACE user completes before using any manager ### Not sure which Manager you need? - If you are a **token issuer or protocol team** deciding which compliance rules to enforce, start with the [Policy Manager Quick Start](/ace/getting-started/policy-manager). - If you are an **identity provider (IDV)** or **credential issuer**, or **sanctions data provider** supplying data for others to consume, start with the [Identity Manager Quick Start](/ace/getting-started/identity-manager). - If your token is already deployed and you want to screen its holders and react to risk changes without changing the contract, start with the [Active Monitoring Quick Start](/ace/active-monitoring/quick-start). - Many organizations use **more than one manager**. Start with whichever is most relevant to your first use case — the guides cross-reference each other where the workflows intersect. ### Background reading Before diving in, these pages provide essential context: - [Signing & Ownership Model](/ace/concepts/signing-ownership) — how ACE manages keys and the delegated and self-signing models - [ACE Architecture](/ace/concepts/architecture) — system components and how they connect - [Key Terms](/ace/concepts/key-terms) — ACE-specific terminology - [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous) — the two ways ACE enforces compliance - [Beta Scope](/ace/beta-scope) — what is and is not available during the Beta program --- # Account Setup Source: https://docs.chain.link/ace/getting-started/account-setup Last Updated: 2026-10-05 This page walks through the shared setup steps for all ACE users. Whether you use the [Policy Manager](/ace/getting-started/policy-manager) to enforce compliance on smart contracts or the [Identity Manager](/ace/getting-started/identity-manager) to manage cross-chain identities and credentials, complete these steps first. ## 1. Create your organization Go to [app.chain.link](https://app.chain.link) and create an account or sign in. Once signed in, click **"My Org"** in the bottom-left corner of the sidebar to open the Organization page. Your **Organization ID** is displayed in the page header — copy it. ![Image](/images/ace/account-setup/organization-id.webp) ## 2. Share your Organization ID Share your Organization ID with your Chainlink contact so they can enable the ACE service for your organization. You cannot create API keys or use ACE until this step is complete. > **NOTE: Beta onboarding** > > During Beta, ACE enablement requires Chainlink to provision the service for your organization. This will become > self-service in a future release. ## 3. Create an API key > **CAUTION: Wait for ACE enablement** > > Do not create an API key until your Chainlink contact confirms that ACE has been enabled for your organization. API > keys created before enablement will not have access to ACE endpoints. Once your account has been provisioned for the ACE service, create an API key for authentication: 1. Log in to the [Chainlink App](https://app.chain.link), click **"My Org"** at the bottom of the left sidebar, then select the **"APIs"** tab. 2. Click **"+ Organization API"**. ![Image](/images/ace/account-setup/api-key-1.webp) 3. Enter a name for the key and select an expiration period (1 day, 1 month, or 1 year). ![Image](/images/ace/account-setup/api-key-2.webp) 4. Click **"Generate"**. The API key is displayed once — copy it immediately. > **CAUTION: Save your API key** > > The API key is only shown once at creation time. Copy it immediately — if you lose it, you will need to generate a new > one. Never share your API key. Each user in your organization should create and manage their own key. You will use this key in the `Authorization` header for all [Coordinator API](/api/ace/coordinator/docs) and [Reporting API](/api/ace/reporting/docs) calls: ```bash curl https://ace.api.chain.link/v1/ \ -H "Authorization: Apikey " ``` > **NOTE: Active Monitoring uses a second key** > > This key authenticates you to ACE. [Active Monitoring](/ace/active-monitoring/overview) also needs your own TRM API > key, which you store separately. See [Configure the TRM API Key and Screening > Schedule](/ace/active-monitoring/guides/configure-screening). ## 4. Set up CRE Connect Wallets Before you can use ACE — whether from the Platform UI or the Coordinator API — you need a **CRE Connect Wallet** on each blockchain network where you want to operate. A CRE Connect Wallet is a dedicated onchain smart contract wallet that acts as the execution gateway for all ACE actions on a given chain. When you trigger an action (deploy a policy engine, register an identity, issue a credential), the ACE platform executes the blockchain transaction through your CRE Connect Wallet. For a full explanation of how the signing and ownership model works, see [Signing & Ownership Model](/ace/concepts/signing-ownership). Key points: - **One wallet per chain.** You need a CRE Connect Wallet on every network where you plan to use ACE. See [Supported Networks](/ace/supported-networks) for available chains. - **You own it.** The `owner_address` you provide when creating the wallet becomes the owner of the CRE Connect Wallet onchain. - **Chainlink operates through it.** Chainlink is registered as an authorized operator — allowed to execute operations on your behalf, but unable to change ownership or authorization settings. - **Self-signing organizations** must also provide the `address` of the ECDSA signer authorized to sign operations. See [Signing & Ownership Model](/ace/concepts/signing-ownership) for details on how each signing model works. ## What happens next Your next step depends on what you need to do: | If you need to... | Next step | | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Enforce compliance policies on smart contracts | Deploy a PolicyEngine and integrate your contract. Continue to the [Policy Manager Quick Start](/ace/getting-started/policy-manager). | | Manage cross-chain identities and issue credentials | Set up identity and credential registries. Continue to the [Identity Manager Quick Start](/ace/getting-started/identity-manager). | | Monitor addresses and act on a token you already run | Prepare the token, store a TRM API key, and create a monitoring rule. Continue to the [Active Monitoring Quick Start](/ace/active-monitoring/quick-start). | Many organizations use more than one. Start with whichever is most relevant to your first use case — the quick start guides cross-reference each other where the workflows intersect. --- # Policy Manager Quick Start Source: https://docs.chain.link/ace/getting-started/policy-manager Last Updated: 2026-10-05 This guide walks you through the **Policy Manager** — the ACE component for attaching and configuring compliance policies on smart contracts. By the end you will have a policy-protected contract running on a supported network. ### 1. Prerequisites - Solidity basics - **Must-read before proceeding:** [Signing & Ownership Model](/ace/concepts/signing-ownership) — understand how ACE manages keys, who owns what, and how the delegated trust model works ### 2. Account setup Complete the [Account Setup](/ace/getting-started/account-setup) steps — organization creation, API key generation, and CRE Connect Wallet deployment — before proceeding. ### 3. Create a PolicyEngine A **PolicyEngine** is the onchain contract that evaluates compliance rules on your smart contract. When you create a PolicyEngine, you also attach **extractors** — modules that decode transaction calldata so the PolicyEngine can evaluate policies against function arguments (sender, recipient, amount, etc.). #### Verify deployment The PolicyEngine and its extractors start with `"creation_pending"` / `"inactive"` status while the onchain transactions are processed. Poll until everything is ready: ```bash curl https://ace.api.chain.link/v1/policy-engines/ \ -H "Authorization: Apikey " ``` Check two things in the response: 1. **PolicyEngine deployed** — Every entry in `onchain_policy_engines` shows `"status": "created"`. 2. **Extractors active** — Every entry in `extractor_registrations[].onchain_extractor_registrations` shows `"status": "active"`. In the Platform UI, confirm the engine status is **Active** and extractors appear in the engine settings page. > **CAUTION: Deployment failure** > > If any chain shows `"status": "creation_failed"`, the onchain deployment did not succeed. Create a new PolicyEngine > and retry. Contact your Chainlink representative if the issue persists. #### Save the PolicyEngine addresses Copy the `address` value from each entry in `onchain_policy_engines` — or from the engine settings page in the UI — you need these addresses to deploy or upgrade your smart contract in the next step. Each chain has a different PolicyEngine contract address. ### 4. Integrate your contract > **NOTE: CCIP Token Pools use a dedicated integration** > > To enforce ACE policies through `AdvancedPoolHooks`, follow [Protect CCIP Token Pools with > ACE](/ace/guides/policy-manager/ccip-token-pools). The hook calls the Policy Engine directly, so it does not use the > generic `PolicyProtected` integration below. See [Making Your Contract ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible) for a full overview of what your contract needs. In short: - Inherit from `PolicyProtected` (or `PolicyProtectedUpgradeable` for upgradeable contracts) - Add the `runPolicy` modifier to the functions you want to protect - Pass the PolicyEngine address during deployment or initialization **Choose your path:** #### New contract If you are building a new token or contract from scratch, ACE provides reference implementations you can use as a starting point: - **ERC-20** — see [Building an ERC-20 Compliance Token](/ace/guides/policy-manager/contracts/erc20-token) for the full guide - **ERC-3643** — see [Building an ERC-3643 Compliance Token](/ace/guides/policy-manager/contracts/erc3643-token) for the full guide #### Existing contract If you have an already-deployed contract you want to add ACE compliance to, this involves modifying your implementation contract, testing, and executing a proxy upgrade. This is typically the longest step in the onboarding process. - See [Upgrading Existing Contracts](/ace/guides/policy-manager/contracts/upgrade-existing) for the step-by-step guide - Non-upgradeable contracts require alternative approaches — contact your Chainlink representative for guidance ### 5. Register your contract as a target After deploying your contract, make sure it appears as a target under your Policy Engine. ACE detects contracts that call `PolicyEngine.attach()` automatically. If ACE does not detect your integration, register the target manually with the request below and provide its name, type, protected methods, and onchain addresses. ```bash curl -X POST https://ace.api.chain.link/v1/targets \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "title": "My ERC-20 Token", "description": "Production ERC-20 token with compliance enforcement", "policy_engine_id": "", "protected_methods": [ "transfer(address,uint256)", "transferFrom(address,address,uint256)", #[any other methods you want to protect] ], "desired_default_allow": true, "metadata": {"contract_type": "ERC-20"}, "onchain_targets": [ { "chain_selector": "16015286601757825753", "address": "0xYourContractAddressOnSepolia" }, { "chain_selector": "3478487238524512106", "address": "0xYourContractAddressOnArbitrumSepolia" }, #[any other chains where your contract is deployed] ] }' ``` Include an entry in `onchain_targets` for every chain where you deployed the contract. Once registered, your target appears in the ACE Platform dashboard under your policy engine. For a full description of all fields and options, see [Managing Targets](/ace/guides/policy-manager/manage-targets#register-a-target). ### 6. Post-setup checklist Before creating policies, confirm that every component is in the expected state. You can verify each of these with a single API call. | Check | What to look for | | ---------------------------- | -------------------------------------------------------------------------------------------------------- | | CRE Connect Wallets created | `GET /wallets` — every wallet shows `"status": "created"` | | PolicyEngine deployed | `GET /policy-engines/` — every `onchain_policy_engines[].status` is `"created"` | | Extractors active | Same response — every `extractor_registrations[].onchain_extractor_registrations[].status` is `"active"` | | Contract visible in platform | Your target contract appears in the ACE Platform dashboard | ### 7. Create and configure policies From the UI or API, create policy instances and attach them to your contract's protected functions. See [Managing Policies](/ace/guides/policy-manager/manage-policies) for creating and configuring policies, then [Protecting Target Functions](/ace/guides/policy-manager/manage-protections) for attaching them to specific functions on your contracts. ### 8. Test it Make a transaction against your protected contract, verify the policy enforces correctly, and check the results in the [Reporting Manager](/ace/concepts/reporting). --- # Identity Manager Quick Start Source: https://docs.chain.link/ace/getting-started/identity-manager Last Updated: 2026-05-26 This guide walks you through the **Identity Manager** — the ACE component for managing identity registries, registering cross-chain identities (CCIDs), and issuing credentials such as Proof of Identity, accreditation proofs, or sanctions clearance. By the end you will have identities registered and credentials issued on a supported network. ### 1. Prerequisites - Familiarity with [Cross-Chain Identity](/ace/concepts/cross-chain-identity) concepts — CCIDs, credential registries, credential types, and credential sources - **Must-read before proceeding:** [Signing & Ownership Model](/ace/concepts/signing-ownership) — understand how ACE manages keys, who owns what, and how the delegated trust model works ### 2. Account setup Complete the [Account Setup](/ace/getting-started/account-setup) steps — organization creation, API key generation, and CRE Connect Wallet deployment — before proceeding. ### 3. Set up your registries A **registry** is the top-level resource that groups an **identity registry** and a **credential registry**, deployed together on each chain you operate on. - The identity registry maps wallet addresses to cross-chain identities (CCIDs) - The credential registry stores credential attestations linked to those CCIDs See [Managing Registries](/ace/guides/identity-manager/manage-registries) for full details. ### 4. Define credential types Credential types represent the categories of attestation you issue — for example, Proof of Identity, accredited investor, or sanctions clearance. Each credential type is scoped to a specific registry and identified by a `credential_type` string that gets hashed on-chain to a `credential_type_hash`. This value is hashed using keccak256 — the standard cryptographic hash function used by Ethereum and EVM-compatible blockchains — and the resulting `credential_type_hash` is what gets recorded on-chain and referenced by policies. ### 5. Register identities and issue credentials A cross-chain identity (CCID) aggregates one or more wallet addresses across EVM chains into a single logical entity. When you register an identity, you provide the on-chain addresses that belong to that entity and ACE writes the mapping into the identity registry on each relevant chain. A single CCID can span multiple chains and addresses — for example, one entity might have wallets on Ethereum, Arbitrum, and Avalanche that all resolve to the same CCID. Credentials are attestations linked to a registered identity. During Beta, ACE uses an **attestation-only** model — the Identity Manager asserts that a credential holds for a given CCID, and the credential registry records that attestation on-chain. ### 6. Verify via Reporting After issuing credentials, confirm they are visible and queryable through the **Reporting Manager**. The Reporting Manager provides a read-only view of all identities and credentials across your registries, which Policy Managers rely on when evaluating identity-based policies at transaction time. - Open the [Reporting API](/api/ace/reporting/docs) to query credentials by identity, entity, or registry - See [Reporting](/ace/concepts/reporting) for details on how reporting data flows into policy evaluation Once credentials appear in reporting, Policy Managers can reference them in identity-based policies such as the [Credential Registry Identity Validator](/ace/reference/policy-library/credential-registry-identity-validator-policy). ### 7. What's next Explore the detailed guides for each Identity Manager workflow: - [Managing Identities](/ace/guides/identity-manager/manage-identities) — add, update, and remove CCIDs and their on-chain address mappings - [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types) — create and organize the credential categories your registry supports - [Managing Credentials](/ace/guides/identity-manager/manage-credentials) — issue, revoke, and set expiration on credentials - [Managing Registries](/ace/guides/identity-manager/manage-registries) — view and manage your identity and credential registry deployments --- # Policy Manager Guides Source: https://docs.chain.link/ace/guides/policy-manager Last Updated: 2026-09-23 These guides cover the day-to-day operations of a Policy Manager — from integrating your smart contracts with ACE to configuring and managing compliance policies. > **NOTE** > > New to ACE? Start with the [Policy Manager Quick Start](/ace/getting-started/policy-manager) for a step-by-step > onboarding walkthrough. ## How it all fits together The Policy Manager revolves around a handful of entities that work together to enforce compliance on your smart contracts. Understanding how they relate to each other makes the individual guides much easier to follow. ![Image](/images/ace/policy-management-overall.png) - **PolicyEngine** — The on-chain orchestrator that evaluates policies. Everything — targets, policy instances, and extractors — is scoped to a single engine. - **Extractors** — Modules that decode transaction calldata into named parameters (sender, amount, etc.) so policies can evaluate them. Attached to the engine at creation time. - **Target** — A smart contract associated with an engine through automatic onchain detection or manual API registration. - **Policy Type** — A reusable compliance rule from the [Policy Library](/ace/reference/policy-library) (e.g., allowlist, volume limit, pause toggle). - **Policy Instance** — A deployed policy contract created from a policy type, configured with your parameters, and scoped to an engine. - **Protection** — The binding between a policy instance and a specific function on a target contract. This is what makes a function "policy-protected." - **Data Validator** — An optional contract attached to an identity policy's credential source that validates the *contents* of a credential (e.g., a jurisdiction allow/deny list), not just its existence. - **Managed offchain policy (MVP)** — A CRE workflow and onchain validator managed by Chainlink that evaluate external risk data before issuing a permit for a specific transaction intent. ### Typical setup flow 1. [Create a PolicyEngine](/ace/guides/policy-manager/manage-engines) with extractors for your contract type: built-in ERC-20 or ERC-3643, built-in [`CCIP-AdvancedPoolHooks`](/ace/guides/policy-manager/ccip-token-pools), or [your own type](/ace/guides/policy-manager/contracts/custom-contract-types). 2. [Integrate your contract](/ace/guides/policy-manager/contracts/ace-compatible) through `PolicyProtected` or a direct Policy Engine call. 3. Deploy and connect your contract. ACE can [detect its onchain attachment or accept manual registration](/ace/guides/policy-manager/manage-targets). 4. [Create policy instances](/ace/guides/policy-manager/manage-policies) from the Policy Library with your configuration. 5. [Attach policies to functions](/ace/guides/policy-manager/manage-protections) by creating protections. ## Smart contract integration - [Making Your Contract ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible) — what your contract needs to work with ACE (inheriting `PolicyProtected`, adding the `runPolicy` modifier) - [Protect CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools) — connect CCIP AdvancedPoolHooks and enforce policies on cross-chain token transfers - [Building a New ERC-20 Token](/ace/guides/policy-manager/contracts/erc20-token) — reference implementation for a compliance-ready ERC-20 token - [Building a New ERC-3643 Token](/ace/guides/policy-manager/contracts/erc3643-token) — reference implementation for an ERC-3643 security token - [Upgrading Existing Contracts](/ace/guides/policy-manager/contracts/upgrade-existing) — how to add ACE compliance to an already-deployed upgradeable contract - [Security Considerations](/ace/guides/policy-manager/contracts/security-considerations) — key security patterns and pitfalls when integrating with ACE ## Policy engine and policy management - [Managing Policy Engines](/ace/guides/policy-manager/manage-engines) — create, view, update, and archive policy engines - [Managing Targets](/ace/guides/policy-manager/manage-targets) — detect or register deployed contracts as targets under a policy engine - [Managing Policies](/ace/guides/policy-manager/manage-policies) — browse policy types, create and configure policy instances - [Protecting Target Functions](/ace/guides/policy-manager/manage-protections) — bind policy instances to specific functions on your target contracts - [Managing Data Validators](/ace/guides/policy-manager/manage-data-validators) — enforce rules on credential contents (e.g., jurisdiction allow/deny lists) by attaching Data Validators to identity policies - [Custom Policies](/ace/guides/policy-manager/custom-policies) — write, deploy, and register your own policy contract, then use it like a library policy > **CAUTION: Offchain risk policies are an MVP** > > Managed offchain risk policies are an MVP. Their interfaces and capabilities can change during Beta. Contact your > Chainlink representative before using this feature and for help with setup. - [Offchain Policies](/ace/guides/policy-manager/offchain-policies) — understand the managed and custom models, configure the managed wallet screening MVP, and integrate offchain permits --- # Making Your Contract ACE-Compatible Source: https://docs.chain.link/ace/guides/policy-manager/contracts/ace-compatible Last Updated: 2026-10-05 A contract is ACE-compatible when it routes function calls through a Policy Engine for compliance checks before execution. Most contracts inherit `PolicyProtected` and add the `runPolicy` modifier. A contract can also construct the ACE payload and call the Policy Engine directly, as `AdvancedPoolHooks` does. This page explains the `PolicyProtected` integration path. For the direct CCIP integration, see [Protect CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools). For background on how these components interact, see the [Architecture page](/ace/concepts/architecture#policy-management-contracts) and the [Policy Management](/ace/concepts/policy-management) concepts page. > **NOTE: Building for production** > > Many teams start on testnets to experiment with ACE during Beta, then move to mainnet when ready. The upgrade guide is > here so you can plan ahead — upgrading production contracts is straightforward with multiple well-defined paths. ## What a PolicyProtected integration needs ### 1. Inherit from PolicyProtected Your contract must inherit from `PolicyProtected` (for new contracts) or `PolicyProtectedUpgradeable` (for contracts deployed behind a proxy that need an upgrade path). This base contract provides: - The `runPolicy` and `runPolicyWithContext` modifiers that hook your functions into the policy system. - Functions to attach and manage the connection to a PolicyEngine. - Context handling for passing additional data (like offchain signatures) to policies. ### 2. Add the runPolicy modifier to protected functions Any function that should be subject to compliance checks needs the `runPolicy` modifier. The modifier intercepts the call and routes it through the [PolicyEngine](/ace/guides/policy-manager/manage-engines) before your function body executes. ```solidity // Before: no compliance checks function transfer(address to, uint256 amount) public returns (bool) { return super.transfer(to, amount); } // After: the PolicyEngine checks all attached policies before execution function transfer(address to, uint256 amount) public runPolicy returns (bool) { return super.transfer(to, amount); } ``` You choose which functions to protect. Unprotected functions continue to work normally without any policy checks. ### 3. Connect to a PolicyEngine Your contract must be connected to a [PolicyEngine](/ace/guides/policy-manager/manage-engines) — the central orchestrator that holds all policies and executes them in order when a protected function is called. The connection is established during initialization (for [new contracts](/ace/guides/policy-manager/contracts/new-contract)) or migration (for [upgrades](/ace/guides/policy-manager/contracts/upgrade-existing)). ### 4. Register extractors for protected functions [Extractors](/ace/concepts/policy-management#the-extractor-and-mapper-pattern) are helper contracts that parse the calldata of your protected functions into named parameters (for example, `to` and `value` for an ERC-20 `transfer`). Policies use these named parameters to make their decisions — a volume limit policy reads `value`, a sanctions check reads `to`. One extractor is registered per function signature. To bind policies to specific functions, see [Protecting Target Functions](/ace/guides/policy-manager/manage-protections). > **NOTE: Extractors: pre-built and custom** > > The platform provides pre-built extractors for **ERC-20 and ERC-3643 function signatures**, attached automatically for > those contract types. The built-in `CCIP-AdvancedPoolHooks` type covers CCIP Token Pool hook functions; see [Protect > CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools). For other function signatures, you write and > deploy your own extractor contract (implementing `IExtractor`) and register it through the Coordinator API. See > [Custom Contract Types & Extractors](/ace/guides/policy-manager/contracts/custom-contract-types) for the complete > flow. ## Integration paths How you integrate ACE depends on where your contract is today: - **[Building a New Contract](/ace/guides/policy-manager/contracts/new-contract)** — Starting a new project? ACE provides audited reference implementations for ERC-20 and ERC-3643 tokens that come pre-integrated with PolicyProtected. This is the fastest path. - **[Upgrading Existing Contracts](/ace/guides/policy-manager/contracts/upgrade-existing)** — Already have a deployed contract behind a proxy? You can add ACE compliance through a standard proxy upgrade without disrupting existing state, balances, or integrations. - **Non-upgradeable contract?** — If your contract is not behind a proxy, the upgrade guide also covers [alternative approaches](/ace/guides/policy-manager/contracts/upgrade-existing#alternatives-for-non-upgradeable-contracts) — wrapped contracts, contract migration, edge protection, and [Active Monitoring](/ace/active-monitoring/overview), which reacts to risk changes without modifying the contract — each with different tradeoffs depending on your constraints. --- # Building a New Contract Source: https://docs.chain.link/ace/guides/policy-manager/contracts/new-contract Last Updated: 2026-03-31 If you are starting a new project, ACE provides audited reference implementations that come pre-integrated with PolicyProtected. You do not need to implement the ACE integration yourself — these contracts are ready to deploy and protect with policies. If you already have a deployed contract, see [Upgrading Existing Contracts](/ace/guides/policy-manager/contracts/upgrade-existing) instead. ## Reference implementations ACE offers two token implementations, each designed for different regulatory contexts: - **ComplianceTokenERC20** — A policy-protected ERC-20 token with advanced frozen token handling, force transfers, and mint/burn controls. - **ComplianceTokenERC3643** — A compliant implementation of the [ERC-3643 (T-REX)](https://eips.ethereum.org/EIPS/eip-3643) standard, using ACE Cross-Chain Identity instead of ONCHAINID and ACE Policy Management instead of T-REX ModularCompliance. Both implementations inherit from `PolicyProtectedUpgradeable` and must be deployed behind a proxy. During ACE Beta, deployment is managed through the ACE Platform. ## Choosing between ERC-20 and ERC-3643 The right choice depends on your regulatory requirements, the asset type you are tokenizing, and how you need frozen tokens to behave. | Aspect | ERC-20 Compliance Token | ERC-3643 Compliance Token | | ------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | **Standard** | ERC-20 | ERC-3643 (T-REX) | | **Regulatory context** | Broad — suitable for any token that needs policy-based compliance | Securities — designed for regulated securities and financial instruments | | **Identity system** | ACE Cross-Chain Identity | ACE Cross-Chain Identity (replaces ONCHAINID) | | **Compliance system** | ACE Policy Management | ACE Policy Management (replaces T-REX ModularCompliance) | | **Frozen token behavior** | Strict preservation — frozen tokens remain frozen during burns and force transfers | Operational flexibility — burns and force transfers can automatically unfreeze tokens when needed | | **Pause support** | No built-in pause (use a PausePolicy instead) | Built-in `pause`/`unpause` with `whenNotPaused` modifier | | **Batch operations** | No | Yes — batch transfer, mint, burn, freeze/unfreeze | | **CCIP admin** | `getCCIPAdmin()` returns the contract owner | Not included | ### Frozen token behavior explained The most significant difference between the two implementations is how frozen tokens are handled during administrative operations: **ERC-20 approach (strict preservation):** When an admin performs a burn or force transfer on an account with frozen tokens, the frozen balance is preserved. The operation only succeeds if the account has sufficient *unfrozen* balance. This means an admin must explicitly unfreeze tokens before they can be burned or force-transferred. **ERC-3643 approach (automatic unfreezing):** When an admin performs a burn or force transfer, the contract automatically unfreezes tokens if the unfrozen balance is insufficient. This follows the T-REX philosophy that administrative actions should not be blocked by frozen status — the admin has already decided the operation is necessary. ### When to choose each **Choose ERC-20** when: - You need a general-purpose compliant token without a specific regulatory framework requirement. - You want strict control over frozen tokens — every unfreeze must be an explicit administrative action. - You plan to integrate with CCIP for cross-chain transfers. **Choose ERC-3643** when: - You are tokenizing regulated securities and need compliance with the ERC-3643 standard. - Your regulatory framework requires or benefits from the T-REX interface (existing tooling, auditor familiarity). - You need batch operations for managing large numbers of holders efficiently. - You prefer operational flexibility for administrative actions on frozen tokens. ## Next steps - **[Building an ERC-20 Compliance Token](/ace/guides/policy-manager/contracts/erc20-token)** — Detailed guide for deploying and configuring the ERC-20 reference implementation. - **[Building an ERC-3643 Compliance Token](/ace/guides/policy-manager/contracts/erc3643-token)** — Detailed guide for deploying and configuring the ERC-3643 reference implementation. > **NOTE: Not building a token?** > > The reference implementations above are specifically for tokens. If you are building a different type of contract > (vault, DEX, lending protocol, etc.), you can make any contract ACE-compatible by following the [integration > requirements](/ace/guides/policy-manager/contracts/ace-compatible) — inherit from `PolicyProtected`, add `runPolicy` > to your functions, and connect to a PolicyEngine. For function signatures beyond ERC-20 and ERC-3643, you write and > register your own extractors. See [Custom Contract Types & > Extractors](/ace/guides/policy-manager/contracts/custom-contract-types). --- # Building an ERC-20 Compliance Token Source: https://docs.chain.link/ace/guides/policy-manager/contracts/erc20-token Last Updated: 2026-03-31 The `ComplianceTokenERC20` is a ready-to-deploy, policy-protected ERC-20 token provided as an ACE reference implementation. It inherits `PolicyProtectedUpgradeable`, routes every state-changing function through a PolicyEngine, and is designed for deployment behind a proxy. For a comparison with the ERC-3643 variant and guidance on which to choose, see [Building a New Contract](/ace/guides/policy-manager/contracts/new-contract#choosing-between-erc-20-and-erc-3643). ## What makes it ACE-compatible The token satisfies all the requirements described in [Making Your Contract ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible): 1. **Inherits `PolicyProtectedUpgradeable`** — The contract calls `__PolicyProtected_init` during initialization, which sets the contract owner and connects it to a PolicyEngine. 2. **All state-changing functions are policy-protected** — Every function that modifies balances, allowances, or frozen state carries the `runPolicy` or `runPolicyWithContext` modifier. The PolicyEngine evaluates all attached policies before the function body executes. 3. **ERC-7201 namespaced storage** — All token state lives in a dedicated `ComplianceTokenStoreERC20` storage struct, following the [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201) pattern for safe upgradeable storage. ## Protected functions Every state-changing function on the token is policy-protected. The [`runPolicy` modifier](/ace/concepts/policy-management#the-policy-execution-flow) intercepts each call and routes it through the PolicyEngine, which evaluates all attached policies before the function body executes. Functions that need to pass additional context (such as offchain signatures or metadata) use [`runPolicyWithContext`](/ace/concepts/policy-management#the-context-parameter) instead, which forwards a `bytes context` parameter to every policy in the chain. ### ERC-20 standard | Function | Modifier | Description | | -------------------------------- | ----------- | ---------------------------------------------------------------- | | `transfer(to, amount)` | `runPolicy` | Transfer tokens from the caller to another address. | | `transferFrom(from, to, amount)` | `runPolicy` | Transfer tokens on behalf of another address using an allowance. | | `approve(spender, amount)` | `runPolicy` | Set an allowance for a spender. | ### Minting and burning | Function | Modifier | Description | | ------------------------ | ----------- | ------------------------------------------------ | | `mint(to, amount)` | `runPolicy` | Create new tokens and assign them to an address. | | `burn(amount)` | `runPolicy` | Destroy tokens from the caller's balance. | | `burnFrom(from, amount)` | `runPolicy` | Destroy tokens from another address. | ### Administrative and compliance | Function | Modifier | Description | | ------------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------------ | | `freeze(account, amount, context)` | `runPolicyWithContext` | Freeze a specific amount of tokens on an account. Frozen tokens cannot be transferred or burned. | | `unfreeze(account, amount, context)` | `runPolicyWithContext` | Unfreeze a previously frozen amount on an account. | | `forceTransfer(from, to, amount, context)` | `runPolicyWithContext` | Administratively move tokens between accounts, subject to frozen balance checks. | > **NOTE: Context parameter** > > Functions that accept a `bytes context` parameter use `runPolicyWithContext`, which forwards the context to the > PolicyEngine. Policies can use this context for additional validation — for example, verifying an offchain signature > or passing metadata about the operation. See [The context > parameter](/ace/concepts/policy-management#the-context-parameter) for details on both methods of passing context. ## Frozen token behavior `ComplianceTokenERC20` uses a **strict preservation** model for frozen tokens: - **Available balance** = total balance - frozen balance. Every transfer, burn, and force transfer checks that the sender has sufficient *unfrozen* balance and reverts if not. - **No automatic unfreezing** — Frozen tokens remain frozen during all operations. An administrator must explicitly call `unfreeze` before those tokens can be moved or burned. - **Pre-freezing** — Tokens can be frozen on an account before they are received. The frozen amount is tracked independently from the balance, so an admin can set a frozen amount in advance and the restriction takes effect as soon as tokens arrive. This model provides maximum compliance control: every change to frozen status is an explicit, auditable administrative action. > **TIP: ERC-3643 handles this differently** > > The [ERC-3643 compliance token](/ace/guides/policy-manager/contracts/erc3643-token) uses automatic unfreezing — burns > and force transfers can proceed even if the unfrozen balance is insufficient, because the contract automatically > reduces the frozen amount. See [Building a New > Contract](/ace/guides/policy-manager/contracts/new-contract#frozen-token-behavior-explained) for a detailed > comparison. ## Storage layout All token state is stored in `ComplianceTokenStoreERC20`, which uses ERC-7201 namespaced storage at a deterministic slot: | Field | Type | Description | | ---------------- | ------------------------------------------------- | --------------------------------- | | `name` | `string` | Token name. | | `symbol` | `string` | Token symbol. | | `decimals` | `uint8` | Decimal precision for display. | | `totalSupply` | `uint256` | Total supply of tokens. | | `balances` | `mapping(address => uint256)` | Per-account token balances. | | `allowances` | `mapping(address => mapping(address => uint256))` | Per-account spender allowances. | | `frozenBalances` | `mapping(address => uint256)` | Per-account frozen token amounts. | | `data` | `mapping(bytes32 => bytes)` | Generic storage for extensions. | ## CCIP compatibility The contract exposes `getCCIPAdmin()`, which returns the contract owner. This enables integration with [Chainlink CCIP](/ccip) for cross-chain token transfers by identifying the admin authorized to configure the token's CCIP settings. ## Reference implementation The full source code for the ERC-20 compliance token: - [ComplianceTokenERC20.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/tokens/erc-20/src/ComplianceTokenERC20.sol) — Token contract with all protected functions and frozen token logic. - [ComplianceTokenStoreERC20.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/tokens/erc-20/src/ComplianceTokenStoreERC20.sol) — ERC-7201 namespaced storage layout. --- # Building an ERC-3643 Compliance Token Source: https://docs.chain.link/ace/guides/policy-manager/contracts/erc3643-token Last Updated: 2026-03-31 The `ComplianceTokenERC3643` implements the [ERC-3643 (T-REX)](https://eips.ethereum.org/EIPS/eip-3643) `IToken` interface but replaces the canonical T-REX identity and compliance systems with ACE equivalents. It inherits `PolicyProtectedUpgradeable`, is deployed behind a proxy, and routes all state-changing functions through a PolicyEngine. For a comparison with the ERC-20 variant and guidance on which to choose, see [Building a New Contract](/ace/guides/policy-manager/contracts/new-contract#choosing-between-erc-20-and-erc-3643). ## What makes it ACE-compatible The token satisfies all the requirements described in [Making Your Contract ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible): 1. **Inherits `PolicyProtectedUpgradeable`** — The contract calls `__PolicyProtected_init` during initialization, which sets the contract owner and connects it to a PolicyEngine. 2. **All state-changing functions are policy-protected** — Every function that modifies state carries the `runPolicy` modifier. The PolicyEngine evaluates all attached policies before the function body executes. 3. **ERC-7201 namespaced storage** — All token state lives in a dedicated `ComplianceTokenStoreERC3643` storage struct, following the [ERC-7201](https://eips.ethereum.org/EIPS/eip-7201) pattern for safe upgradeable storage. ## How it differs from canonical T-REX This implementation keeps the `IToken` interface that T-REX tooling and auditors expect, but swaps out the two internal subsystems for ACE equivalents: ### Identity: ACE Cross-Chain Identity replaces ONCHAINID The canonical T-REX stack uses ONCHAINID for on-chain identity claims. This implementation replaces it with ACE's [Cross-Chain Identity](/ace/concepts/cross-chain-identity) infrastructure (IdentityRegistry and CredentialRegistry). The legacy interface stubs remain to satisfy `IToken` but are not functional: - `identityRegistry()` returns `address(0)`. - `onchainID()` returns `address(0)`. - `setIdentityRegistry()` reverts with "Not implemented". - `setOnchainID()` reverts with "Not implemented". Identity verification is handled through ACE policies that validate credentials against the IdentityRegistry and CredentialRegistry. ### Compliance: ACE Policy Management replaces ModularCompliance The canonical T-REX stack uses `ModularCompliance` for transfer rules. This implementation replaces it with ACE's [Policy Management](/ace/concepts/policy-management) system, where compliance rules are defined as policies attached to the PolicyEngine. The legacy stub remains: - `compliance()` returns `address(0)`. - `setCompliance()` reverts with "Not implemented". ### Wallet recovery not implemented - `recoveryAddress()` reverts with "Not implemented". Wallet recovery is not supported in this implementation. ## Protected functions Every state-changing function on the token is policy-protected with [`runPolicy`](/ace/concepts/policy-management#the-policy-execution-flow), which intercepts each call and routes it through the PolicyEngine. The engine evaluates all attached policies before the function body executes. Functions that interact with user balances also carry the `whenNotPaused` modifier, which checks the token's pause state before proceeding. ### Transfers | Function | Modifiers | Description | | ---------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `transfer(to, amount)` | `whenNotPaused`, `runPolicy` | Transfer tokens from the caller to another address. Checks that neither wallet is frozen and that the sender has sufficient unfrozen balance. | | `transferFrom(from, to, amount)` | `whenNotPaused`, `runPolicy` | Transfer tokens on behalf of another address using an allowance. Same frozen and balance checks as `transfer`. | | `forcedTransfer(from, to, amount)` | `runPolicy` | Administrative transfer that auto-unfreezes tokens if the unfrozen balance is insufficient. | ### Allowances | Function | Modifiers | Description | | --------------------------------------------- | ---------------------------- | ------------------------------- | | `approve(spender, amount)` | `whenNotPaused`, `runPolicy` | Set an allowance for a spender. | | `increaseAllowance(spender, addedValue)` | `whenNotPaused`, `runPolicy` | Increase an existing allowance. | | `decreaseAllowance(spender, subtractedValue)` | `whenNotPaused`, `runPolicy` | Decrease an existing allowance. | ### Minting and burning | Function | Modifiers | Description | | --------------------------- | ----------- | ---------------------------------------------------------------------------------------------- | | `mint(to, amount)` | `runPolicy` | Create new tokens and assign them to an address. | | `burn(userAddress, amount)` | `runPolicy` | Destroy tokens from an address. Auto-unfreezes tokens if the unfrozen balance is insufficient. | ### Freezing | Function | Modifiers | Description | | -------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------- | | `setAddressFrozen(userAddress, freeze)` | `runPolicy` | Freeze or unfreeze an entire address. A frozen address cannot send or receive tokens through regular transfers. | | `freezePartialTokens(userAddress, amount)` | `runPolicy` | Freeze a specific amount of tokens on an account. | | `unfreezePartialTokens(userAddress, amount)` | `runPolicy` | Unfreeze a previously frozen amount on an account. | ### Token administration | Function | Modifiers | Description | | ------------------- | ----------- | ---------------------------------------------------------------- | | `pause()` | `runPolicy` | Pause the token. All functions with `whenNotPaused` will revert. | | `unpause()` | `runPolicy` | Unpause the token. | | `setName(name)` | `runPolicy` | Update the token name. | | `setSymbol(symbol)` | `runPolicy` | Update the token symbol. | ## Frozen token behavior `ComplianceTokenERC3643` uses an **automatic unfreezing** model, following the standard T-REX approach. There are two independent freeze mechanisms: - **Address freeze** — A boolean flag (`frozen[address]`) that blocks an address from sending or receiving tokens through regular `transfer` and `transferFrom` calls. - **Partial token freeze** — A numeric amount (`frozenTokens[address]`) that restricts how many of an account's tokens can be moved. Available balance = total balance - frozen tokens. Regular transfers check both: the wallet must not be address-frozen, and the transfer amount must not exceed the unfrozen balance. **Administrative operations auto-unfreeze.** When `forcedTransfer` or `burn` is called and the unfrozen balance is insufficient, the contract automatically reduces `frozenTokens` by the shortfall and emits a `TokensUnfrozen` event. This means administrative actions are never blocked by partial frozen status — the admin has already decided the operation is necessary. > **TIP: ERC-20 handles this differently** > > The [ERC-20 compliance token](/ace/guides/policy-manager/contracts/erc20-token) uses strict preservation — frozen > tokens remain frozen during all operations, and an admin must explicitly unfreeze before burning or > force-transferring. See [Building a New > Contract](/ace/guides/policy-manager/contracts/new-contract#frozen-token-behavior-explained) for a detailed > comparison. ## Built-in pause The token includes a built-in `pause`/`unpause` mechanism. Both functions are policy-protected. When paused, all functions carrying the `whenNotPaused` modifier revert — this includes `transfer`, `transferFrom`, `approve`, `increaseAllowance`, and `decreaseAllowance`. Administrative functions (`mint`, `burn`, `forcedTransfer`, freeze operations) do **not** carry `whenNotPaused` and remain callable while the token is paused. > **NOTE: ERC-20 uses a PausePolicy instead** > > The ERC-20 compliance token does not have a built-in pause mechanism. To add pause functionality to an ERC-20 token, > attach a PausePolicy to the relevant functions through the PolicyEngine. ## Batch operations The ERC-3643 token supports batch operations for managing large numbers of holders efficiently: - `batchTransfer` — Transfer to multiple recipients in a single transaction. - `batchForcedTransfer` — Force-transfer between multiple address pairs. - `batchMint` — Mint to multiple recipients. - `batchBurn` — Burn from multiple addresses. - `batchSetAddressFrozen` — Freeze or unfreeze multiple addresses. - `batchFreezePartialTokens` — Freeze token amounts on multiple accounts. - `batchUnfreezePartialTokens` — Unfreeze token amounts on multiple accounts. Each batch function delegates to its single-item counterpart in a loop, so every individual operation goes through `runPolicy` independently. ## Storage layout All token state is stored in `ComplianceTokenStoreERC3643`, which uses ERC-7201 namespaced storage at a deterministic slot: | Field | Type | Description | | --------------- | ------------------------------------------------- | --------------------------------- | | `tokenName` | `string` | Token name. | | `tokenSymbol` | `string` | Token symbol. | | `tokenDecimals` | `uint8` | Decimal precision for display. | | `tokenPaused` | `bool` | Whether the token is paused. | | `totalSupply` | `uint256` | Total supply of tokens. | | `balances` | `mapping(address => uint256)` | Per-account token balances. | | `allowances` | `mapping(address => mapping(address => uint256))` | Per-account spender allowances. | | `frozen` | `mapping(address => bool)` | Per-account address freeze flag. | | `frozenTokens` | `mapping(address => uint256)` | Per-account frozen token amounts. | ## Reference implementation The full source code for the ERC-3643 compliance token: - [ComplianceTokenERC3643.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/tokens/erc-3643/src/ComplianceTokenERC3643.sol) — Token contract implementing the `IToken` interface with ACE policy protection. - [ComplianceTokenStoreERC3643.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/tokens/erc-3643/src/ComplianceTokenStoreERC3643.sol) — ERC-7201 namespaced storage layout. --- # Upgrading Existing Contracts Source: https://docs.chain.link/ace/guides/policy-manager/contracts/upgrade-existing Last Updated: 2026-10-07 This guide explains how to add ACE compliance to a contract that is already deployed. The process is a standard proxy upgrade — your existing state (balances, allowances, mappings) is fully preserved, your contract address stays the same, and all existing integrations continue to work. > **NOTE: Plan ahead for production** > > ACE Beta is available on [supported mainnet and testnet networks](/ace/supported-networks), so most teams will [build > new contracts](/ace/guides/policy-manager/contracts/new-contract) to experiment before upgrading existing production > contracts. This guide helps you plan ahead: upgrading production contracts is straightforward with multiple paths > depending on your constraints. > **NOTE: Supported contract types** > > The platform provides pre-built extractors for ERC-20 and ERC-3643 function signatures. If you are upgrading a > contract with different function signatures, you write and deploy your own extractor contract and register it via the > Coordinator API. See [Custom Contract Types & Extractors](/ace/guides/policy-manager/contracts/custom-contract-types). ## Prerequisites Before starting, you should be familiar with: - [ACE Architecture](/ace/concepts/architecture) — how PolicyEngine, policies, and extractors work together - [Policy Management](/ace/concepts/policy-management) — the execution model and policy outcomes ### Your contract must be upgradeable This guide covers contracts deployed behind a proxy pattern — UUPS, Transparent Proxy, or Beacon Proxy. You need upgrade authority over the contract. If your contract is **not upgradeable**, see [Alternatives for non-upgradeable contracts](#alternatives-for-non-upgradeable-contracts) below. ## Key concept: Storage safety with ERC-7201 When upgrading a contract, new variables must not overwrite existing state. `PolicyProtectedUpgradeable` uses [ERC-7201 namespaced storage](https://eips.ethereum.org/EIPS/eip-7201), which isolates all ACE data in a deterministic storage slot that cannot collide with your existing storage layout. ```solidity bytes32 private constant STORAGE_LOCATION = keccak256(abi.encode(uint256(keccak256("chainlink.ace.PolicyProtected")) - 1)) & ~bytes32(uint256(0xff)); ``` This formula produces a storage location that is guaranteed not to overlap with Solidity's default sequential storage layout. Your existing balances, allowances, and other state remain untouched. ## Choosing your approach There are two ways to integrate ACE into an upgradeable contract: | Aspect | Approach 1: Extend PolicyProtectedUpgradeable | Approach 2: Implement IPolicyProtected | | ------------------------- | --------------------------------------------- | ------------------------------------------- | | **Bytecode impact** | +5-6 KB | +1-2 KB | | **Implementation effort** | Add inheritance + modifiers | Write storage, context, and execution logic | | **Maintenance** | Inherits ACE updates automatically | You maintain all custom code | | **Risk** | Lower — proven patterns | Higher — custom code means custom bugs | **Recommendation:** Use Approach 1 unless your contract is near the 24 KB bytecode limit or you need custom control over how context is stored or policies are executed. ## Approach 1: Extend PolicyProtectedUpgradeable (recommended) This approach inherits from `PolicyProtectedUpgradeable`, which provides built-in modifiers and automatic storage management. ### Step 1: Update contract inheritance Add `PolicyProtectedUpgradeable` to your inheritance chain. **Before:** ```solidity import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; contract MyToken is Initializable, ERC20Upgradeable, OwnableUpgradeable { // ... } ``` **After:** ```solidity import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {PolicyProtectedUpgradeable} from "@chainlink/policy-management/core/PolicyProtectedUpgradeable.sol"; contract MyToken is PolicyProtectedUpgradeable, ERC20Upgradeable, UUPSUpgradeable { // ... } ``` > **CAUTION: Inheritance conflict** > > `PolicyProtectedUpgradeable` already inherits from `Initializable` and `OwnableUpgradeable`. If your contract > explicitly lists these, remove them from your inheritance to avoid a "Linearization of inheritance graph impossible" > error. ### Step 2: Add a migration function Your original `initialize()` has already been called, so you cannot modify it. Instead, add a migration function using `reinitializer`: ```solidity function migrateToACE(address policyEngine) public reinitializer(2) onlyOwner { __PolicyProtected_init_unchained(policyEngine); } ``` `reinitializer(2)` ensures this migration runs exactly once (version 1 was your original `initialize()`). If you have had previous upgrades with reinitializers, increment the version accordingly. `__PolicyProtected_init_unchained()` stores the PolicyEngine address in namespaced storage and registers your contract with the PolicyEngine. > **NOTE: Where does the PolicyEngine address come from?** > > During ACE Beta, you receive the PolicyEngine address from the ACE Platform after Chainlink deploys your > infrastructure. Use this address as the `policyEngine` argument when calling `migrateToACE()`. If you need to switch to a different PolicyEngine later, call `attachPolicyEngine(newAddress)` (owner-only). ### Step 3: Add runPolicy to protected functions Add the `runPolicy` modifier to each function that should be subject to policy checks. **Before:** ```solidity function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } function transfer(address to, uint256 amount) public virtual override returns (bool) { return super.transfer(to, amount); } ``` **After:** ```solidity function mint(address to, uint256 amount) public runPolicy { _mint(to, amount); } function transfer(address to, uint256 amount) public virtual override runPolicy returns (bool) { return super.transfer(to, amount); } ``` Access control (restricting who can mint, for example) is now enforced through policies rather than traditional `onlyOwner` modifiers. This lets you change access rules by updating policies without upgrading the contract. For functions that need additional data passed to policies (signatures, proofs), use `runPolicyWithContext`: ```solidity function forceTransfer( address from, address to, uint256 amount, bytes calldata context ) public runPolicyWithContext(context) { _update(from, to, amount); } ``` ### Complete before/after example **Before (standard upgradeable ERC-20):** ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; contract MyToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable { constructor() { _disableInitializers(); } function initialize(address initialOwner) public initializer { __ERC20_init("MyToken", "MTK"); __Ownable_init(initialOwner); } function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} } ``` **After (with ACE integration):** ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {PolicyProtectedUpgradeable} from "@chainlink/policy-management/core/PolicyProtectedUpgradeable.sol"; contract MyToken is PolicyProtectedUpgradeable, ERC20Upgradeable, UUPSUpgradeable { constructor() { _disableInitializers(); } function initialize(address initialOwner) public initializer { __ERC20_init("MyToken", "MTK"); __Ownable_init(initialOwner); } function migrateToACE(address policyEngine) public reinitializer(2) onlyOwner { __PolicyProtected_init_unchained(policyEngine); } function mint(address to, uint256 amount) public runPolicy { _mint(to, amount); } function transfer(address to, uint256 amount) public virtual override runPolicy returns (bool) { return super.transfer(to, amount); } function transferFrom(address from, address to, uint256 amount) public virtual override runPolicy returns (bool) { return super.transferFrom(from, to, amount); } function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} } ``` **Key changes:** 1. Import and inherit `PolicyProtectedUpgradeable` (remove explicit `Initializable` and `OwnableUpgradeable` — they are inherited through `PolicyProtectedUpgradeable`). 2. Add `migrateToACE()` with `reinitializer(2)`. 3. Add `runPolicy` to functions that need policy protection. ## Approach 2: Implement IPolicyProtected (advanced) If your contract is near the 24 KB bytecode limit or you need custom control over policy execution, you can implement the `IPolicyProtected` interface directly instead of inheriting from `PolicyProtectedUpgradeable`. This adds only \~1-2 KB of bytecode but requires more code. ### What you must implement You are responsible for: 1. **Storage** — Storing the PolicyEngine address and per-sender context using ERC-7201 namespaced storage. 2. **Policy execution** — Calling `policyEngine.run()` with the correct payload in each protected function. 3. **Context handling** — Storing, retrieving, and clearing context data. 4. **Registration** — Attaching to and detaching from the PolicyEngine. 5. **ERC-165 support** — Implementing `supportsInterface()`. ### Interface methods ```solidity interface IPolicyProtected { function attachPolicyEngine(address policyEngine) external; function getPolicyEngine() external view returns (address); function setContext(bytes calldata context) external; function getContext() external view returns (bytes memory); function clearContext() external; } ``` | Method | Purpose | | -------------------- | ----------------------------------------------- | | `attachPolicyEngine` | Registers your contract with a PolicyEngine | | `getPolicyEngine` | Returns the current PolicyEngine address | | `setContext` | Stores context data for the next protected call | | `getContext` | Retrieves stored context for the current caller | | `clearContext` | Clears context after use to prevent replay | ### Implementation skeleton The following skeleton shows the key pieces for an ERC-20 token. It uses the same migration pattern as Approach 1, but all ACE logic is implemented manually. ```solidity import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {IPolicyProtected} from "@chainlink/policy-management/interfaces/IPolicyProtected.sol"; import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; contract MyToken is ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable, IPolicyProtected { // --- ERC-7201 Namespaced Storage --- struct ACEStorage { address policyEngine; mapping(address => bytes) senderContext; } // Replace with your calculated ERC-7201 storage slot bytes32 private constant ACE_STORAGE_LOCATION = 0x...; function _getACEStorage() private pure returns (ACEStorage storage $) { assembly { $.slot := ACE_STORAGE_LOCATION } } // --- Migration --- function migrateToACE(address policyEngine) public reinitializer(2) onlyOwner { _attachPolicyEngine(policyEngine); } // --- IPolicyProtected --- function attachPolicyEngine(address policyEngine) external onlyOwner { _attachPolicyEngine(policyEngine); } function _attachPolicyEngine(address policyEngine) internal { require(policyEngine != address(0), "Zero address"); ACEStorage storage $ = _getACEStorage(); $.policyEngine = policyEngine; IPolicyEngine(policyEngine).attach(); } function getPolicyEngine() public view returns (address) { return _getACEStorage().policyEngine; } function setContext(bytes calldata context) external { _getACEStorage().senderContext[msg.sender] = context; } function getContext() public view returns (bytes memory) { return _getACEStorage().senderContext[msg.sender]; } function clearContext() public { delete _getACEStorage().senderContext[msg.sender]; } function supportsInterface(bytes4 interfaceId) external pure returns (bool) { return interfaceId == type(IPolicyProtected).interfaceId || interfaceId == type(IERC165).interfaceId; } // --- Policy Execution --- function _runPolicy() internal { ACEStorage storage $ = _getACEStorage(); require($.policyEngine != address(0), "PolicyEngine not set"); bytes memory context = getContext(); IPolicyEngine($.policyEngine).run( IPolicyEngine.Payload({ selector: msg.sig, sender: msg.sender, data: msg.data[4:], context: context }) ); if (context.length > 0) { clearContext(); } } // --- Protected Functions --- function transfer(address to, uint256 amount) public virtual override returns (bool) { _runPolicy(); return super.transfer(to, amount); } // ... other protected functions follow the same pattern } ``` > **NOTE: ERC-7201 storage location** > > To calculate your storage slot, choose a unique namespace string (e.g., `"mycompany.mytoken.ace.storage"`) and apply > the ERC-7201 formula: `keccak256(abi.encode(uint256(keccak256("your.namespace")) - 1)) & ~bytes32(uint256(0xff))`. See > `PolicyProtectedUpgradeable.sol` in the [chainlink-ace repository](https://github.com/smartcontractkit/chainlink-ace) > for a working reference. ## Execute the upgrade At this point your updated implementation contract is ready. You need the PolicyEngine address to proceed. ### Pre-upgrade checklist **Development:** - Updated contract compiles successfully - Final bytecode is under 24 KB - Unit tests pass - Integration tests with PolicyEngine pass **Infrastructure:** - PolicyEngine address received from the ACE Platform (Beta) or deployed by your team (GA) ### Upgrade execution Deploy the new implementation, then execute the upgrade and migration in one transaction. The exact pattern depends on your proxy type: **UUPS:** ```solidity bytes memory data = abi.encodeCall(MyToken.migrateToACE, (policyEngineAddress)); MyToken(proxyAddress).upgradeToAndCall(newImplementationAddress, data); ``` **Transparent Proxy:** ```solidity bytes memory data = abi.encodeCall(MyToken.migrateToACE, (policyEngineAddress)); ProxyAdmin(proxyAdminAddress).upgradeAndCall(proxyAddress, newImplementationAddress, data); ``` **Beacon Proxy:** ```solidity // Beacon does not support upgradeAndCall — execute separately UpgradeableBeacon(beaconAddress).upgradeTo(newImplementationAddress); MyToken(proxyAddress).migrateToACE(policyEngineAddress); ``` ### Post-upgrade verification - `getPolicyEngine()` returns the correct address - Protected functions trigger policy checks - Policies allow and reject transactions as expected - Existing balances, allowances, and other state are unchanged ## Alternatives for non-upgradeable contracts If your contract is not deployed behind a proxy, a standard upgrade is not possible. Depending on your situation, there are four alternative approaches to bring ACE compliance to your application. The first three place ACE policies in front of your contract. The fourth, Active Monitoring, reacts after the fact and needs no policy integration. ### Wrapped contract Deploy a new ACE-compatible wrapper contract that sits in front of your original contract. Users interact with the wrapper, which enforces policies before delegating calls to the underlying contract. **How it works:** The wrapper inherits from `PolicyProtected` and exposes the same external interface as the original contract. Each function on the wrapper calls `runPolicy`, then forwards the call to the original contract. The original contract remains completely untouched. **When to use:** Your contract's logic does not need to change, but you need compliance checks on interactions with it. Works well for contracts where you can redirect user traffic to a new entry point. **Tradeoffs:** - The wrapper has a **different contract address**, so integrators (DEXs, lending protocols, front ends) must update their references. - If wrapping a token, users may need to **migrate balances** or **re-approve allowances** to the wrapper. - Adds a layer of indirection, which slightly increases gas costs per call. ### Contract migration Deploy a brand-new ACE-native contract and migrate state from the old contract to the new one. The new contract is built from scratch with `PolicyProtected` integrated from the start. **How it works:** You take a snapshot of the old contract's state (balances, allowances, roles, etc.) and seed the new contract with that data during deployment or through a claim-based migration. The old contract is then deprecated or paused. **When to use:** You want no wrapper indirection, no legacy contract to maintain. Particularly suited for tokens where a coordinated migration event is feasible (for example, a token swap or airdrop). **Tradeoffs:** - Requires a **coordinated migration event** — all holders and integrators must move to the new contract. - The new contract has a **different address**, which affects all downstream integrations. - Migration patterns (snapshot + airdrop, or claim-based redemption) add operational complexity. - The old contract must be handled (paused, drained, or deprecated) to prevent confusion. ### Edge protection Instead of modifying your contract, apply ACE policies at the integration points that interact with it — for example, a DEX pool, a bridge, or a lending protocol front end. **How it works:** The protected contract is not your original contract, but the integration layer. A DEX pool contract or a custom router contract inherits `PolicyProtected` and enforces compliance checks before interacting with your original token or vault. Your contract is never modified. **When to use:** Modifying the contract is not an option (immutable deployment, no migration path), and you can control the integration points where compliance matters. Works well when compliance is needed at specific boundaries rather than on every direct interaction. **Tradeoffs:** - **Does not protect direct contract interactions** — any user who calls your contract directly (bypassing the protected integration point) is not subject to policy checks. - Only covers the specific integration points where ACE is applied. Comprehensive coverage requires wrapping all relevant entry points. - The original contract's functionality is unchanged, which may be a regulatory concern if direct access remains open. ### Active Monitoring Screen the addresses you choose with TRM and react when their risk changes, using functions your contract already has, such as a freeze or a blocklist entry. **How it works:** You register the token and select its existing admin functions. ACE screens a watchlist on a schedule. When an address's risk level changes, a rule you define records it, flags it for review, or calls the function through your CRE Connect Wallet. The contract is not modified, and you grant the wallet an onchain role. **When to use:** You cannot change the contract, it has admin functions that restrict an address, and you want to react to holders whose risk changes after they receive tokens. **Tradeoffs:** - It is reactive, not preventive. It does not block a transaction: between a risk change and the next screening run, the address can still transact. - It needs an existing admin function and an onchain role for your CRE Connect Wallet. - It acts on the watchlist of addresses you maintain, and it does not apply ACE policies such as volume limits or credential checks to each transfer. See [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous) and the [Active Monitoring Quick Start](/ace/active-monitoring/quick-start). > **NOTE: Need guidance?** > > Each of these approaches involves architectural decisions specific to your contract, user base, and regulatory > requirements. [Contact the Chainlink team](https://chain.link/ace-early-access) to discuss which option fits your > situation. ## FAQ ### Will this upgrade overwrite my existing state? No. `PolicyProtectedUpgradeable` uses ERC-7201 namespaced storage, which stores ACE data in an isolated slot. Your existing balances, allowances, and all other state remain untouched. ### What happens to token balances and allowances? All state is preserved. The upgrade replaces the implementation contract (the code), but all state lives in the proxy's storage and is not affected. Users do not need to re-approve. ### What about tokens held in external contracts (DEXs, protocols)? Unaffected. Your contract address does not change, so all existing integrations continue working. The only difference is that transactions may revert if policies reject them. ### Can I protect only some functions? Yes. You only add `runPolicy` to the functions you want to protect. All other functions continue working normally without policy checks. ### Can I update policies after the upgrade? Yes. Policies can be added, removed, reordered, and reconfigured through the ACE Platform without touching your contract code. ### What if I need to switch to a different PolicyEngine? Call `attachPolicyEngine(newAddress)` (owner-only). This detaches the old engine and registers your contract with the new one. Once ACE is integrated, a PolicyEngine is always required — you cannot set it to the zero address. ### How many policies can I attach to a single function? The PolicyEngine supports up to 8 policies per function selector. ### My contract is near the 24 KB bytecode limit. What can I do? Use [Approach 2](#approach-2-implement-ipolicyprotected-advanced), which adds only \~1-2 KB. You can also enable the Solidity optimizer with higher runs, move logic to external libraries, or split functionality into separate contracts. --- # Custom Contract Types & Extractors Source: https://docs.chain.link/ace/guides/policy-manager/contracts/custom-contract-types Last Updated: 2026-09-23 Any contract that routes calls through a Policy Engine can use ACE policy enforcement. Most custom integrations inherit `PolicyProtected`, mark protected functions with the `runPolicy` modifier, and connect to a Policy Engine. The platform ships built-in ERC-20, ERC-3643, and `CCIP-AdvancedPoolHooks` contract types with pre-built, audited extractors. For function signatures beyond the pre-built set, you combine two building blocks: - A **custom contract type**: a declaration of your contract's functions (their ABIs) that you register with the Coordinator API. - A **custom extractor**: a contract you write and deploy that parses your function calldata into named parameters for policies. > **NOTE: When you need this** > > If your contract is a standard ERC-20, ERC-3643, or `AdvancedPoolHooks` contract, use its built-in type and pre-built > extractor. See the [Policy Manager Quick Start](/ace/getting-started/policy-manager#extractor-ids-for-api-creation) > for ERC-20 and ERC-3643, or [Protect CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools) for > `AdvancedPoolHooks`. Custom contract types are for contracts with additional or different function signatures, such as > a vault, lending pool, or token with a custom `mint` variant. ## Prerequisites - An [API key](/ace/getting-started/account-setup#3-create-an-api-key) for the Coordinator API. - An ACE-compatible contract with the functions you want to protect. - A development environment for compiling and deploying Solidity contracts (for example, Foundry or Hardhat). ## How it works A **contract type** describes what functions a contract has. An **extractor** parses those functions' calldata. When a protected function is called, the PolicyEngine looks up the extractor registered for that function's selector, calls `extract()`, and passes the named parameters to each attached policy. 1. You declare a custom contract type with your function ABIs (`POST /contract-types`). 2. You write an extractor contract implementing `IExtractor` and deploy it onchain. 3. You register the extractor with the Coordinator API (`POST /extractors`), pointing to the deployed contract address. 4. You attach the contract type and extractors to your [policy engine](/ace/guides/policy-manager/manage-engines) and [target](/ace/guides/policy-manager/manage-targets). ## The IExtractor interface Your extractor contract must implement [`IExtractor`](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/interfaces/IExtractor.sol) from the [chainlink-ace](https://github.com/smartcontractkit/chainlink-ace) repository: ```solidity // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; import {IExtractor} from "@chainlink/policy-management/interfaces/IExtractor.sol"; import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol"; contract MyVaultExtractor is IExtractor { string public constant override typeAndVersion = "MyVaultExtractor 1.0.0"; function extract(IPolicyEngine.Payload calldata payload) external pure returns (IPolicyEngine.Parameter[] memory) { // payload.selector: the function selector of the protected call // payload.sender: the transaction sender // payload.data: the raw calldata after the selector (address depositor, uint256 amount) = abi.decode(payload.data, (address, uint256)); IPolicyEngine.Parameter[] memory result = new IPolicyEngine.Parameter[](2); result[0] = IPolicyEngine.Parameter(keccak256("account"), abi.encode(depositor)); result[1] = IPolicyEngine.Parameter(keccak256("amount"), abi.encode(amount)); return result; } } ``` The `Payload` struct contains the raw call context, and `Parameter` is a named `bytes32`/`bytes` pair: ```solidity struct Payload { bytes4 selector; // function selector of the protected call address sender; // transaction sender bytes data; // calldata after the selector bytes context; // additional context (e.g., offchain signatures) } struct Parameter { bytes32 name; // parameter name (keccak256 hash) bytes value; // ABI-encoded value } ``` > **CAUTION: Parameter names must match your policy** > > Policies receive parameters by name. When you attach a policy to a protected function, the parameter names your > extractor produces must match the names that policy expects (for example, a volume limit policy reads `amount`). See > [Policy Management — the extractor and mapper > pattern](/ace/concepts/policy-management#the-extractor-and-mapper-pattern) and [Custom > Policies](/ace/guides/policy-manager/custom-policies) for how policies declare their expected parameters. For reference implementations, see the [pre-built extractors](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/policy-management/src/extractors) in the chainlink-ace repository. [`ERC20TransferExtractor`](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/extractors/ERC20TransferExtractor.sol) decodes `transfer` and `transferFrom` calldata into `from`, `to`, and `amount` parameters. ## Create a custom contract type Register your contract type with its function ABIs: ```bash curl -X POST https://ace.api.chain.link/v1/contract-types \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "name": "My Vault", "description": "Vault with deposit/withdraw compliance checks", "contract_type_functions": { "function_abis": [ { "type": "function", "name": "deposit", "inputs": [ { "name": "depositor", "type": "address" }, { "name": "amount", "type": "uint256" } ], "outputs": [], "state_mutability": "nonpayable" } ] }, "extractor_ids": [""] }' ``` | Field | Required | Description | | --------------------------------------- | -------- | ----------------------------------------------- | | `name` | Yes | Human-readable name of the contract type | | `description` | No | Optional description | | `contract_type_functions.function_abis` | Yes | Array of function ABIs the contract type covers | | `extractor_ids` | No | Extractors to associate with the contract type | Function ABIs support tuple types: use `type: "tuple"` or `"tuple[]"` with a `components` array describing each field, and `internal_type` for the Solidity internal type. To list contract types (yours plus the built-ins) or fetch one by ID: ```bash curl https://ace.api.chain.link/v1/contract-types \ -H "Authorization: Apikey " curl https://ace.api.chain.link/v1/contract-types/ \ -H "Authorization: Apikey " ``` `GET /contract-types` accepts a `policy_engine_id` filter to list only the types registered on a given engine. ## Register your extractor After deploying your extractor contract onchain, register it with the Coordinator API. The `onchain_extractors` array maps each deployment chain to the deployed contract address: ```bash curl -X POST https://ace.api.chain.link/v1/extractors \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "name": "MyVaultExtractor", "supported_function_signatures": ["deposit(address,uint256)"], "outputs": [ { "name": "account", "type": "address" }, { "name": "amount", "type": "uint256" } ], "onchain_extractors": [ { "chain_selector": "16015286601757825753", "address": "0xYourExtractorAddress" } ] }' ``` | Field | Required | Description | | ------------------------------- | -------- | --------------------------------------------------------- | | `name` | Yes | Human-readable name, unique per organization | | `supported_function_signatures` | Yes | Function signatures the extractor parses | | `outputs` | Yes | Named outputs the extractor produces, in order | | `onchain_extractors` | No | Per-chain deployment addresses of your extractor contract | > **NOTE: Extractor names are unique per organization** > > An active extractor name must be unique within your organization. If you re-register an updated version, archive the > old extractor first (`PATCH /extractors/{id}` with `{"status": "archived"}`) or use a new name. The `outputs` you declare must match what your contract's `extract()` returns: same names, same order. ## Attach the contract type to your engine and target When creating (or updating) your [policy engine](/ace/guides/policy-manager/manage-engines), pass `contract_type_ids` alongside `extractor_ids`: ```bash curl -X PUT https://ace.api.chain.link/v1/policy-engines/ \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "name": "My Policy Engine", "contract_type_ids": [""], "extractor_ids": [""], "onchain_policy_engines": [{ "chain_selector": "16015286601757825753" }] }' ``` When registering your [target](/ace/guides/policy-manager/manage-targets), pass `contract_type_ids` so the platform knows which contract type the target implements: ```bash curl -X POST https://ace.api.chain.link/v1/targets \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "title": "My Vault", "policy_engine_id": "", "contract_type_ids": [""], "protected_methods": ["deposit(address,uint256)"], "onchain_targets": [ { "chain_selector": "16015286601757825753", "address": "0xYourVaultAddress" } ] }' ``` From there, the flow is the same as for built-in types: [create policy instances](/ace/guides/policy-manager/manage-policies) and [attach them to your protected functions](/ace/guides/policy-manager/manage-protections) with `extractor_output_ids` mapping your extractor's outputs to the policy's parameters. ## Verify your setup - `GET /contract-types`: lists your custom types plus the built-ins; each type shows its functions and associated extractors. - `GET /extractors`: your registered extractor appears with its `assigned_contract_types`. - `GET /policy-engines/`: the engine response includes its `contract_types` and `extractor_registrations`. ## Archive a custom contract type Archive a contract type you no longer need with a PATCH request: ```bash curl -X PATCH https://ace.api.chain.link/v1/contract-types/ \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "status": "archived" }' ``` Chainlink maintains the built-in contract types (ERC-20, ERC-3643, and `CCIP-AdvancedPoolHooks`); you cannot archive them. ## Use the built-in CCIP type `CCIP-AdvancedPoolHooks` is a built-in contract type with a pre-built extractor for `preflightCheck` and `postflightCheck`. Do not register it as a custom contract type or create your own extractor for these functions. See [Protect CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools) to configure the type, connect `AdvancedPoolHooks`, use its automatically detected target, and map hook parameters to policies. --- # Security Considerations Source: https://docs.chain.link/ace/guides/policy-manager/contracts/security-considerations Last Updated: 2026-03-31 This page covers the implementation-level security concerns that developers should understand when integrating with ACE contracts — trust boundaries, external call risks, and defensive programming patterns. For governance-level security (administration controls, execution ordering, registry governance, privacy guarantees), see the [Security Model](/ace/concepts/security) concepts page. ## Trust model for policies and extractors The PolicyEngine delegates trust to the individual Policy, Extractor, and Mapper contracts it is configured to use. A vulnerability in any one of these components can compromise the entire system. ### Policy trust A malicious or poorly written policy can introduce vulnerabilities at two levels: - **The `run()` function** (read-only) — A policy that always returns Allow would bypass all subsequent policies. A policy that makes dangerous external calls could be exploited for denial of service. - **The `postRun()` function** (state-changing) — This function executes after a successful check and can modify onchain state. A malicious postRun could drain funds, change ownership, or corrupt state. Only install trusted, audited policies. ACE provides a library of pre-built, audited policies for common use cases. ### Extractor trust The PolicyEngine relies on Extractors to correctly and honestly parse transaction calldata. If an Extractor is compromised, it could misrepresent the data that policies use for their decisions. For example, an Extractor could report a false `value` for a transfer, causing a VolumePolicy to undercount and allow transactions that should be blocked. ### External call risks Many policies make external calls during execution — for example, querying a credential registry or checking an external data source. Since most policy `run()` functions are `view` (read-only), traditional reentrancy attacks are not possible. However, other risks apply: - **Denial of service** — A malicious external contract could revert or consume excessive gas, causing the entire policy chain to fail. - **Inconsistent reads** — External contract state could change between multiple calls within the same transaction. - **Gas exhaustion** — Deep call chains across multiple policies with external calls could exceed gas limits. For policies with state-changing functions (like `postRun()`), traditional reentrancy protections should be considered if those functions make external calls. **Mitigation:** Only interact with well-established, audited external contracts in policy logic. Implement proper error handling so that policies gracefully handle external contract failures rather than cascading reverts. ## Context handling and race conditions The [context parameter](/ace/concepts/policy-management#the-context-parameter) is a powerful feature for passing arbitrary data to policies, but it requires careful handling. When using the two-step method (calling `setContext` followed by the protected function), the context is stored per sender in the PolicyProtected contract. If context is set but not consumed in the same atomic transaction, stale context from a previous call could be reused. In contracts used by multiple senders (like relayers or governance contracts), one user's context could potentially be overwritten by another before it is consumed. **Mitigation:** Always set and consume context within the same atomic transaction. For contracts with multiple concurrent users, prefer the direct argument method (`runPolicyWithContext`) over the two-step approach. ## Non-reverting view functions All validator functions in the Cross-Chain Identity system — `validate()`, `validateCredentialData()`, and related view functions — must **never revert** under any circumstances. They must always return a boolean result. This is a critical reliability requirement. If a validator reverted during a policy check (for example, because an external call to a credential registry failed), it would break the entire policy chain for that transaction. The PolicyEngine would not be able to distinguish between "credential is invalid" and "validator is broken." Implementations must use defensive programming patterns: - Wrap external calls in try-catch blocks. - Return `false` on any external call failure rather than allowing the revert to propagate. - Validate all inputs before making external calls. This guarantees that the policy chain always completes and returns a definitive result, even when downstream dependencies fail. --- # Managing Policy Engines Source: https://docs.chain.link/ace/guides/policy-manager/manage-engines Last Updated: 2026-09-23 A **policy engine** is the on-chain orchestrator that evaluates policies whenever a protected function is called. Each policy engine is deployed as a smart contract on one or more chains, and all your targets, policies, and protections are scoped to a specific engine. For a deeper explanation of how policy engines fit into the architecture, see [Architecture](/ace/concepts/architecture) and [Policy Management](/ace/concepts/policy-management). > **NOTE** > > Before creating a policy engine, make sure you have completed [Account Setup](/ace/getting-started/account-setup) and > have a [CRE Connect Wallet](/ace/getting-started/account-setup#4-set-up-cre-connect-wallets) deployed on each chain > where you want the engine to operate. ## Create a policy engine ## View policy engines ## Update a policy engine You can update a policy engine's name, description, and extractor associations. ## Add or remove extractors Extractors decode transaction calldata into named parameters (sender, recipient, amount, etc.) so policies can evaluate them. If you forgot to attach an extractor during engine creation or need to remove one, use the `PUT /policy-engines/{id}` endpoint. > **CAUTION: Full replacement** > > The `extractor_ids` field in a PUT request **replaces all current extractors** — it is not additive. Always include > every extractor you want to keep. Omitting an extractor from the list detaches it from the engine. ### Add a missing extractor First, retrieve your engine to see which extractors are currently attached: ```bash curl https://ace.api.chain.link/v1/policy-engines/ \ -H "Authorization: Apikey " ``` Check the `extractor_registrations` array in the response. Then send a PUT request that includes the existing extractor IDs plus the new one: ```bash curl -X PUT https://ace.api.chain.link/v1/policy-engines/ \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "name": "My Policy Engine", "extractor_ids": [ "d4b8cd51-7d5a-487a-9ba5-bb7236e3184c", "572201bb-170b-4fda-ab89-a65b5bbc594b", "f17bbe8b-8462-4dd7-8fb7-a4973dff04fc" ], "onchain_policy_engines": [ { "chain_selector": "16015286601757825753" }, { "chain_selector": "3478487238524512106" }, { "chain_selector": "14767482510784806043" }, { "chain_selector": "16281711391670634445" }, { "chain_selector": "10344971235874465080" } ] }' ``` In this example, `f17bbe8b-8462-4dd7-8fb7-a4973dff04fc` is the new extractor being added alongside two that were already attached. ### Remove an extractor Send a PUT request with the `extractor_ids` array that **omits** the extractor you want to detach: ```bash curl -X PUT https://ace.api.chain.link/v1/policy-engines/ \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "name": "My Policy Engine", "extractor_ids": [ "d4b8cd51-7d5a-487a-9ba5-bb7236e3184c", "572201bb-170b-4fda-ab89-a65b5bbc594b" ], "onchain_policy_engines": [ { "chain_selector": "16015286601757825753" }, { "chain_selector": "3478487238524512106" }, { "chain_selector": "14767482510784806043" }, { "chain_selector": "16281711391670634445" }, { "chain_selector": "10344971235874465080" } ] }' ``` > **TIP: Available extractors** > > See the [Policy Manager Quick Start](/ace/getting-started/policy-manager#extractor-ids-for-api-creation) for ERC-20 > and ERC-3643 extractor IDs. For `CCIP-AdvancedPoolHooks`, see [Protect CCIP Token Pools with > ACE](/ace/guides/policy-manager/ccip-token-pools). You can retrieve every available extractor with `GET > https://ace.api.chain.link/v1/extractors`. ## Archive a policy engine Archiving a policy engine deactivates it and prevents any further operations. All policy instances associated with the engine must be archived first. > **CAUTION** > > All policy instances must be archived before the engine itself can be archived. Attempting to archive an engine with > active policies returns an error. ## Related pages - [Architecture](/ace/concepts/architecture) — how PolicyEngine contracts fit into the ACE system - [Policy Management](/ace/concepts/policy-management) — how policy chains and evaluation work - [Managing Targets](/ace/guides/policy-manager/manage-targets) — detect or register contracts to protect under an engine - [Managing Policies](/ace/guides/policy-manager/manage-policies) — create and configure policy instances - [Protect CCIP Token Pools with ACE](/ace/guides/policy-manager/ccip-token-pools) — configure a policy engine for CCIP AdvancedPoolHooks - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema --- # Managing Targets Source: https://docs.chain.link/ace/guides/policy-manager/manage-targets Last Updated: 2026-10-05 A **target** is a smart contract protected by ACE and associated with a Policy Engine. ACE can detect a target from its onchain attachment to the engine, or you can register it through the Coordinator API. Once the target appears under the engine, you can configure its [default policy result](#default-allow-behavior) and attach [policy instances](/ace/guides/policy-manager/manage-policies) to its functions through [protections](/ace/guides/policy-manager/manage-protections). > **NOTE** > > Before adding a target, connect the contract to a [deployed Policy Engine](/ace/guides/policy-manager/manage-engines). > Most contracts inherit `PolicyProtected` and use the `runPolicy` modifier. A contract can also call the Policy Engine > directly and call `attach()` when it connects, as `AdvancedPoolHooks` does. See [Making Your Contract > ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible) for both integration paths. > **NOTE: Targets and monitored tokens** > > A target is a contract protected by a Policy Engine. A [monitored > token](/ace/active-monitoring/concepts/monitored-tokens) in Active Monitoring is a different resource: it needs no > Policy Engine and no contract integration. ## Register a target When a contract calls `PolicyEngine.attach()`, ACE detects the onchain attachment and creates a target automatically. If ACE does not detect your integration, register the target with a `POST` request. Provide the contract name, type, protected methods, and onchain addresses: ```bash curl -X POST https://ace.api.chain.link/v1/targets \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "title": "My ERC-20 Token", "description": "Production ERC-20 token with compliance enforcement", "policy_engine_id": "", "protected_methods": [ "transfer(address,uint256)", "transferFrom(address,address,uint256)" #[any other methods you want to protect] ], "desired_default_allow": true, "metadata": {"contract_type": "ERC-20"}, "onchain_targets": [ { "chain_selector": "16015286601757825753", "address": "0xYourContractAddressOnSepolia" }, { "chain_selector": "3478487238524512106", "address": "0xYourContractAddressOnArbitrumSepolia" }, #[any other chains where your contract is deployed] ] }' ``` | Field | Required | Description | | ----------------------- | -------- | ------------------------------------------------------------- | | `title` | Yes | Human-readable name for the target | | `description` | No | Description of the contract | | `policy_engine_id` | Yes | UUID of the policy engine to associate with | | `protected_methods` | No | Array of function signatures that can be protected | | `contract_type_ids` | No | Array of contract type UUIDs the target implements | | `desired_default_allow` | No | Whether to allow transactions by default (default: `true`) | | `onchain_targets` | No | Array of objects with `chain_selector` and contract `address` | | `metadata` | No | Arbitrary JSON metadata (e.g., `{"contract_type": "ERC-20"}`) | ## Default allow behavior The **default policy result** controls what happens when a transaction passes through the entire policy chain and no policy explicitly returns Allow or Reject (i.e., every policy returns Continue). This is configured per target contract via the `desired_default_allow` field: - **`true` (default)** — The transaction is allowed. This is appropriate when you want policies to act as blockers (reject specific cases), and everything else passes through. - **`false`** — The transaction is rejected. This is appropriate for allowlist-style enforcement where only explicitly approved transactions proceed. For more on how policy evaluation ordering works, see [Policy Ordering & Composition](/ace/concepts/policy-ordering#the-default-result). ### Change the default policy result ## View targets ## Update a target You can update a target's name, description, contract type, protected methods, default allow behavior, and on-chain addresses. > **CAUTION: PUT is a full replacement** > > The `PUT /targets/{id}` endpoint **replaces the entire resource** — any field you omit is reset to its default (empty > string, empty array, or `true` for `desired_default_allow`). Always include every field you want to keep, not just the > ones you are changing. ## Link targets When a contract attaches to a Policy Engine on a new chain, the control plane creates a separate **detected target** (titled "unknown target"). Rather than managing each chain deployment as its own target, you can **merge** detected targets into an existing target to keep a single multi-chain target with all its onchain addresses in one place. ### Conditions A detected target can be merged (linked) into an existing target when: - The detected target was auto-discovered — it still has the default "unknown target" title. - The detected target has at least one on-chain address. - A valid destination target exists in the same policy engine: it must be a different target, already named, and deployed on a **different chain** than the source (no shared chain selectors). If no valid destination exists, the detected target cannot be merged — you can only rename it via **Edit details**. ### How it works Merging transfers the on-chain addresses from the source target(s) to the destination target, then archives the sources. After the merge, the destination target contains all chain deployments and any protections remain on the destination. > **CAUTION** > > Merging is irreversible. The source targets are archived and their on-chain addresses become part of the destination > target. Make sure you select the correct destination before confirming. ## Archive a target Archiving a target removes it from active use. All target protections associated with the target must be archived first. > **CAUTION** > > All target protections must be archived before the target itself can be archived. See [Protecting Target > Functions](/ace/guides/policy-manager/manage-protections) for how to archive protections. ## Related pages - [Making Your Contract ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible) — how to integrate `PolicyProtected` into your contract - [Managing Policy Engines](/ace/guides/policy-manager/manage-engines) — create the engine your target will use - [Managing Policies](/ace/guides/policy-manager/manage-policies) — create policy instances to attach to your target - [Protecting Target Functions](/ace/guides/policy-manager/manage-protections) — attach policies to specific functions on your target - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema --- # Managing Policies Source: https://docs.chain.link/ace/guides/policy-manager/manage-policies Last Updated: 2026-04-15 This guide covers how to browse available policy types, create policy instances, and configure their parameters. For attaching policies to specific functions on your contracts, see [Protecting Target Functions](/ace/guides/policy-manager/manage-protections). ## Policy implementations vs policy instances ACE distinguishes between two concepts: - **Policy implementation** — A reusable policy template (smart contract code) that defines specific compliance logic, such as an allowlist check or volume limit. ACE provides a [pre-built library](/ace/reference/policy-library) of audited implementations, and you can register your own [custom policy](/ace/guides/policy-manager/custom-policies) implementations, which are scoped to your organization. - **Policy instance** — A deployed copy of a policy implementation, configured with your specific parameters and associated with a [policy engine](/ace/guides/policy-manager/manage-engines). You create a policy instance from an implementation and then attach it to target functions via [protections](/ace/guides/policy-manager/manage-protections). For example, the "Allow List Policy" implementation can be instantiated multiple times with different allowlists. ## Browse policy implementations ## Create a policy instance A policy instance is created from a policy implementation and deployed on-chain within a policy engine. ## View and filter policies ## Update policy configuration After deploying a policy instance, you can update its on-chain configuration parameters — for example, adding an address to an allowlist or changing a volume threshold — without redeploying the policy. ## Archive a policy Archiving a policy instance removes it from active use. All target protections that reference this policy must be archived first. > **CAUTION** > > All target protections referencing this policy must be archived before the policy itself can be archived. See > [Protecting Target Functions](/ace/guides/policy-manager/manage-protections) for how to archive protections. ## Related pages - [Policy Management](/ace/concepts/policy-management) — how policy chains and evaluation work - [Policy Library](/ace/reference/policy-library) — pre-built policy implementations with configuration details - [Managing Policy Engines](/ace/guides/policy-manager/manage-engines) — create the engine your policies belong to - [Protecting Target Functions](/ace/guides/policy-manager/manage-protections) — attach policy instances to target functions - [Policy Ordering & Composition](/ace/concepts/policy-ordering) — how to compose effective rulesets - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema --- # Protecting Target Functions Source: https://docs.chain.link/ace/guides/policy-manager/manage-protections Last Updated: 2026-09-23 A **target protection** is the link between a [policy instance](/ace/guides/policy-manager/manage-policies) and a specific function on a [target contract](/ace/guides/policy-manager/manage-targets). When a user calls the protected function, the policy engine evaluates the bound policies in order and decides whether to allow or reject the transaction. Target protections are the final step in setting up on-chain compliance enforcement. ## Prerequisites Before creating a target protection, you need: 1. A [policy engine](/ace/guides/policy-manager/manage-engines) deployed on your target chains. 2. A [target contract](/ace/guides/policy-manager/manage-targets) detected or registered under that engine. 3. A [policy instance](/ace/guides/policy-manager/manage-policies) created from a policy type and associated with the same engine. 4. Extractors attached to the engine that support the function signatures you want to protect (see the [Policy Manager Quick Start](/ace/getting-started/policy-manager#extractor-ids-for-api-creation) for the full list). ## Create a target protection A protection binds a policy instance to a specific function on your target contract. Once created, every call to that function is evaluated against the policy. ## Position and evaluation order The `desired_position` determines the order in which policies are evaluated when a protected function is called: - **Position 0** is evaluated first. - Policies are evaluated sequentially. If a policy returns **Reject**, the transaction is reverted immediately and remaining policies are not evaluated. - If all policies return **Allow**, or if no policy explicitly rejects, the `desired_default_allow` setting on the [target](/ace/guides/policy-manager/manage-targets#default-allow-behavior) determines the outcome. For detailed information on composing effective rulesets, see [Policy Ordering & Composition](/ace/concepts/policy-ordering). ## View protections ## Manage an existing protection From the **Functions** or **Policies** view on your target contract (see [View protections](#view-protections) above), click on a policy instance to open a detail drawer. From there you can: - **Detach policy** — removes the protection so the policy no longer evaluates this function (see [Archive a protection](#archive-a-protection) below). - **Edit instance** — opens the policy instance configuration (see [Update policy configuration](/ace/guides/policy-manager/manage-policies#update-policy-configuration)). ### Extend a protection to additional chains If you created a protection on one chain and later want it to apply on additional chains, you can extend it via the API. ### Archive a protection Archiving a protection unbinds the policy from the function. Once archived, the policy no longer evaluates transactions on that function. > **NOTE** > > Protections must be archived before their parent [policy](/ace/guides/policy-manager/manage-policies#archive-a-policy) > or [target](/ace/guides/policy-manager/manage-targets#archive-a-target) can be archived. Always archive protections > first in the dependency chain. ## Archival dependency chain ACE enforces an ordered archival flow. You must archive resources from the outside in: 1. **Target protections** — archive these first 2. **Policy instances** — archive after all protections referencing them are archived 3. **Targets** — archive after all protections on the target are archived 4. **Policy engines** — archive after all policies in the engine are archived ## Related pages - [Policy Ordering & Composition](/ace/concepts/policy-ordering) — how evaluation order affects transaction outcomes - [Managing Policy Engines](/ace/guides/policy-manager/manage-engines) — create the engine that manages policies - [Managing Targets](/ace/guides/policy-manager/manage-targets) — detect or register contracts to protect - [Managing Policies](/ace/guides/policy-manager/manage-policies) — create and configure policy instances - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema --- # Managing Data Validators Source: https://docs.chain.link/ace/guides/policy-manager/manage-data-validators Last Updated: 2026-07-17 A **Data Validator** is an on-chain contract that inspects the **contents** of a credential — not just whether it exists. Attaching a Data Validator to an identity-validation policy lets you enforce rules on credential data, such as "only allow investors whose credential says they are in the US or Canada" or "reject any account whose credential country is on a sanctions list". This guide covers creating, configuring, and attaching Data Validators. For the credential side of the workflow — linking a data schema to a credential type and issuing credentials with data — see [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types#typed-credentials-with-data-schemas) and [Managing Credentials](/ace/guides/identity-manager/manage-credentials#issue-a-credential-with-data). > **NOTE: Two halves of one feature** > > Credential data has a **producer** and a **consumer**. The credential issuer produces the data (Identity Manager: > attach a data schema to a credential type, issue credentials with values). The application consumes it (Policy > Manager: attach a Data Validator to a policy's credential source). This page covers the consumer side. ## How Data Validators fit in Identity-validation policies — the [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) and the [GroupedIdentityValidatorPolicy](/ace/reference/policy-library/grouped-identity-validator-policy) — resolve a caller's address to a CCID and check credentials from **credential sources**. Each credential source can optionally reference a Data Validator. When a credential source has a Data Validator configured, the policy performs an extra step at transaction time: 1. Resolve the account's CCID and confirm the credential exists (attestation check). 2. Fetch the credential's stored `credentialData`. 3. Call the Data Validator's `validateCredentialData(...)`, which returns `true` or `false`. The credential passes only if **both** the attestation check and the data check succeed. Without a Data Validator, the source is attestation-only — it confirms the credential exists but ignores its contents. > **NOTE: Data Validator vs. data routing** > > A Data Validator checks whether a credential's contents are acceptable for a **requirement**. This is different from > the [GroupedIdentityValidatorPolicy](/ace/reference/policy-library/grouped-identity-validator-policy)'s **data > routing**, which uses credential contents to decide **which group** an account belongs to. Both read `credentialData`, > but they serve different purposes and can be combined. ## The AllowDenyList Data Validator ACE provides a pre-built, audited Data Validator implementation: the **AllowDenyList Data Validator**. It validates a credential payload against an **allowlist** and a **denylist**, with an optional restriction by credential type. Its rules are: - If the **denylist** contains any value present in the credential, validation **fails**. - If the **allowlist** is non-empty, at least one value in the credential must be allowlisted; otherwise validation **fails**. - If the allowlist is empty, the allow check passes (deny-only mode). The first use case shipped on top of this implementation is **jurisdiction control** using [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes (e.g., `US`, `CA`, `GB`). The country codes are the values checked against the allow and deny lists. > **TIP: Generic building block** > > The AllowDenyList Data Validator works with any `bytes32` values, not only country codes. The jurisdiction use case is > a convention layered on top: country codes are encoded as `bytes32` and matched against the lists. ## Prerequisites Before creating a Data Validator: 1. A [policy engine](/ace/guides/policy-manager/manage-engines) deployed on your target chains. 2. A credential type linked to a **data schema** so its credentials carry data — for the jurisdiction use case, the ISO 3166-1 alpha-2 country code schema. See [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types#typed-credentials-with-data-schemas). 3. Credentials issued **with data** against that credential type. See [Managing Credentials](/ace/guides/identity-manager/manage-credentials#issue-a-credential-with-data). ## Create a Data Validator A **Data Validator instance** is a deployed copy of a Data Validator implementation (such as the AllowDenyList country-code validator), configured with your specific allow and deny lists and scoped to one or more chains — the same shape as a policy instance. The AllowDenyList (country codes) Data Validator implementation ID is: ```text 2aed366a-38af-4f48-b8e2-8fd1489db9fa ``` Create a Data Validator instance with a `POST` request. Provide the implementation ID and, for each chain, the `initial_config` with your allow and deny lists: ```bash curl -X POST https://ace.api.chain.link/v1/data-validators \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "name": "Jurisdiction allow/deny", "description": "Allow US and CA, deny KP", "data_validator_implementation_id": "2aed366a-38af-4f48-b8e2-8fd1489db9fa", "onchain_data_validators": [ { "chain_selector": "16015286601757825753", "initial_config": { "allowlist": [{ "item": "US" }, { "item": "CA" }], "denylist": [{ "item": "KP" }], "supportedDataTypes": [] } } ] }' ``` | Field | Required | Description | | ---------------------------------- | -------- | ------------------------------------------------------------------------- | | `name` | Yes | Human-readable name for the instance | | `description` | No | Description of the instance's purpose | | `data_validator_implementation_id` | Yes | UUID of the Data Validator implementation to instantiate | | `onchain_data_validators` | Yes | Array of per-chain deployments with `chain_selector` and `initial_config` | Each on-chain Data Validator starts in `creation_pending` status until deployment completes. The response includes the instance `id` and the on-chain addresses per chain. > **NOTE: Config fields** > > `allowlist` and `denylist` are country codes. `supportedDataTypes` optionally restricts the validator to specific > credential type hashes — when non-empty, the validator returns `false` for any other credential type. Leave it empty > to accept any credential type routed to it. ## Update a Data Validator configuration You can update the allow and deny lists after deployment without redeploying the validator. Configuration changes use JSON Patch and are version-checked per chain for optimistic concurrency. Update the configuration with a `PATCH` request. Supply `on_chains` with the `current_config_version` for each chain you are changing: ```bash curl -X PATCH https://ace.api.chain.link/v1/data-validators//configs \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "patches": [ { "op": "add", "path": "/allowlist/-", "value": "GB" } ], "on_chains": [ { "chain_selector": "16015286601757825753", "current_config_version": "0" } ] }' ``` The JSON Patch format follows [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902). If the `current_config_version` does not match the on-chain state, the request is rejected — re-fetch the instance and retry with the current version. ## Attach a Data Validator to a credential source A Data Validator takes effect only when it is referenced by a **credential source** on an identity-validation policy. Each credential source has a `dataValidator` field: - `0x0000000000000000000000000000000000000000` — attestation-only (default). The source checks only that the credential exists. - A Data Validator address — the source additionally validates credential contents through that validator. Set the `dataValidator` field to your deployed Data Validator address when configuring the credential source on your [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) or [GroupedIdentityValidatorPolicy](/ace/reference/policy-library/grouped-identity-validator-policy) instance. See [Managing Policies — Update policy configuration](/ace/guides/policy-manager/manage-policies#update-policy-configuration) for how to change a policy instance's configuration. > **CAUTION: Match the validator to the credential's chain** > > Credential sources are configured per network with on-chain addresses. Use the Data Validator address deployed on the > same chain as the credential source's identity and credential registries. ## View Data Validators List all Data Validators: ```bash curl https://ace.api.chain.link/v1/data-validators \ -H "Authorization: Apikey " ``` | Parameter | Description | | ---------------------------------- | ------------------------------------ | | `page` | Page number (default: 1) | | `page_size` | Results per page | | `include_onchains` | Include per-chain deployment details | | `data_validator_implementation_id` | Filter by implementation | | `chain_selector` | Filter by chain | | `address` | Filter by on-chain address | | `status` | Filter by on-chain status | To retrieve a specific Data Validator by ID: ```bash curl https://ace.api.chain.link/v1/data-validators/ \ -H "Authorization: Apikey " ``` ## Archive a Data Validator Archiving a Data Validator deactivates the instance. Before archiving, detach it from any credential source that references it (set that source's `dataValidator` back to the zero address). Archive a Data Validator with a `PATCH` request: ```bash curl -X PATCH https://ace.api.chain.link/v1/data-validators/ \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "status": "archived" }' ``` ## Related pages - [Cross-Chain Identity — Credential data and privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy) — attestation-only vs. Data Validator checks - [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types) — link a data schema to a credential type - [Managing Credentials](/ace/guides/identity-manager/manage-credentials) — issue credentials with data - [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) — the policy that consumes Data Validators via credential sources - [GroupedIdentityValidatorPolicy](/ace/reference/policy-library/grouped-identity-validator-policy) — grouped identity validation with routing and Data Validators - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema --- # Protect CCIP Token Pools with ACE Source: https://docs.chain.link/ace/guides/policy-manager/ccip-token-pools Last Updated: 2026-09-23 Use the built-in `CCIP-AdvancedPoolHooks` contract type to apply ACE policies to cross-chain token transfers. You can evaluate a transfer before the source Token Pool locks or burns tokens and before the destination Token Pool releases or mints tokens. The built-in contract type defines the two supported hook functions and includes a pre-built extractor for their calldata. You do not need to create a custom contract type or extractor. It does not deploy or configure `AdvancedPoolHooks` for you. > **NOTE: EVM support** > > ACE currently supports EVM chains. This integration applies ACE policies to `AdvancedPoolHooks` and Token Pool > deployments on EVM chains. ## Before you begin If you are new to ACE Policy Manager, start with the [Policy Manager Quick Start](/ace/getting-started/policy-manager). It introduces Policy Engines, targets, policy types, policy instances, and protections. To understand how extractors convert calldata into named parameters for policies, read [Policy Management Concepts](/ace/concepts/policy-management#the-extractor-and-mapper-pattern). For the broader CCIP architecture, supported capabilities, and hook lifecycle, read [Advanced Pool Hooks](/ccip/concepts/cross-chain-token/advanced-pool-hooks). You should also understand how [CCIP Token Pools](/ccip/concepts/cross-chain-token/evm/token-pools) lock or burn tokens on the source chain and release or mint tokens on the destination chain. For each EVM chain where you want to enforce policies, you need: - An ACE-enabled organization on the [Chainlink Platform](https://app.chain.link) and a completed [ACE account setup](/ace/getting-started/account-setup). - A Policy Engine deployed on the chain. - An `AdvancedPoolHooks` contract deployed on the chain. - The Token Pool on that chain configured to use the hook and included in the hook's authorized callers. - The onchain Policy Engine address for that chain. For source-chain enforcement, complete these prerequisites on the source chain. For destination-chain enforcement, complete them on the destination chain. Complete them on both chains if you want both checks. ## How the integration works At runtime, each cross-chain token transfer follows this call path: `TokenPool` → `AdvancedPoolHooks` → `PolicyEngine` → extractor and policies 1. The source or destination Token Pool calls `preflightCheck` or `postflightCheck` on its configured `AdvancedPoolHooks` contract. 2. `AdvancedPoolHooks` sends the call data to the Policy Engine deployed on the same chain. 3. The pre-built extractor converts the hook calldata into named parameters such as `from`, `to`, `amount`, and `remote_chain_selector`. 4. The Policy Engine maps the required parameters to each policy and evaluates the policies in their configured order. 5. If a policy rejects the call, the hook reverts and prevents the Token Pool from completing that operation. The ACE target is the `AdvancedPoolHooks` contract, not the Token Pool. The Token Pool is an authorized caller of the hook. When `AdvancedPoolHooks` connects to a Policy Engine, it calls `attach()` on that engine. ACE detects this onchain attachment and automatically adds the hook contract as a target under the engine. Configure the integration separately on each EVM chain. Deploy an `AdvancedPoolHooks` contract and a Policy Engine on each source or destination chain where you want to enforce policies. You can apply different policies in each direction. ## Choose where to enforce policies | Hook | Direction | Enforcement chain | Runs | Result when a policy rejects | | ----------------- | --------- | ----------------- | ---------------------------------------------- | --------------------------------------------------------------------------- | | `preflightCheck` | Outbound | Source | Before the Token Pool locks or burns tokens | The source transaction reverts, so the cross-chain transfer does not start. | | `postflightCheck` | Inbound | Destination | Before the Token Pool releases or mints tokens | Destination execution fails before the recipient receives the tokens. | ### Source-chain enforcement Use `preflightCheck` to reject a transfer before it leaves the source chain. This avoids starting a transfer that already violates sender, token, amount, or destination-chain requirements. ### Destination-chain enforcement Use `postflightCheck` to enforce recipient or destination-specific requirements before tokens reach the recipient. If a policy rejects the call, the tokens remain unreleased or unminted. After resolving the rejection condition, use the standard CCIP recovery process to retry the message. You can protect either hook or both. Protecting both provides independent enforcement at the source and destination. ## Configure ACE ### Create a policy engine Follow [Create a policy engine](/ace/guides/policy-manager/manage-engines#create-a-policy-engine) for the complete Platform UI and API procedures. For this integration: 1. Include every EVM chain where you want to enforce a hook policy. 2. Wait until the engine is **Active**, then copy its onchain address for each chain. A Policy Engine can manage targets of different contract types. You assign the `CCIP-AdvancedPoolHooks` type to the detected hook target later in this workflow, not to the Policy Engine itself. ### Connect AdvancedPoolHooks to the policy engine Configure each `AdvancedPoolHooks` deployment with the Policy Engine address from the same chain. You can set the address when you deploy the hook or the hook owner can call `setPolicyEngine` afterward. When the hook connects to the engine, it calls `PolicyEngine.attach()`. This onchain attachment starts ACE target detection. Do not register the hook manually as a second target. ### Verify the detected target 1. In the [Chainlink Platform](https://app.chain.link), go to **Compliance > Policy Manager** and open the Policy Engine. 2. In the **Contracts** tab, find the target whose address matches your `AdvancedPoolHooks` deployment. 3. Open the target and assign the built-in `CCIP-AdvancedPoolHooks` contract type. This type provides the supported hook functions and their pre-built extractor. 4. Verify that: - **Type** is `CCIP-AdvancedPoolHooks`. - **Functions** lists `preflightCheck` and `postflightCheck`. - The onchain address and network match the hook deployment you configured. ACE can detect separate targets for hook deployments on different chains. Use [Managing Targets](/ace/guides/policy-manager/manage-targets) only when you need to view, rename, or link those detected targets. ### Create a policy instance Follow [Create a policy instance](/ace/guides/policy-manager/manage-policies#create-a-policy-instance). Create the instance under the same Policy Engine and on the same chain as the hook function you want to protect. The examples below use the `reject` policy type. Configure the policy instance with the EVM address you want to deny. ### Protect a hook function Follow [Create a target protection](/ace/guides/policy-manager/manage-protections#create-a-target-protection) for the generic attachment procedure. On the detected `AdvancedPoolHooks` target, select the hook function for the direction you want to protect and map the policy parameter to an output available for that function. The Platform creates the protection only on chains where the target and policy instance are both deployed. Confirm that the selected chain matches the hook deployment. ## Extracted parameters The built-in extractor exposes a separate set of outputs for each hook. When you protect a function, map only outputs listed for that function to the policy parameters. ### Preflight parameters `preflightCheck` exposes these seven outputs for outbound transfers: | Output | Type | Value | | ----------------------- | --------- | -------------------------------------------------------------------- | | `from` | `address` | Original sender that initiated the transfer | | `to` | `address` | Recipient on the destination chain | | `amount` | `uint256` | Transfer amount in the source token denomination, before pool fees | | `amount_post_fee` | `uint256` | Transfer amount after the source Token Pool deducts its transfer fee | | `remote_chain_selector` | `uint64` | Destination chain selector | | `token` | `address` | Token address on the source chain | | `requested_finality` | `bytes4` | Requested finality configuration | ### Postflight parameters `postflightCheck` exposes these nine outputs for inbound transfers: | Output | Type | Value | | --------------------------- | --------- | --------------------------------------------------------------- | | `from` | `address` | Original sender on the source chain | | `to` | `address` | Recipient on the destination chain | | `amount` | `uint256` | Amount to release or mint in the destination token denomination | | `remote_chain_selector` | `uint64` | Source chain selector | | `token` | `address` | Token address on the destination chain | | `requested_finality` | `bytes4` | Requested finality configuration | | `source_pool_address` | `address` | Token Pool address on the source chain | | `source_pool_data` | `bytes` | Data supplied by the source Token Pool | | `source_denominated_amount` | `uint256` | Transfer amount in the source token denomination | `remote_chain_selector` identifies the destination chain during preflight and the source chain during postflight. Similarly, `amount` uses the source token denomination during preflight and the destination token denomination during postflight. `amount_post_fee` exists only during preflight. `source_pool_address`, `source_pool_data`, and `source_denominated_amount` exist only during postflight. CCIP transports remote addresses as `bytes` because the protocol supports different address formats. For this EVM-only ACE integration, the built-in contract type exposes `from`, `to`, and `source_pool_address` as `address` outputs so you can map them directly to address-based policy parameters. > **CAUTION: Map the transfer sender from the extractor** > > The hook sets the ACE payload sender to `msg.sender`. Because the Token Pool calls the hook, this value identifies the > Token Pool, not the user who initiated the transfer. To evaluate the user, map the extracted `from` output to the > policy parameter. A policy that relies only on payload sender semantics evaluates the Token Pool instead. ## Example policy mappings ### Restrict source senders To reject a specific address before its transfer leaves the source chain, configure this protection: | Setting | Selection | | ---------------- | ------------------------------------------------------- | | Policy type | [`reject`](/ace/reference/policy-library/reject-policy) | | Function | `preflightCheck` | | Policy parameter | `Account (address)` | | Extractor output | `from` | If `from` appears in the [Reject policy](/ace/reference/policy-library/reject-policy) instance's denylist, the source transaction reverts before the Token Pool locks or burns tokens. A sender that does not appear in the denylist proceeds unless another policy rejects the transfer. ### Enforce destination-controlled recipient restrictions Both hooks expose `to`. If you control the source-chain policy and want to reject a recipient as early as possible, map `to` to a Reject policy on `preflightCheck`. This prevents the transfer from starting and avoids sending a CCIP message that will fail at the destination. Use `postflightCheck` when the destination must enforce its own recipient policy independently of its source chains. For example, one destination policy can protect transfers from multiple source chains or apply a denylist update to a message that is already in transit. To enforce that destination-controlled check, configure this protection: | Setting | Selection | | ---------------- | ------------------- | | Policy type | `reject` | | Function | `postflightCheck` | | Policy parameter | `Account (address)` | | Extractor output | `to` | If `to` appears in the destination Reject policy instance's denylist, destination execution fails before the Token Pool releases or mints tokens. After resolving the restriction, you must retry the failed message through the standard CCIP recovery process. Use this postflight check as an independently governed destination control or as defense in depth, not as a substitute for preflight rejection when your goal is to stop the transfer at its source. > **CAUTION: Use outputs from the selected hook** > > `source_pool_address` is available only for `postflightCheck`. Do not map it to a policy on `preflightCheck`, even if > the current Platform menu displays it. Use the preflight and postflight tables above as the source of truth. ## Verify the integration - The Policy Engine is **Active** on every chain where you want enforcement. - Each source or destination Token Pool points to the expected hook and appears in that hook's authorized callers. - Each hook points to the Policy Engine deployed on the same chain. - The engine's **Contracts** tab shows the hook address as a detected target. - The detected target uses the `CCIP-AdvancedPoolHooks` contract type and its pre-built extractor. - The target lists both hook functions. - Each policy instance and protection uses the intended chain, function, and extractor output, and its onchain status is active. - A transfer involving the restricted address fails at the intended enforcement point. - A comparable transfer involving an unrestricted address succeeds. ## Security considerations - Authorize only the Token Pool contracts that should call each hook. Otherwise, an unexpected contract could invoke policy evaluation with fabricated hook calldata. - Protect the Token Pool owner and hook owner accounts. The Token Pool owner can replace or remove the hook, and the hook owner can replace the Policy Engine. - Do not set the hook's Policy Engine to the zero address unless you intend to disable ACE policy evaluation. - Test source and destination protections independently. A successful preflight check does not guarantee that the destination policy will accept the transfer. ## Next steps - [Enforce ACE policies on CCIP token transfers using Foundry](/ccip/evm/tutorials/cross-chain-tokens/enforce-ace-policies-foundry) or [Hardhat](/ccip/evm/tutorials/cross-chain-tokens/enforce-ace-policies-hardhat): connect hooks to Policy Engines on a working CCT lane and exercise preflight and postflight rejections with live transfers. - [Manage policy instances](/ace/guides/policy-manager/manage-policies) - [Review or update target protections](/ace/guides/policy-manager/manage-protections) - [Plan policy ordering and composition](/ace/concepts/policy-ordering) - [Review smart contract integration security](/ace/guides/policy-manager/contracts/security-considerations) --- # Custom Policies Source: https://docs.chain.link/ace/guides/policy-manager/custom-policies Last Updated: 2026-07-17 In addition to the [pre-built Policy Library](/ace/reference/policy-library), you can write and deploy **your own policy contract** and register it with the ACE Platform. Once registered, a custom policy behaves exactly like a library policy — you create instances of it, configure them, and attach them to protected functions. A custom policy implementation is **private to your organization**: it appears in your Policy Manager alongside the global library, but other organizations do not see it. > **NOTE: You own the contract** > > With a custom policy, your organization writes, deploys, and owns the policy contract. The ACE Platform references and > orchestrates it, but the security of the policy is your responsibility. Read [Security > Considerations](/ace/guides/policy-manager/contracts/security-considerations) before deploying one. ## How it fits together A custom policy follows the same [implementation vs. instance](/ace/guides/policy-manager/manage-policies#policy-implementations-vs-policy-instances) model as library policies: 1. **Write** a policy contract that implements the `IPolicy` interface. 2. **Deploy** it — this is your policy **implementation** contract — on each chain where you need it. 3. **Register** the implementation with the ACE Platform, providing its on-chain addresses and a **config schema**. This makes it an org-scoped policy type. 4. **Create instances** from it and **attach** them to target functions, exactly like a library policy. At instance-creation time, ACE's on-chain `PolicyFactory` clones your implementation into an instance and initializes it. The factory verifies that your implementation declares support for `IPolicy` (via ERC-165) — a contract that does not implement `IPolicy` cannot be instantiated. ## Prerequisites - Solidity development experience and a deployment toolchain (Foundry, Hardhat, etc.). - Familiarity with [Policy Management](/ace/concepts/policy-management) (the execution model, `run`/`postRun`, extractors, and parameters) and [Policy Ordering & Composition](/ace/concepts/policy-ordering). - A deployed [PolicyEngine](/ace/guides/policy-manager/manage-engines). - The `@chainlink/policy-management` contracts available in your project. ## Step 1: Write the policy contract Every policy inherits from the base `Policy` contract and implements `run`. The base contract provides ownership, upgradeability, ERC-165 support, and the binding to a PolicyEngine. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Policy} from "@chainlink/policy-management/core/Policy.sol"; import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol"; contract LockoutPolicy is Policy { string public constant override typeAndVersion = "LockoutPolicy 1.0.0"; mapping(address => uint256) public lockoutExpiresAt; /// @notice Configuration setter — locks an address for a duration (seconds). function setLockout(address account, uint256 duration) public onlyOwner { lockoutExpiresAt[account] = block.timestamp + duration; } /// @notice Authorize setLockout so the PolicyEngine can apply configuration changes. function authorizeConfigSelector(bytes4 selector) public pure override returns (bool) { return selector == this.setLockout.selector; } function run( address, /* caller */ address, /* subject */ bytes4, /* selector */ bytes[] calldata parameters, bytes calldata /* context */ ) public view override returns (IPolicyEngine.PolicyResult) { // Always validate the inputs your policy expects. require(parameters.length == 1, "LockoutPolicy: expected 1 parameter"); address recipient = abi.decode(parameters[0], (address)); if (lockoutExpiresAt[recipient] > block.timestamp) { revert IPolicyEngine.PolicyRejected("LockoutPolicy: address is locked out"); } return IPolicyEngine.PolicyResult.Continue; } } ``` Key pieces: - **`run(...)`** — read-only evaluation returning `Continue` (defer to the next policy), `Allowed` (approve and skip the rest of the chain), or reverting with `PolicyRejected` to block the transaction. The `parameters` array holds the extractor outputs mapped to this policy; always validate its length and decode defensively. - **`postRun(...)`** *(optional)* — override it to mutate state after a successful check (for example, incrementing a counter). It is `onlyPolicyEngine` and is not called when the policy rejects. - **`configure(bytes)`** *(optional)* — override it to decode initial configuration passed at instance creation. The base `initialize` calls it. - **Configuration setters + `authorizeConfigSelector`** — expose owner-callable setters (like `setLockout`) to reconfigure the policy after deployment, and override `authorizeConfigSelector` to return `true` for those selectors so the PolicyEngine is allowed to call them. Selectors you do not authorize can only be called by the owner directly, not through the platform. - **`typeAndVersion`** — a human-readable identifier, e.g. `"LockoutPolicy 1.0.0"`. > **TIP: Start from the boilerplate** > > The Chainlink ACE repository includes a full walkthrough and a copy-pasteable template — the [Custom Policies > Tutorial](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/CUSTOM_POLICIES_TUTORIAL.md). > The pre-built policies in the [policy-management > package](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/policy-management/src/policies) are also > good references. ## Step 2: Deploy the implementation Deploy your policy contract on each chain where you intend to use it. This deployed contract is the **implementation** — ACE clones it into instances; you do not attach the implementation to functions directly. Record the deployed address per chain; you need them in the next step. > **CAUTION: ERC-165 is required** > > Your implementation must return `true` from `supportsInterface(type(IPolicy).interfaceId)`. Inheriting from the base > `Policy` contract handles this. If it does not, instance creation reverts. ## Step 3: Register the implementation Register the deployed implementation with the Coordinator API so the platform can manage it. Provide a name, description, the on-chain addresses, and a **config schema**. ```bash curl -X POST https://ace.api.chain.link/v1/policy-implementations \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "name": "Lockout Policy", "description": "Blocks transfers to locked-out recipients for a period of time", "onchain_policy_implementations": [ { "chain_selector": "16015286601757825753", "address": "0xYourImplementationOnSepolia" } ], "policy_config_schema": { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "lockouts": { "type": "array", "description": "Accounts to lock out and for how long.", "items": { "type": "object", "required": ["account", "duration"], "properties": { "account": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "duration": { "type": "integer" } } }, "metadata": { "display_hints": { "network_behaviour": "apply_per_chain", "title": "Lockouts" }, "primary_key_fields": ["account"], "on_chain_operations": [ { "type": "add", "function_abi": { "name": "setLockout", "type": "function", "stateMutability": "nonpayable", "inputs": [ { "name": "account", "type": "address" }, { "name": "duration", "type": "uint256" } ], "outputs": [] } } ] } } }, "policy_run_parameters": [ { "name": "Recipient", "type": "address", "max": 1 } ], "initial_configs": [] } }' ``` | Field | Required | Description | | -------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | Yes | Human-readable name shown in Policy Manager | | `description` | Yes | What the policy does | | `policy_config_schema` | Yes | JSON Schema describing configurable fields, the parameters the policy consumes, and how configuration maps to on-chain setters (see below) | | `onchain_policy_implementations` | No | Array of `{ chain_selector, address }` for your deployed implementation on each chain. ACE records these; it does **not** deploy the implementation for you. | The registered implementation is created with type `custom` and scoped to your organization. It now appears in `GET /policy-implementations` alongside the global library. ## The config schema The `policy_config_schema` is a [JSON Schema (draft-07)](https://json-schema.org/draft-07) document with three ACE-specific parts. It drives the Platform UI, validates the configuration you supply, and tells the platform how to translate configuration into on-chain calls. ### `properties` — configurable fields Each property is a configurable field of your policy. Its `metadata.on_chain_operations` map configuration changes to your contract's setter functions: - `add` / `remove` — for list-style fields (add or remove an entry), pointing at setters like `setLockout`. - `replace` — for scalar fields (set a single value), pointing at a setter like `setMax`. Each operation carries the `function_abi` of the setter to call. **Those setters must be authorized by your contract's `authorizeConfigSelector`** — otherwise the PolicyEngine cannot call them and configuration changes will fail. ### `policy_run_parameters` — what the policy consumes An ordered array declaring the parameters your `run` function expects, each with a `name`, a Solidity `type`, and a `max`: - `max: 1` — exactly one value at that position. - `max: -1` — a variable number of values (must be the last parameter). Use this for policies that check an arbitrary number of addresses. When you attach the policy to a function, the extractor outputs you map to it must match these parameters by type and position. See [Policy Management — the extractor and mapper pattern](/ace/concepts/policy-management#the-extractor-and-mapper-pattern). ### `initial_configs` — what is set at creation An array of property names that are provided when an instance is **created** (in the instance's `initial_config`) rather than configured afterward. Leave it empty to configure everything after deployment. ## Step 4: Create and use instances From here, a custom policy is used exactly like a library policy: 1. [Create a policy instance](/ace/guides/policy-manager/manage-policies#create-a-policy-instance) from your implementation, supplying an `initial_config` that matches your config schema. ACE clones your implementation through the `PolicyFactory` and initializes the instance. 2. [Attach the instance to a protected function](/ace/guides/policy-manager/manage-protections), mapping the extractor outputs to your `policy_run_parameters`. 3. [Update the configuration](/ace/guides/policy-manager/manage-policies#update-policy-configuration) over time through the authorized config selectors. ## Manage a custom implementation - **Update** name, description, or on-chain addresses with `PUT /policy-implementations/{id}`. - **Archive** with `PATCH /policy-implementations/{id}` (`{"status":"archived"}`). All instances of the implementation must be archived first. ## Security considerations A custom policy runs inside the policy chain of every function it protects, so a bug or malicious construct affects those transactions. In particular: - Keep `run` read-only and defensive — validate `parameters` length and decode carefully. - Ensure any external calls cannot revert the whole chain unexpectedly; return a decision rather than propagating failures. - Treat `postRun` state changes with the same care as any state-changing external function (reentrancy, access control). See [Security Considerations](/ace/guides/policy-manager/contracts/security-considerations) and the [Security Model](/ace/concepts/security) for the full trust model. ## Related pages - [Custom Policies Tutorial](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/CUSTOM_POLICIES_TUTORIAL.md) — end-to-end contract walkthrough with a boilerplate template - [Policy Management](/ace/concepts/policy-management) — execution model, `run`/`postRun`, parameters - [Policy Management Contracts](/ace/reference/policy-management-contracts) — `IPolicy`, `IPolicyEngine`, and other interfaces - [Managing Policies](/ace/guides/policy-manager/manage-policies) — create and configure policy instances - [Protecting Target Functions](/ace/guides/policy-manager/manage-protections) — attach policies to functions - [Security Considerations](/ace/guides/policy-manager/contracts/security-considerations) — trust boundaries and defensive patterns --- # Offchain Policies Source: https://docs.chain.link/ace/guides/policy-manager/offchain-policies Last Updated: 2026-10-05 ACE offchain policies evaluate data or logic outside the blockchain before authorizing a protected onchain action. The authorization is delivered onchain as a permit that is bound to a specific transaction intent. ACE supports two offchain policy models: - **Managed offchain policies** provide an out-of-the-box workflow. You configure the risk rules and protections, while Chainlink manages the CRE workflow, external provider call, onchain validator deployment, and permit delivery. The current implementation supports wallet risk screening with TRM Wallet Screening. - **Custom offchain policies** support bespoke compliance logic and providers. You host the policy endpoint and work with Chainlink to configure and operate the integration. > **CAUTION: Managed offchain policies are an MVP** > > Managed offchain policies and the Evaluation API are an MVP. Their interfaces and capabilities can change during Beta. > Contact your Chainlink representative before using this feature and for help with setup. > **NOTE: Looking for continuous screening?** > > Managed offchain policies check an address for each transaction request. To screen a watchlist on a schedule and act > on tokens you already run, see [Active Monitoring](/ace/active-monitoring/overview). It uses its own TRM API key. ## Managed offchain policy guides - [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) — meet the TRM and CRE prerequisites, configure wallet risk rules, create the policy, and attach protections to target functions. - [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) — call the Evaluation API, monitor an evaluation, and submit the protected transaction after its permit is ready onchain. - [Granting Evaluation Access](/ace/guides/policy-manager/offchain-policies/grant-evaluation-access) — let another organization request permit evaluations against your protected target contract. ## Learn how offchain policies work See [Offchain Policies](/ace/concepts/off-chain-policies) for the architecture and execution flows of both managed and custom offchain policy models. --- # Managing Offchain Policies (MVP) Source: https://docs.chain.link/ace/guides/policy-manager/offchain-policies/manage-offchain-policies Last Updated: 2026-10-05 ACE managed offchain risk policies screen wallet addresses with [TRM Wallet Screening](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening) before allowing a protected onchain action. You configure the risk rules and the target functions to protect. Chainlink manages the CRE workflow, deploys the onchain permit validator, calls TRM, and delivers approved permits onchain. This guide covers policy setup. To integrate permit requests into your application, see [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits). > **CAUTION: MVP feature** > > Managed offchain risk policies are an MVP. Their interfaces and capabilities can change during Beta. Contact your > Chainlink representative before using this feature and for help with setup. This guide does not cover custom offchain > policy integrations, where you host your own policy endpoint and work with Chainlink to configure a custom workflow. > **NOTE: Active Monitoring uses a different TRM key** > > This guide stores your TRM credential in the Vault DON for managed offchain policies. [Active > Monitoring](/ace/active-monitoring/overview) stores a TRM API key in the Platform, under **General settings > API access**. > The two are configured separately. See [Configure the TRM API Key and Screening > Schedule](/ace/active-monitoring/guides/configure-screening). ## How the managed policy works Creating a managed offchain policy provisions two components: - A managed CRE workflow that screens the configured wallet addresses with TRM Wallet Screening. - A [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) (CADV) contract on each selected chain. The workflow writes approved permits to this contract through the Keystone Forwarder. When you attach the policy to a target function, the CADV becomes part of that function's policy chain. A call without a matching permit is rejected. A permit is valid only for its caller, target, function, and extracted parameters. ## Prerequisites Before creating a managed offchain risk policy, you need: 1. An ACE organization and [ACE API key](/ace/getting-started/account-setup#3-create-an-api-key). 2. A [policy engine](/ace/guides/policy-manager/manage-engines) deployed on every chain where you want to use the policy. 3. A [target contract](/ace/guides/policy-manager/manage-targets) associated with that policy engine. 4. An extractor attached to the policy engine that supports the target function. The extractor outputs determine which transaction parameters the permit must match. 5. A Chainlink CRE account with the [CRE CLI](https://docs.chain.link/cre/getting-started/cli-installation/macos-linux) installed and authenticated. 6. A TRM Labs account with Wallet Screening API access and a valid API key. ACE does not provide a TRM account or API credentials. See [TRM Wallet Screening](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening) to learn about the product and request access. ## Store the TRM credential in Vault DON The managed workflow retrieves your TRM credential from Vault DON at runtime. The credential remains encrypted and is not included in the offchain policy configuration. TRM uses HTTP Basic authentication with the API key as both the username and password. Before uploading it, encode `:` as Base64 without a trailing newline: ```bash export TRM_API_KEY="" export TRM_BASIC_AUTH=$(printf '%s:%s' "$TRM_API_KEY" "$TRM_API_KEY" | base64 | tr -d '\n') ``` Create a secrets file that maps the Vault DON secret identifier to the environment variable: ```yaml secretsNames: trmApiKey: - TRM_BASIC_AUTH ``` Upload the secret using the CRE CLI. Replace `` with your CRE target: ```bash cre secrets create production-secrets.yaml \ --target \ --secrets-auth=browser ``` The identifier under `secretsNames` is the value to use for `secret_name` when you create the policy. In this example, it is `trmApiKey`. For prerequisites, authentication options, secret lifecycle operations, and troubleshooting, see [Using Secrets with Deployed Workflows](https://docs.chain.link/cre/guides/workflow/secrets/using-secrets-deployed). > **CAUTION: Keep the TRM API key private** > > Do not put the raw TRM API key or its Base64-encoded credential in the policy configuration, source control, or > client-side code. The policy configuration contains only the Vault DON secret identifier. ## Configure the risk policy The `wallet_risk_scoring` policy supports the following configuration: | Field | Required | Description | | -------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `secret_name` | Yes | Vault DON identifier containing the Base64-encoded TRM Basic Auth credential. | | `addresses_to_check` | Yes | Which addresses to screen: `CALLER`, `PARAMETERS`, or `ALL`. | | `risk_threshold` | Yes | Reject an address whose highest TRM risk level is at or above this threshold: `LOW`, `MEDIUM`, `HIGH`, or `SEVERE`. | | `block_unknown` | No | When `true`, reject an address whose TRM risk level is `UNKNOWN`. Defaults to `false`. | | `category_filters` | No | Category-specific thresholds. Each entry contains `category` and an optional `threshold`. If omitted, the global `risk_threshold` applies to that category. | | `fail_mode` | No | `CLOSED` fails the evaluation when TRM returns an unsuccessful HTTP response. `OPEN` allows it to continue. Defaults to `CLOSED`. | ### Select addresses to screen The `addresses_to_check` setting controls which addresses are sent to TRM: | Value | Addresses screened | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CALLER` | Only `caller_address` from the evaluation request. | | `PARAMETERS` | Addresses found in `permit_parameters`. The first permit parameter represents the sender; subsequent address values are identified from the function signature. | | `ALL` | The caller and all addresses found in `permit_parameters`, with duplicates removed. | For an ERC-20 `transfer(address,uint256)` evaluation with permit parameters `[from, to, amount]`, `CALLER` screens `from`, while `PARAMETERS` and `ALL` screen both `from` and `to`. The workflow accepts at most ten unique addresses per evaluation. ### Apply global and category thresholds TRM assigns an overall risk level to each address. ACE orders the levels as follows: ```text UNKNOWN < LOW < MEDIUM < HIGH < SEVERE ``` An address is rejected when its overall level meets or exceeds `risk_threshold`. For example, a `HIGH` threshold rejects `HIGH` and `SEVERE` results. You can also apply different thresholds to individual TRM risk categories. The following configuration rejects: - Any address with an overall risk level of `HIGH` or `SEVERE`. - Any `Sanctions` indicator at `LOW` or above. - Any `Darknet Market` indicator at `MEDIUM` or above. ```json { "risk_threshold": "HIGH", "block_unknown": false, "category_filters": [ { "category": "Sanctions", "threshold": "LOW" }, { "category": "Darknet Market", "threshold": "MEDIUM" } ] } ``` Category names are matched case-insensitively against the categories returned by TRM. Consult your TRM Wallet Screening account for the categories available to your organization. > **CAUTION: Choose fail mode deliberately** > > `OPEN` can produce a permit when TRM returns an unsuccessful HTTP response. Use it only when allowing the action is > preferable to blocking it during a provider error. For example, an invalid Vault DON credential can cause TRM to > return `401`; with `OPEN`, that response does not block the permit. `OPEN` applies to unsuccessful TRM HTTP responses, > but other workflow or transport failures can still produce an `error`. `CLOSED` is the default. ## Create the offchain policy Create the policy with `POST /v1/policies`. Use the same policy engine and chains as the target you plan to protect: ```bash curl -X POST https://ace.api.chain.link/v1/policies \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "policy_kind": "offchain", "name": "Transaction wallet screening", "type": "wallet_risk_scoring", "policy_engine_id": "", "onchain_policies": [ { "chain_selector": "" } ], "config": { "secret_name": "trmApiKey", "addresses_to_check": "ALL", "fail_mode": "CLOSED", "risk_threshold": "HIGH", "block_unknown": false, "category_filters": [ { "category": "Sanctions", "threshold": "LOW" }, { "category": "Darknet Market", "threshold": "MEDIUM" } ] } }' ``` ACE allows one active offchain policy per organization. Creating another returns a conflict until the existing policy is archived. Policy creation is asynchronous. The initial response includes the policy ID and a `deployment_status` such as `pending` or `deploying`. Poll the policy until it becomes `active`: ```bash curl https://ace.api.chain.link/v1/policies/ \ -H "Authorization: Apikey " ``` ACE creates a managed CRE workflow and deploys one CADV contract per selected chain. When the policy becomes active, `action_validators` contains each chain selector and CADV address. > **NOTE** > > Deployment can take several minutes. Do not attach protections or request evaluations until `deployment_status` is > `active`. ## Attach the policy to a target function A protection connects the managed policy to a function on your target. The `extractor_output_ids` must identify, in order, the values that the permit will bind to onchain. For `transfer(address,uint256)`, use the `from`, `to`, and `amount` outputs from the same `ERC20TransferExtractor`: ```bash curl -X POST https://ace.api.chain.link/v1/targets//protections \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "policy_kind": "offchain", "policy_instance_id": "", "function_signature": "transfer(address,uint256)", "desired_position": 0, "extractor_output_ids": [ "", "", "" ], "onchain_target_protections": [ { "chain_selector": "" } ] }' ``` The selected chains must be a subset of the chains configured on the offchain policy. The target and policy must also belong to the same policy engine. Protection attachment is asynchronous and returns `202 Accepted`. Poll the policy's protections until the new protection becomes `active`: ```bash curl https://ace.api.chain.link/v1/policies//protections \ -H "Authorization: Apikey " ``` Once active, calls to the protected function require a matching permit. Continue with [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits). ## Update the policy configuration Updating the configuration redeploys the managed workflow but does not replace its CADV contracts or protections: ```bash curl -X PUT https://ace.api.chain.link/v1/policies//config \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "config": { "secret_name": "trmApiKey", "addresses_to_check": "ALL", "fail_mode": "CLOSED", "risk_threshold": "SEVERE", "block_unknown": true, "category_filters": [ { "category": "Sanctions", "threshold": "LOW" } ] } }' ``` The policy enters `config_updating` and returns to `active` after the workflow is redeployed. Do not request new evaluations while the configuration is updating. ## Remove a protection or policy Remove a protection before archiving its policy: ```bash curl -X DELETE \ https://ace.api.chain.link/v1/policies//protections/ \ -H "Authorization: Apikey " ``` The removal is asynchronous. After all protections are removed, archive the policy: ```bash curl -X PATCH https://ace.api.chain.link/v1/policies/ \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "policy_kind": "offchain", "status": "archived" }' ``` Archiving removes the managed workflow and its event watchers. It also allows the organization to create a new offchain policy. ## Beta and MVP limitations - `wallet_risk_scoring` is the only managed offchain policy type. - Each organization can have one active offchain policy. - Each evaluation can screen at most ten unique addresses. - Every permit is single-use (`maxUses = 1`) and does not expire (`expiry = 0`). These values are not configurable in the current release. - The values in `permit_parameters` must match the outputs configured on the protection and the values extracted from the eventual onchain call. - General CRE service limits also apply. See [CRE Service Quotas](https://docs.chain.link/cre/service-quotas). ## Related pages - [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) — integrate evaluations and permits into an application - [Off-Chain Policy Execution](/ace/concepts/off-chain-policies) — conceptual overview of managed and custom offchain policies - [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) — onchain permit validation - [Protecting Target Functions](/ace/guides/policy-manager/manage-protections) — protection concepts and evaluation order - [Coordinator API Reference](/api/ace/coordinator/docs) — policy and protection API schemas --- # Requesting Offchain Permits Source: https://docs.chain.link/ace/guides/policy-manager/offchain-policies/request-offchain-permits Last Updated: 2026-07-17 After a managed offchain risk policy protects a function, the function rejects calls that do not have a matching permit. Your application must request an evaluation, wait for the permit to be stored onchain, and then submit the protected transaction. This guide uses an ERC-20 `transfer(address,uint256)` as the example. For policy and protection setup, see [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies). > **CAUTION: MVP feature** > > Managed offchain risk policies and the Evaluation API are an MVP. Their interfaces and capabilities can change during > Beta. Contact your Chainlink representative before integrating this feature and for help with setup. ## Evaluation flow 1. Your application describes the intended transaction to the ACE Evaluation API. 2. ACE triggers the managed CRE workflow for your organization. 3. The workflow extracts the configured addresses and screens them with TRM Wallet Screening. 4. If the risk rules reject any address, the evaluation becomes `rejected` and no permit is created. 5. If the risk rules pass, the workflow writes a permit to the CADV contract through the Keystone Forwarder. 6. After ACE observes the onchain `PermitStored` event, the evaluation becomes `ready`. 7. Your application submits the protected transaction with the same caller, target, function, and parameter values. 8. The policy engine finds and consumes the permit. The permit cannot authorize another transaction. ## Prerequisites Before requesting an evaluation, verify that: - The offchain policy has `deployment_status: active`. - Its protection for the target function has `status: active`. - You know the target contract address and chain selector. - You know the wallet that will submit the onchain transaction. It must be the same address as `caller_address`. - You know the ordered extractor outputs configured on the protection. Your `permit_parameters` must use that same order. ## Evaluation API The production Evaluation API base URL is: ```text https://ace.api.chain.link/v1/evaluation ``` It uses the same ACE API key as the Coordinator API: ```http Authorization: Apikey ``` > **CAUTION** > > Call the Evaluation API from a trusted backend. Do not expose your ACE API key in browser or mobile application code. ## Construct the evaluation request Start an evaluation with `POST /evaluate`: ```json { "caller_address": "0x1111111111111111111111111111111111111111", "subject": "0x2222222222222222222222222222222222222222", "function_signature": "transfer(address,uint256)", "parameters": { "to": "0x3333333333333333333333333333333333333333", "amount": "100" }, "permit_parameters": [ "0x0000000000000000000000001111111111111111111111111111111111111111", "0x0000000000000000000000003333333333333333333333333333333333333333", "0x0000000000000000000000000000000000000000000000000000000000000064" ], "chain_selector": "", "unique_evaluation_id": "transfer-018f6b3e-7c42-7a1f-a8ed-5ecf90c03b30" } ``` | Field | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------- | | `caller_address` | Wallet that will submit the protected transaction. | | `subject` | Address of the protected target contract. | | `function_signature` | Canonical function signature, such as `transfer(address,uint256)`. Do not send the four-byte selector. | | `parameters` | Structured representation of the function arguments. ACE stores it with the evaluation as contextual data. | | `permit_parameters` | Ordered ABI-encoded values used for address screening and exact onchain permit matching. | | `chain_selector` | Chain where the target, policy engine, protection, and CADV are deployed. | | `unique_evaluation_id` | Client-generated identifier unique to this transaction intent. ACE uses it to derive the permit ID. | ### Encode permit parameters Each `permit_parameters` item is a `0x`-prefixed, 32-byte ABI word. The items must have the same order as the `extractor_output_ids` on the protection. For `transfer(address,uint256)`, the `ERC20TransferExtractor` produces: ```text [from, to, amount] ``` Therefore, encode: 1. `from`: the transaction caller, as an ABI `address`. 2. `to`: the transfer recipient, as an ABI `address`. 3. `amount`: the transfer amount, as an ABI `uint256`. Use a standard ABI library rather than concatenating untrusted values manually. For example, with ethers v6: ```javascript import { AbiCoder } from "ethers" const abiCoder = AbiCoder.defaultAbiCoder() const permitParameters = [ abiCoder.encode(["address"], [callerAddress]), abiCoder.encode(["address"], [recipientAddress]), abiCoder.encode(["uint256"], [amount]), ] ``` > **CAUTION: The permit must match the eventual transaction** > > A permit can become `ready` but still fail to authorize the transaction if `caller_address`, `subject`, the function > selector, or any extracted parameter differs. Construct the evaluation and transaction from the same immutable intent > in your application. ### Choose a unique evaluation ID `unique_evaluation_id` is scoped to your ACE organization. ACE combines it with the organization ID to derive a deterministic `permit_id`. Retrying with the same `unique_evaluation_id` is idempotent: ACE returns the existing evaluation instead of triggering another workflow execution. Never reuse an ID for a different caller, target, function, or set of parameters. Use a UUID or another collision-resistant identifier generated by your backend. Store it with the transaction intent so you can safely recover from a lost HTTP response. ## Start the evaluation ```bash curl -X POST https://ace.api.chain.link/v1/evaluation/evaluate \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d @evaluation.json ``` The response contains the deterministic permit ID and initial status: ```json { "permit_id": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "status": "evaluating" } ``` The response does not mean that the transaction is approved. Wait until the evaluation becomes `ready`. ## Poll the evaluation Retrieve the evaluation using the returned permit ID: ```bash curl \ https://ace.api.chain.link/v1/evaluation/evaluate/ \ -H "Authorization: Apikey " ``` Polling every five seconds is a reasonable default. Stop when the evaluation reaches a terminal status. | Status | Terminal | Meaning | | ------------ | -------- | ----------------------------------------------------------------------------------- | | `evaluating` | No | The workflow is screening the configured addresses. | | `approving` | No | TRM checks passed and the workflow is publishing the permit onchain. | | `ready` | Yes | The permit was stored onchain. The protected transaction can now be submitted. | | `rejected` | Yes | At least one configured risk rule rejected the evaluation. No permit was created. | | `error` | Yes | The evaluation or onchain permit publication failed. No usable permit is available. | For `rejected` and `error`, the response can include a `reason`. `workflow_execution_id` identifies the CRE execution when available. Because permits do not expire in the current release, `expires_at` is normally `null`. ```json { "permit_id": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "status": "ready", "reason": null, "workflow_execution_id": "", "expires_at": null } ``` ## Retry an evaluation The CRE HTTP trigger allows one new execution per workflow every 60 seconds. Polling an existing evaluation does not trigger the workflow and is not subject to that trigger rate. - If the initial HTTP response is lost or ambiguous, retry `POST /evaluate` with the same `unique_evaluation_id`. ACE returns the existing evaluation if it was created. - If an evaluation reaches `rejected`, changing the identifier alone does not change the policy decision. Review the risk result or transaction intent. - If an evaluation reaches `error` and the underlying issue is resolved, wait at least 60 seconds and submit a new evaluation with a new `unique_evaluation_id`. See [CRE Service Quotas](https://docs.chain.link/cre/service-quotas) for current workflow limits. ## Submit the protected transaction Submit the transaction only after the evaluation becomes `ready`. The sender must be `caller_address`, and the target function must receive values that produce the same extracted parameters as `permit_parameters`. No permit bytes are added to the transaction. The CADV already stores the permit and looks it up from the action's caller, target, selector, and extracted parameters. After the protected call succeeds, the CADV increments the permit's usage counter. Managed risk policy permits have `maxUses = 1`, so another transaction with the same intent requires a new evaluation and permit. > **CAUTION: Request permits shortly before use** > > Managed risk policy permits do not expire in the current release. Request an evaluation only when the application is > ready to submit the corresponding transaction, and do not treat the passage of time as invalidating an unused permit. ## Troubleshooting ### Evaluation is rejected - At least one address met or exceeded the global `risk_threshold`. - A TRM risk indicator met or exceeded a configured category threshold. - TRM returned `UNKNOWN` and `block_unknown` is enabled. Review the response `reason` and the policy configuration. Do not retry a rejected intent without understanding why it was rejected. ### Evaluation returns an error - The Vault DON secret identifier does not match `secret_name`. - The TRM credential was not encoded as `:` before Base64 encoding. - TRM or the CRE confidential HTTP request failed. - The selected chain does not have an active CADV for the policy. - The workflow could not write the permit onchain. If a TRM HTTP error should allow the action, review the policy's `fail_mode`. Use `OPEN` only after assessing the compliance impact. ### Evaluation is ready but the transaction reverts - The transaction sender differs from `caller_address`. - The target address or function differs from the evaluation. - The eventual transaction produces different extractor values than `permit_parameters`. - The protection's extractor outputs are missing or ordered differently. - The permit has already been consumed. - Another policy in the target function's policy chain rejected the call. ## Related pages - [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) — configure TRM screening and attach protections - [Granting Evaluation Access](/ace/guides/policy-manager/offchain-policies/grant-evaluation-access) — let another organization request evaluations against your target - [Off-Chain Policy Execution](/ace/concepts/off-chain-policies) — conceptual overview - [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) — how permits are stored and consumed onchain - [Evaluation API Reference](/api/ace/evaluation/docs) — complete request, response, and error schemas - [CRE Service Quotas](https://docs.chain.link/cre/service-quotas) — current CRE workflow limits --- # Granting Evaluation Access Source: https://docs.chain.link/ace/guides/policy-manager/offchain-policies/grant-evaluation-access Last Updated: 2026-07-17 By default, only the organization that owns a target contract can request offchain permit evaluations for it through the [Evaluation API](/ace/guides/policy-manager/offchain-policies/request-offchain-permits). **Evaluation access grants** let you extend this capability to other organizations — for example, allowing a DEX or lending protocol to request permits against your token's compliance rules. When you grant evaluation access, the grantee organization can call the Evaluation API for the specified target and offchain policy. The evaluation runs against **your** managed CRE workflow and risk configuration — the grantee does not need its own offchain policy or TRM credential. > **CAUTION: MVP feature** > > Managed offchain risk policies and the Evaluation API are an MVP. Their interfaces and capabilities can change during > Beta. Contact your Chainlink representative before using this feature and for help with setup. ## Roles and concepts | Term | Meaning | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Grantor** | The organization that **owns** the target contract and the offchain policy, and grants evaluation access. | | **Grantee** | The organization that **receives** evaluation access. It can call the Evaluation API for the specified target and policy. | | **Access grant** | The link between an offchain policy–target pair and a grantee organization. It is either `active` or `revoked`. | | **Org ID** | The identifier of an organization. The grantee shares theirs with the grantor so the grantor can create the grant. Retrieve it with `GET /organizations/me` ([Coordinator API](/api/ace/coordinator/docs#/Organizations)). | ## What the grantee can and cannot do An active evaluation access grant lets the grantee: - **Call the Evaluation API** (`POST /evaluate`) for the granted target and offchain policy. The evaluation uses the grantor's managed workflow and TRM configuration. - **Poll evaluation status** (`GET /evaluate/{permitId}`) for evaluations the grantee started. - **List granted targets** using `GET /targets?include_granted=true` to discover targets other organizations have shared with them. The grantee **cannot**: - Modify the offchain policy, its risk thresholds, or the protection configuration. - Manage the target contract, its policy engine, or any other resource owned by the grantor. - Re-share evaluation access with a third organization. > **NOTE: Only the owner manages grants** > > Creating, listing, and revoking evaluation access grants requires **ownership** of both the offchain policy and the > target. A grantee cannot re-share access that was shared with it. ## Prerequisites Before granting evaluation access: 1. You have a [managed offchain policy](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) with `deployment_status: active`. 2. The offchain policy has an active [protection](/ace/guides/policy-manager/manage-protections) on the target function. 3. You know the grantee's **Org ID**. Ask them to retrieve it: ```bash # Run by the grantee curl https://ace.api.chain.link/v1/organizations/me \ -H "Authorization: Apikey " ``` ## Grant evaluation access As the target and policy owner, create the grant by specifying the offchain policy ID, target ID, and the grantee's Org ID: ```bash curl -X POST https://ace.api.chain.link/v1/policies//targets//access-grants \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "grantee_org_id": "" }' ``` The response is the created grant: ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "grantee_org_id": "org-456", "grantor_org_id": "org-123", "status": "active", "granted_at": 1800000000, "revoked_at": null } ``` ## View who has access List the active and past grants for a specific offchain policy and target pair: ```bash curl https://ace.api.chain.link/v1/policies//targets//access-grants \ -H "Authorization: Apikey " ``` Each entry includes the grantee, the status (`active` or `revoked`), and timestamps, giving you an audit trail of who was granted access and when. ## Discover granted targets (grantee) As a grantee, include `include_granted=true` when listing targets to see targets other organizations have shared with you, alongside your own: ```bash curl "https://ace.api.chain.link/v1/targets?include_granted=true" \ -H "Authorization: Apikey " ``` Once you can see a granted target, you can call the Evaluation API for it the same way you would for your own targets. See [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) for the full evaluation workflow. ## Revoke access As the policy and target owner, revoke a grant by setting its status to `revoked`: ```bash curl -X PATCH \ https://ace.api.chain.link/v1/policies//targets//access-grants/ \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "status": "revoked" }' ``` Revocation takes effect immediately. The grantee can no longer request evaluations for this target and policy. The grant record is retained with a `revoked_at` timestamp for audit purposes. To restore access later, create a new grant. ## Related pages - [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) — call the Evaluation API and submit the protected transaction - [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) — configure TRM screening and attach protections - [External Registries](/ace/guides/identity-manager/external-registries) — a similar grant model for sharing identity and credential registries - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema --- # Identity Manager Guides Source: https://docs.chain.link/ace/guides/identity-manager Last Updated: 2026-04-06 These guides cover the day-to-day operations of an Identity Manager — from setting up registries to issuing and managing credentials across chains. > **NOTE** > > New to ACE? Start with the [Identity Manager Quick Start](/ace/getting-started/identity-manager) for a step-by-step > onboarding walkthrough. ## Available guides - [Managing Registries](/ace/guides/identity-manager/manage-registries) — create, view, import, and archive identity and credential registry pairs - [Managing Identities](/ace/guides/identity-manager/manage-identities) — register cross-chain identities (CCIDs), map wallet addresses across chains, and manage identity lifecycle - [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types) — define the categories of credentials your registry supports (e.g., KYC, accreditation, sanctions clearance) - [Managing Credentials](/ace/guides/identity-manager/manage-credentials) — issue, renew, revoke, and set expiration on credentials using the attestation model - [External Registries](/ace/guides/identity-manager/external-registries) — share a registry with another organization, and use registries shared with you as credential sources --- # Managing Registries Source: https://docs.chain.link/ace/guides/identity-manager/manage-registries Last Updated: 2026-10-05 ## What are registries? A **registry** in ACE is the top-level organizational unit for the Identity Manager. Each registry bundles two types of sub-registries: - **Identity registries** — map wallet addresses to [Cross-Chain Identifiers (CCIDs)](/ace/concepts/cross-chain-identity#the-cross-chain-identifier-ccid). - **Credential registries** — manage the lifecycle of credentials linked to CCIDs. Each sub-registry corresponds to a smart contract deployed on a specific blockchain. A single registry can span multiple chains by including sub-registries on each target network. For a deeper explanation of the registry model and how it fits into the identity lifecycle, see [Cross-Chain Identity](/ace/concepts/cross-chain-identity). > **TIP: Use a registry as a watchlist** > > [Active Monitoring](/ace/active-monitoring/overview) can import the addresses of the identities in a registry to its > watchlist. See [Manage the > Watchlist](/ace/active-monitoring/guides/manage-watchlist#add-addresses-from-an-identity-registry). ## Create a registry ## View registries ## Multi-chain setup Registries are designed to work across multiple chains. Each entry in `identity_registries` and `credential_registries` targets a specific `chain_selector`, and the platform deploys (or imports) a contract on each specified chain independently. A typical multi-chain configuration: - **Identity registries** on every chain where users interact — so the IdentityRegistry on each chain can resolve wallet addresses to CCIDs locally. - **Credential registries** on every chain where policies need to verify credentials at runtime. Because CCIDs are chain-agnostic identifiers, a credential issued on one chain's CredentialRegistry is logically valid across all chains. The multi-chain deployment ensures that each chain has a local copy of the registry contracts for low-latency, on-chain lookups. For more on how this model works, see [Cross-Chain Identity](/ace/concepts/cross-chain-identity). > **NOTE** > > You must have a [CRE Connect Wallet](/ace/getting-started/account-setup#4-set-up-cre-connect-wallets) deployed on each > chain before ACE can deploy managed registry contracts there. ## Update a registry You can update a registry's name, description, and add new sub-registries. Updates are **additive** — you can add new identity or credential sub-registries to additional chains, but you cannot remove existing sub-registry pairs. ## Share across organizations You can grant another organization read access to a registry you own, so it can use your identities and credentials as a credential source without re-issuing them. Grants are read-only for the recipient and can be revoked at any time. For the full workflow — granting, discovering registries shared with you, using them, and revoking — see [External Registries](/ace/guides/identity-manager/external-registries). ## Archive a registry Archiving a registry deactivates it and prevents any further operations. Before archiving, all identities associated with the registry must be removed or archived first. > **NOTE** > > All identities in the registry must be archived or removed before the registry itself can be archived. Attempting to > archive a registry with active identities returns an error. ## Related pages - [Cross-Chain Identity](/ace/concepts/cross-chain-identity) — conceptual background on CCIDs, registries, and credential sources - [Managing Identities and Credentials](/ace/guides/identity-manager/manage-identities) — register identities and issue credentials within a registry - [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types) — define and manage the credential types used in credential registries - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema and parameters --- # Managing Identities Source: https://docs.chain.link/ace/guides/identity-manager/manage-identities Last Updated: 2026-04-06 This guide covers how to register, view, update, and archive cross-chain identities (CCIDs) using the ACE Platform UI or the Coordinator API. Identities are the foundation of ACE's credential system — every credential is issued against an identity. > **NOTE** > > This page focuses on identity management. For related workflows, see [Managing > Registries](/ace/guides/identity-manager/manage-registries), [Managing Credential > Types](/ace/guides/identity-manager/manage-credential-types), and [Managing > Credentials](/ace/guides/identity-manager/manage-credentials). ## What are identities (CCIDs)? A **cross-chain identity (CCID)** aggregates multiple wallet addresses across EVM chains into a single logical entity. Rather than treating each address on each chain as a separate user, ACE maps them all to one CCID. Credentials issued against that CCID are then valid for every linked address on every chain — no re-issuance or bridging required. Each identity includes: - **Title** — A human-readable label for internal use only (e.g., "Jane Doe"). This value is never written on-chain. - **Entity ID** — A unique external identifier that ties the identity back to your system of record (e.g., a KYC provider user ID). This value must be unique within a registry. - **Registry** — The registry the identity belongs to. - **On-chain identities** — One or more wallet address + chain selector pairs that map to this CCID on-chain. For a deeper look at how CCIDs work, how they are generated, and the privacy considerations involved, see [Cross-Chain Identity](/ace/concepts/cross-chain-identity). ## Register an identity ## Bulk import identities When onboarding many users at once, use the batch endpoint to create multiple identities in a single atomic request. Each identity in the batch follows the same schema as the single-create endpoint, including the optional `credentials` array — so you can register identities and issue credentials in one call. This feature is **API-only**. In the Platform UI, the **Bulk import via API** option under **+ Add identity** links to this documentation. Send a `POST` request to `/identities/batch`: ```bash curl -X POST "https://ace.api.chain.link/v1/identities/batch" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "identities": [ { "title": "Identity A", "entity_id": "user-001", "registry_id": "", "onchain_identities": [ { "address": "0x1111111111111111111111111111111111111111", "chain_selector": "16015286601757825753" } ], "credentials": [ { "credential_type_id": "", "expires_at": 1800000000 } ] }, { "title": "Identity B", "entity_id": "user-002", "registry_id": "", "onchain_identities": [ { "address": "0x2222222222222222222222222222222222222222", "chain_selector": "16015286601757825753" }, { "address": "0x3333333333333333333333333333333333333333", "chain_selector": "3478487238524512106" } ] } ] }' ``` The `credentials` array is optional on each identity. The second identity in this example is created without credentials. > **CAUTION: Atomic operation** > > Batch creation is all-or-nothing. If any identity in the batch fails validation, the entire request is rejected and no > identities are created. Verify each entry before submitting. ## View and search identities ## Update an identity You can update an identity's title, description, and on-chain address mappings. ACE offers two update approaches: full replacement and partial update. ## Cross-chain identity mapping A single CCID can span as many chains and addresses as needed. This is the core value proposition of ACE's identity model: one credential verification applies everywhere. For example, an entity operating wallets on Ethereum Sepolia, Arbitrum Sepolia, and Base Sepolia would have a single identity with three on-chain mappings: ```bash curl -X POST "https://ace.api.chain.link/v1/identities" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "title": "Multi-Chain Operator", "entity_id": "operator-xyz-007", "registry_id": "a1b2c3d4-5678-9abc-def0-1234567890ab", "onchain_identities": [ { "chain_selector": "16015286601757825753", "address": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, { "chain_selector": "3478487238524512106", "address": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" }, { "chain_selector": "10344971235874465080", "address": "0xcccccccccccccccccccccccccccccccccccccccc" } ] }' ``` | Chain | Chain Selector | Address | | :--------------- | :--------------------- | :-------------- | | Ethereum Sepolia | `16015286601757825753` | `0xaaaa...aaaa` | | Arbitrum Sepolia | `3478487238524512106` | `0xbbbb...bbbb` | | Base Sepolia | `10344971235874465080` | `0xcccc...cccc` | All three addresses resolve to the same CCID. A credential issued against this identity — such as a KYC attestation — is valid for all three addresses across all three chains. When any of these addresses interacts with a policy-protected contract, the policy resolves the address to the shared CCID and checks credentials from there. To add or remove chains later, use the [full update (PUT)](#full-update-put) endpoint with the updated list of on-chain identities. ## Archive an identity Archiving marks an identity as inactive. Archived identities are retained for audit purposes but are no longer considered active. > **CAUTION: Archive credentials first** > > Before archiving an identity, archive or revoke any active credentials associated with it. Credentials linked to an > archived identity may no longer be evaluated correctly by policies. See [Managing > Credentials](/ace/guides/identity-manager/manage-credentials) for credential lifecycle management. --- # Managing Credential Types Source: https://docs.chain.link/ace/guides/identity-manager/manage-credential-types Last Updated: 2026-07-17 Credential types define the categories of attestations you can issue to [cross-chain identities (CCIDs)](/ace/concepts/cross-chain-identity). Each credential type represents a distinct kind of verification — for example, KYC completion, accredited investor status, or sanctions clearance. When you create a credential type, the `credential_type` string you provide is hashed to produce a `credential_type_hash` that policy contracts reference on-chain. Credential types are scoped to a specific credential registry. Before creating credential types, make sure your [registries are set up](/ace/guides/identity-manager/manage-registries). A credential type can optionally be linked to a **data schema**, which lets the credentials you issue against it carry structured data (for example, a jurisdiction code). See [Typed credentials with data schemas](#typed-credentials-with-data-schemas) below. ## What is a credential type? A credential type is a string you define to represent a specific compliance check or verification. This string is hashed and registered on-chain, so it cannot be changed after creation. You can create any credential types that match your requirements — for example: | Credential type string | Use case | | ---------------------- | -------------------------------------- | | `PROOF_OF_IDENTITY` | Identity verification | | `PROOF_OF_FUNDS` | Source of funds or reserves check | | `AML_CHECK` | Anti-money-laundering screening result | The `credential_type` string is case-sensitive and must be unique within a registry. ## Define a credential type Register a credential type with a `POST` request: ```bash curl -X POST "https://ace.api.chain.link/v1/credential-types" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "registry_id": "", "title": "KYC Verification", "credential_type": "KYC", "description": "Basic Know Your Customer identity verification" }' ``` The response includes the generated `credential_type_id` and the `credential_type_hash` derived from your `credential_type` string. ## Understand credential type hashes When you create a credential type, ACE hashes the `credential_type` string to produce a deterministic `credential_type_hash`. This hash is what gets written on-chain and what policy contracts use when evaluating identity-based rules. ```text credential_type string → credential_type_hash → on-chain reference "KYC" → 0x7a8b...3f21 → used by policy contracts ``` Because the hash is derived from the string, choosing your `credential_type` strings carefully matters — they cannot be changed after creation. Policy contracts such as the [Credential Registry Identity Validator](/ace/reference/policy-library/credential-registry-identity-validator-policy) reference credentials by their `credential_type_hash` when checking whether an identity holds a required attestation. > **CAUTION** > > The `credential_type` string and its hash are immutable once the credential type is created. Choose your naming > convention before issuing credentials against a type. ## Typed credentials with data schemas By default, credentials are **attestation-only**: they record that an identity holds a credential of a given type, with no additional data. You can instead create a **typed** credential type by linking it to a **data schema**. Credentials issued against a typed credential type carry structured data (validated against the schema), which policies can then evaluate through a [Data Validator](/ace/guides/policy-manager/manage-data-validators). A **data schema** is a reusable definition of the shape and format of a credential's data. ACE provides shared, ready-to-use schemas — the first is an [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code schema for jurisdiction use cases (an array of two-letter country codes such as `US`, `CA`, `GB`). To make a credential type typed, pass a `data_schema_id` when you create it. The ISO 3166-1 alpha-2 country code data schema ID is: ```text fb786cd7-6397-4ac6-790c-35746f343cad ``` ```bash curl -X POST "https://ace.api.chain.link/v1/credential-types" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "registry_id": "", "title": "Jurisdiction", "credential_type": "common.country", "description": "Holder jurisdiction as ISO 3166-1 alpha-2 country codes", "data_schema_id": "fb786cd7-6397-4ac6-790c-35746f343cad" }' ``` Once a credential type is linked to a data schema, every credential you issue against it **must** include `credential_data` matching that schema — see [Issue a credential with data](/ace/guides/identity-manager/manage-credentials#issue-a-credential-with-data). > **NOTE: Attestation-only stays the default** > > Linking a data schema is optional. Omit `data_schema_id` for a plain attestation credential type — the common case for > yes/no checks like KYC or AML. ## View credential types List credential types with a `GET` request. Use the `registry_id` query parameter to filter by registry: ```bash curl "https://ace.api.chain.link/v1/credential-types?registry_id=&page=1&page_size=25" \ -H "Authorization: Apikey " ``` To retrieve a single credential type by ID: ```bash curl "https://ace.api.chain.link/v1/credential-types/" \ -H "Authorization: Apikey " ``` ## Update a credential type You can update a credential type's **title** and **description**. The `credential_type` string and `credential_type_hash` cannot be changed. Update a credential type with a `PUT` request: ```bash curl -X PUT "https://ace.api.chain.link/v1/credential-types/" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "title": "KYC Verification (Enhanced)", "description": "Enhanced KYC verification including document and liveness checks" }' ``` You can also make partial updates with a `PATCH` request: ```bash curl -X PATCH "https://ace.api.chain.link/v1/credential-types/" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "description": "Updated description for KYC verification" }' ``` ## Archive a credential type Archiving a credential type prevents new credentials of that type from being issued. Existing credentials remain valid until they are individually archived or expire. > **CAUTION** > > All credentials issued under a credential type must be archived before the credential type itself can be archived. See > [Managing Credentials](/ace/guides/identity-manager/manage-credentials) for how to archive individual credentials. Archive a credential type with a `PATCH` request: ```bash curl -X PATCH "https://ace.api.chain.link/v1/credential-types/" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "status": "archived" }' ``` If active credentials still reference the type, the request returns an error. Archive all associated credentials first, then retry. ## Related resources - [Cross-Chain Identity](/ace/concepts/cross-chain-identity) — conceptual overview of CCIDs, registries, and credential types - [Managing Credentials](/ace/guides/identity-manager/manage-credentials) — issue, revoke, and manage credentials linked to CCIDs - [Managing Registries](/ace/guides/identity-manager/manage-registries) — view and manage identity and credential registry deployments - [Credential Registry Identity Validator Policy](/ace/reference/policy-library/credential-registry-identity-validator-policy) — the policy contract that checks credentials on-chain --- # Managing Credentials Source: https://docs.chain.link/ace/guides/identity-manager/manage-credentials Last Updated: 2026-07-17 Credentials are attestations that a [cross-chain identity (CCID)](/ace/concepts/cross-chain-identity) holds a specific qualification — for example, KYC verification, accredited investor status, or sanctions clearance. Each credential links a **credential type** to an **identity** and is recorded on-chain across every chain where the credential registry is deployed. This guide covers the full credential lifecycle: issuing, viewing, updating, expiring, and revoking credentials through the Coordinator API. ## Attestation vs. typed credentials ACE supports two kinds of credentials: - **Attestation-only** (the default) — The credential records only that an identity holds a credential of a given type, with no additional data. When you issue one, the on-chain record contains just the **credential type hash**, the **identity** (CCID) it is issued to, and an **issuance timestamp**. Policies verify existence — for example, "does this address have a valid KYC credential?" — without accessing any personally identifiable information (PII). - **Typed** — When the credential type is linked to a [data schema](/ace/guides/identity-manager/manage-credential-types#typed-credentials-with-data-schemas), the credential also carries structured `credential_data` (for example, a jurisdiction code). Policies can then evaluate the contents through a [Data Validator](/ace/guides/policy-manager/manage-data-validators), not just the credential's existence. In both cases, no PII should be stored on-chain — credential data must be a minimal, non-sensitive value (such as an ISO country code) or a hash. For a deeper discussion of credential data and privacy, see [Credential Data and Privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy). ## Issue a credential To issue a credential, you need a registered [identity](/ace/guides/identity-manager/manage-identities) and at least one [credential type](/ace/guides/identity-manager/manage-credential-types) defined in your registry. > **CAUTION** > > Each identity can hold only **one credential per credential type**. Attempting to issue a duplicate returns an error. Issue a credential with a `POST` request: ```bash curl -X POST "https://ace.api.chain.link/v1/credentials" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "credential_type_id": "", "identity_id": "", "external_unique_id": "kyc-2026-04-acme", "expires_at": 1806883200 }' ``` | Field | Required | Description | | -------------------- | -------- | ------------------------------------------------------------ | | `credential_type_id` | Yes | UUID of the credential type to issue | | `identity_id` | Yes | UUID of the target identity (CCID) | | `external_unique_id` | No | Your own reference identifier for this credential | | `expires_at` | No | Unix timestamp (integer); omit for a non-expiring credential | ## Issue a credential with data When the credential type is linked to a [data schema](/ace/guides/identity-manager/manage-credential-types#typed-credentials-with-data-schemas), include a `credential_data` field. The value must match the schema — for the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code schema, that is a JSON array of two-letter country codes. ACE validates the data against the schema and encodes it on-chain. ```bash curl -X POST "https://ace.api.chain.link/v1/credentials" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "credential_type_id": "", "identity_id": "", "credential_data": ["US"], "external_unique_id": "jurisdiction-2026-04-acme", "expires_at": 1806883200 }' ``` > **CAUTION: Data is required for typed credential types** > > If the credential type is linked to a data schema, `credential_data` is **required** and must satisfy the schema. > Conversely, do not send `credential_data` for an attestation-only credential type. Keep the value minimal and > non-sensitive — never store PII on-chain. Once issued, the credential data can be enforced at transaction time by attaching a [Data Validator](/ace/guides/policy-manager/manage-data-validators) to the credential source of an identity-validation policy. ## Issue credentials during identity creation You can issue credentials inline when registering a new identity by including a `credentials` array in the `POST /identities` request body. This is useful when you have completed verification before registration and want to create the identity and its credentials in a single call. ```bash curl -X POST "https://ace.api.chain.link/v1/identities" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "title": "Acme Corp Treasury", "entity_id": "acme-corp-001", "registry_id": "", "onchain_identities": [ { "chain_selector": "16015286601757825753", "address": "0x1234567890abcdef1234567890abcdef12345678" } ], "credentials": [ { "credential_type_id": "", "external_unique_id": "kyc-2026-04-acme", "expires_at": 1806883200 }, { "credential_type_id": "" } ] }' ``` Each entry in the `credentials` array follows the same schema as the standalone `POST /credentials` endpoint, except that `identity_id` is inferred from the identity being created. See [Managing Identities](/ace/guides/identity-manager/manage-identities) for the full identity creation reference. ## View and filter credentials List credentials with a `GET` request. All query parameters are optional: ```bash curl "https://ace.api.chain.link/v1/credentials?credential_type_id=&page=1&page_size=25" \ -H "Authorization: Apikey " ``` | Parameter | Description | | -------------------- | --------------------------------------------------- | | `credential_type_id` | Filter by credential type | | `identity_id` | Filter by identity | | `entity_id` | Filter by entity | | `registry_id` | Filter by registry | | `include_onchains` | Include on-chain deployment details in the response | | `page` | Page number (default: 1) | | `page_size` | Results per page | To retrieve a single credential by its ID: ```bash curl "https://ace.api.chain.link/v1/credentials/" \ -H "Authorization: Apikey " ``` ## Credential expiration The `expires_at` field controls whether a credential has a limited validity period. - **No expiration** — Omit `expires_at` when issuing. The credential remains valid indefinitely until explicitly archived. - **With expiration** — Provide a Unix timestamp (integer). Once the timestamp passes, policy checks that require this credential type will treat the credential as invalid. To **renew** an expiring credential, update it with a new `expires_at` value (see the next section). Alternatively, you can archive the expired credential and issue a new one. > **TIP** > > Set `expires_at` to align with your compliance review cycle. For example, if KYC reviews happen annually, set > expiration to one year from issuance and update upon re-verification. ## Update a credential You can update a credential's `external_unique_id` and `expires_at` fields. You **cannot** change the credential type or the associated identity — to change either, archive the credential and issue a new one. Update a credential with a `PUT` request: ```bash curl -X PUT "https://ace.api.chain.link/v1/credentials/" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "external_unique_id": "kyc-2026-04-acme-renewed", "expires_at": 1838419200 }' ``` | Field | Required | Description | | -------------------- | -------- | ------------------------------------------------- | | `external_unique_id` | Yes | Updated reference identifier | | `expires_at` | No | New expiration timestamp; omit to leave unchanged | You can also perform a partial update with `PATCH`. The `PATCH` endpoint accepts `external_unique_id` and `expires_at` independently, but you **cannot** combine field updates with a status change in the same request: ```bash curl -X PATCH "https://ace.api.chain.link/v1/credentials/" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "expires_at": 1838419200 }' ``` ## Revoke (archive) a credential Archiving a credential removes it from the on-chain credential registry. After archival, policy contracts will no longer see this credential — any policy that requires it (such as the [Credential Registry Identity Validator](/ace/reference/policy-library/credential-registry-identity-validator-policy)) will reject transactions from the associated addresses. Common reasons to revoke a credential: - KYC verification expired or failed re-verification - Sanctions status changed - Accreditation lapsed - Entity relationship terminated Archive a credential with a `PATCH` request: ```bash curl -X PATCH "https://ace.api.chain.link/v1/credentials/" \ -H "Authorization: Apikey " \ -H "Content-Type: application/json" \ -d '{ "status": "archived" }' ``` > **CAUTION** > > Archiving triggers on-chain removal. The credential cannot be un-archived — to restore the attestation, issue a new > credential of the same type to the same identity. ## Related resources - [Cross-Chain Identity](/ace/concepts/cross-chain-identity) — CCID model, credential registries, and the attestation lifecycle - [Managing Identities](/ace/guides/identity-manager/manage-identities) — register and manage CCIDs and their on-chain address mappings - [Managing Credential Types](/ace/guides/identity-manager/manage-credential-types) — create and organize the credential categories your registry supports - [Credential Registry Identity Validator Policy](/ace/reference/policy-library/credential-registry-identity-validator-policy) — the policy that checks credentials at transaction time - [Managing Data Validators](/ace/guides/policy-manager/manage-data-validators) — enforce rules on credential data at transaction time - [Beta Scope](/ace/beta-scope) — current scope and limitations --- # External Registries Source: https://docs.chain.link/ace/guides/identity-manager/external-registries Last Updated: 2026-07-17 **External registries** let one organization reuse another organization's [registry](/ace/guides/identity-manager/manage-registries) without re-issuing identities or credentials. The registry owner grants a second organization **read access**, and that organization can then reference the registry's identities and credentials — for example, to enforce KYC in its own policies using a KYC provider's registry. Access is granted per registry, is **read-only** for the recipient, and can be revoked at any time. ## Roles and concepts | Term | Meaning | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Grantor** | The organization that **owns** the registry and grants access to it. | | **Grantee** | The organization that **receives** read access to the registry. | | **Access grant** | The link between a registry and a grantee organization. It is either `active` or `revoked`. | | **Org ID** | The identifier of an organization. The grantee shares theirs with the grantor so the grantor can grant access. Retrieve it with `GET /organizations/me` ([Coordinator API](/api/ace/coordinator/docs#/Organizations)). | | **`access_type`** | A field on a registry indicating whether the caller `owned` it or was `granted` access to it. | ## What the grantee can and cannot do An active grant gives the grantee **read access** to the registry: - **Can** list and view the registry, and read its [credential types](/ace/guides/identity-manager/manage-credential-types), [identities](/ace/guides/identity-manager/manage-identities), and [credentials](/ace/guides/identity-manager/manage-credentials). - **Can** reference the registry's on-chain contracts as a credential source in its own [identity-validation policies](/ace/reference/policy-library/credential-registry-identity-validator-policy). - **Cannot** write to the registry — registering identities, issuing credentials, or changing configuration remains exclusive to the grantor. > **NOTE: Only the owner manages grants** > > Creating, listing, and revoking access grants requires **ownership** of the registry. A grantee cannot re-share a > registry that was shared with it. ## Grant access to another organization Granting access requires the grantee's **Org ID**. Ask the grantee to retrieve it and share it with you: ```bash # Run by the grantee — returns their organization, including its id curl https://ace.api.chain.link/v1/organizations/me \ -H "Authorization: Apikey " ``` As the registry owner, create the grant: ```bash curl -X POST https://ace.api.chain.link/v1/registries//access-grants \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "grantee_org_id": "org-456" }' ``` The response is the created grant: ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "grantee_org_id": "org-456", "grantor_org_id": "org-123", "status": "active", "granted_at": 1800000000, "revoked_at": null } ``` ## View who has access List the active and past grants for a registry you own: ```bash curl https://ace.api.chain.link/v1/registries//access-grants \ -H "Authorization: Apikey " ``` Each entry includes the grantee, the status (`active` or `revoked`), and the `granted_at` / `revoked_at` timestamps, giving you an audit trail of who was granted access and when. ## Use a registry shared with you As a grantee, include `include_granted=true` when listing registries to see registries other organizations have shared with you, alongside your own: ```bash curl "https://ace.api.chain.link/v1/registries?include_granted=true" \ -H "Authorization: Apikey " ``` Each registry in the response carries an **`access_type`** field: - `"owned"` — your organization owns the registry. - `"granted"` — another organization (shown in `org_id`) granted you access. Once you can see a granted registry, you use it the same way you would reference any credential source: 1. Read the registry to get the on-chain **identity registry** and **credential registry** contract addresses per chain, and read its **credential types** to get the `credential_type_hash` values you need. 2. Add those addresses and credential type hashes as a **credential source** on your [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) or [GroupedIdentityValidatorPolicy](/ace/reference/policy-library/grouped-identity-validator-policy) instance. Your policy then validates credentials issued by the other organization at transaction time. Because the grant is read-only, you rely on the grantor to keep the credentials current; if they revoke a credential, your policy sees the change immediately. > **CAUTION: Access can be revoked** > > A granted registry remains usable only while the grant is active. If the grantor revokes access, your organization > loses read access to the registry and its sub-resources. ## Revoke access As the registry owner, revoke a grant by setting its status to `revoked`. Identify the grant by the grantee's Org ID: ```bash curl -X PATCH https://ace.api.chain.link/v1/registries//access-grants/org-456 \ -H "Content-Type: application/json" \ -H "Authorization: Apikey " \ -d '{ "status": "revoked" }' ``` Revocation takes effect immediately. The grant record is retained with a `revoked_at` timestamp for audit purposes rather than deleted, so the history of grants and revocations is preserved. To restore access later, create a new grant. ## What happens when access is revoked Revoking a registry access grant is a **platform-level action only** — it removes the registry from the grantee's view in the ACE Platform (UI and API). The grantee can no longer browse the registry, read its credentials, or reference it in new policy configurations. **Onchain, nothing changes automatically.** If the grantee's policies already reference the revoked registry's onchain contracts as a credential source, those policies continue to validate credentials from that registry at transaction time. The onchain policy contracts have no awareness of platform-level access grants — they only know the registry contract addresses that were configured as credential sources. ### What each party should do **Grantor** — After revoking access, be aware that the grantee's existing policies may still reference your registry onchain. **Grantee** — After a grant is revoked, the ACE Platform displays a warning on any policy instance that references a source from the revoked registry. You should remove the revoked registry source from your policy configuration to ensure your compliance setup reflects the current state of your access agreements. Until you remove it: - The policy continues to validate credentials from the revoked registry onchain. - You cannot edit the revoked source — you can only remove it. - You cannot reference the revoked registry in new policy configurations. > **CAUTION: Remove revoked sources to stay compliant** > > Revoking access does not reconfigure the grantee's policies onchain. The grantee must remove the revoked credential > source from their policy configuration. The ACE Platform warns you when a policy references a revoked source. ## Related pages - [Managing Registries](/ace/guides/identity-manager/manage-registries) — create and manage the registries you own - [Cross-Chain Identity](/ace/concepts/cross-chain-identity) — CCIDs, registries, and credential sources - [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) — reference a registry as a credential source - [Managing Policies](/ace/guides/policy-manager/manage-policies) — configure policy instances and their credential sources - [Coordinator API Reference](/api/ace/coordinator/docs) — full API schema --- # Policy Management Contracts Source: https://docs.chain.link/ace/reference/policy-management-contracts Last Updated: 2026-04-15 > **NOTE: On-chain contracts vs. ACE Platform** > > The contracts on this page are the **on-chain foundation** of ACE — smart contracts deployed on EVM chains that > enforce policies at transaction time. The **ACE Platform** (Policy Manager, Identity Manager, Reporting Manager) is a > separate set of off-chain services built on top of these contracts, providing a managed experience through the > Platform UI and APIs. See [Beta Scope](/ace/beta-scope) for current platform limitations. The Policy Management contracts handle on-chain policy enforcement for ACE-compatible contracts. The source code and full documentation are available in the [policy-management package](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/policy-management) of the [chainlink-ace repository](https://github.com/smartcontractkit/chainlink-ace) (Business Source License 1.1). ## Core interfaces | Interface | Description | | :--------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [IPolicyEngine](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/interfaces/IPolicyEngine.sol) | Central orchestrator that manages policies, extractors, and mappers for protected contracts. Receives calls from `PolicyProtected` targets, runs the policy chain, and returns allow/reject decisions. | | [IPolicyProtected](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/interfaces/IPolicyProtected.sol) | Base interface for any contract that wants policy enforcement. Provides the `runPolicy` modifier, the connection to a `PolicyEngine`, and context handling for passing off-chain data to policies. | | [IPolicy](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/interfaces/IPolicy.sol) | Standard interface for all policy contracts. Each policy implements `run` (read-only evaluation that returns allow/continue/reject) and optionally `postRun` (state changes after execution, such as updating volume counters). | | [IExtractor](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/interfaces/IExtractor.sol) | Parses transaction calldata into named parameters (e.g., `to` and `value` from an ERC-20 `transfer`) so policies can evaluate them. One extractor is registered per function signature. | | [IMapper](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/interfaces/IMapper.sol) | Optional interface for transforming or combining extracted parameters before they reach a policy. Only needed for advanced scenarios where a policy expects a different parameter shape than the extractor provides. | ## Pre-built policies ACE provides a library of audited, ready-to-use policy implementations covering common compliance scenarios — allowlists, volume limits, role-based access, pause controls, and more. See the [policies source code](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/policy-management/src/policies) for implementation details, or the [Policy Library](/ace/reference/policy-library) page for configuration and usage. ## Reference token implementations The repository includes reference token contracts that demonstrate full ACE integration: - [ERC-20 Compliance Token](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/tokens/erc-20) — A policy-protected ERC-20 with frozen token handling. - [ERC-3643 Compliance Token](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/tokens/erc-3643) — A compliant implementation of the ERC-3643 T-REX standard. ## Repository documentation The [policy-management docs](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/policy-management/docs) folder contains detailed guides: - [Concepts](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/CONCEPTS.md) — Architecture, policy flow, extractors, mappers, and context handling - [API Guide](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/API_GUIDE.md) — Task-oriented guide with code examples for common operations - [API Reference](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/API_REFERENCE.md) — Complete interface specifications with function signatures and events - [Custom Policies Tutorial](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/CUSTOM_POLICIES_TUTORIAL.md) — End-to-end walkthrough for building a custom policy contract - [Policy Ordering Guide](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/POLICY_ORDERING_GUIDE.md) — How evaluation order affects transaction outcomes - [Security Considerations](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/docs/SECURITY.md) — Trust model, gas considerations, and context handling ## Related pages - [Architecture](/ace/concepts/architecture) — How PolicyEngine contracts fit into the ACE system - [Policy Management](/ace/concepts/policy-management) — Conceptual overview of policy chains and evaluation - [Making Your Contract ACE-Compatible](/ace/guides/policy-manager/contracts/ace-compatible) — What your contract needs to work with ACE - [Policy Library](/ace/reference/policy-library) — Pre-built policy implementations with configuration details --- # Policy Library Source: https://docs.chain.link/ace/reference/policy-library Last Updated: 2026-07-17 ACE ships with a library of pre-built, audited policies that cover the most common compliance and access control patterns. Each policy is a standalone smart contract that plugs into a PolicyEngine and evaluates transactions at runtime. For guidance on combining policies and understanding execution order, see [Policy Ordering & Composition](/ace/concepts/policy-ordering). ## Policy summary | Policy | Description | | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | | [AllowPolicy](/ace/reference/policy-library/allow-policy) | Maintains an allowlist. Rejects the transaction if any checked address is **not** on the list. | | [BypassPolicy](/ace/reference/policy-library/bypass-policy) | Maintains an allowlist. If **all** checked addresses are on the list, immediately allows the transaction and **skips all remaining policies**. | | [RejectPolicy](/ace/reference/policy-library/reject-policy) | Maintains a denylist. Rejects the transaction if any checked address **is** on the list. | | [OnlyAuthorizedSenderPolicy](/ace/reference/policy-library/only-authorized-sender-policy) | Rejects the transaction if the sender (`msg.sender`) is not on the authorized list. | | [RoleBasedAccessControlPolicy](/ace/reference/policy-library/role-based-access-control-policy) | Maps roles to function selectors. Rejects if the sender does not hold a role allowed for the called function. | | [MaxPolicy](/ace/reference/policy-library/max-policy) | Rejects the transaction if the extracted value exceeds a configured maximum. | | [VolumePolicy](/ace/reference/policy-library/volume-policy) | Rejects the transaction if the extracted value is below a minimum or above a maximum. | | [VolumeRatePolicy](/ace/reference/policy-library/volume-rate-policy) | Tracks cumulative volume per account per time period. Rejects if the period's cap would be exceeded. | | [SecureMintPolicy](/ace/reference/policy-library/secure-mint-policy) | Checks a Chainlink Proof of Reserve feed. Rejects if minting would push total supply beyond verified reserves. | | [IntervalPolicy](/ace/reference/policy-library/interval-policy) | Divides time into repeating slot-based cycles. Rejects if the current slot is outside the allowed window. | | [PausePolicy](/ace/reference/policy-library/pause-policy) | Global toggle. Rejects every transaction when paused; passes through when unpaused. | | [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) | Checks each address against configured credential requirements. Rejects if any address lacks required credentials. | | [GroupedIdentityValidatorPolicy](/ace/reference/policy-library/grouped-identity-validator-policy) | Routes each address to a credential group, then validates it against that group's requirements. Rejects if no group matches or requirements fail. | | [CertifiedActionDONValidatorPolicy](/ace/reference/policy-library/certified-action-don-validator-policy) | Validates DON-issued permits delivered on-chain via the Keystone Forwarder. Rejects if no valid permit exists. | --- # AllowPolicy Source: https://docs.chain.link/ace/reference/policy-library/allow-policy Last Updated: 2026-03-31 The AllowPolicy restricts transactions to a known set of approved addresses. It checks every address extracted from the transaction against an allowlist and immediately rejects if any of them is not on the list, halting all subsequent policy checks. ## Configuration ### Address allowlist The allowlist defines which addresses are permitted to participate in transactions protected by this policy. The list starts empty at deployment and must be populated afterward — until you add at least one address, every transaction will be rejected. Each address is added or removed individually. When a protected function is called, the extractor provides one or more addresses from the transaction (for example, both the sender and receiver of a token transfer). Which addresses the policy receives depends on the [mapper configuration](/ace/concepts/policy-management#worked-example-erc-20-transfer). All of those addresses must be on the allowlist for the transaction to pass. ## Runtime behavior The policy expects a variable number of parameters from the extractor, each an address. All provided addresses are checked against the allowlist. - **`run()`** — Reverts if *any* address is not on the allowlist. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`allowAddress(address account)`** — Adds an address to the allowlist. Reverts if the address is already listed. - **`disallowAddress(address account)`** — Removes an address from the allowlist. Reverts if the address is not listed. ### View functions - **`addressAllowed(address account)`** — Returns `true` if the address is on the allowlist. ## Use cases - **Regulated access** — Restrict token transfers to a known set of approved addresses. - **Gradual rollout** — Start with a small allowlist and expand as new addresses are vetted. ## Source [AllowPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/AllowPolicy.sol) --- # BypassPolicy Source: https://docs.chain.link/ace/reference/policy-library/bypass-policy Last Updated: 2026-03-31 The BypassPolicy gives privileged addresses a fast path through the policy chain. If *all* addresses extracted from the transaction are on the bypass list, the policy immediately allows the transaction and skips every remaining policy in the chain. If any address is not on the list, the policy returns `Continue` and lets subsequent policies decide. This is the only built-in policy that returns `Allowed`. > **TIP: Place this policy first** > > Because the BypassPolicy short-circuits the entire chain when it matches, place it at the beginning of your policy > chain. This way, privileged addresses skip volume limits, identity checks, and all other restrictions in a single > step. ## Configuration ### Address allowlist The bypass list defines which addresses can skip the rest of the policy chain. The list starts empty at deployment and must be populated afterward. Each address is added or removed individually. When a protected function is called, the extractor provides one or more addresses from the transaction. Which addresses the policy receives depends on the [mapper configuration](/ace/concepts/policy-management#worked-example-erc-20-transfer). All of those addresses must be on the bypass list for the fast path to activate — if even one address is missing, the policy returns `Continue` and normal policy evaluation continues. ## Runtime behavior The policy expects a variable number of parameters from the extractor, each an address. - **`run()`** — Returns `Allowed` if all provided addresses are on the bypass list, skipping all subsequent policies. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`allowAddress(address account)`** — Adds an address to the bypass list. Reverts if the address is already listed. - **`disallowAddress(address account)`** — Removes an address from the bypass list. Reverts if the address is not listed. ### View functions - **`addressAllowed(address account)`** — Returns `true` if the address is on the bypass list. ## Use cases - **Privileged access** — Let administrators or system contracts bypass compliance checks entirely. - **Layered permissions** — Place at the top of a policy chain so that listed addresses skip volume limits, identity checks, and other restrictions. ## Source [BypassPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/BypassPolicy.sol) --- # CertifiedActionDONValidatorPolicy Source: https://docs.chain.link/ace/reference/policy-library/certified-action-don-validator-policy Last Updated: 2026-07-17 The CertifiedActionDONValidatorPolicy (CADV) is the onchain contract that validates permits generated by [offchain policy execution](/ace/concepts/off-chain-policies). When a Chainlink DON workflow approves an action, it delivers a permit through the Keystone Forwarder. The CADV stores the permit and verifies it when the protected function is called. For a full explanation of how off-chain policies work, what you can connect to, and how the permit flow operates, see [Off-Chain Policy Execution](/ace/concepts/off-chain-policies). > **CAUTION: MVP managed service** > > Managed offchain risk policies are an MVP. Contact your Chainlink representative before using the service and for help > with setup. When you create a [managed offchain risk > policy](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies), ACE deploys and configures one CADV > per selected chain and authorizes the managed workflow to publish permits. You do not deploy or configure the CADV > directly. ## Permit lifecycle Managed offchain permits are **pre-presented**: the DON writes the permit to the CADV before the user submits the protected transaction. The user does not include permit bytes in the transaction. The CADV indexes a permit by its transaction intent, which includes: - Caller address - Protected target address - Function selector - Ordered parameters produced by the target function's extractor At execution time, the policy engine passes the actual caller, target, selector, and extracted parameters to the CADV. The call is allowed only when they match a stored, valid permit. After the protected call succeeds, the policy engine invokes `postRun`, which increments the permit's usage count and emits `PermitUsed`. The managed `wallet_risk_scoring` policy currently creates permits with: - `maxUses = 1` — the permit can authorize one successful transaction. - `expiry = 0` — the permit does not expire. These values are fixed in the current Beta release. The CADV also emits `PermitStored` when the workflow publishes a permit. ACE waits for this event before changing the corresponding evaluation status to `ready`. ## Combining with other policies The CADV is attached to a target function like any other policy. It can run before or after onchain policies such as identity, allowlist, or volume checks. All policies in the function's policy chain must allow the call. See [Policy Ordering & Composition](/ace/concepts/policy-ordering) for guidance on evaluation order. ## Related pages - [Managing Offchain Policies (MVP)](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies) - [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits) - [Off-Chain Policy Execution](/ace/concepts/off-chain-policies) ## Source - [CertifiedActionDONValidatorPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/CertifiedActionDONValidatorPolicy.sol) --- # CredentialRegistryIdentityValidatorPolicy Source: https://docs.chain.link/ace/reference/policy-library/credential-registry-identity-validator-policy Last Updated: 2026-07-17 The CredentialRegistryIdentityValidatorPolicy validates that accounts involved in a transaction hold the required credentials from ACE's [Cross-Chain Identity](/ace/concepts/cross-chain-identity) infrastructure. It checks each account against configured credential sources (IdentityRegistry + CredentialRegistry pairs) and credential requirements (which credential types must be present and how many validations are needed). This is the primary policy for enforcing identity-based compliance such as KYC, accreditation, or sanctions screening. > **NOTE: Attestation and data validation** > > A credential source checks that a credential exists (attestation). It can optionally also validate the credential's > contents by referencing a **Data Validator** — for example, to enforce a jurisdiction allow/deny list. See [Managing > Data Validators](/ace/guides/policy-manager/manage-data-validators) and [Credential data and > privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy). ## Configuration Both properties below can be set when the policy is first deployed and updated afterward by the policy owner. Credential sources contain on-chain addresses and must be configured per network. Credential requirements define rules that apply across chains. ### Credential sources A credential source defines *where* to look up identity and credential data for a given credential type. Each source is a tuple of: - **Credential type ID** — A `bytes32` identifier for the credential type this source applies to (e.g., KYC, accreditation). - **Identity registry address** — The IdentityRegistry contract that maps wallet addresses to Cross-Chain Identifiers (CCIDs). - **Credential registry address** — The CredentialRegistry contract that stores credentials linked to CCIDs. - **Data validator address** (optional) — A contract that performs additional validation on the credential data. Set to `address(0)` for attestation-only checks, or a [Data Validator](/ace/guides/policy-manager/manage-data-validators) address to validate credential contents (for example, a jurisdiction allow/deny list). See [Credential data and privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy) for a full explanation of attestation-only vs. Credential Data Validator checks. Multiple sources can be registered for the same credential type. The policy checks all configured sources and counts validations across them. Source uniqueness is determined by the `(identityRegistry, credentialRegistry)` pair — two sources with the same registry pair but different `dataValidator` addresses are considered duplicates. **Limits:** Up to 8 sources per credential type. ### Credential requirements A credential requirement defines *what* credentials an account must hold. Each requirement specifies: - **Requirement ID** — A unique `bytes32` identifier for this requirement. - **Credential type IDs** — An array of `bytes32` credential types to check (e.g., KYC, accreditation). - **Minimum validations** — How many of the listed credential types must validate successfully. Must be at least 1. - **Invert flag** — When `true`, the check passes if the credential does *not* exist. This is useful for "must not be sanctioned" checks, where you want the transaction to succeed only if the account does not hold a sanctions credential. An account passes a requirement if it accumulates at least `minValidations` successful validations across the listed credential types and configured sources. **Limits:** Up to 8 requirements total, up to 32 credential types per requirement. ## Runtime behavior The policy expects a variable number of parameters from the extractor, each an address to validate. Every address is checked against all configured requirements. For each address, the validation process: 1. Iterates through all credential requirements. 2. For each requirement, checks the listed credential types against each configured source. 3. For each source, looks up the account's CCID in the IdentityRegistry, then checks whether the CredentialRegistry holds the credential for that CCID. 4. If a DataValidator is configured, it additionally validates the credential data. 5. Counts successful validations. If the count meets `minValidations`, the requirement passes. - **`run()`** — Reverts if any address fails any requirement. Returns `Continue` if all addresses pass all requirements. - **`postRun()`** — No state changes. ## API reference ### Setter functions **Credential sources:** - **`addCredentialSource(CredentialSourceInput input)`** — Adds a source for a credential type. Reverts if the source already exists or if the maximum number of sources (8) for that credential type has been reached. - **`removeCredentialSource(bytes32 credentialTypeId, address identityRegistry, address credentialRegistry)`** — Removes a source. Reverts if the source is not found. **Credential requirements:** - **`addCredentialRequirement(CredentialRequirementInput input)`** — Adds a requirement. Reverts if a requirement with the same ID already exists or if the configuration is invalid. - **`removeCredentialRequirement(bytes32 requirementId)`** — Removes a requirement. Reverts if the requirement ID is not found. ### View functions - **`getCredentialSources(bytes32 credentialTypeId)`** — Returns all sources for a credential type. - **`getCredentialRequirement(bytes32 requirementId)`** — Returns a requirement's configuration. - **`getCredentialRequirementIds()`** — Returns all requirement IDs. ## Use cases - **KYC enforcement** — Require that both sender and receiver hold a valid KYC credential before a token transfer. - **Accredited investor checks** — Restrict security token operations to accounts with accreditation credentials. - **Sanctions screening** — Use the `invert` flag to reject accounts that hold a sanctions credential. - **Multi-source validation** — Require credentials from multiple identity providers by configuring multiple sources and setting `minValidations > 1`. ## Source - [CredentialRegistryIdentityValidatorPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/CredentialRegistryIdentityValidatorPolicy.sol) - [CredentialRegistryIdentityValidator.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/CredentialRegistryIdentityValidator.sol) --- # GroupedIdentityValidatorPolicy Source: https://docs.chain.link/ace/reference/policy-library/grouped-identity-validator-policy Last Updated: 2026-07-17 The GroupedIdentityValidatorPolicy validates transaction participants against **different credential requirements depending on who they are**. Instead of applying one fixed rule set to every account — as the [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) does — it first **routes** each account to a **group**, then validates the account against that group's requirements. This lets a single policy enforce, for example, one set of rules for individuals and another for businesses, or different rules per jurisdiction. It is built on ACE's [Cross-Chain Identity](/ace/concepts/cross-chain-identity) infrastructure and, like the flat validator, resolves each account's address to a CCID and checks credentials from configured sources (IdentityRegistry + CredentialRegistry pairs). > **NOTE: When to use this policy** > > Use the > [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy) > when every account must satisfy the **same** credential requirements. Use the GroupedIdentityValidatorPolicy when > requirements **differ by segment** (individual vs. business, jurisdiction, tier) and each account should be matched to > exactly one segment. ## How it works The policy evaluates each account in two phases: 1. **Routing** — The policy determines which group an account belongs to. Groups are evaluated in the order they were added, and the **first group that matches wins**. An account matches a group when it satisfies that group's routing configuration. 2. **Validation** — Once routed, the account must satisfy **all** of the matched group's credential requirements. If the account matches no group, or fails the matched group's requirements, the transaction is rejected. Because the first matching group wins, group order matters. Place more specific groups before broader ones so that an account is not routed into a broader group before its intended one is evaluated. ## Configuration A group has three parts: - A **routing configuration** (how accounts are matched to the group), - **Credential requirements** (what a routed account must hold), and - One or more **credential sources** (where to look up identities and credentials). All three can be set when the policy is deployed and updated afterward by the policy owner. ### Groups Each group has a unique `bytes32` group identifier (`groupId`) — typically the `keccak256` hash of a namespaced label, such as `keccak256("PERSON")` or `keccak256("BUSINESS")`. A `groupId` of zero is rejected. ### Routing configuration Routing decides which accounts belong to a group. Each group has one routing configuration with: - **Credential type IDs** — The credential types used for routing (e.g., a `kyc` credential, or a `country` credential). - **Routing kind** — Either `Attestation` or `Data` (see below). - **Criteria** — For `Data` routing only: the list of accepted credential-data values. Each criterion is a `bytes32` hash of the accepted credential data (`keccak256(abi.encode(data))`). Must be empty for `Attestation` routing. - **Minimum validations** — How many distinct configured sources must match before the group is selected. Must be at least 1. There are two routing kinds: **Attestation routing** matches an account if it simply **holds** the routing credential — the credential data is ignored. For example, "route any account that holds a KYB credential into the business group". **Data routing** matches an account only if the **contents** of its routing credential match one of the configured criteria. For example, "route accounts whose `country` credential data is `US` into the US group". The policy fetches the stored credential data, hashes it, and compares it against the group's criteria. > **NOTE: Data routing granularity** > > Data routing matches on the stored credential payload, so its granularity is bounded by the credential schema chosen > by the issuer. A broad `country`-only credential cannot distinguish a sub-country region from the rest of the country. > For geography-sensitive use cases where future jurisdiction carve-outs may matter, issue credentials with granular > schemas (for example, separate region or territory identifiers) rather than a single broad field. See [Credential data > and privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy). > **CAUTION: Routing must be unambiguous** > > A given routing target — the combination of a credential type and (for data routing) a criterion — can belong to only > **one** group. Attempting to configure the same routing target on two groups reverts with `RoutingOverlap`. This > guarantees every account routes to at most one group deterministically. For data routing, a routing credential type may have **at most one source per group**. Configuring more than one source for a data-routing credential type reverts with `MultipleSourcesForDataRouting`. ### Group requirements A requirement defines what a routed account must hold to pass. Each requirement has: - **Requirement ID** — A unique `bytes32` identifier within the group. Cannot be zero. - **Credential type IDs** — An array of `bytes32` credential types to check. - **Minimum validations** — How many of the listed credential types must validate successfully across the group's sources. Must be at least 1. - **Invert flag** — When `true`, the check passes if the credential does *not* exist. Useful for "must not be sanctioned" checks. An account passes a requirement when it accumulates at least `minValidations` successful validations across the listed credential types and configured sources. An account passes the group only when it satisfies **every** requirement in that group. A group with no requirements passes any account that routes to it. > **NOTE: Inverted requirements and missing identities** > > For an inverted requirement, an account with **no identity** in the source's IdentityRegistry counts as a successful > validation — the account provably does not hold the credential. This matches the behavior of the > [CredentialRegistryIdentityValidatorPolicy](/ace/reference/policy-library/credential-registry-identity-validator-policy). ### Credential sources A source tells the policy where to resolve identities and credentials for a given credential type within a group. Each source is a tuple of: - **Credential type ID** — The `bytes32` credential type this source applies to. - **Identity registry address** — The IdentityRegistry that maps wallet addresses to CCIDs. - **Credential registry address** — The CredentialRegistry that stores credentials linked to CCIDs. - **Data validator address** (optional) — A contract that performs additional validation on the credential data. Set to `address(0)` for attestation-only checks, or a [Data Validator](/ace/guides/policy-manager/manage-data-validators) address to validate credential contents. See [Credential data and privacy](/ace/concepts/cross-chain-identity#credential-data-and-privacy). Source uniqueness within a group and credential type is determined by the `(identityRegistry, credentialRegistry)` pair. Both routing and requirements draw on the same per-group sources. ### Limits | Constraint | Maximum | | :--------------------------------- | :------ | | Groups | 8 | | Requirements per group | 8 | | Sources per credential type | 8 | | Credential types per requirement | 32 | | Routing credential types per group | 32 | | Criteria per group (data routing) | 32 | ## Runtime behavior The policy expects a variable number of parameters from the extractor, each an address to route and validate. Every address is processed independently. For each address, the policy: 1. Routes the account to the first group whose routing configuration matches (at least `minValidations` matching sources). 2. Validates the account against every requirement in the matched group. - **`run()`** — Reverts with `PolicyRejected("no routing match")` if an address matches no group, or `PolicyRejected("group requirements failed")` if it fails the matched group's requirements. Returns `Continue` when every address routes and passes. - **`postRun()`** — No state changes. Emits an `IdentityValidated` event for each account's matched routing sources and satisfied requirements. ## API reference ### Setter functions **Groups:** - **`addGroup(GroupInput input)`** — Adds a group with its routing configuration. Reverts if the group already exists, the maximum number of groups (8) is reached, the routing configuration is invalid, or the routing target overlaps another group. - **`removeGroup(bytes32 groupId)`** — Removes a group and clears its requirements and sources. Reverts if the group is not found. - **`updateGroupRouting(bytes32 groupId, RoutingConfig routing)`** — Replaces a group's routing configuration. Reverts if the group is not found, the configuration is invalid, or the new routing target overlaps another group. **Group requirements:** - **`addGroupRequirement(bytes32 groupId, bytes32 requirementId, bytes32[] credentialTypeIds, uint256 minValidations, bool invert)`** — Adds a requirement to a group. Reverts if the group is not found, the requirement already exists, the configuration is invalid, or the maximum number of requirements (8) is reached. - **`removeGroupRequirement(bytes32 groupId, bytes32 requirementId)`** — Removes a requirement from a group. Reverts if the group or requirement is not found. **Group sources:** - **`addGroupSource(bytes32 groupId, bytes32 credentialTypeId, address identityRegistry, address credentialRegistry, address dataValidator)`** — Adds a source to a group. Reverts if the group is not found, the source already exists, the maximum number of sources (8) is reached, a registry address is zero, the credential type is zero, the data validator is a non-contract address, or a second source is added for a data-routing credential type. - **`removeGroupSource(bytes32 groupId, bytes32 credentialTypeId, address identityRegistry, address credentialRegistry)`** — Removes a source. Reverts if the group or source is not found. ### View functions - **`getGroupIds()`** — Returns all configured group identifiers, in evaluation order. - **`getGroupRouting(bytes32 groupId)`** — Returns a group's routing configuration. - **`getGroupRequirementIds(bytes32 groupId)`** — Returns all requirement identifiers for a group. - **`getGroupRequirement(bytes32 groupId, bytes32 requirementId)`** — Returns a requirement's configuration. - **`getGroupSources(bytes32 groupId, bytes32 credentialTypeId)`** — Returns the sources configured for a group and credential type. - **`validate(address account, bytes context)`** — Returns `true` if the account routes to a group and satisfies its requirements. ## Use cases - **Individual vs. business rules** — Route accounts holding a KYC credential to a personal group and accounts holding a KYB credential to a business group, each with its own requirements. - **Jurisdiction-based compliance** — Use data routing on a `country` credential to route accounts into per-jurisdiction groups, applying region-specific requirements (e.g., accreditation for one jurisdiction, additional AML checks for another). - **Tiered access** — Route accounts by an investor-tier credential and enforce progressively stricter requirements per tier. - **Higher-assurance routing** — Require multiple independent sources to agree (`minValidations > 1`) before an account is routed into a sensitive group. ## Source - [GroupedIdentityValidatorPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/GroupedIdentityValidatorPolicy.sol) - [GroupedCredentialRegistryIdentityValidator.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/GroupedCredentialRegistryIdentityValidator.sol) --- # IntervalPolicy Source: https://docs.chain.link/ace/reference/policy-library/interval-policy Last Updated: 2026-03-31 The IntervalPolicy restricts transaction execution to specific time slots within a repeating cycle. It divides time into fixed-length slots, groups those slots into a cycle, and only allows transactions when the current slot falls within a configured range. Transactions attempted outside the allowed window are rejected. ## Configuration All properties are set when the policy is first deployed and can be updated afterward by the policy owner. ### Start and end slots The start slot and end slot define the allowed window within each cycle. The start slot is **inclusive** and the end slot is **exclusive** — a transaction is permitted when the current slot is at or after the start and strictly before the end. For example, with a start slot of `9` and an end slot of `17`, slots 9 through 16 are allowed while slots 0-8 and 17-23 are blocked. The start slot must always be strictly less than the end slot. ### Cycle parameters The cycle parameters control how time is divided into slots and repeated: - **Slot duration** — The length of each slot in seconds. For example, `3600` for one-hour slots or `86400` for one-day slots. - **Cycle size** — The total number of slots in one cycle. For example, `24` for a daily cycle using hourly slots, or `7` for a weekly cycle using daily slots. - **Cycle offset** — Shifts the slot numbering within the cycle. Use this to align the cycle with a specific starting point (for example, adjusting for time zones or aligning day numbering so that slot `1` corresponds to Monday). Must be less than the cycle size. ## How the slot calculation works The policy computes the current slot from the block timestamp: ```solidity currentSlot = ((block.timestamp / slotDuration) % cycleSize + cycleOffset) % cycleSize ``` A transaction is permitted only if `currentSlot` falls within `[startSlot, endSlot)`. ## Configuration examples **Daily business hours (9 AM - 5 PM UTC):** | Property | Value | | ------------- | ------------- | | Slot duration | 3600 (1 hour) | | Cycle size | 24 | | Cycle offset | 0 | | Start slot | 9 | | End slot | 17 | **Weekday operations (Monday - Friday):** | Property | Value | | ------------- | ----------------------- | | Slot duration | 86400 (1 day) | | Cycle size | 7 | | Cycle offset | 0 | | Start slot | 1 (Monday) | | End slot | 6 (Saturday, exclusive) | ## Runtime behavior This policy does not use extracted parameters. It relies entirely on `block.timestamp` and its configured cycle. - **`run()`** — Reverts if the current slot is outside `[startSlot, endSlot)`. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`setStartSlot(uint256 startSlot)`** — Updates the start of the allowed window (inclusive). Must be strictly less than the current end slot. - **`setEndSlot(uint256 endSlot)`** — Updates the end of the allowed window (exclusive). Must be strictly greater than the current start slot and less than or equal to the cycle size. - **`setCycleParameters(uint256 slotDuration, uint256 cycleSize, uint256 cycleOffset)`** — Updates the slot duration (in seconds), the number of slots per cycle, and the cycle offset. Slot duration must be greater than zero. Cycle size must be greater than or equal to the current end slot. Cycle offset must be less than the cycle size. ### View functions - **`getStartSlot()`** — Returns the current start slot (inclusive). - **`getEndSlot()`** — Returns the current end slot (exclusive). - **`getCycleParameters()`** — Returns the slot duration, cycle size, and cycle offset. ## Use cases - **Operating hours** — Restrict transactions to business hours. - **Weekly schedules** — Allow operations only on weekdays. - **Maintenance windows** — Automatically block transactions outside scheduled operating periods. ## Source [IntervalPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/IntervalPolicy.sol) --- # MaxPolicy Source: https://docs.chain.link/ace/reference/policy-library/max-policy Last Updated: 2026-03-31 The MaxPolicy enforces a maximum value constraint on individual transactions. It compares a value extracted from the transaction (for example, a transfer amount) against a configured ceiling and rejects any transaction where the value exceeds that ceiling. ## Configuration ### Maximum amount Set a single `uint256` value that defines the upper limit for the extracted parameter. Any transaction where the extracted value exceeds this number will be rejected. The maximum is set when the policy is first deployed and can be updated afterward by the policy owner. ## Runtime behavior The policy expects exactly one parameter from the extractor: | Parameter | Type | Description | | --------- | --------- | --------------------------------------- | | `amount` | `uint256` | The value to check against the maximum. | - **`run()`** — Reverts if `amount > max`. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`setMax(uint256 max)`** — Updates the maximum allowed value. ### View functions - **`getMax()`** — Returns the current maximum. ## Use cases - **Transfer caps** — Limit individual token transfers to a maximum amount. - **Spending limits** — Prevent single transactions from exceeding a threshold. ## Source [MaxPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/MaxPolicy.sol) --- # OnlyAuthorizedSenderPolicy Source: https://docs.chain.link/ace/reference/policy-library/only-authorized-sender-policy Last Updated: 2026-03-31 The OnlyAuthorizedSenderPolicy restricts who can call a protected function based on the transaction sender. Unlike the AllowPolicy and RejectPolicy (which check addresses extracted from the transaction parameters), this policy checks `msg.sender` directly and rejects if the sender is not on the authorized list. ## Configuration ### Authorized sender list The authorized sender list defines which addresses can call the protected function. The list starts empty at deployment and must be populated afterward — until you add at least one address, every transaction will be rejected. Each address is added or removed individually. > **NOTE: Sender vs. extracted addresses** > > This policy ignores the addresses extracted by the extractor. It checks only the `msg.sender` of the transaction. If > you need to validate addresses from the transaction parameters (such as a transfer recipient), use the > [AllowPolicy](/ace/reference/policy-library/allow-policy) or > [RejectPolicy](/ace/reference/policy-library/reject-policy) instead. ## Runtime behavior This policy does not use extracted parameters. It checks `msg.sender` directly. - **`run()`** — Reverts if the sender is not on the authorized list. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`authorizeSender(address account)`** — Adds an address to the authorized list. Reverts if the address is already authorized. - **`unauthorizeSender(address account)`** — Removes an address from the authorized list. Reverts if the address is not authorized. ### View functions - **`senderAuthorized(address account)`** — Returns `true` if the address is authorized. ## Use cases - **Restricted operations** — Limit who can call specific contract functions regardless of the function arguments. ## Source [OnlyAuthorizedSenderPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/OnlyAuthorizedSenderPolicy.sol) --- # PausePolicy Source: https://docs.chain.link/ace/reference/policy-library/pause-policy Last Updated: 2026-03-31 The PausePolicy provides a global pause/unpause mechanism for protected functions. When paused, the policy rejects every transaction regardless of any other conditions. When unpaused, it returns `Continue` and lets subsequent policies decide. ## Configuration ### Paused state A single boolean toggle that controls whether the policy is active. When set to `true`, all transactions through this policy are rejected. When set to `false`, transactions pass through normally. The initial state is set when the policy is first deployed. You can deploy the contract in a paused state by passing `true` during initialization — this is useful when you want to finalize other configuration (such as allowlists or volume limits on other policies in the chain) before going live. ## Runtime behavior This policy does not use extracted parameters. It checks only its internal pause flag. - **`run()`** — Reverts if paused. Returns `Continue` if unpaused. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`setPausedState(bool paused)`** — Sets the pause state. Pass `true` to pause (reject all transactions) or `false` to unpause (resume normal flow). Reverts if the new state is the same as the current one. ### View functions - **`s_paused()`** — Returns `true` if the policy is currently paused. ## Use cases - **Emergency stop** — Immediately halt all protected functions during a security incident or contract vulnerability. - **Maintenance mode** — Temporarily pause interactions during upgrades or migrations. - **Gradual rollout** — Deploy the contract paused and activate it when everything is ready. ## Source [PausePolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/PausePolicy.sol) --- # RejectPolicy Source: https://docs.chain.link/ace/reference/policy-library/reject-policy Last Updated: 2026-03-31 The RejectPolicy blocks transactions involving addresses on a denylist. It checks every address extracted from the transaction and immediately rejects if any of them is on the list, halting all subsequent policy checks. ## Configuration ### Address denylist The denylist defines which addresses are blocked from participating in protected transactions. The list starts empty at deployment and must be populated afterward — until you add addresses, no transactions will be blocked by this policy. Each address is added or removed individually. When a protected function is called, the extractor provides one or more addresses from the transaction. Which addresses the policy receives depends on the [mapper configuration](/ace/concepts/policy-management#worked-example-erc-20-transfer). If *any* of those addresses is on the denylist, the transaction is rejected immediately. ## Runtime behavior The policy expects a variable number of parameters from the extractor, each an address. - **`run()`** — Reverts if *any* address is on the denylist. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`rejectAddress(address account)`** — Adds an address to the denylist. Reverts if the address is already listed. - **`unrejectAddress(address account)`** — Removes an address from the denylist. Reverts if the address is not listed. ### View functions - **`addressRejected(address account)`** — Returns `true` if the address is on the denylist. ## Use cases - **Account blocking** — Block known malicious addresses, compromised wallets, or sanctioned entities from interacting with the contract. ## Source [RejectPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/RejectPolicy.sol) --- # RoleBasedAccessControlPolicy Source: https://docs.chain.link/ace/reference/policy-library/role-based-access-control-policy Last Updated: 2026-03-31 The RoleBasedAccessControlPolicy provides fine-grained, role-based access control for protected functions. It maps named roles to specific function selectors (operations) and checks whether the transaction sender holds a role that is permitted to perform the requested operation. If the sender lacks the required role, the transaction is rejected. This policy is built on [OpenZeppelin's AccessControlUpgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/AccessControlUpgradeable.sol), which handles role storage and administration. ## Configuration Both mappings described below are empty at deployment and must be populated afterward. The policy owner automatically receives the `DEFAULT_ADMIN_ROLE` at initialization, which grants the ability to manage other roles. ### Operation allowances An operation allowance links a role to a function selector. A function selector is a 4-byte identifier (e.g., `0xa9059cbb` for `transfer(address,uint256)`) that uniquely identifies a smart contract function. To permit a role to call a specific function, grant an operation allowance for that function's selector. You can grant multiple roles permission for the same function, and a single role can be allowed across multiple functions. ### Role assignments A role assignment links an address to a role. Once an address holds a role, it can call any function that role has an operation allowance for. Roles are identified by `bytes32` values. You define your own role identifiers — for example, `keccak256("MINTER_ROLE")` for a minting role or `keccak256("COMPLIANCE_OFFICER")` for a compliance role. ## Runtime behavior This policy checks `msg.sender` against role assignments for the current function selector. It does not use extracted parameters. - **`run()`** — Reverts if the sender does not hold any role with an operation allowance for the current function selector. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions **Operation allowances:** - **`grantOperationAllowanceToRole(bytes4 operation, bytes32 role)`** — Grants a role permission to call the specified function. Reverts if the role already has an allowance for this operation. - **`removeOperationAllowanceFromRole(bytes4 operation, bytes32 role)`** — Revokes a role's permission for the specified function. Reverts if the role does not have an allowance for this operation. **Role assignments:** - **`grantRole(bytes32 role, address account)`** — Assigns a role to an address. - **`revokeRole(bytes32 role, address account)`** — Revokes a role from an address. ### View functions - **`hasAllowedRole(bytes4 operation, address account)`** — Returns `true` if the account holds any role that is permitted to perform the operation. ## Use cases - **Granular function permissions** — Assign different roles (admin, minter, compliance officer) and control which functions each role can call. - **Team management** — Grant or revoke privileges across multiple operations and roles without redeploying contracts. ## Source [RoleBasedAccessControlPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/RoleBasedAccessControlPolicy.sol) --- # SecureMintPolicy Source: https://docs.chain.link/ace/reference/policy-library/secure-mint-policy Last Updated: 2026-03-31 The SecureMintPolicy ensures the total supply of a token does not exceed the actual reserves of the underlying asset. Before every mint, it reads the latest reserve value from a [Chainlink Proof of Reserve](/data-feeds/proof-of-reserve) data feed and checks whether the new total supply (current supply plus the requested mint amount) would exceed what the reserves can back. If it would, the policy rejects the transaction. ## Configuration All properties are set when the policy is first deployed and can be updated individually afterward by the policy owner. ### Proof of Reserve feed The policy needs the address of a data feed contract that reports the reserves backing your token. This is typically a [Chainlink Proof of Reserve](/data-feeds/proof-of-reserve) feed, but any contract that implements the `AggregatorV3Interface` will work. You can find available Proof of Reserve feeds on the [Data Feeds page](/data-feeds/smartdata/addresses). Each SecureMintPolicy instance supports one feed. If your token is backed by multiple reserve sources, deploy a separate SecureMintPolicy instance for each feed and add them all to the same policy chain. ### Token metadata Provide the address of the token this policy protects and its number of decimals (between 1 and 18). The policy needs both values to work correctly: - **Token address** — At runtime, the policy calls `totalSupply()` on the protected contract to determine the current supply. The token address you provide here is used during configuration to validate the decimals value against the token's on-chain metadata. - **Token decimals** — The policy uses the decimals to scale the reserve feed's value so the comparison is accurate. If the token exposes a `decimals()` function on-chain, the policy validates your input against it automatically. If the token does not expose `decimals()`, the value you provide is used as-is — make sure it is correct. > **CAUTION: Decimal scaling** > > The policy scales between the feed's decimals and the token's decimals automatically. However, if you provide an > incorrect decimal value for a token that does not expose `decimals()` on-chain, the reserve calculation will be wrong > — potentially allowing over-minting or blocking valid mints. Always verify the token's actual decimals before > configuring this policy. ### Reserve margin By default, the maximum mintable supply equals the reported reserves exactly. A reserve margin lets you adjust this relationship — either requiring reserves to exceed the supply by a buffer (positive margin) or allowing the supply to slightly exceed reserves for operational flexibility (negative margin). The margin has two parts: a **mode** that determines how the margin is calculated, and an **amount** that sets how large the margin is. | Mode | Effect | Example (reserves = 1000) | | -------------------- | -------------------------------------------------------------------------- | -------------------------- | | `None` | No margin. Maximum supply equals reserves directly. | Limit = 1000 | | `PositivePercentage` | Reserves must exceed supply by a percentage. Keeps a safety cushion. | 10% margin -> limit = 900 | | `PositiveAbsolute` | Reserves must exceed supply by a fixed amount. | 50 margin -> limit = 950 | | `NegativePercentage` | Supply can exceed reserves by a percentage. Provides operational headroom. | 10% margin -> limit = 1100 | | `NegativeAbsolute` | Supply can exceed reserves by a fixed amount. | 50 margin -> limit = 1050 | For percentage-based modes, the amount is specified in basis points (hundredths of a percent). For example, 12.34% is represented as `1234`, and 100% is `10000`. ### Staleness threshold Reserve data can become outdated if the feed hasn't been updated recently. The staleness threshold defines the maximum age (in seconds) of the reserve data before the policy rejects. If the feed's last update is older than this threshold, any mint attempt will fail. To choose the right value, check the **heartbeat** of your Proof of Reserve feed on the [Data Feeds page](/data-feeds/proof-of-reserve). The heartbeat tells you the maximum interval between feed updates. Your staleness threshold should typically match or slightly exceed the heartbeat. Setting the staleness threshold to `0` disables the check entirely — the policy will accept reserve data of any age. ## Runtime behavior The policy expects one parameter from the extractor: | Parameter | Type | Description | | --------- | --------- | ---------------------------------- | | `amount` | `uint256` | The number of tokens being minted. | - **`run()`** — Fetches the latest reserve value from the configured data feed. Rejects if the reserve value is negative. If a staleness threshold is set and the data is too old, rejects. Scales the reserve value to match the token's decimals, then calculates the maximum mintable supply using the reserve margin configuration. Rejects if `totalSupply + amount` would exceed this limit. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`setReservesFeed(address reservesFeed)`** — Updates the data feed address. Reverts if the new address is the same as the current one. - **`setTokenMetadata(address tokenAddress, uint8 tokenDecimals)`** — Updates the token decimals. The token address must match the current one (you cannot change the protected token). Decimals must be between 1 and 18, must differ from the current value, and are validated against the token's on-chain metadata when available. - **`setReserveMargin(ReserveMarginMode mode, uint256 amount)`** — Updates the margin mode and amount. For percentage modes, the amount is in basis points and must be `<= 10000`. For absolute modes, the amount must be `> 0`. Reverts if both values are identical to the current configuration. - **`setMaxStalenessSeconds(uint256 value)`** — Updates the staleness threshold in seconds. Set to `0` to disable the check. Reverts if the value is the same as the current one. ### View functions - **`reservesFeed()`** — Returns the address of the current data feed. - **`reserveMarginMode()`** — Returns the current margin mode. - **`reserveMarginAmount()`** — Returns the current margin amount. - **`maxStalenessSeconds()`** — Returns the current staleness threshold in seconds. ## Use cases - **Collateralized token minting** — Ensure that a stablecoin or asset-backed token cannot be minted beyond the value of its verified reserves. ## Source [SecureMintPolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/SecureMintPolicy.sol) --- # VolumePolicy Source: https://docs.chain.link/ace/reference/policy-library/volume-policy Last Updated: 2026-03-31 The VolumePolicy enforces minimum and maximum value constraints on individual transactions. It compares a value extracted from the transaction against configured bounds and rejects if the value falls outside the allowed range. ## Configuration Both bounds are set when the policy is first deployed and can be updated independently afterward by the policy owner. ### Minimum amount The minimum defines the lowest acceptable value for the extracted parameter. Any transaction where the extracted value is below this number will be rejected. Setting the minimum to `0` effectively disables the lower bound — all values will pass the minimum check. ### Maximum amount The maximum defines the highest acceptable value for the extracted parameter. Any transaction where the extracted value exceeds this number will be rejected. Setting the maximum to `0` disables the upper bound — there will be no ceiling on the value. > **NOTE: Relationship between minimum and maximum** > > The minimum must be strictly less than the maximum, unless one of them is `0`. For example, you cannot set a minimum > of 100 and a maximum of 50. You can set a minimum of 100 and a maximum of `0` (no upper limit), or a minimum of `0` > and a maximum of 50 (no lower limit). ## Runtime behavior The policy expects exactly one parameter from the extractor: | Parameter | Type | Description | | --------- | --------- | ------------------------------------------------- | | `amount` | `uint256` | The value to check against the configured bounds. | - **`run()`** — Reverts if `amount < min` or (when `max != 0`) `amount > max`. Returns `Continue` otherwise. - **`postRun()`** — No state changes. ## API reference ### Setter functions - **`setMin(uint256 minAmount)`** — Updates the minimum. Must be strictly less than the current maximum (unless max is `0`). Reverts if the new value is the same as the current minimum. - **`setMax(uint256 maxAmount)`** — Updates the maximum. Must be strictly greater than the current minimum (unless setting to `0`). Reverts if the new value is the same as the current maximum. ### View functions - **`getMin()`** — Returns the current minimum. - **`getMax()`** — Returns the current maximum. ## Use cases - **Transfer range limits** — Require transfers to fall within a minimum and maximum amount. - **Anti-dust protection** — Reject extremely small transactions that could be disruptive. ## Source [VolumePolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/VolumePolicy.sol) --- # VolumeRatePolicy Source: https://docs.chain.link/ace/reference/policy-library/volume-rate-policy Last Updated: 2026-03-31 The VolumeRatePolicy enforces per-account volume limits within configurable time periods. It tracks cumulative transaction amounts for each account and rejects transactions that would push the account's total volume past the allowed maximum for the current period. When a new period begins, the counter resets automatically. ## Configuration Both values are set when the policy is first deployed and can be updated afterward by the policy owner. ### Maximum amount per period The maximum amount defines the cumulative volume an individual account is allowed to transact within a single time period. Each account is tracked independently — one account reaching its limit does not affect others. For example, setting the maximum to `10000` means each account can transact up to 10,000 units per period. If an account has already transacted 8,000 units in the current period, a new transaction of 3,000 units would be rejected because `8000 + 3000 > 10000`. ### Time period duration The time period duration (in seconds) defines how long each tracking window lasts. The policy derives the current period from the block timestamp: `block.timestamp / timePeriodDuration`. When this value changes (a new period starts), each account's cumulative counter resets to zero. Common values: - `3600` — 1 hour - `86400` — 1 day - `604800` — 1 week > **CAUTION: Time period must be greater than zero** > > Setting the time period duration to `0` is not allowed and will cause the configuration to revert. ## Runtime behavior The policy expects exactly two parameters from the extractor: | Parameter | Type | Description | | --------- | --------- | ----------------------------------------------- | | `amount` | `uint256` | The transaction amount. | | `account` | `address` | The account whose cumulative volume is tracked. | - **`run()`** — Derives the current period from `block.timestamp / timePeriodDuration`. If the account's cumulative volume for this period plus the new amount exceeds the maximum, reverts. Returns `Continue` otherwise. - **`postRun()`** — Updates the stored volume for the account. If this is a new period, resets the counter to the current amount. If the same period, adds the amount to the existing total. ## API reference ### Setter functions - **`setMaxAmount(uint256 maxAmount)`** — Updates the maximum volume per period. Reverts if the new value is the same as the current one. - **`setTimePeriodDuration(uint256 timePeriodDuration)`** — Updates the time period length in seconds. Must be greater than zero. Reverts if the new value is the same as the current one. > **CAUTION: Changing the duration resets all tracking** > > Updating the time period duration effectively resets all volume tracking for all accounts. Any cumulative volume > recorded under the previous duration is discarded, and every account starts with a clean counter. ### View functions - **`getMaxAmount()`** — Returns the current maximum volume per period. - **`getTimePeriodDuration()`** — Returns the current time period duration in seconds. ## Use cases - **Rate limiting** — Cap transfer volume per account per hour or per day. - **Regulatory compliance** — Implement time-based transfer limits required by regulations. - **Resource protection** — Prevent single accounts from exhausting capacity within a time window. ## Source [VolumeRatePolicy.sol](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/policy-management/src/policies/VolumeRatePolicy.sol) --- # Cross-Chain Identity Contracts Source: https://docs.chain.link/ace/reference/cross-chain-identity-contracts Last Updated: 2026-07-17 > **NOTE: On-chain contracts vs. ACE Platform** > > The contracts on this page are the **on-chain foundation** of ACE — smart contracts deployed on EVM chains that manage > identities and credentials. The **ACE Platform** (Policy Manager, Identity Manager, Reporting Manager) is a separate > set of off-chain services built on top of these contracts, providing a managed experience through the Platform UI and > Coordinator API. You can use the platform to manage these contracts, or interact with them directly. See [Beta > Scope](/ace/beta-scope) for current platform limitations. The Cross-Chain Identity contracts manage identity registries and credential lifecycles for ACE. They depend on the [Policy Management contracts](/ace/reference/policy-management-contracts) — registries are owned and governed by a PolicyEngine. The source code and full documentation are available in the [cross-chain-identity package](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/cross-chain-identity) of the [chainlink-ace repository](https://github.com/smartcontractkit/chainlink-ace) (Business Source License 1.1). ## Core interfaces | Interface | Description | | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [IIdentityRegistry](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/IIdentityRegistry.sol) | Maps wallet addresses to Cross-Chain Identifiers (CCIDs). Supports registering and removing address-to-CCID mappings, and looking up the CCID for a given address or all addresses for a given CCID. | | [ICredentialRegistry](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/ICredentialRegistry.sol) | Manages the lifecycle of credentials linked to a CCID — registration, renewal, removal, and expiration checks. Each credential is identified by a `credentialTypeId` (a `keccak256` hash of the credential type string). | | [ICredentialRequirements](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/ICredentialRequirements.sol) | Defines which credentials a policy requires, which registries to check, and how many validations must pass. Supports complex rules via credential sources, minimum validation thresholds, and inverted checks. | | [IIdentityValidator](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/IIdentityValidator.sol) | Validates whether an account meets all configured credential requirements. Used by the `CredentialRegistryIdentityValidatorPolicy` to check identities during policy evaluation. | | [ICredentialValidator](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/ICredentialValidator.sol) | Validates whether specific credentials exist and are valid for a given CCID. Provides both single-credential and batch validation functions. | | [ICredentialDataValidator](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/ICredentialDataValidator.sol) | Optional interface for inspecting the contents of a credential's `credentialData` field. Enables granular, data-level checks beyond simple attestation (e.g., verifying a specific claim within the credential payload). | | [ITrustedIssuerRegistry](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/ITrustedIssuerRegistry.sol) | Manages the list of trusted credential issuers — addresses authorized to register and manage credentials in a credential registry. | ## Data validators Data validators inspect the contents of a credential's `credentialData` field, enabling checks beyond simple attestation (for example, a jurisdiction allow/deny list). They are attached to a [Credential Source](/ace/concepts/cross-chain-identity#credential-sources) and invoked by the identity validator policies after the credential's existence is confirmed. For how to configure them through the ACE Platform, see [Managing Data Validators](/ace/guides/policy-manager/manage-data-validators). | Contract | Description | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [AllowDenyListDataValidator](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/data-validators/AllowDenyListDataValidator.sol) | Pre-built `ICredentialDataValidator` that validates a `bytes32[]` credential payload against an allowlist and denylist, with optional restriction by credential type. Used for jurisdiction control (ISO 3166-1 alpha-2 country codes). Configuration is versioned for optimistic concurrency. | | [DataValidatorFactory](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/data-validators/DataValidatorFactory.sol) | Deploys deterministic data validator instances (minimal-proxy clones or ERC-1967 proxies) from an implementation, with `create` and idempotent `getOrCreate` variants. Verifies the implementation supports the required interfaces via ERC-165. | | [IInitializableDataValidator](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/src/interfaces/IInitializableDataValidator.sol) | Minimal initializer surface (`initialize(initialOwner, configData)`) implemented by data validators so the factory can deploy and configure them in one step. | ## Repository documentation The [cross-chain-identity docs](https://github.com/smartcontractkit/chainlink-ace/tree/main/packages/cross-chain-identity/docs) folder contains detailed guides: - [Concepts](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/docs/CONCEPTS.md) — CCID model, credential type IDs, privacy, and design rationale - [API Guide](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/docs/API_GUIDE.md) — Deploy registries, configure validator policies, and authorize issuers - [API Reference](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/docs/API_REFERENCE.md) — Complete interface specifications with function signatures, events, and errors - [Credential Flow](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/docs/CREDENTIAL_FLOW.md) — End-to-end lifecycle from issuance to validation - [Security Considerations](https://github.com/smartcontractkit/chainlink-ace/blob/main/packages/cross-chain-identity/docs/SECURITY.md) — Issuer trust, PII handling, CCID correlation, and non-reverting requirements ## Related pages - [Cross-Chain Identity](/ace/concepts/cross-chain-identity) — Conceptual overview of CCIDs, registries, and credential sources - [Managing Registries](/ace/guides/identity-manager/manage-registries) — Create and manage identity and credential registries via the ACE Platform - [Managing Credentials](/ace/guides/identity-manager/manage-credentials) — Issue, renew, and revoke credentials via the ACE Platform - [Credential Registry Identity Validator Policy](/ace/reference/policy-library/credential-registry-identity-validator-policy) — The policy that checks credentials at transaction time --- # ACE API Overview Source: https://docs.chain.link/ace/reference/apis Last Updated: 2026-10-05 ACE exposes three APIs. They share the same host and authentication model, but serve different purposes and use different base paths. | API | Purpose | Access | Reference | | -------------------- | ------------------------------------------------------ | ---------- | ------------------------------------------------------ | | Coordinator API | Create, update, and delete ACE resources. | Read-write | [Coordinator API Reference](/api/ace/coordinator/docs) | | Evaluation API (MVP) | Start and monitor managed offchain policy evaluations. | Read-write | [Evaluation API Reference](/api/ace/evaluation/docs) | | Reporting API | Query onchain state and transaction history. | Read-only | [Reporting API Reference](/api/ace/reporting/docs) | ## Authentication All API requests require an API key passed in the `Authorization` header. Create your API key in the [Chainlink App](https://app.chain.link) — see [Account Setup](/ace/getting-started/account-setup#3-create-an-api-key) for instructions. ```bash curl https://ace.api.chain.link/v1/ \ -H "Authorization: Apikey " ``` > **CAUTION: API key security** > > Your API key grants full read-write access to your organization's ACE resources. Never share it, commit it to source > control, or expose it in client-side code. ## Base URLs The APIs use different base paths on the same host: | API | Base URL | | -------------------- | ------------------------------------------ | | Coordinator API | `https://ace.api.chain.link/v1` | | Evaluation API (MVP) | `https://ace.api.chain.link/v1/evaluation` | | Reporting API | `https://ace.api.chain.link/v1/reporting` | ## Coordinator API The Coordinator API is the management interface for all ACE resources. Everything available in the [Platform UI](https://app.chain.link) is also available through this API. **Policy Manager operations:** - Create and manage CRE Connect Wallets - Deploy and configure PolicyEngine instances - Create and configure policy instances (from the [Policy Library](/ace/reference/policy-library)) - Register targets and attach policy protections to function selectors - Manage extractors - Create and configure [Data Validators](/ace/guides/policy-manager/manage-data-validators) for credential-data checks **Identity Manager operations:** - Create and manage identity and credential registries - Register cross-chain identities (CCIDs) and map wallet addresses - Define credential types (optionally typed with data schemas) and issue credentials **Active Monitoring operations:** - Register monitored tokens and their associated contracts - Create and archive the monitoring rule of a token - Add addresses to the watchlist - Store the TRM API key, set the screening interval, and start or stop screening - List decisions and read their details Active Monitoring uses the same host, base URL, and API key as the rest of the Coordinator API. For the endpoints by task, see [Active Monitoring API](/ace/active-monitoring/reference/api). The Coordinator API also exposes cross-organization access grants (sharing registries and policies), organization settings, and off-chain policy management. For the full schema, parameters, and request/response examples, see the [Coordinator API Reference](/api/ace/coordinator/docs). ## Evaluation API (MVP) The Evaluation API is the runtime interface for [managed offchain risk policies](/ace/guides/policy-manager/offchain-policies/manage-offchain-policies). An application uses it to: > **CAUTION: MVP API** > > The Evaluation API and managed offchain risk policies are an MVP. Their interfaces and capabilities can change during > Beta. Contact your Chainlink representative before integrating this API and for help with setup. - Request a TRM wallet risk evaluation for a specific caller, target, function, chain, and set of permit parameters. - Receive the deterministic permit ID for that evaluation. - Poll the evaluation through `evaluating`, `approving`, and a terminal status. - Submit the protected transaction after the status becomes `ready` and the permit is stored onchain. For the integration flow, ABI encoding requirements, idempotency, and retry guidance, see [Requesting Offchain Permits](/ace/guides/policy-manager/offchain-policies/request-offchain-permits). For the full schema, see the [Evaluation API Reference](/api/ace/evaluation/docs). ## Reporting API The Reporting API provides read-only access to onchain state indexed by Chainlink's infrastructure. Use it for audit trails, compliance verification, and monitoring. For a detailed explanation of the data model and indexing pipeline, see [Reporting Manager](/ace/concepts/reporting). **Queryable resources:** - **Transactions** — every policy engine evaluation, including the policies that ran, parameters extracted, and outcomes. Filterable by chain, time range, sender, target, function selector, and policy address. - **Policies** — deployed policy instances and their configuration state at any point in time. - **Targets** — protected contracts and their full policy wiring (engines, selectors, extractors, policies, defaults). - **Identities** — identity records, registry memberships, and credentials. Optionally includes full credential details. - **Permits** — off-chain policy permits delivered on-chain (from off-chain policy evaluation). - **Registry usage** — registry usage events, for metering and monitoring. The Policies, Targets, and Identities endpoints accept an `as_of` timestamp for point-in-time historical queries. The Transactions endpoint uses `from`/`to` time range filters. > **NOTE: API-only reporting** > > The Reporting Manager is API-only — there is no reporting UI. See [Beta Scope](/ace/beta-scope) for details. For the full schema, parameters, and request/response examples, see the [Reporting API Reference](/api/ace/reporting/docs). --- # What is Active Monitoring? Source: https://docs.chain.link/ace/active-monitoring/overview Last Updated: 2026-10-05 **Chainlink ACE Active Monitoring** screens the wallet addresses you choose with TRM, a blockchain analytics provider that scores address risk, on a schedule, applies the rules you define when an address's risk changes, and acts onchain on tokens you already run, without changing their contracts. Every decision is recorded, so you can show what was detected, what the rule decided, and what was executed. Active Monitoring is the continuous part of ACE. [Policy Manager and Identity Manager](/ace/concepts/preventive-vs-continuous) are preventive: they block a transaction while it executes. Active Monitoring is reactive: it covers holders who were compliant when they received your token and changed risk level afterward. ## What problem does it solve? A regulated token issuer has to react when a holder becomes a risk, for example when an address you onboarded months ago is flagged for sanctions exposure. Without automation, the steps are spread across tools: an alert in one system, a decision in a chat, an action in a wallet interface, and an audit trail rebuilt afterward. That is slow, easy to get wrong, and hard to prove. Active Monitoring closes that loop for tokens that are already deployed: 1. **Detect.** It screens your watchlist with [TRM Wallet Screening](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening) at the interval you choose. 2. **Decide.** It applies your monitoring rule to each risk change: record it, flag it for a person, or enforce an action. 3. **Act.** For an enforced action, it calls the enforcement function you registered, through your CRE Connect Wallet. 4. **Prove.** It keeps the TRM result, the rule, the balance reading, the operation, and who acted, in one record. ![Active Monitoring loop: detect a risk change with TRM, decide with your rule, act onchain, and prove with one record. Screening repeats at the next run.](/images/ace/active-monitoring-loop.svg) ## Who is it for? - **Compliance teams** define the rule for each token, review flagged decisions, and read the audit trail. - **Platform and custody engineers** register the token and its ABI, set up wallets and the TRM API key, and grant the onchain role. Your keys and your signer stay with you. - **Token issuers and asset managers** with tokens already in production. You do not need to use Policy Manager or Identity Manager. ## How it works: a real-world example An issuer runs **Example Treasury Fund (EXTF)** on Ethereum Sepolia and Arbitrum Sepolia. It uses an ERC-3643 token with `freezePartialTokens` and `setAddressFrozen`, and a separate blocklist contract with `addBlacklist`. The blocklist is optional: it shows that an action can run on a second contract. The issuer sets this rule: | TRM risk level | Response | | :------------------------------- | :---------------------------------------------------------------------------------------------- | | 15 - Severe | Enforce: freeze the full balance if the address holds one, and add the address to the blocklist | | 10 - High | Flag for a person to review | | 5 - Medium, 1 - Low, 0 - Unknown | Silently log | At the next screening run, TRM reports one watchlist address as **Severe**. The address holds EXTF on Ethereum Sepolia and none on Arbitrum Sepolia. Active Monitoring creates these **decisions**: | Network | Freeze rule | Blocklist rule | | :--------------- | :--------------------------- | :------------------------------------ | | Ethereum Sepolia | The balance is frozen | The address is added to the blocklist | | Arbitrum Sepolia | No balance - flag for review | The address is added to the blocklist | Each enforced decision links to its operation and shows the status until it succeeds. On Arbitrum Sepolia, a person reviews the decision that has no balance to freeze, and resolves it or enforces another action. No contract was changed, and the issuer can remove the onchain role at any time to stop all enforcement. This example is for illustration. You choose which levels to enforce, and you can start with Silently log and Flag only. ## Key features - **No contract changes.** You upload the ABI of your token and select the functions Active Monitoring may call. It works with any contract that has such functions, such as ERC-3643 tokens or your own blocklist contract. - **One configuration, every network.** You register a token once with its address on each network. One rule applies everywhere, and each network gets its own decisions. - **Three responses.** Silently log, Flag for human review, or Enforce an action automatically. - **Balance-aware actions.** An action can run only if the address holds a balance, and can use the balance as an argument. - **Human review.** A person resolves a flagged decision with a recorded reason, or enforces an action, and the record names them. - **Your signer.** You choose the signing model. With delegated signing, Chainlink signs and executes operations on your behalf, within the rule and the onchain role you set. With self-signing, your own key signs each operation. Both run through your CRE Connect Wallet, which you own. - **Audit trail.** Each decision keeps the TRM result as returned, the conditions evaluated, and the operation history. - **Platform UI and API.** You configure and run Active Monitoring in the [Chainlink Platform](https://app.chain.link) UI or with the Coordinator API. ## What you need - An organization with ACE enabled and a [CRE Connect Wallet](/ace/getting-started/account-setup) on each network where your token runs. - A TRM Labs account with access to the [Wallet Screening API](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening). - Admin authority on your token, to grant the onchain role that its enforcement functions require. ACE is in Beta. See [Beta Scope](/ace/beta-scope#active-monitoring-is-limited-during-beta) for the current limits. ## Where to go next ### Understand Active Monitoring 1. **[Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous)**: how Active Monitoring differs from Policy Manager. 2. **[How Active Monitoring Works](/ace/active-monitoring/concepts/how-it-works)**: one alert, step by step. 3. **[Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules)**: how a risk level becomes an outcome. 4. **[Enforcement and Security Model](/ace/active-monitoring/concepts/enforcement-and-security)**: what Active Monitoring can and cannot do. ### Build with Active Monitoring - **[Active Monitoring Quick Start](/ace/active-monitoring/quick-start)**: set up the loop end to end. - **[Guides](/ace/active-monitoring/guides)**: prepare a token, configure rules, manage the watchlist, and review decisions. - **[Active Monitoring API](/ace/active-monitoring/reference/api)**: the endpoints by task. --- # Active Monitoring Quick Start Source: https://docs.chain.link/ace/active-monitoring/quick-start Last Updated: 2026-10-07 By the end of this guide, Active Monitoring screens a watchlist for a token you already run, applies a monitoring rule to each risk change, and shows its first decisions in the Decisions log. Each step links to the full guide for options. **Active Monitoring** screens wallet addresses with TRM on a schedule and acts onchain on tokens you already run, without changing their contracts. For the model, see [How Active Monitoring Works](/ace/active-monitoring/concepts/how-it-works). If you are choosing between Active Monitoring and Policy Manager, see [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous). The examples use a token called Example Treasury Fund (EXTF) on Ethereum Sepolia and Arbitrum Sepolia, with a separate blocklist contract. The blocklist only shows that an action can run on a second contract: a token that has the functions you need does not require one. ## Prerequisites - An organization with ACE enabled, and Active Monitoring available to it. Complete [Account Setup](/ace/getting-started/account-setup) first: steps 1 and 2 (organization and enablement), and for the API, step 3 (API key). - A TRM Labs account with access to the [Wallet Screening API](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening), and its API key. - A token that has admin functions you can call to restrict an address, such as a freeze or a blocklist function, and the account that can grant roles on it. - The JSON ABI of the token, and of any associated contract you want to enforce on. - One or more wallet addresses to screen. ## 1. Create your CRE Connect Wallets Active Monitoring executes enforcement operations through your CRE Connect Wallet, one per network. If you do not have one on every network where your token runs, create it. See [Set up CRE Connect Wallets](/ace/getting-started/account-setup#4-set-up-cre-connect-wallets). Only networks with a created wallet are offered when you register a token. ## 2. Grant the onchain role An enforcement operation reverts unless your CRE Connect Wallet is allowed to call the function. Copy the wallet address of each network, then allow it on your token and on each associated contract. For a standard ERC-3643 token, call `addAgent(address)` from the owner account. The following example uses [Foundry's `cast`](https://book.getfoundry.sh/cast/), but any tool that sends transactions works: ```bash cast send "addAgent(address)" \ --rpc-url \ --private-key ``` Repeat on every network. Other contracts use other mechanisms, such as an `AccessControl` role or a policy. See [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token), which also explains how to revoke the role and prepare the ABI. To try Active Monitoring without any onchain action, skip this step and map every risk level to Silently log or Flag in step 5. ## 3. Store your TRM API key Saving the key does not start screening. See [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening). ## 4. Register your token The token appears with the tag **Missing rule**. You cannot edit a registered token, so check the addresses and the ABI before you submit. See [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens). ## 5. Create the monitoring rule A rule maps each TRM risk level to a response. This example enforces on Severe, flags High, and logs the rest. In the table, **Screened address** is the address that TRM flagged, and **Balance** is that address's balance of the token: | Risk level | Response | | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 15 - Severe | Enforce: `freezePartialTokens` with **Screened address** and **Balance**, only if the address holds a balance; `addBlacklist` with **Screened address** and the constant `Severe risk` | | 10 - High | Flag | | 5 - Medium, 1 - Low, 0 - Unknown | Silently log | The token tag changes to **Active rule**. A rule cannot be edited: to change it, delete it and create a new one. See [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules). Enforce acts without review. For a first run, you can map Severe to Flag instead, review what the rule catches, and move levels to Enforce later. ## 6. Add the watchlist You cannot remove an address after you add it. See [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist). ## 7. Start screening The first run starts immediately and repeats at the interval you chose. ## 8. Read your first decisions When the first run completes, the **Decisions log** lists a decision for each rule that matched a risk event. Every address creates a risk event at its first screening, so each address appears for each rule it matches. 1. Click a decision to see the TRM result, the rule that matched, the action, and the operation status. 2. For an enforced decision, watch the **Operation status** move to **Success**. A status of **Failed** usually means the onchain role is missing. 3. For a flagged decision, click **Resolve** or **Enforce action**. With the API, call `GET /active-monitoring/decision-logs`. See [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions). If nothing appears, see [Troubleshoot Active Monitoring](/ace/active-monitoring/guides/troubleshooting). ## Next steps - [Enforcement and Security Model](/ace/active-monitoring/concepts/enforcement-and-security): what Active Monitoring can do with the role you granted. - [Decisions, Review, and Audit Trail](/ace/active-monitoring/concepts/decisions-and-audit): the evidence each decision keeps. - [Active Monitoring API](/ace/active-monitoring/reference/api): every endpoint by task. --- # How Active Monitoring Works Source: https://docs.chain.link/ace/active-monitoring/concepts/how-it-works Last Updated: 2026-10-05 Active Monitoring runs one loop for the tokens you register: **detect** a risk signal, **decide** with your rules, **act** onchain, and **prove** what happened. This page follows one risk alert through that loop, so you know what runs when, and which part of it you control. Four terms recur on this page: - **TRM** is TRM Labs, the blockchain analytics provider that scores the risk of each address. You use your own TRM account. - A **risk event** is a change in an address's TRM risk level, or its first screening. - A **decision** is the record Active Monitoring keeps when a rule matches a risk event. - An **enforcement function** is a function on your token, or on a contract associated with it, that Active Monitoring calls to act onchain. Active Monitoring is the continuous side of ACE: it reacts after the fact, on tokens that are already deployed. [Policy Management](/ace/concepts/policy-management) is the preventive side: it blocks a transaction while it executes. See [Preventive and Continuous Compliance](/ace/concepts/preventive-vs-continuous) for when to use each. ![Active Monitoring components: the ACE Platform screens addresses with TRM Wallet Screening, reads balances and sends operations through CRE Connect, which executes the call from your CRE Connect Wallet on your token. CRE Connect reports balances and operation status back to ACE.](/images/ace/active-monitoring-components.svg) ## What you set up Before the loop runs, you configure five things: 1. A [TRM API key and a screening interval](/ace/active-monitoring/guides/configure-screening). 2. A [monitored token](/ace/active-monitoring/concepts/monitored-tokens) with the enforcement functions Active Monitoring may call. 3. The [onchain role](/ace/active-monitoring/guides/prepare-your-token) that lets your CRE Connect Wallet call those functions. 4. A [monitoring rule](/ace/active-monitoring/concepts/monitoring-rules) that maps each TRM risk level to a response. 5. A [watchlist](/ace/active-monitoring/concepts/screening) of the addresses to screen. Then you start screening. Nothing runs before that. ## One alert, step by step 1. **Screen.** At each scheduled run, Active Monitoring sends every watchlist address to TRM Wallet Screening with the TRM API key you stored. TRM returns the highest risk level of each address. 2. **Detect a change.** Active Monitoring compares each risk level with the previous one. It raises a risk event for an address the first time it is screened and whenever its level changes. An address whose level stays the same raises no new event. 3. **Match the rule.** Active Monitoring checks each risk event against the monitoring rule of every monitored token, on every network of that token. Each rule that matches creates a decision on that network. 4. **Read the balance, if the rule asks for it.** When a rule has the balance condition and the risk level matches, Active Monitoring reads the balance of the address with `balanceOf(address)` on the token, through CRE Connect, on the network of the decision. The decision stays **Evaluating** until the balance arrives. The decision records the block of the reading. 5. **Decide.** The rule's response sets the outcome: Silently log records it, Flag waits for a person, and Enforce creates an operation for each enforced action. 6. **Act.** For each enforced action, Active Monitoring encodes the call to your enforcement function and creates an operation. CRE Connect executes it through your organization's CRE Connect Wallet. Who signs depends on your [signing model](/ace/active-monitoring/concepts/enforcement-and-security#signing-models). 7. **Track.** The operation moves through the statuses **Submitted**, **Sending**, **Pending signature** (self-signing only), **Executing**, and **Success** or **Failed**. The decision shows the latest status. 8. **Prove.** The decision keeps the TRM result as returned, the conditions that were evaluated, the balance reading, the arguments of each call, the operation, and who resolved or enforced it. See [Decisions, Review, and Audit Trail](/ace/active-monitoring/concepts/decisions-and-audit). ## What runs where | Part | What it does | | ----------------------------------- | -------------------------------------------------------------------------------------------- | | ACE Platform | Stores your configuration, calls TRM on schedule, evaluates your rules, and keeps decisions. | | TRM Wallet Screening | Returns the risk level of each address. You provide your own TRM API key. | | [CRE Connect](/crec) | Reads balances and executes operations, and reports their status back to ACE. | | Your CRE Connect Wallet | Executes the call on your token on each network. It needs the onchain role. | | Your token and associated contracts | Run the enforcement function. Active Monitoring never changes their code. | Active Monitoring does not run any code on your contracts and does not add a contract to your token. It needs only the ABI, the addresses, and the role. ## How long each step takes The time between a risk change at TRM and a decision depends mostly on your screening interval: a change is seen at the next scheduled run. After a run, evaluation and the balance reading usually complete shortly after. For an Enforce response, the time to a confirmed operation then depends on the signing model and the network. With self-signing, the operation waits for your signer. With delegated signing, it does not wait for a signature, and the time is bounded by CRE Connect and the confirmation time of the network. A Flag response waits for a person for as long as it takes to review it. ## What Active Monitoring does not do - It screens only the addresses on your watchlist. It does not discover your token holders or screen the counterparties of transfers. - It does not block a transaction. Preventive blocking is the role of [Policy Management](/ace/concepts/policy-management). - It does not grant or check onchain roles. You grant the role, and an operation fails if the role is missing. - It uses TRM as its only screening provider in Beta. ## Next steps - [Watchlist and Screening](/ace/active-monitoring/concepts/screening): what is screened, how often, and what counts as a change. - [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules): how a risk level becomes an outcome. - [Active Monitoring Quick Start](/ace/active-monitoring/quick-start): set up the loop end to end. --- # Monitored Tokens and Associated Contracts Source: https://docs.chain.link/ace/active-monitoring/concepts/monitored-tokens Last Updated: 2026-10-05 A **monitored token** is a token contract you register with Active Monitoring so that [monitoring rules](/ace/active-monitoring/concepts/monitoring-rules) can read an address's balance and call enforcement functions on it. This page explains what you register, which functions Active Monitoring can call, and how one token maps to several networks. Registering a token does not change the contract and does not grant anything onchain. You grant the required role separately: see [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token). ## What you register A monitored token is a group of one primary token and, optionally, associated contracts. | Part | Purpose | Required | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | **Primary token** | The token you monitor. Rules attach to it, and Active Monitoring calls its `balanceOf(address)` function to read balances. | Yes, exactly one | | **Associated contract** | Another contract that an enforced action runs on, such as a separate blocklist or registry contract that your token's compliance logic reads. | No | Use an associated contract when the control you need does not live on the token. A freeze acts on a balance, so it belongs on the token. A deny list often has its own owner and upgrade path, so it lives in its own contract. A rule can enforce on either contract. The Platform UI registers one associated contract. To register several, use the API. ## Contract ABI and functions You upload the JSON ABI of each contract. Active Monitoring uses it to encode the calldata of the functions it calls. Only functions of type `function` are read from the file. - **Enforcement functions** are the write functions you select when you register the contract. Active Monitoring can call only these. A function that is in the ABI but not selected cannot be used in a rule or in a manual enforcement. - **Read functions** (`view` and `pure`) are registered automatically. Active Monitoring needs `balanceOf(address)` on the primary token to evaluate balance conditions. A function is available as an enforcement function when it meets all of these conditions: | Condition | Why | | -------------------------------------------------- | -------------------------------------------------------------------------------------- | | It is a write function (`nonpayable` or `payable`) | Read functions cannot change state. | | Every input has a name | Rules map each argument by name. | | No input is an array or a tuple | Active Monitoring cannot type-check a mapping for these types. | | It has at least one input | Current Beta limit. A function with no arguments does not appear in the function list. | ## Networks and addresses A token can have a different address on each network, or the same address on several. - Register each address with its network. The Platform UI lets you pick several networks for one address. - Only networks where your organization has a created [CRE Connect Wallet](/ace/getting-started/account-setup#4-set-up-cre-connect-wallets) are available. - Register an associated contract on every network of the primary token. The Platform UI locks the networks of the associated contract to those of the primary token. The API does not check this: an action that targets a network where the associated contract has no address cannot run. - You can register a given address on a given network once. A second registration returns a conflict. ## Decimals You enter the token's decimals when you register the primary token. Active Monitoring stores them to display balances in token units on a decision. Rules and parameter mapping use **raw base units**. The `Balance` parameter passes the exact `balanceOf` value, and a constant you type is sent as written, with no decimal scaling. For a token with 6 decimals, `1000000` is one token. ## One configuration, one decision per network You configure the token once, and one monitoring rule applies to all of its networks. Active Monitoring evaluates the rule on every network of the token, so a single risk alert can produce a different decision on each network. For example, an address that holds a balance on one network and none on another gets a freeze on the first only. Each network produces its own decisions and operations. An operation that reverts on one network does not change what happens on another. ## Limits in Beta You cannot edit or remove a monitored token after you register it. Check the addresses, networks, and ABI before you submit. ## Next steps - [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token): grant the role and prepare the ABI. - [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens): register the token in the Platform UI or with the API. - [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules): decide what happens for each risk score. --- # Monitoring Rules and Responses Source: https://docs.chain.link/ace/active-monitoring/concepts/monitoring-rules Last Updated: 2026-10-05 A **monitoring rule** tells Active Monitoring what to do when TRM reports a risk level for a screened address. Each monitored token has one rule. Active Monitoring does nothing for a token until its rule exists: registering a token only describes it. ## Risk levels and responses A rule maps each of the five TRM risk levels to one of three responses. | Response | What happens | | ---------------- | ------------------------------------------------------------------------------------ | | **Silently log** | Active Monitoring records the decision. Nothing else happens. | | **Flag** | The decision waits in the Decisions log. A person resolves it or enforces an action. | | **Enforce** | Active Monitoring creates an operation for each enforced action, with no review. | In the Platform UI, you group levels that share a response in one **Decision logic** card. Every level must be in exactly one card. For example: | Risk level | Response | | -------------------------------- | ------------ | | 15 - Severe | Enforce | | 10 - High | Flag | | 5 - Medium, 1 - Low, 0 - Unknown | Silently log | Use Silently log and Flag while you learn how your watchlist behaves. Use Enforce for the levels where you accept an automatic action. See [Enforcement and Security Model](/ace/active-monitoring/concepts/enforcement-and-security) for what Enforce can do under each signing model. ## Enforced actions An Enforce response contains one or more **enforced actions**. Each action calls one enforcement function: - **Enforce on**: the contract that runs the function, either the primary token or an associated contract. See [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens). - **Enforcement function**: one of the write functions you selected when you registered that contract. - **Parameter mapping**: where each argument of the function gets its value. The same contract and function pair cannot appear twice in one Decision logic card. ### Parameter mapping Active Monitoring reads the argument names and types from the ABI. You choose only the value of each argument: | Source | Value sent | | -------------------- | ------------------------------------------------------------------------------------------------- | | **Screened address** | The address that TRM screened. | | **Balance** | The raw `balanceOf` value of the screened address on the primary token, in base units. | | **Other** (constant) | A value you type. Active Monitoring sends it as written, and checks it against the argument type. | The Platform UI lists only the sources whose type fits the argument. For example, an `address` argument offers **Screened address**, and a `uint256` argument offers **Balance**. See [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values#parameter-mapping) for the full compatibility table. This is Active Monitoring's own parameter mapping. It is unrelated to the extractor mapping of [Policy Management](/ace/concepts/policy-management#the-extractor-and-mapper-pattern). ### The balance condition Many enforcement functions only make sense when the address holds tokens. Freezing a balance of zero costs a transaction and changes nothing. Each enforced action has the option **Only execute if the address holds a balance on this token**. When you select it, Active Monitoring reads the balance of the screened address on the primary token, on the network of the decision, and runs the action only if the balance is greater than zero. When you do not select it, the action runs without reading the balance. > **CAUTION: Select the condition when you map Balance** > > The `Balance` source needs a balance reading, and the reading exists only when the condition is selected. Select the > condition on every enforced action that maps `Balance`. If the risk level matches but the balance is zero, the action does not run. The decision becomes **No balance - flag for review**, so that a person can decide what to do for an address that holds nothing. ## One rule, several decisions An Enforce response with several actions is not one decision. The Platform UI turns the response into separate rules: actions with the balance condition go in one rule, and actions without it go in another. Active Monitoring evaluates each rule independently, so one risk event can create several decisions. For example, this Enforce response applies to **15 - Severe** on a token with two actions: | Action | Balance condition | | -------------------------------------------------------------------------------------------- | ----------------- | | `freezePartialTokens(_userAddress, _amount)` on the token, with `_amount` set to **Balance** | Selected | | `addBlacklist(account, reason)` on an associated blocklist contract | Not selected | For a Severe address, Active Monitoring evaluates two rules: | Address holds a balance on the network | Decision from the freeze rule | Decision from the blocklist rule | | -------------------------------------- | ----------------------------- | -------------------------------- | | Yes | Freeze enforced | Blocklist entry enforced | | No | No balance - flag for review | Blocklist entry enforced | Every decision is for one address on one network, so the same alert can produce different results on different networks. The blocklist entry above is added on every network, and the freeze runs only on networks where the address holds a balance. ## A rule is immutable You cannot edit a rule. To change it, delete it and create a new one. Deleting the rule of a token removes the rule only: the token and its configuration stay. While a token has no rule, Active Monitoring creates no decisions for it. Risk events that occur in that period are not evaluated against the new rule later. ## Next steps - [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules): create a rule in the Platform UI or with the API. - [Decisions, Review, and Audit Trail](/ace/active-monitoring/concepts/decisions-and-audit): what each outcome means. --- # Watchlist and Screening Source: https://docs.chain.link/ace/active-monitoring/concepts/screening Last Updated: 2026-10-07 Screening is how Active Monitoring detects risk: it checks the addresses on your watchlist against TRM Wallet Screening on a schedule you choose. TRM Labs is the blockchain analytics provider that scores the risk of each address, and you use your own TRM account. This page explains what is screened, how a result becomes a risk event, and what limits apply. ## The watchlist The **watchlist** is the list of wallet addresses that Active Monitoring screens. You build it from a CSV file or from an [ACE identity registry](/ace/guides/identity-manager/manage-registries). See [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist). The watchlist belongs to your organization, not to one token. Active Monitoring evaluates every watchlist address against the monitoring rule of every monitored token, on each network of that token. Active Monitoring screens only these addresses. It does not read your token's holder list and does not screen the counterparties of transfers. ## TRM risk levels TRM returns one risk level for each screened address: **Severe** (score 15), **High** (10), **Medium** (5), **Low** (1), or **Unknown** (0). Active Monitoring asks TRM about the address across all the networks that TRM covers, so the level applies to the address as a whole, not to one network. Your monitoring rule maps each level to a response. See [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values#trm-risk-levels) for the scores and API values. TRM also returns the categories behind the level, for example sanctions exposure or mixer activity. Active Monitoring keeps the full TRM result with each decision. You can read it in the **TRM raw data** tab of the decision. ## Schedule You choose how often Active Monitoring screens, in whole hours, when you [add your TRM API key](/ace/active-monitoring/guides/configure-screening). The Platform UI offers 6, 12, and 24 hours. The API accepts 1 to 168 hours. Screening starts when you click **Start screening**, or call `POST /active-monitoring/screening-interval/start`. The first run starts immediately, and later runs repeat at the interval. Each run screens the whole watchlist. The **Decisions log** header shows when the least recently screened address was screened, and the interval: for example, "Last screened 2:15 hrs ago | Updates every 6 hrs". If TRM cannot screen some addresses in a run, for example because the API key is invalid or TRM is unavailable, those addresses keep their previous result and are tried again at the next run. ## When a result becomes a risk event A screening result does not always lead to a decision. Active Monitoring raises a **risk event** only when: - an address is screened for the first time, or - the highest risk level of an address differs from the level of the previous run. An address that stays at the same level raises no new event, so it produces no new decision. For example, an address that is **Severe** at the first run, and **Severe** at every later run, produces one decision for each matching rule, at the first run only. This has three consequences: - A monitoring rule reacts to **new** events. When you create or replace a rule, addresses whose level does not change are not evaluated again. - A decision reflects the balance at the moment of the event, not later. An address that gets a balance after its decision does not trigger a new decision until its level changes. - To see Active Monitoring act on an address again, its risk level must change. ## Limits | Item | Limit | | ------------------ | ---------------------------------------------------------------- | | Watchlist size | No fixed limit | | Screening provider | TRM only | | Screening interval | 6, 12, or 24 hours in the Platform UI; 1 to 168 hours in the API | | TRM API keys | One per organization | You need a TRM API key for [TRM Wallet Screening](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening). Active Monitoring does not provide TRM credentials. See the TRM website for how to get access. ## Next steps - [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist): add the addresses to screen. - [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening): store the key and start screening. - [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules): decide what happens for each risk level. --- # Decisions, Review, and Audit Trail Source: https://docs.chain.link/ace/active-monitoring/concepts/decisions-and-audit Last Updated: 2026-10-05 A **decision** is the record Active Monitoring keeps each time a monitoring rule matches a risk event for an address on one network. The **Decisions log** lists them. This page explains the outcomes, how a person reviews a flagged decision, and what each decision keeps as evidence. ## One decision per rule, address, and network When a risk event matches a rule, Active Monitoring creates one decision for that rule, that address, and each network of the monitored token. A rule that does not match creates no decision, so the log lists only what Active Monitoring acted on or recorded. A single risk alert can therefore produce several decisions: one per matching rule and per network. See [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules#one-rule-several-decisions). ## Decision outcomes | Outcome in the Decisions log | What it means | | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | **Evaluating** | The rule is still being evaluated, usually while the balance is read. | | **Logged** | A rule with the Silently log response matched. Nothing else happens. | | **Flag for review** | A rule with the Flag response matched. A person must act. | | **No balance - flag for review** | The risk level matched an Enforce rule, but the address holds no balance. A person must act. | | A function signature, such as `freezePartialTokens(address,uint256)` | A rule with the Enforce response created an operation. | | The same, from a person | A person enforced an action on a flagged decision. | | **Resolved** | A person closed a flagged decision with a reason, and no action was taken. | For the API values and the operation statuses, see [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values). ## Review a flagged decision A flagged decision stays open until a person closes it. There are two ways to close it: - **Resolve** records a required reason and closes the decision without any onchain action. The reason is recorded with the person who resolved it. - **Enforce action** lets the reviewer choose a contract, an enforcement function, and the argument values, and creates an operation. Every argument is a constant: a source such as **Balance** is replaced by the value read for that decision. Both actions are final. A decision that is resolved or enforced cannot be flagged again, and a person cannot act on a decision that is not flagged. Both actions require a signed-in user, so the record always names a person. See [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions) for the steps. ## Operations When a decision creates an operation, the decision shows the status of the operation, from **Submitted** to **Success** or **Failed**. The **Operations activity** card keeps every status with its time. An operation that fails stays failed. To try again, enforce the action manually from a flagged decision, or wait for the next risk event on that address. The most common cause of a failure is a missing onchain role. See [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token). A failed operation shows as **Failed** in the **Operation status** column of the Decisions log. Open the decision: the **Action · Enforcement failed** card shows the CRE Connect operation ID. Use it to look up what happened to the operation in CRE Connect. ## What each decision keeps A decision is the audit record of one evaluation. It holds: | Evidence | Where to find it | | --------------------------- | ------------------------------------------------------------------------------------------------------------ | | The TRM result, as returned | **TRM raw data** tab. Active Monitoring stores the full response for the address. | | The rule that matched | **Rule · Matched** card: the response and each condition, marked **Condition met** or **Condition not met**. | | The balance reading | **Rule · Matched** card: the balance, the time of the reading, and the block it was read at. | | The enforced action | **Action** card: the contract, the function, and the value sent for each argument. | | The operation | **Action** card and **Operations activity**: the operation ID, and each status with its time. | | Who acted on it | **Action** card: the person who resolved the decision and the reason, or who enforced the action. | | Every step, in order | The activity history of the decision, in the API: append-only, oldest first, with the actor of each step. | The record is created as the events happen, not rebuilt afterward. The API returns the same data, including the transaction hash of an operation once it is known. Read decisions with the Coordinator API. The [Reporting API](/ace/concepts/reporting) does not cover Active Monitoring data. ## Next steps - [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions): read the log and act on flagged decisions. - [Enforcement and Security Model](/ace/active-monitoring/concepts/enforcement-and-security): what Active Monitoring can and cannot do. --- # Enforcement and Security Model Source: https://docs.chain.link/ace/active-monitoring/concepts/enforcement-and-security Last Updated: 2026-10-05 This page explains what Active Monitoring can do with the role you grant on your token, which controls you keep, and how to start with limited authority. Read it before you set a response to Enforce. ## What Active Monitoring can do An enforced action calls a function on your token or on an associated contract. Active Monitoring can create such a call only when all of these hold: - The function is one you selected when you registered the contract. Active Monitoring builds calls only from the ABI functions you registered, so it cannot construct a call to any other function. - The call comes from a monitoring rule you created, or from a signed-in user who enforces an action on a flagged decision. - The arguments come from the rule: the screened address, the balance read for that address, or constants you typed. - The call runs through your organization's CRE Connect Wallet on the network of the decision. ## What Active Monitoring cannot do - It cannot call a function you did not register, or run code on your contracts. - It cannot act on another organization's tokens. - It cannot succeed without the onchain role. If your CRE Connect Wallet lacks the role, the call reverts and the operation fails. - It does not grant, check, or manage roles. You do that on your contract. ## The onchain role is the boundary The role you grant to your CRE Connect Wallet defines what an operation can do. ACE configuration narrows it further, but the contract enforces it: - **Grant only what you need.** Roles are defined by your contract. In an ERC-3643 (T-REX) token, the agent role covers every `onlyAgent` function, including `mint`, `burn`, and `pause`, not only the functions you selected in ACE. Active Monitoring calls only the registered functions, but the role allows more. If you need onchain least privilege, put a guard contract in front of the token and register that contract, or restrict the wallet with an access check that lists exact functions. - **Revoke at any time.** Removing the role stops enforcement immediately: the next operation reverts. You do not need ACE for this. - **Rules cannot exceed the role.** A rule that enforces a function your wallet cannot call produces failed operations, not unauthorized changes. See [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token) for the steps. ## Signing models Your organization chooses a signing model when it is onboarded. Both models use the same CRE Connect Wallet, and you own your contracts in both. Only the approval step differs. See [Signing and Ownership Model](/ace/concepts/signing-ownership) for the full model. | Signing model | What happens when a rule enforces an action | Automatic execution | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | **Delegated** | Chainlink signs and executes the operation on your behalf, as the authorized operator of your CRE Connect Wallet, within the rule and onchain role you set. | Yes | | **Self-signing** | The operation waits with the status **Pending signature** until you sign it with your own key. | No | Choose the model that fits how much automation you accept. With delegated signing, an Enforce response executes as soon as a risk event matches, within the rule you defined and the onchain role you granted. With self-signing, an Enforce response prepares each operation, and your signer signs it before it executes. This adds a human checkpoint to every action and delays it until someone signs. ## Start with limited authority You can run Active Monitoring without enforcement and add it step by step: 1. Map every risk level to **Silently log** or **Flag**. You see what the rule would have caught, and no operation is created. 2. Review flagged decisions. Use **Enforce action** on the cases you decide to act on. 3. Move the risk levels you trust to **Enforce**. Until you grant the onchain role, an Enforce response creates operations that revert. The token is effectively detect-only. ## Control who can change monitoring Anyone with your organization API key can create and delete monitoring rules, register tokens, and change the watchlist. Resolving or enforcing a flagged decision requires a signed-in user, so the record names a person. Protect the API key as described in [ACE API Overview](/ace/reference/apis#authentication). ## Data and keys - **TRM API key.** Active Monitoring stores your key encrypted and never shows it again. The API returns only its last four characters. Removing the key deletes it. - **TRM results.** Each decision keeps the full TRM response for the address. It can contain entity names and risk categories. Your organization can read it in the Platform and the API. - **Onchain data.** The arguments of an enforced call are written onchain. A constant such as a reason string is public. Do not put personal data in it. ## Next steps - [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token): grant the role and prepare the ABI. - [Signing and Ownership Model](/ace/concepts/signing-ownership): how each signing model works across ACE. --- # Active Monitoring Guides Source: https://docs.chain.link/ace/active-monitoring/guides Last Updated: 2026-10-05 These guides cover the tasks you repeat when you run Active Monitoring: from preparing a token to reviewing the decisions that screening produces. Each guide has Platform UI steps and Coordinator API requests. > **NOTE** > > New to Active Monitoring? Start with the [Active Monitoring Quick Start](/ace/active-monitoring/quick-start) for a > step-by-step setup, or read [How Active Monitoring Works](/ace/active-monitoring/concepts/how-it-works) first. ## Typical order 1. [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token): grant your CRE Connect Wallet the onchain role and prepare the ABI. 2. [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening): store the key and choose how often to screen. 3. [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens): register the token and its associated contracts. 4. [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules): map each risk level to a response. 5. [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist): add the addresses to screen. 6. Start screening, from the same guide as step 2. 7. [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions): read the Decisions log, and resolve or enforce flagged decisions. ## Available guides - [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token): the onchain role that enforcement needs, and the ABI file. - [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening): store or remove the key, set the interval, start or stop screening. - [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens): register and view tokens and associated contracts. - [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules): create, view, and delete the rule of a token. - [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist): add addresses from a CSV file or an identity registry. - [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions): list and open decisions, resolve or enforce, track operations. - [Troubleshoot Active Monitoring](/ace/active-monitoring/guides/troubleshooting): causes and fixes for common problems. - [Rule Examples](/ace/active-monitoring/guides/rule-examples): ready-to-adapt rules for common token designs. ## Related pages - [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values): every value and limit. - [Active Monitoring API](/ace/active-monitoring/reference/api): the endpoints by task. --- # Prepare Your Token Source: https://docs.chain.link/ace/active-monitoring/guides/prepare-your-token Last Updated: 2026-10-05 Before Active Monitoring can enforce an action on your token, grant your CRE Connect Wallet the onchain role that each enforcement function requires, and prepare the JSON ABI you will upload. An **enforcement function** is a function on your token or on an associated contract that Active Monitoring calls when a rule enforces an action, such as `freezePartialTokens` or a blocklist function. Active Monitoring calls it through your organization's [CRE Connect Wallet](/ace/concepts/signing-ownership) on that network. If the wallet lacks the required role, the call reverts. See [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens) for the full model. ## Prerequisites - A [CRE Connect Wallet](/ace/getting-started/account-setup#4-set-up-cre-connect-wallets) on every network where your token runs. - Admin authority on the token and on each associated contract: the account that can change who may call their functions. - The JSON ABI of the token, and of any associated contract you plan to enforce on. > **NOTE: The role depends on your contract** > > Active Monitoring needs only one thing: your CRE Connect Wallet must be allowed to call the enforcement functions. How > you allow it depends on the contract. This guide uses a standard ERC-3643 (T-REX) token, where the owner adds the > wallet as an agent. Other contracts use an OpenZeppelin `AccessControl` role, an owner-only function, or another > check. Use the access check your contract implements. ## Grant the role to your CRE Connect Wallet 1. List the actions you want Active Monitoring to take, and the function that performs each one. For example, freeze a balance with `freezePartialTokens(address,uint256)` and block an address with a blocklist function. 2. For each function, find the access check in the contract. 3. Copy the address of your CRE Connect Wallet on each network. In the [Chainlink Platform](https://app.chain.link), go to **Compliance > Home**, click **View settings**, and open the **ACE wallets** tab. Through the API, send `GET /wallets`. 4. On each network, allow the wallet to call each function, as described in the next sections. ### Example: a standard ERC-3643 token In a standard ERC-3643 (T-REX) token, the owner adds the wallet as an agent. The agent role covers every `onlyAgent` function, including `mint`, `burn`, and `pause`, not only the functions you select. Active Monitoring calls only the functions you register and map in a rule, but the role allows more. Review your contract to know what else the role allows. Call `addAgent(address)` on the token from the owner account, with any tool you use to send transactions, such as a multisig, a script, or a block explorer. The following example uses [Foundry's `cast`](https://book.getfoundry.sh/cast/). Replace the placeholders with your token address, the wallet address of the network, and your RPC URL: ```bash cast send "addAgent(address)" \ --rpc-url \ --private-key ``` Confirm the grant by calling `isAgent(address)`. It returns `true` when the wallet is an agent: ```bash cast call "isAgent(address)(bool)" \ --rpc-url ``` Repeat on every network of the token. ### Example: an associated contract An associated contract has its own access check. For a blocklist that has an operator role, grant the wallet that role on each network. The following example uses `cast` again: ```bash cast send "setOperator(address,bool)" true \ --rpc-url \ --private-key ``` ### Example: a token that uses ACE policies If your token also uses ACE for preventive enforcement, a [RoleBasedAccessControlPolicy](/ace/reference/policy-library/role-based-access-control-policy) on its policy engine can decide who calls each function. In that case, [create the policy](/ace/guides/policy-manager/manage-policies#create-a-policy-instance), assign the role to your CRE Connect Wallet, and [attach the policy](/ace/guides/policy-manager/manage-protections#create-a-target-protection) to each enforcement function. Active Monitoring does not require ACE policies on your token. > **CAUTION: Without the role, the token is detect-only** > > Active Monitoring still screens addresses and creates decisions when the role is missing. Each enforcement operation > is submitted and reverts onchain, and the decision shows the operation status **Failed**. Grant the role on every > network before you start screening if you want enforcement to work. ## Prepare the ABI Active Monitoring needs a JSON ABI for each contract you register. Use the ABI that your compiler produces: a JSON array of entries. Only entries with `type` set to `function` are read. For each function, the entry must include `name`, `inputs`, `outputs`, and `stateMutability`. To enforce a function, every input must have a name and none can be an array or tuple. See [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens#contract-abi-and-functions) for all conditions. If you plan to use the balance condition in a rule, the primary token ABI must include `balanceOf(address)`. The following fragment is a valid ABI for a token that has a balance getter and two enforcement functions: ```json [ { "type": "function", "name": "balanceOf", "stateMutability": "view", "inputs": [{ "name": "_userAddress", "type": "address" }], "outputs": [{ "name": "", "type": "uint256" }] }, { "type": "function", "name": "freezePartialTokens", "stateMutability": "nonpayable", "inputs": [ { "name": "_userAddress", "type": "address" }, { "name": "_amount", "type": "uint256" } ], "outputs": [] }, { "type": "function", "name": "setAddressFrozen", "stateMutability": "nonpayable", "inputs": [ { "name": "_userAddress", "type": "address" }, { "name": "_freeze", "type": "bool" } ], "outputs": [] } ] ``` ## Stop Active Monitoring from acting You control enforcement from your token, not only from ACE: - **Revoke the role.** Use the revocation mechanism of your contract: remove the wallet's role assignment from the `RoleBasedAccessControlPolicy`, call `removeAgent(address)` on an ERC-3643 token, or the equivalent. Enforcement operations then revert. The change takes effect on the next operation. - **Delete the monitoring rule.** Active Monitoring stops creating decisions for the token. See [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules). ## Next steps - [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens): register the token and its associated contract. - [Enforcement and Security Model](/ace/active-monitoring/concepts/enforcement-and-security): what Active Monitoring can and cannot do with the role you grant. --- # Configure the TRM API Key and Screening Schedule Source: https://docs.chain.link/ace/active-monitoring/guides/configure-screening Last Updated: 2026-10-05 Active Monitoring screens your watchlist with TRM Wallet Screening, using your own TRM API key, on the schedule you choose. TRM Labs is the blockchain analytics provider that scores the risk of each address. The **screening interval** is how often Active Monitoring screens the whole watchlist. This guide covers four operations: store the key, set the screening interval, start screening, and remove the key. For how screening works, see [Watchlist and Screening](/ace/active-monitoring/concepts/screening). ## Prerequisites - A TRM Labs account with access to the [Wallet Screening API](https://www.trmlabs.com/blockchain-intelligence-platform/wallet-screening), and its API key from your TRM Labs dashboard. - To use the **API** tab, an ACE API key, sent in the `Authorization: Apikey ` header of each request. See [Create an API key](/ace/getting-started/account-setup#3-create-an-api-key). It is a different key from your TRM API key. ## Store the TRM API key and choose the interval ## Start screening Starting screening needs four things to exist: the TRM API key, a screening interval, a [monitoring rule](/ace/active-monitoring/guides/configure-monitoring-rules) on at least one token, and at least one address on the [watchlist](/ace/active-monitoring/guides/manage-watchlist). The first run starts immediately and repeats at the interval. ## Stop screening ## Remove the TRM API key Removing the key stops screening: Active Monitoring has no key to call TRM with, and it screens nothing until you store a new key. Decisions already created stay in the log. ## Next steps - [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions): read the decisions that screening produces. - [Troubleshoot Active Monitoring](/ace/active-monitoring/guides/troubleshooting): find out why no decision appears. --- # Manage Monitored Tokens Source: https://docs.chain.link/ace/active-monitoring/guides/manage-monitored-tokens Last Updated: 2026-10-05 A **monitored token** is a token contract you register with Active Monitoring, optionally with associated contracts that enforcement actions can run on. This guide covers the two operations available for monitored tokens: register one, and view them. After you register a token, you [configure a monitoring rule](/ace/active-monitoring/guides/configure-monitoring-rules) for it. ## Prerequisites - A [CRE Connect Wallet](/ace/getting-started/account-setup#4-set-up-cre-connect-wallets) on every network where the token runs. Only these networks are offered. - The token address on each network, and the JSON ABI of the token. See [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token). - If enforcement runs on a separate contract, its address on every network of the token and its ABI. - To use the **API** tab, an ACE API key, sent in the `Authorization: Apikey ` header of each request. See [Create an API key](/ace/getting-started/account-setup#3-create-an-api-key). ## Register a monitored token A monitored token has a **primary** contract, which is your token, and optionally one or more **associated** contracts. You select the **enforcement functions** of each contract: the functions that Active Monitoring may call when a rule enforces an action. In most cases the token itself has the functions you need, such as `freezePartialTokens`, so you register the primary contract alone. Add an associated contract only when an action must run on a different contract, for example a separate blocklist that your token does not call. ## View monitored tokens ## Edit or remove a monitored token You cannot edit or remove a monitored token after you register it. Check the addresses, networks, and ABI before you submit. ## Next steps - [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules): choose what happens for each risk score. - [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist): add the addresses to screen. --- # Configure Monitoring Rules Source: https://docs.chain.link/ace/active-monitoring/guides/configure-monitoring-rules Last Updated: 2026-10-05 A **monitoring rule** maps each TRM risk level to a response (Silently log, Flag, or Enforce) for one monitored token. This guide covers three operations: create the rule, view it, and delete it. For how rules behave, including why one rule can create several decisions, see [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules). A token has one active rule, and you cannot edit it. To change a rule, delete it and create a new one. ## Prerequisites - A [monitored token](/ace/active-monitoring/guides/manage-monitored-tokens), with the enforcement functions you want to use. - For an Enforce response with the balance condition, a primary token ABI that includes `balanceOf(address)`. - The [onchain role](/ace/active-monitoring/guides/prepare-your-token) granted to your CRE Connect Wallet on every network, so that enforced actions do not revert. - To use the **API** tab, an ACE API key, sent in the `Authorization: Apikey ` header of each request. See [Create an API key](/ace/getting-started/account-setup#3-create-an-api-key). ## Create a monitoring rule The following rule is used in both tabs. It applies to a token called Example Treasury Fund, registered as in [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens): the token has the enforcement function `freezePartialTokens`, and an associated contract called Example Blocklist has `addBlacklist`. The blocklist is only an example of an action on a second contract. If you registered your token alone, keep the freeze and leave out the blocklist action. TRM gives each address a risk level, from 0 - Unknown to 15 - Severe. See [Watchlist and Screening](/ace/active-monitoring/concepts/screening) for how the level is chosen. The rule chooses a response for each level: | Risk level | Response | Detail | | -------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 15 - Severe | Enforce | Freeze the balance with `freezePartialTokens(_userAddress, _amount)`, only if the address holds a balance. Add the address to a blocklist with `addBlacklist(account, reason)`. | | 10 - High | Flag | A person reviews the address. | | 5 - Medium, 1 - Low, 0 - Unknown | Silently log | Record only. | ## View a monitoring rule ## Delete a monitoring rule Deleting a rule archives it. Active Monitoring stops creating decisions for the token until you create a new rule. Existing decisions stay in the log. ## Next steps - [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist): add the addresses to screen. - [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening): start screening. --- # Manage the Watchlist Source: https://docs.chain.link/ace/active-monitoring/guides/manage-watchlist Last Updated: 2026-10-07 The **watchlist** is the set of wallet addresses that Active Monitoring screens with TRM. This guide covers the three watchlist operations: add addresses from a CSV file, add addresses from an identity registry, and view the list. For how screening uses the watchlist, see [Watchlist and Screening](/ace/active-monitoring/concepts/screening). The watchlist belongs to your organization and is shared by all your monitored tokens. Add the addresses you want to watch, such as holders of your token. Each address is listed with one or more networks, identified by **chain selector**: the unique ID that Chainlink assigns to a network. Find selectors in [Supported Networks](/ace/supported-networks). The watchlist has no fixed size limit. The Platform UI accepts up to 100 rows per CSV upload: upload several files to add more. The API does not enforce this limit per request. ## Prerequisites - To upload a file, a CSV with one row per address. The format is described in the next section. - To import from a registry, an [identity registry](/ace/guides/identity-manager/manage-registries) in your organization that contains registered identities. Only organizations that use the Identity Manager can use this option. - To use the **API** tab, an ACE API key, sent in the `Authorization: Apikey ` header of each request. See [Create an API key](/ace/getting-started/account-setup#3-create-an-api-key). ## Add addresses from a CSV file ### CSV format The file needs two columns, with these exact header names (case-insensitive): `address` and `chain_selector`. - Use one row per address. - Set `chain_selector` to the chain selector of the network, not its name. Find selectors in [Supported Networks](/ace/supported-networks). - To list several networks for one address, wrap the selectors in quotes and separate them with commas. The following file adds two addresses. The second address is listed on two networks: ```csv address,chain_selector 0x5555555555555555555555555555555555555555,16015286601757825753 0x6666666666666666666666666666666666666666,"16015286601757825753,3478487238524512106" ``` The Platform UI checks the file before it sends anything: the headers, the address format, that each network is supported, and the 100-row limit. It does not check for duplicates. If an address appears twice in the file, or is already on the watchlist, the whole upload fails and no address is added. The networks you list for an address do not change what is screened or where decisions are created. TRM screens an address across all the networks it covers, and Active Monitoring evaluates a risk event on every network of each monitored token. ## Add addresses from an identity registry An identity registry holds the wallet addresses of your registered identities. Importing a registry adds every active onchain address of its identities to the watchlist, so you do not maintain a separate list. The import is a snapshot of the registry at the time you import it. Addresses you add to the registry later are not added to the watchlist. ## View the watchlist ## Remove addresses You cannot remove an address from the watchlist in Beta, in the Platform UI or in the API. ## Next steps - [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening): start screening the watchlist. - [Review Decisions and Track Operations](/ace/active-monitoring/guides/review-decisions): see the decisions that screening produces. --- # Review Decisions and Track Operations Source: https://docs.chain.link/ace/active-monitoring/guides/review-decisions Last Updated: 2026-10-05 A **decision** is the record Active Monitoring keeps when a monitoring rule matches a risk event for an address on one network. This guide covers four operations: list decisions, open one, resolve or enforce a flagged decision, and track the operation behind an enforced decision. For the outcomes and the evidence a decision keeps, see [Decisions, Review, and Audit Trail](/ace/active-monitoring/concepts/decisions-and-audit). Most decisions need nothing from you: a **silently logged** decision is a record only, and an **enforced** decision already ran its action. You act on **flagged** decisions, which wait for a person to review the address. You can then resolve the decision, or enforce an action yourself. ## Prerequisites - Screening is running, and at least one risk event matched a rule. See [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening). - To resolve or enforce a flagged decision, a signed-in user. These two actions are available in the Platform UI only. - To use the **API** tab, an ACE API key, sent in the `Authorization: Apikey ` header of each request. See [Create an API key](/ace/getting-started/account-setup#3-create-an-api-key). ## List decisions ## Open a decision ## Resolve a flagged decision Resolve a flagged decision when you review it and decide that no onchain action is needed. The decision closes, and your reason is recorded with your name. ## Enforce an action on a flagged decision Enforce an action when you decide that the address needs an onchain action. You choose the function and the values, and Active Monitoring creates the operation on the network of the decision. ## Track an operation Each enforced decision links to an operation. Its status moves forward until it is final: | Status | Meaning | | --------------------- | -------------------------------------------------------------------------------- | | **Submitted** | Active Monitoring created the operation. | | **Sending** | The operation is being sent to CRE Connect. | | **Pending signature** | The operation waits for your signer. This status appears only with self-signing. | | **Executing** | The operation is signed and executing onchain. | | **Success** | The operation is confirmed onchain. | | **Failed** | The operation failed. The most common cause is a missing onchain role. | In the Platform UI, the status is in the **Operation status** column and in **Operations activity**. In the API, it is in `activity_history` of each enforced action, and `transaction_hash` holds the hash once the transaction is known. **Pending signature** means your signer must approve the operation before it runs. See [Signing and Ownership Model](/ace/concepts/signing-ownership#signing-in-active-monitoring) for how you sign. If an operation fails, open the decision. The **Action · Enforcement failed** card shows the CRE Connect operation ID, which you can use to see what happened. Then fix the cause, and enforce the action again from a flagged decision, or wait for the next risk event on that address. See [Troubleshoot Active Monitoring](/ace/active-monitoring/guides/troubleshooting). ## Next steps - [Troubleshoot Active Monitoring](/ace/active-monitoring/guides/troubleshooting): why a decision is missing, stuck, or failed. - [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values): every outcome and status value. --- # Troubleshoot Active Monitoring Source: https://docs.chain.link/ace/active-monitoring/guides/troubleshooting Last Updated: 2026-10-07 This guide lists the problems you are most likely to hit with Active Monitoring, with the cause and the fix for each. Find your symptom, then follow the steps. ## The Start screening button is disabled Starting screening needs a TRM API key, a monitored token with an active monitoring rule, and at least one watchlist address. Hover over the button to see which one is missing, then complete it: - [Configure the TRM API Key and Screening Schedule](/ace/active-monitoring/guides/configure-screening) - [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules) - [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist) The API returns `400` from `POST /active-monitoring/screening-interval/start` in the same case, and also when no screening interval is set. ## No decision appears Check these causes in order: 1. **Screening has not started, or has not run yet.** The **Decisions log** header shows when the watchlist was last screened. If it shows no time, start screening. After you start, the first run creates decisions as soon as it completes. Later runs follow the screening interval. 2. **The risk level of the address did not change.** Active Monitoring raises a risk event the first time an address is screened and when its level changes. An address that keeps the same level creates no new decision. See [Watchlist and Screening](/ace/active-monitoring/concepts/screening#when-a-result-becomes-a-risk-event). 3. **The token has no active rule.** A token without a rule shows **Missing rule** in **Monitored tokens**. Create the rule, then wait for the next risk event. 4. **No rule matches the risk level.** The Platform UI requires all five levels. If you created the rule with the API, a level without a rule creates no decision. 5. **TRM could not screen the address.** If the TRM API key is wrong or removed, Active Monitoring screens nothing. Add the key again. Active Monitoring does not check the key when you save it. ## A decision stays on Evaluating A rule that has the balance condition reads the balance of the address through CRE Connect before it decides. The decision shows **Evaluating** until the reading arrives. Wait a few minutes and refresh the log. If it stays on **Evaluating**, check that the primary token ABI includes `balanceOf(address)` and that the address and network of the token are correct. ## The decision says No balance - flag for review The risk level matched an Enforce rule, but the rule runs the action only if the address holds a balance, and the address holds none on that network. Active Monitoring does not run the action. Review the decision, then resolve it or enforce a different action. See [Monitoring Rules and Responses](/ace/active-monitoring/concepts/monitoring-rules#the-balance-condition). ## An operation stays on Pending signature Your organization uses self-signing. The operation waits for your signer to approve it before CRE Connect executes it. See [Signing and Ownership Model](/ace/concepts/signing-ownership#signing-in-active-monitoring). ## An operation failed The operation was submitted and did not succeed. Open the decision: the **Action · Enforcement failed** card shows the CRE Connect operation ID, which you can use to see what happened. Check these causes: - **The CRE Connect Wallet has no role on the contract.** The function call reverts. Grant the role on that network. See [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token). - **The role was revoked.** Operations revert from the moment you revoke it. - **The contract rejects the call.** For example, the function refuses an amount larger than the balance, or the address is already in the state the function sets. Open the transaction in the block explorer of the network, using the operation ID or the transaction hash in the decision. - **The wrong contract address was registered.** Compare the addresses on the token page with the deployed contracts. After you fix the cause, enforce the action again from a flagged decision, or wait for the next risk event on that address. ## Creating a monitoring rule fails | Message or status | Cause and fix | | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | This token already has a monitoring rule (`409`) | A token has one active rule. Delete the rule, then create the new one. | | The primary contract needs a `balanceOf(address)` function (`400`) | The balance condition reads the balance with this function. You cannot edit a registered token: contact your Chainlink representative. | | An argument is not mapped, or has the wrong type (`400`) | Map every argument of the function. Use a source that fits the argument type, or a valid constant. | | The function is not a write function (`400`) | Select a function that changes state. Read functions cannot be enforced. | ## A watchlist upload fails | Symptom | Cause and fix | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Missing required column(s) | The file needs the headers `address` and `chain_selector`. | | CSV exceeds 100 items limit | Split the file. The Platform UI accepts up to 100 rows per upload. Upload several files. | | A row shows an address or network error | Fix the row. Addresses must be valid, and `chain_selector` must be a supported chain selector, not a network name. | | The upload fails and no address is added | An address appears twice in the file or is already on the watchlist. Remove the duplicates and upload again. | ## Registering a token fails - **A network is missing from the network list.** Only networks where your organization has a created CRE Connect Wallet appear. Create the wallet in the general settings. - **The token already exists.** An address can be registered once on a network. - **You registered a wrong address or ABI.** You cannot edit or remove a registered token in Beta. Contact your Chainlink representative. - **No enforcement function in the list.** The ABI has no write function that qualifies. A function must have named inputs, no array or tuple inputs, and at least one input. See [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens#contract-abi-and-functions). ## Next steps - [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token): the onchain role that enforcement needs. - [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values): every status and limit. --- # Rule Examples Source: https://docs.chain.link/ace/active-monitoring/guides/rule-examples Last Updated: 2026-10-05 These examples show how to fill the **Decision logic** cards of a [monitoring rule](/ace/active-monitoring/concepts/monitoring-rules) for common token designs. Each one lists the settings for the Platform UI. To create the same rule with the API, see [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules#create-a-monitoring-rule). The examples illustrate what is possible. They are not recommendations: the right responses depend on your token, your compliance policy, and how much automation you accept. Function names and argument names come from each example's ABI, so match them to your own contract. In the tables, **Screened address** is the address that TRM flagged, and **Balance** is that address's balance of the token. ## Detect only Use this to learn what your watchlist produces before any onchain action. No role is needed, and no operation is created. | Risk level | Response | | :------------------------------- | :----------- | | 15 - Severe, 10 - High | Flag | | 5 - Medium, 1 - Low, 0 - Unknown | Silently log | Severe and High addresses appear as **Flag for review**. A person resolves each one or enforces an action. When you trust the results, [delete the rule and create a new one](/ace/active-monitoring/guides/configure-monitoring-rules#delete-a-monitoring-rule) that enforces. ## A blocklist function on the token Use this when the token itself has a function that blocks an address, for example `blacklist(address account)`. No associated contract is needed. | Risk level | Response | | :------------------------------- | :----------- | | 15 - Severe | Enforce | | 10 - High | Flag | | 5 - Medium, 1 - Low, 0 - Unknown | Silently log | For the Enforce response, add one action: | Field | Value | | :---------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | | **Enforce on** | The token | | **Enforcement function** | `blacklist(address)` | | **Parameter mapping**, `account` | **Screened address** | | **Only execute if the address holds a balance** | Not selected: the address is blocked even if it holds nothing, which stops it from receiving tokens later. | ## Freeze an address Use this on a token with a function that freezes an address, for example `setAddressFrozen(address _userAddress, bool _freeze)` on an ERC-3643 token. A frozen address cannot send or receive tokens through regular transfers. For the Enforce response, add one action: | Field | Value | | :---------------------------------------------- | :---------------------------------- | | **Enforce on** | The token | | **Enforcement function** | `setAddressFrozen(address,bool)` | | **Parameter mapping**, `_userAddress` | **Screened address** | | **Parameter mapping**, `_freeze` | **Other**, with the constant `true` | | **Only execute if the address holds a balance** | Not selected | The `Balance` source does not fit a `bool` argument, so you type the constant. `setAddressFrozen` blocks inbound transfers too, which a balance-based freeze does not. ## Freeze a balance and add a blocklist entry This is the rule used in the [Quick Start](/ace/active-monitoring/quick-start). It freezes the tokens an address holds and blocks the address. For the Enforce response, add two actions: | | Action 1 | Action 2 | | :---------------------------------------------- | :--------------------------------------------------------- | :---------------------------------------------------------------- | | **Enforce on** | The token | An associated blocklist contract | | **Enforcement function** | `freezePartialTokens(address,uint256)` | `addBlacklist(address,string)` | | **Parameter mapping** | `_userAddress`: **Screened address**`_amount`: **Balance** | `account`: **Screened address**`reason`: **Other**, `Severe risk` | | **Only execute if the address holds a balance** | Selected | Not selected | The freeze runs on networks where the address holds a balance. The blocklist entry runs on every network. Where the address holds no balance, the decision for the freeze is **No balance - flag for review**. ## A lighter response for High Use two Enforce cards to apply a lighter action to High addresses than to Severe addresses. For example: | Risk level | Response | | :------------------------------- | :-------------------------------------------------------------- | | 15 - Severe | Enforce: freeze the balance and add a blocklist entry, as above | | 10 - High | Enforce: add a blocklist entry only | | 5 - Medium, 1 - Low, 0 - Unknown | Silently log | Each card has its own **Enforced action** fields, so the two cards can use different functions. ## Next steps - [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules): create the rule. - [Prepare Your Token](/ace/active-monitoring/guides/prepare-your-token): grant the role that these functions require. --- # Active Monitoring API Source: https://docs.chain.link/ace/active-monitoring/reference/api Last Updated: 2026-10-05 Use the Active Monitoring endpoints of the Coordinator API to set up monitoring, run it, and read its decisions without the Platform UI. This page lists the endpoints by task and explains which identifier goes where. For request and response schemas, see the [Coordinator API Reference](/api/ace/coordinator/docs), where these endpoints appear under the **Active Monitoring** tags. ## Authentication and base URL Active Monitoring endpoints share the host, base URL, and API key of the rest of the Coordinator API. All paths on this page are relative to `https://ace.api.chain.link/v1`. Pass your [API key](/ace/getting-started/account-setup#3-create-an-api-key) in the `Authorization` header: ```bash curl https://ace.api.chain.link/v1/active-monitoring/contract-groups \ -H "Authorization: Apikey " ``` ## Endpoints by task | Task | Method and path | Guide | | ------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | Store, check, or remove the TRM API key | `POST /active-monitoring/provider-api-keys` | [Configure Screening](/ace/active-monitoring/guides/configure-screening) | | | `GET /active-monitoring/provider-api-keys/{screening_provider}` | | | | `DELETE /active-monitoring/provider-api-keys/{screening_provider}` | | | Set, read, or remove the screening interval | `POST`, `GET`, `DELETE /active-monitoring/screening-interval` | [Configure Screening](/ace/active-monitoring/guides/configure-screening) | | Start or stop screening | `POST /active-monitoring/screening-interval/start` | [Configure Screening](/ace/active-monitoring/guides/configure-screening) | | | `POST /active-monitoring/screening-interval/stop` | | | Register or list monitored tokens | `POST`, `GET /active-monitoring/contract-groups` | [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens) | | Get one monitored token | `GET /active-monitoring/contract-groups/{contract_group_id}` | [Manage Monitored Tokens](/ace/active-monitoring/guides/manage-monitored-tokens) | | List the action variables for a rule | `GET /active-monitoring/action-variables` | [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules) | | Create or list monitoring rules | `POST`, `GET /active-monitoring/rule-groups` | [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules) | | Get or archive a monitoring rule | `GET`, `PATCH /active-monitoring/rule-groups/{rule_group_id}` | [Configure Monitoring Rules](/ace/active-monitoring/guides/configure-monitoring-rules) | | Add or list watchlist addresses | `POST`, `GET /active-monitoring/monitored-addresses` | [Manage the Watchlist](/ace/active-monitoring/guides/manage-watchlist) | | List decisions | `GET /active-monitoring/decision-logs` | [Review Decisions](/ace/active-monitoring/guides/review-decisions) | | Get a decision | `GET /active-monitoring/decision-logs/{decision_log_id}` | [Review Decisions](/ace/active-monitoring/guides/review-decisions) | Resolving and enforcing a flagged decision are not available with an API key: they require a signed-in user. Use the Platform UI. See [Review Decisions](/ace/active-monitoring/guides/review-decisions). The API uses the names `contract group` for a monitored token, `rule group` for a monitoring rule, `monitored address` for a watchlist address, and `decision log` for a decision. ## Identifiers to carry between requests Several requests need an identifier returned by an earlier one. | You need | Where it comes from | Where you use it | | --------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | `group_id` of the monitored token | Response of `POST /contract-groups`. It equals the `id` of the primary contract. | `contract_group_id` when you create a monitoring rule | | `id` of a contract | `contracts[].id` in the monitored token response | `contract_id` in an enforced action, and in a balance condition (the primary contract) | | `func_id` of a function | `contracts[].functions[].func_id` in the monitored token response | `admin_function_id` in an enforced action | | `id` of a monitoring rule | Response of `POST /rule-groups` | `rule_group_id` to get or archive the rule | | `id` of a decision | `decision_logs[].id` in the list response | `decision_log_id` to get the decision | | `registry_id` | `id` of an [identity registry](/ace/guides/identity-manager/manage-registries) | `registry_id` when you import a watchlist from a registry | ## Status codes Errors return a JSON body with `error` and `message`. | Status | When | | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `400` | The request is invalid. For example, a network has no created CRE Connect Wallet, a rule does not cover an ABI argument, or `POST /screening-interval/start` is called before a prerequisite is met. | | `401` | The API key is missing or invalid. | | `404` | The resource does not exist, or a `contract_group_id` is the `id` of an associated contract. | | `409` | The resource already exists: a token address on a network, an address on the watchlist, or a second active rule on one token. Also returned when `POST /screening-interval/stop` is called while a screening run is executing. | Start screening returns `400` until a screening interval, a TRM API key, an active monitoring rule, and at least one watchlist address exist. ## Related pages - [Limits, Statuses, and Values](/ace/active-monitoring/reference/limits-and-values): the values these endpoints accept and return. - [Active Monitoring Quick Start](/ace/active-monitoring/quick-start): the full setup in order, with UI and API steps. --- # Limits, Statuses, and Values Source: https://docs.chain.link/ace/active-monitoring/reference/limits-and-values Last Updated: 2026-10-07 This page lists the fixed values and limits of Active Monitoring, with the label you see in the Platform UI and the value the API uses. For how they fit together, see [How Active Monitoring Works](/ace/active-monitoring/concepts/how-it-works). ## TRM risk levels A monitoring rule matches the highest TRM risk level of a screened address. The Platform UI shows the TRM score next to the label. | Platform UI label | TRM score | API value (`risk_level`) | | ----------------- | --------- | ------------------------ | | Severe | 15 | `severe` | | High | 10 | `high` | | Medium | 5 | `medium` | | Low | 1 | `low` | | Unknown | 0 | `unknown` | TRM returns `Unknown` when it has no risk label for the address. Active Monitoring applies the same value when the label is missing or empty. ## Rule responses | Platform UI label | API value (`action_type`) | Effect | | ----------------- | ----------------------------- | -------------------------------------------------------- | | Silently log | `silently_log` | Records the decision. No other action. | | Flag | `flag_for_review` | Creates a decision that waits for a human to resolve it. | | Enforce | `auto_enforce_onchain_action` | Creates one onchain operation per enforced action. | ## Decision outcomes The **Decision** column of the Decisions log shows the outcome. The API returns it in the `output` field of a decision log. | Platform UI label (table) | API value (`output`) | Meaning | | ----------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Evaluating | `null` | The rule is still being evaluated, for example while the balance is being read. | | Logged | `silently_logged` | A rule with the Silently log response matched. | | Flag for review | `flagged_for_review` | A rule with the Flag response matched. A person must resolve it. | | No balance - flag for review | `flagged_for_review` | The risk score matched an Enforce rule, but its balance condition was not met. | | Function signature, such as `freeze(address,uint256)` | `auto_enforced_onchain_action` | A rule with the Enforce response matched and created an operation. | | Function signature | `manual_enforced_onchain_action` | A person enforced a flagged decision with **Enforce action**. | | Resolved | `user_ignored` | A person dismissed a flagged decision with **Resolve** and a reason. The Decision filter calls this **Ignored**. | A rule whose conditions do not match produces no entry in the Decisions log. ## Operation statuses An enforced decision links to an operation. The **Operation status** column shows its latest status. | Platform UI label | API value | Terminal | Meaning | | ----------------- | ------------------- | -------- | ------------------------------------------------------------- | | Submitted | `pending` | No | The operation is created and queued. | | Sending | `sending` | No | The operation is being sent to CRE Connect. | | Pending signature | `pending_signature` | No | The operation waits for your signer. Applies to self-signing. | | Executing | `executing` | No | The operation is signed and being executed onchain. | | Success | `success` | Yes | The operation is confirmed onchain. | | Failed | `failed` | Yes | The operation failed, expired, or was cancelled. | ## Parameter mapping Each argument of an enforcement function gets a value from an action variable or from a constant you type. ### Action variables | Platform UI label | API key (`reference` value) | Type | Resolves to | | ----------------- | --------------------------- | ------- | --------------------------------------------------------------------------------------------------------------- | | Screened address | `screened_address` | address | The address that TRM screened. | | Balance | `holder_balance` | number | The raw `balanceOf` value of the screened address on the primary token, in base units, read at evaluation time. | > **CAUTION: Use Balance with the balance condition** > > The `Balance` variable needs a balance reading. Select **Only execute if the address holds a balance on this token** > on every enforced action that maps `Balance`. Without it, the rule has no balance to resolve. ### Which variable fits which argument The Platform UI lists only the variables whose type fits the argument. | ABI type of the argument | Available variables | Constant ("Other") format | | ------------------------ | ------------------- | ---------------------------------------------- | | `address` | Screened address | A valid address | | `uint*` | Balance | Digits only, no sign | | `int*` | Balance | A whole number, with an optional leading minus | | `bool` | None | `true` or `false` | | `string` | Screened address | Any non-empty text | | `bytes*` | Screened address | A value that starts with `0x` | In the API, a mapping has a `mapping_type` of `reference` (the value is an action variable key) or `constant` (the value is the literal). ## Limits | Item | Limit | | ---------------------------------------- | ---------------------------------------------------------------------------------------------- | | Watchlist size | No fixed limit | | CSV upload | 100 rows per file in the Platform UI. Columns `address` and `chain_selector`. | | Screening interval, Platform UI | 6, 12, or 24 hours | | Screening interval, API | Whole hours from 1 to 168 | | Monitoring rules per token | 1 | | Risk levels covered by a rule | All 5, each in exactly one **Decision logic** card (Platform UI) | | Monitoring rule edits | None. Delete the rule and create a new one. | | Actions in one enforced response | One or more. The same contract and function pair cannot repeat in one **Decision logic** card. | | Operations per manual **Enforce action** | 1. Every argument is a constant. | | Primary tokens per monitored token | 1 | | A token address on a network | Registered once | | TRM API keys per organization | 1. The key is never shown again after you save it. | | List endpoints, `page_size` | Up to 100 | ## Related pages - [Monitored Tokens and Associated Contracts](/ace/active-monitoring/concepts/monitored-tokens): which functions qualify as enforcement functions. - [Active Monitoring API](/ace/active-monitoring/reference/api): endpoints and request examples. --- # Chainlink ACE - Automated Compliance Engine Source: https://docs.chain.link/ace Last Updated: 2026-10-05