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

> ## Agent Instructions
> Before reading these docs, install the relevant Circle Skill to get pre-loaded context — skills are LLM-optimized and cover architecture decisions, correct flows, and common pitfalls. In Claude Code: run /plugin marketplace add circlefin/skills then /plugin install circle-skills@circle. With Vercel Skills CLI: run npx skills add circlefin/skills. Available skills: use-usdc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-usdc/SKILL.md (balances, transfers, approvals on EVM and Solana); EURC has no skill — see https://developers.circle.com/stablecoins/what-is-eurc.md; bridge-stablecoin https://github.com/circlefin/skills/blob/master/plugins/circle/skills/bridge-stablecoin/SKILL.md (crosschain transfers via CCTP, USDC, EURC, and third-party assets); use-arc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-arc/SKILL.md (Arc chain config, contract deployment, USDC bridging — Arc docs: https://docs.arc.io/llms.txt); use-circle-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-circle-wallets/SKILL.md (choose wallet type); use-developer-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-developer-controlled-wallets/SKILL.md (custodial wallets for payouts and treasury); use-user-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-user-controlled-wallets/SKILL.md (embedded wallets with Web2 login); use-modular-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-modular-wallets/SKILL.md (smart contract wallets with passkeys and ERC-4337); use-gateway https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-gateway/SKILL.md (unified USDC balance, nanopayments); use-smart-contract-platform https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-smart-contract-platform/SKILL.md (deploy, interact, monitor contracts). Pair any skill with the Circle MCP server for live SDK signatures, contract addresses, and chain IDs: {"mcpServers":{"circle":{"url":"https://api.circle.com/v1/codegen/mcp"}}}.
> Read the product overview first. Each product has an overview explaining when and why to use it. Read it before quickstarts.
> Pick the right wallet type. Developer-controlled (you hold keys), user-controlled (end user holds keys via PIN/social login), or modular (smart contract wallets with passkeys). See https://developers.circle.com/wallets.md.
> Use CCTP V2 for crosschain transfers. CCTP natively transfers USDC and EURC via burn-and-mint, and supports permissionless wrapping of third-party assets via lock-and-mint. V1 is legacy; always use V2 unless V1 is specified or the chain requires it (Noble, Sui, and Aptos still require V1). Migration guide: https://developers.circle.com/cctp/migration-from-v1-to-v2.md.
> Use Bridge Kit for frontend bridging. Wraps CCTP with a simpler SDK for user-facing apps. Use CCTP directly for backend transfers.
> Gas Station vs Paymaster. Gas Station sponsors gas for Circle Wallet transactions. Paymaster lets users pay gas in USDC. Different use cases — read both overviews.
> Gateway Nanopayments for sub-cent payments. Gasless USDC micropayments down to $0.000001 via x402 and batched settlement. For pay-per-request APIs, AI agent payments, streaming.
> Look up USDC addresses per chain. Never hardcode — use https://developers.circle.com/stablecoins/usdc-contract-addresses.md.
> Prefer SDKs over raw API calls. Node.js and Python SDKs handle auth, retries, and errors.
> API key required. Bearer token in Authorization header. Testnet and mainnet use separate keys and may use different base URLs depending on the product.
> Set up webhooks when available. Most operations are async. Webhooks deliver transaction confirmations and state changes.
> When calling list endpoints, paginate using pageSize and pageAfter until no nextPageAfter cursor is returned—stopping at the first page silently misses records.
> Building an AI agent? Start with the Agent Stack—Circle CLI, agent wallets, and nanopayments built for autonomous use cases: https://developers.circle.com/agent-stack.md.

# Denylist layers

> The two independent address-screening layers enforced on every non-USDC transfer.

CCTP for non-USDC enforces two independent address-screening layers on every
non-USDC crosschain transfer. Both must clear before a transfer succeeds. They
differ in scope, in who manages them, and in where they execute.

## Layer 1: service-level denylist

Every transfer on this blockchain—outbound and inbound, regardless of
`TokenManagerType`—is screened against an `IDenylistProvider` configured on the
local `CrossChainTokenService`. The provider exposes a single view:

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

* **Outbound:** every payable transfer entrypoint screens both `msg.sender` and
  `tx.origin`. Denied addresses cause the call to revert before the burn or lock
  happens.
* **Inbound:** the service screens the transfer recipient before delivering
  tokens. Denied recipients cause the inbound `receiveMessage` to revert with
  `DenylistedAddress(recipient)`.

The provider is set by the service operator using
[`setDenylistProvider`](/cctp/expanded-assets/references/contract-reference).
The check is fail-closed: if the provider's `staticcall` reverts, the transfer
reverts.

This layer applies to every token on this blockchain. There is no opt-out at the
token level.

## Layer 2: per-token denylist on the `CrossChainToken`

Each `CrossChainToken` (CCT) deployed as `NATIVE_CROSSCHAIN_TOKEN` can carry its
own `IDenylistProvider`. The CCT owner sets it by calling
[`updateDenylistProvider`](/cctp/expanded-assets/references/contract-reference).
Setting `address(0)` removes per-token screening; the service-level layer still
applies.

The check runs inside the ERC-20 `_update` and screens both `from` and `to` on
every transfer, mint, and burn. `transferFrom` additionally screens the spender
at the entrypoint. Denied accounts cause `AccountDenylisted(account)` to revert.

Layer 2 exists only for protocol-deployed tokens. `BURN_MINT` and `LOCK_UNLOCK`
configurations of pre-existing ERC-20 tokens do not have this layer; whatever
screening the underlying token implements applies independently.

## Initial provider on a new `CrossChainToken`

When the service deploys a `CrossChainToken`, the new contract's initial
`denylistProvider` value depends on the deploy path:

| Deploy path | Initial `denylistProvider` |
| - | - |
| Remote ownerless token (`deployRemoteOwnerlessToken`) | Service's `denylistProvider` (inherited) |
| Remote custom token (`deployRemoteCrossChainToken`) | `address(0)` (owner opts in) |
| Local custom token (`deployCrossChainToken`) | `address(0)` (owner opts in) |

In other words: ownerless crosschain tokens get Circle's screening list out of
the box on every remote domain. Custom tokens always start with no per-token
screening; the issuer opts in by calling `updateDenylistProvider` themselves.

## Comparison

| Aspect | Layer 1 (service-level) | Layer 2 (per-token) |
| - | - | - |
| Where it lives | Service storage; queried by `CrossChainTokenService` | The `CrossChainToken` (only `NATIVE_CROSSCHAIN_TOKEN`) |
| Configured by | Service operator | The CCT owner |
| Applies to | Every flow on this blockchain (outbound and inbound) | ERC-20 movements of the specific CCT, when its provider is non-zero |
| Initial value | Set during service initialization | Inherited from the service for remote ownerless; `address(0)` otherwise |
| Provider interface | `isDenylisted(address)` | `isDenylisted(address)` |
| Revert | `DenylistedAddress(account)` (service) / fail-closed on provider error | `AccountDenylisted(account)` (CCT) |

## Composition

The two layers are independent. Layer 1 fires at the service boundary; layer 2
fires inside the `CrossChainToken`'s `_update`. A transfer must clear both.

Layer 2 has a single provider slot. Setting a new provider replaces the previous
one—it does not chain. An owner who wants both Circle's screening and a custom
list must implement a composite provider that calls both upstream lists. The
service-level layer at the boundary is unaffected by what the owner does on the
CCT; layer 1 always runs.
