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

# CCTP for non-USDC contract reference

> CCTP for non-USDC contracts, types, errors, and non-obvious behaviors integrators need.

The following tables index contracts, types, errors, and sentinels, and document
behaviors that aren't obvious from reading the source.

For contract addresses, see
[Contract Addresses](/cctp/expanded-assets/references/contract-addresses).

## Contracts

| Contract                    | Purpose                                                                                                                          |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `CrossChainTokenService`    | Singleton entry point on each domain. Registers tokens, deploys remote token contracts, and routes crosschain transfers.         |
| `TokenManager`              | Deployed per token on each domain. Moves tokens during transfers; enforces rate limit, max transfer amount, and pause.           |
| `CrossChainToken`           | ERC-20 with `ERC20Permit` deployed for natively crosschain tokens. Holders call `crossChainTransfer` directly.                   |
| `CrossChainTokenExecutable` | Optional abstract helper for destination-domain hook receivers. Integrators can implement `executeWithCrossChainToken` directly. |
| `IDenylistProvider`         | Interface for service-level and per-token denylist providers. One view: `isDenylisted(address)`.                                 |

## Types

### `QuoteClaim`

Passed to every payable entrypoint to attach a signed fee quote and a refund
address.

```solidity Solidity theme={null}
struct QuoteClaim {
    bytes signedQuote;
    address payable refundAddress;
}
```

* `signedQuote`: version-prefixed, ABI-encoded fee quote returned by the Iris
  fee-quote endpoint
  (`POST /v1/quote/cctpx/{tokenId}/{sourceDomain}/{destinationDomain}`). Pass
  the bytes value verbatim.
* `refundAddress`: receives fee refunds. Set to `address(0)` to opt out of
  refund attribution. Non-zero values are screened against the USDC denylist on
  collection; the call reverts if denied.

### `DeploymentParams`

Passed when registering or deploying a token.

```solidity Solidity theme={null}
struct DeploymentParams {
    TokenManagerType tokenManagerType;
    bytes tokenManagerOwner;
    bytes tokenManagerOperator;
    bytes tokenOwner;
    bytes tokenMinter;
    uint256 initialSupply;
    TokenManagerSettings tokenManagerSettings;
}

struct TokenManagerSettings {
    uint256 rateLimit;
    uint256 maxTransferAmount;
}
```

Address fields are `bytes` rather than `address` so the struct can carry non-EVM
addresses for non-EVM domains.

Some deployments omit `initialSupply` from the onchain ABI (for example current
sandbox `registerCustomToken`). Encoding a field the contract does not expect
produces an empty revert. Always verify the live ABI or function selector before
copying struct layouts from docs.

### `TokenManagerType`

```solidity Solidity theme={null}
enum TokenManagerType {
    NATIVE_CROSSCHAIN_TOKEN,  // 0
    BURN_MINT,                // 1
    LOCK_UNLOCK               // 2
}
```

