Making Your Contract ACE-Compatible

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.

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

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