Skip to main content

Denylistable Role

The Denylistable contract provides denylist management capabilities for CCTP V2 contracts. It allows designated denylister addresses to add or remove accounts from a denylist, preventing them from participating in cross-chain transfers. Source: Denylistable.sol
Denylistable is a V2-only feature. V1 contracts do not support denylisting.

Overview

Denylistable extends Ownable2Step and provides:
  • A designated denylister role that can manage the denylist
  • Functions to add and remove addresses from the denylist
  • A modifier to check if an address is denylisted
  • Events for tracking denylist changes
This role is used by TokenMessengerV2 to enforce compliance and prevent sanctioned addresses from using CCTP.

State Variables

_denylister

address internal _denylister;
The address with permission to add or remove accounts from the denylist.

_denylisted

mapping(address => uint256) internal _denylisted;
Maps addresses to their denylist status (1 = denylisted, 0 = not denylisted). Uses uint256 instead of bool for gas optimization.

Events

DenylisterChanged

Emitted when the denylister address is updated.
event DenylisterChanged(
    address indexed oldDenylister,
    address indexed newDenylister
);

Denylisted

Emitted when an account is added to the denylist.
event Denylisted(address indexed account);

UnDenylisted

Emitted when an account is removed from the denylist.
event UnDenylisted(address indexed account);

Functions

updateDenylister

Updates the denylister address. Only callable by the contract owner.
function updateDenylister(address newDenylister) external onlyOwner;
Parameters:
  • newDenylister (address): The address of the new denylister
Requirements:
  • Caller must be the contract owner
  • newDenylister must not be the zero address
Reference: src/roles/v2/Denylistable.sol:71

addToDenylist

Adds an account to the denylist. Only callable by the denylister.
function addToDenylist(address account) external onlyDenylister;
Parameters:
  • account (address): The address to add to the denylist
Requirements:
  • Caller must be the denylister
  • account must not already be denylisted
Emits: Denylisted event Reference: src/roles/v2/Denylistable.sol:87

removeFromDenylist

Removes an account from the denylist. Only callable by the denylister.
function removeFromDenylist(address account) external onlyDenylister;
Parameters:
  • account (address): The address to remove from the denylist
Requirements:
  • Caller must be the denylister
  • account must currently be denylisted
Emits: UnDenylisted event Reference: src/roles/v2/Denylistable.sol:101

isDenylisted

Checks if an address is on the denylist.
function isDenylisted(address account) external view returns (bool);
Parameters:
  • account (address): The address to check
Returns:
  • bool: true if the account is denylisted, false otherwise
Reference: src/roles/v2/Denylistable.sol:113

denylister

Returns the current denylister address.
function denylister() external view returns (address);
Returns:
  • address: The address of the current denylister
Reference: src/roles/v2/Denylistable.sol:121

Modifiers

onlyDenylister

Restricts function access to the denylister address only.
modifier onlyDenylister();
Reverts with “Caller is not the denylister” if the caller is not the denylister.

notDenylisted

Checks that an address is not on the denylist.
modifier notDenylisted(address account);
Parameters:
  • account (address): The address to check
Reverts with “Account is denylisted” if the address is on the denylist.

Usage in TokenMessengerV2

TokenMessengerV2 uses Denylistable to prevent denylisted addresses from:
  • Depositing USDC for burn
  • Receiving minted USDC on the destination chain

Deposit Validation

function depositForBurn(
    uint256 amount,
    uint32 destinationDomain,
    bytes32 mintRecipient,
    address burnToken,
    uint256 maxFee,
    uint32 minFinalityThreshold,
    bytes32 destinationCaller
) external notDenylisted(msg.sender) returns (uint64) {
    // Deposit logic...
}

Mint Recipient Validation

Before minting, TokenMessengerV2 checks that the mint recipient is not denylisted:
bytes32 mintRecipient = burnMessage.mintRecipient();
address mintRecipientAddress = mintRecipient.toAddress();

require(
    !isDenylisted(mintRecipientAddress),
    "Mint recipient is denylisted"
);

Configuration Example

Setting up denylist management:
// Deploy TokenMessengerV2 with denylister role
address denylister = 0x123...; // Compliance team address

// Update denylister (owner only)
tokenMessengerV2.updateDenylister(denylister);

// Add address to denylist (denylister only)
address sanctionedAddress = 0x456...;
tokenMessengerV2.addToDenylist(sanctionedAddress);

// Check if address is denylisted
bool isDenied = tokenMessengerV2.isDenylisted(sanctionedAddress);
// Returns: true

// Remove from denylist when appropriate
tokenMessengerV2.removeFromDenylist(sanctionedAddress);

Security Considerations

The denylister role has significant power to block addresses from using CCTP. This role should be assigned to trusted compliance personnel or multi-sig wallets.

Access Control

  • Owner Control: Only the contract owner can change the denylister address
  • Denylister Independence: The denylister operates independently from the owner for day-to-day denylist management
  • Two-Step Ownership: Inherits secure ownership transfer from Ownable2Step

Best Practices

  1. Multi-Sig Denylister: Use a multi-sig wallet as the denylister to prevent single-point-of-failure
  2. Monitoring: Emit and monitor all denylist events for transparency
  3. Regular Reviews: Periodically review the denylist and remove addresses when appropriate
  4. Emergency Procedures: Have documented procedures for urgent denylist additions
  5. Legal Compliance: Ensure denylist operations comply with applicable regulations

Gas Optimization

The contract uses uint256 constants (_TRUE = 1, _FALSE = 0) instead of booleans for the denylist mapping to optimize gas costs during storage operations.
  • TokenMessengerV2 - Implements Denylistable for deposit and mint protection
  • Ownable2Step - Base contract providing secure ownership management
  • V2 Overview - Learn more about V2 features including denylist

Comparison with V1

V1 contracts do not have denylist functionality. This is a new feature in V2 designed to enhance compliance capabilities:
FeatureV1V2
Denylist Support❌ No✅ Yes
Compliance Role❌ No✅ Denylister
Address Blocking❌ No✅ Yes
Granular Control❌ No✅ Yes

Migration Considerations

When migrating from V1 to V2, you’ll need to:
  1. Designate a denylister address
  2. Import any existing blocklists from external sources
  3. Update client code to handle denylist-related errors
  4. Implement monitoring for denylist events
See the V2 Migration Guide for complete migration instructions.

Build docs developers (and LLMs) love