See
[Token manager types](/cctp/expanded-assets/concepts/architecture#token-manager-types)
for when each applies.

### `ConnectionType`

```solidity Solidity theme={null}
enum ConnectionType {
    OWNERLESS,
    CUSTOM
}
```

Distinguishes the two integration models. See
[Ownerless and custom tokens](/cctp/expanded-assets/concepts/ownerless-vs-custom-tokens).

## Errors

Integrators should monitor for these reverts:

| Error                                                                       | Source                   | Meaning                                                                                        |
| --------------------------------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------- |
| `TokenNotRegistered(bytes32 tokenId)`                                       | `CrossChainTokenService` | `resolveTokenManager` or `resolveTokenAddress` called with a `tokenId` that has no deployment. |
| `HookDataExecutionFailed(bytes32 tokenId, address recipient, bytes reason)` | `CrossChainTokenService` | Recipient's hook reverted, or returned a value other than the `EXECUTE_SUCCESS` sentinel.      |
| `DenylistedAddress(address account)`                                        | `CrossChainTokenService` | Service-level denylist rejected `msg.sender`, `tx.origin`, or the inbound recipient.           |
| `AccountDenylisted(address account)`                                        | `CrossChainToken`        | Per-token denylist rejected `from`, `to`, or the spender on an ERC-20 movement.                |

## Sentinels

### `EXECUTE_SUCCESS`

```solidity Solidity theme={null}
bytes32 constant EXECUTE_SUCCESS = keccak256("circle-cctpx-execute-success");
```

The value that `executeWithCrossChainToken` must return. The service verifies
this on every hook call. Implement the public entrypoint on your receiver,
restrict `msg.sender` to the local `CrossChainTokenService`, and return this
sentinel. See
[Build a hook receiver](/cctp/expanded-assets/howtos/build-hook-receiver).

When the contracts are open-sourced, an optional `CrossChainTokenExecutable`
base can wrap the same pattern (external entrypoint returns the sentinel;
subclasses override an internal hook). Until then, implement the write ABI
directly.

## Non-obvious behaviors

These behaviors aren't apparent from reading the source but commonly trip up
integrators.

### `approve` goes to the `TokenManager`, not the service

For `BURN_MINT` and `LOCK_UNLOCK` transfers, the source token is moved by the
per-token `TokenManager`. ERC-20 approvals must target the `TokenManager`
address (returned by `resolveTokenManager(tokenId)`), not the
`CrossChainTokenService`.

### `destinationAddress` is packed address bytes, not ABI-encoded

`crossChainTransfer` takes `destinationAddress` as `bytes`. On EVM, pass the raw
20-byte address (`abi.encodePacked(recipient)` /
`encodePacked(["address"], [recipient])`). Do **not** use `abi.encode` /
`encodeAbiParameters([{ type: "address" }], …)`, which produces a 32-byte
left-padded word and breaks destination delivery. This is separate from
application `hookData`, which may still be ABI-encoded.

### Custom `tokenId` uses `customTokenDeploySalt`

For custom tokens, the `tokenId` is
`crossChainTokenId(deployer, customTokenDeploySalt(deployer, salt))`. Calling
`crossChainTokenId(deployer, salt)` alone yields a different id. Native
crosschain tokens use `crossChainTokenId(deployer, salt)` directly.

### `resolveTokenManager` and `resolveTokenAddress` revert on miss

Both revert with `TokenNotRegistered(tokenId)` when the predicted address has no
deployed code. They do not return `address(0)`. To probe for existence without
throwing, wrap the call in `try`/`catch` or check `extcodesize` on the
deterministic CREATE3-predicted address.

### `CrossChainToken` denylist provider replaces, it does not chain

`CrossChainToken` holds a single denylist provider slot. Calling
`updateDenylistProvider` overwrites the previous value rather than adding to it.
To combine multiple lists, deploy a composite provider that internally calls
each upstream. The service-level denylist still runs in addition to the
per-token layer.

### Ownerless deploys inherit Circle's denylist provider

When `deployRemoteOwnerlessToken` lands on a remote domain, the new
`CrossChainToken`'s `denylistProvider` is initialized to the service's provider.

### `tokenManagerOwner`, `tokenManagerOperator`, and related fields are `bytes`

The fields are typed `bytes` instead of `address` so the same struct works for
non-EVM domains in the future. On EVM, pack the address with
`abi.encodePacked(addr)` / `encodePacked(["address"], [addr])` (20 bytes). Do
not use `encodeAbiParameters([{ type: "address" }], [addr])`, which ABI-pads to
32 bytes and is not equivalent.

For an **existing** ERC-20 registered with `registerCustomToken`, pass empty
bytes (`0x`) for `tokenOwner` and `tokenMinter`; grant the `TokenManager` minter
role on the token in a separate step.

### `sourceAddress` in hook callbacks is `bytes`

Same reason as the deployment params: the hook signature carries the source
address as `bytes` so receivers can decode senders from non-EVM blockchains.

### Hook execution can be reorged on fast transfers

When a hook executes with `finalityThresholdExecuted < 2000`, the source
blockchain transaction is still pre-finality and could be reorged. Receivers
that opt into fast hook execution must design their logic to tolerate rollback
by deferring irreversible side-effects until finality is confirmed.

## Function index

### `CrossChainTokenService`

| Group               | Functions                                                                                                                                                        |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Crosschain transfer | `crossChainTransfer`, `transmitCrossChainTransfer`                                                                                                               |
| Resolve and inspect | `resolveTokenManager`, `resolveTokenAddress`, `getOwnerlessTokenId`, `crossChainTokenId`, `customTokenDeploySalt`, `ownerlessTokenDeploySalt`, `isTrustedDomain` |
| Trust and limits    | `addTrustedDomain`, `removeTrustedDomain`, `setRateLimits`                                                                                                       |
| Denylist            | `denylistProvider`, `setDenylistProvider`, `initializeDenylistProvider`                                                                                          |
| Roles and upgrade   | `feeService`, `messageTransmitter`, `operator`, `transferOwnership`, `acceptOwnership`, `pause`, `unpause`, `upgradeToAndCall`                                   |

### `TokenManager`

| Group             | Functions                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------ |
| Transfer limits   | `setRateLimit`, `setMaxTransferAmount`, `setRateLimitWindow`                               |
| Pause             | `pause`, `unpause`, `updatePauser`                                                         |
| Implementation    | `upgradeToCustomImplementation`, `resetToDefaultImplementation`                            |
| Ownership         | `transferOwnership`, `acceptOwnership`, `transferOperatorship`                             |
| Circle assignment | `assignOwnership`, `assignOperatorship` (only callable on ownerless `TokenManager`s)       |
| Service-only      | `takeToken`, `giveToken` (called by `CrossChainTokenService`; not callable by integrators) |

### `CrossChainToken`

| Group             | Functions                                                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| Crosschain        | `crossChainTransfer`, `crossChainTransferFrom`                                                                      |
| Mint and burn     | `mint`, `burn`, `burnFrom`, `addMinter`, `removeMinter`, `transferMintership`                                       |
| Denylist          | `denylistProvider`, `isDenylisted`, `updateDenylistProvider`                                                        |
| Implementation    | `upgradeToCustomImplementation`, `resetToDefaultImplementation`                                                     |
| Ownership         | `transferOwnership`, `acceptOwnership`                                                                              |
| Circle assignment | `assignOwnership`, `assignOperatorship` (only callable on ownerless `CrossChainToken`s)                             |
| ERC-20 surface    | Standard ERC-20 (`transfer`, `transferFrom`, `approve`, `allowance`, `balanceOf`, `totalSupply`) plus `ERC20Permit` |

### `CrossChainTokenExecutable`

Optional helper (when published). Integrators may instead implement the public
entrypoint directly.

| Group               | Functions                                                             |
| ------------------- | --------------------------------------------------------------------- |
| Hook entry point    | `executeWithCrossChainToken` (returns `EXECUTE_SUCCESS`)              |
| Hook implementation | `_executeWithCrossChainToken` (override when using the abstract base) |
