Openzeppelin Ownable: Use Ownable2Step Instead

The onlyOwner modifier is probably one of the most common patterns in Solidity.

In the following example, the function setMessage() can only be called by the address designated to be the owner.

function setMessage(string calldata _message) external onlyOwner {
    message = _message;
}

However, the commonly used Openzeppelin ownable implementation has a shortcoming that it allows the owner to transfer ownership to a non-existent or mistyped address.

Ownable2Step is safer than Ownable for smart contracts because the owner cannot accidentally transfer smart contract ownership to a mistyped address. Rather than directly transferring to the new owner, the transfer only completes when the new owner accepts ownership.

Ownable2Step was released in January 2023 during the OpenZeppelin version 4.8 update. Here is the code.

Here is an minimal example:

import "@openzeppelin/contracts/access/Ownable2Step.sol";

contract ExampleOwnable2Step is Ownable2Step {
    string public message;

    constructor() Ownable(msg.sender) {}

    function setMessage(string calldata _message) external onlyOwner {
        message = _message;
    }
}

Ownable2Step inherits from Ownable and overrides transferOwnership() to make the new owner “pending.” The receiver must then call acceptOwnership() to finalize the transfer. This ensures only an address that has access to it’s private keys, or control of the smart contract address, can control the smart contract.

There is still no two-step verification for renouncing ownership, i.e. transferring ownership to the zero address. If there is no need to renounce ownership, then it is safer to override “renounceOwnership()” to revert when called.

Learn More

This tutorial is part of our advanced solidity bootcamp.

Originally Published Apr 8, 2023

20 Common Solidity Beginner Mistakes

20 Common Solidity Beginner Mistakes Our intent is not to be patronizing towards developers early in their journey with this article. Having reviewed code from numerous Solidity developers, we’ve seen some mistakes occur more frequently and we list those here. By no means is this an exhaustive list of mistakes a Solidity developer can make. […]

Smart Contract Foundry Upgrades with the OpenZeppelin Plugin

Smart Contract Foundry Upgrades with the OpenZeppelin Plugin Upgrading a smart contract is a multistep and error-prone process, so to minimize the chances of human error, it is desirable to use a tool that automates the procedure as much as possible. Therefore, the OpenZeppelin Upgrade Plugin streamlines deploying, upgrading and managing smart contracts built with Foundry or […]

UUPS: Universal Upgradeable Proxy Standard (ERC-1822)

UUPS: Universal Upgradeable Proxy Standard (ERC-1822) The UUPS pattern is a proxy pattern where the upgrade function resides in the implementation contract, but changes the implementation address stored in the proxy contract via a delegatecall from the proxy. The high level mechanism is shown in the animation below: Similar to the Transparent Upgradeable Proxy, the […]

Try Catch and all the ways Solidity can revert

Try Catch and all the ways Solidity can revert This article describes all the kinds of errors that can happen when a smart contract is called, and how the Solidity Try / Catch block responds (or fails to respond) to each of them. To understand how Try / Catch works in Solidity, we must understand […]