> ## Documentation Index
> Fetch the complete documentation index at: https://developers.circle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How-to: Configure a denylist provider

> Configure a per-token denylist provider on a CrossChainToken you own.

Configure the per-token denylist on a `CrossChainToken` you own. The per-token
layer is independent of the service-level denylist that Circle configures on
every blockchain—see
[Denylist layers](/cctp/expanded-assets/concepts/denylist-layers) for the
two-layer model. The service-level layer always runs; this guide only covers the
layer the CCT owner controls.

<Note>
  Only `CrossChainToken` contracts deployed as `NATIVE_CROSSCHAIN_TOKEN` have a
  per-token denylist. `BURN_MINT` and `LOCK_UNLOCK` configurations of pre-existing
  ERC-20 tokens rely on whatever screening the underlying token already
  implements.
</Note>

## Prerequisites

Before you begin, ensure that you've:

* Obtained the `tokenId` of the `CrossChainToken` you want to configure.
* Confirmed that the wallet you'll use owns the `CrossChainToken` (the value
  returned by `owner()` on the CCT). The denylist provider can only be changed
  by the owner.

## Steps

<Steps>
  <Step title="Choose a denylist provider">
    A denylist provider is any contract that implements:

    ```solidity Solidity theme={null}
    interface IDenylistProvider {
        function isDenylisted(address account) external view returns (bool);
    }
    ```

    Common choices:

    * **Reuse Circle's screening list.** Read the service-level provider from the
      local `CrossChainTokenService` and configure your CCT to use the same one.
      Per-token screening then matches Circle's central list.
    * **Bridge a FiatToken denylist.** Wrap an existing FiatToken's `isBlacklisted`
      view in an `IDenylistProvider` adapter (Circle ships
      `FiatTokenDenylistAdapter`).
    * **Implement your own.** Any contract that exposes `isDenylisted(address)` can
      serve as a provider.
    * **Compose multiple lists.** Write a small provider that combines results from
      several upstream lists using a logical OR.

    The CCT has a single provider slot. Setting a new provider **replaces** the
    previous one. If you want both Circle's list and your own, deploy a composite
    provider that calls both internally.
  </Step>

  <Step title="Discover the CrossChainToken address">
    ```typescript TypeScript theme={null}
    const tokenAddress = await publicClient.readContract({
      address: serviceAddress,
      abi: serviceAbi,
      functionName: "resolveTokenAddress",
      args: [tokenId],
    });
    ```
  </Step>

  <Step title="Set the denylist provider">
    Call
    [`updateDenylistProvider`](/cctp/expanded-assets/references/contract-reference)
    on the `CrossChainToken` with the address of your chosen provider.

    ```typescript TypeScript theme={null}
    await walletClient.writeContract({
      address: tokenAddress,
      abi: crossChainTokenAbi,
      functionName: "updateDenylistProvider",
      args: [newProviderAddress],
    });
    ```

    The contract emits `DenylistProviderUpdated(oldProvider, newProvider)`.
  </Step>

  <Step title="Verify the new provider">
    ```typescript TypeScript theme={null}
    const current = await publicClient.readContract({
      address: tokenAddress,
      abi: crossChainTokenAbi,
      functionName: "denylistProvider",
    });
    ```
  </Step>

  <Step title="Disable per-token screening (optional)">
    To remove per-token screening, set the provider to `address(0)`:

    ```typescript TypeScript theme={null}
    await walletClient.writeContract({
      address: tokenAddress,
      abi: crossChainTokenAbi,
      functionName: "updateDenylistProvider",
      args: ["0x0000000000000000000000000000000000000000"],
    });
    ```

    The service-level layer continues to apply. There is no way for a CCT owner to
    opt out of the service-level layer.
  </Step>
</Steps>
