Skip to main content
A TokenManager is a per-token, per-domain contract that controls crosschain transfers for a custom token registered on CCTP for non-USDC. Each domain where your token is active has its own independent TokenManager, and three roles govern it: the owner (highest privilege, can change all roles), the operator (sets rate limits and transfer caps), and the pauser (can pause and unpause the token on this domain). You need to manage these roles when rotating keys, delegating operator duties to a separate address, or responding to a security incident.

Prerequisites

Before you begin, ensure that you’ve:
  • Registered a custom token using registerCustomToken or deployCrossChainToken, and noted the tokenId. See Configure a custom token
  • Noted the CrossChainTokenService address for each domain where you want to change roles. See Contract addresses
  • Verified access to the wallet that currently holds the role you want to change: the owner wallet for ownership, operator, or pauser changes; the pauser wallet to call pause or unpause

Steps

Step 1: Find the TokenManager address

Every token has a separate TokenManager per domain. Resolve its address from the CrossChainTokenService before making any role change.
TypeScript
Repeat this on each domain where you want to change roles. Each domain’s TokenManager is an independent contract with its own role state.

Step 2: Transfer ownership

Ownership transfer is a two-step operation. The current owner calls transferOwnership to initiate. The TokenManager enters a pending state—ownership has not changed yet. The new owner must then call acceptOwnership from their address to complete the transfer.
Transferring ownership to an address you do not control is irreversible. If the new owner never calls acceptOwnership, the current owner retains control—but there is no cancel mechanism, and the pending transfer remains indefinitely. Verify the new address carefully before sending the transaction.
Both transactions require a private key. Keep keys out of version control—load them from environment variables or a secrets manager. Initiate the transfer (current owner):
TypeScript
Complete the transfer (new owner):
TypeScript
This applies only to the domain where you call it. Repeat on each domain’s TokenManager independently.

Step 3: Replace the operator

Unlike ownership, operator replacement is a single-step operation. The current owner calls transferOperatorship and the change takes effect immediately—no acceptance step is required.
TypeScript
The operator can call setRateLimit, setMaxTransferAmount, and setRateLimitWindow on the TokenManager. For details on configuring those values, see Configure rate limits and caps.

Step 4: Replace the pauser

The pauser role defaults to the operator address at deployment time. If you never call updatePauser, the operator is also the pauser. The current owner can replace the pauser by calling updatePauser.
TypeScript
The pauser can call pause() to block all crosschain transfers of this token on this domain, and unpause() to restore them. These calls affect only this token on this domain—they are not protocol-wide.
TypeScript