Skip to main content
The Ownable and Ownable2Step contracts provide ownership and access control mechanisms for CCTP contracts. Ownable2Step extends Ownable with a two-step ownership transfer pattern for additional security. Contracts:
  • src/roles/Ownable.sol
  • src/roles/Ownable2Step.sol

Ownable

Base contract providing single-owner access control mechanism.

State Variables

_owner

address private _owner
Private state variable storing the current owner address.

Functions

owner

function owner() public view virtual returns (address)
Returns the address of the current owner. Returns: Address of the current owner Source: Ownable.sol:65

transferOwnership

function transferOwnership(address newOwner) public virtual onlyOwner
Transfers ownership of the contract to a new account. In the base Ownable contract, this is a single-step process. Parameters:
  • newOwner: Address of the new owner
Requirements:
  • Caller must be the current owner
  • New owner must be non-zero address
Emits: OwnershipTransferred(address indexed previousOwner, address indexed newOwner) Source: Ownable.sol:80

Internal Functions

_checkOwner

function _checkOwner() internal view virtual
Verifies that the caller is the owner. Reverts: “Ownable: caller is not the owner” if caller is not the owner Source: Ownable.sol:72

_transferOwnership

function _transferOwnership(address newOwner) internal virtual
Internal function to transfer ownership without access restriction. Parameters:
  • newOwner: Address of the new owner
Emits: OwnershipTransferred(address indexed previousOwner, address indexed newOwner) Source: Ownable.sol:92

Modifiers

onlyOwner

modifier onlyOwner()
Restricts function access to the owner only. Reverts: “Ownable: caller is not the owner” if caller is not the owner Source: Ownable.sol:57

Events

OwnershipTransferred

event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
)
Emitted when ownership is transferred. Parameters:
  • previousOwner: Address of the previous owner
  • newOwner: Address of the new owner

Ownable2Step

Extends Ownable with a two-step ownership transfer pattern for enhanced security.

Key Concepts

Two-Step Transfer Pattern:
  1. Current owner calls transferOwnership() to propose a new owner
  2. New owner must call acceptOwnership() to complete the transfer
This prevents accidental transfers to incorrect addresses and ensures the new owner can access the account.

State Variables

_pendingOwner

address private _pendingOwner
Private state variable storing the pending owner address during transfer.

Functions

pendingOwner

function pendingOwner() public view virtual returns (address)
Returns the address of the pending owner during a transfer. Returns: Address of the pending owner, or zero address if no transfer is pending Source: Ownable2Step.sol:47

transferOwnership

function transferOwnership(address newOwner)
    public
    virtual
    override
    onlyOwner
Starts the ownership transfer by setting the pending owner. This overrides the base Ownable function. Parameters:
  • newOwner: Address of the proposed new owner
Requirements:
  • Caller must be the current owner
Effects:
  • Sets _pendingOwner to newOwner
  • Does NOT immediately transfer ownership
Emits: OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) Source: Ownable2Step.sol:55

acceptOwnership

function acceptOwnership() external
Completes the ownership transfer. Must be called by the pending owner. Requirements:
  • Caller must be the pending owner
Effects:
  • Transfers ownership to the caller
  • Clears the pending owner
Emits: OwnershipTransferred(address indexed previousOwner, address indexed newOwner) Source: Ownable2Step.sol:77

Internal Functions

_transferOwnership

function _transferOwnership(address newOwner) internal virtual override
Internal function to complete ownership transfer. Clears the pending owner before transferring. Parameters:
  • newOwner: Address of the new owner
Source: Ownable2Step.sol:69

Events

OwnershipTransferStarted

event OwnershipTransferStarted(
    address indexed previousOwner,
    address indexed newOwner
)
Emitted when an ownership transfer is initiated. Parameters:
  • previousOwner: Address of the current owner
  • newOwner: Address of the proposed new owner

Usage Examples

// Step 1: Current owner initiates transfer
ownable2Step.transferOwnership(0xNEW_OWNER_ADDRESS);

// Check pending owner
address pending = ownable2Step.pendingOwner(); // 0xNEW_OWNER_ADDRESS

// Step 2: New owner accepts ownership
// (Must be called by 0xNEW_OWNER_ADDRESS)
ownable2Step.acceptOwnership();

// Verify new owner
address currentOwner = ownable2Step.owner(); // 0xNEW_OWNER_ADDRESS

Replacing a Pending Transfer

// Initiate transfer to address A
ownable2Step.transferOwnership(0xADDRESS_A);

// Change mind and transfer to address B instead
// This replaces the pending transfer
ownable2Step.transferOwnership(0xADDRESS_B);

// Now only address B can accept ownership

Owner Privileges

In CCTP contracts, the owner role typically has the following privileges:
  • Update the attester manager (Attestable)
  • Update the pauser (Pausable)
  • Update the rescuer (Rescuable)
  • Update the token controller (TokenController)
  • Other administrative functions

Security Considerations

Why Use Ownable2Step?

  1. Prevents Typos: Cannot accidentally transfer to wrong address
  2. Verifies Access: Ensures new owner can sign transactions from that address
  3. Allows Cancellation: Can initiate new transfer to different address before acceptance
  4. No Renounce: Neither contract includes renounceOwnership() to prevent accidental loss of control

Best Practices

  • Use Ownable2Step for all production contracts (as CCTP does)
  • Owner should be a multisig or DAO governance contract
  • Never transfer ownership to an unverified address
  • Ensure the new owner address is controlled and accessible
  • Monitor for OwnershipTransferStarted events
  • Complete pending transfers promptly or replace them

Inheritance Hierarchy

Context (OpenZeppelin)
    └── Ownable
            └── Ownable2Step
All CCTP role contracts inherit from Ownable2Step for maximum security.

Origin

Forked from OpenZeppelin Contracts with modifications:
  • Updated Solidity version from 0.8.0 to 0.7.6
  • Removed renounceOwnership() function for safety
  • Based on v8 contracts which include internal _transferOwnership method

Build docs developers (and LLMs) love