Making Your Contract ACE-Compatible

A contract is ACE-compatible when it can route function calls through a PolicyEngine for compliance checks before execution. This requires four things: inheriting a base contract, marking functions for protection, connecting to a PolicyEngine, and registering extractors. This page explains what each requirement means at a high level — the sub-pages linked below walk through the implementation in detail.

For background on how these components interact, see the Architecture page and the Policy Management concepts page.

What your contract 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 before your function body executes.

// 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 — 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) or migration (for upgrades).

4. Register extractors for protected functions

Extractors 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.

Integration paths

How you integrate ACE depends on where your contract is today:

  • Building a 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 — 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 — wrapped contracts, contract migration, and edge protection — each with different tradeoffs depending on your constraints.

Get the latest Chainlink content straight to your inbox.