Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-8023: Multi-step Contract Ownership

Contract ownership management with multi-step ownership transfer mechanism for secure smart contract ownership

Authors David Kim (@PowerStream3604)
Created 2025-09-16
Discussion Link https://ethereum-magicians.org/t/erc-8023-multi-step-contract-ownership/25475

Abstract

We define a multi-stepped contract ownership interface for more secure contract ownership management. This makes the ownership transfer into 3 distinct steps. With the first 2 steps, performed by the original owner (initiate → confirm) and the remaining 1 step performed by the new owner (accept).

We enforce an optional time window between the initiate and confirm stages to give additional room for review, and make ownership key compromise scenarios less fatal.

Motivation

Ownership management is crucial in on-chain security and a significant portion of the security assumptions of defi protocols, smart contract wallets and on-chain utilities rely on the contract ownership.

The single-step transferOwnership() style ownership mechanism has been in the industry for a long time, (e.g.,ERC-173), and has been used ubiquitously as an industry standard. As the industry evolves and attacks get more sophisticated there is a strong need for a multi-step, time gated ownership management process to enhance the ecosystem’s contract ownership to be more reviewable, stoppable, thorough and secure.

The main objective of this standard is to make ownership more secure and handled in a multi-stepped approach that enables the operation to be conducted with more caution and lesser operational mistakes, while being immune to potential scam attacks.

Key factors taken into consideration for the standard:

  1. Reduce probability of operational mistakes.
  2. Foster on-chain reviewal practice for ownership transfer.
  3. Ability to rollback ownership transfer, during the transfer process.
  4. Simplicity.

1. Reduce probability of operational mistakes: The standard makes the ownership transfer stage into 3 different clear steps. Initiation → Confirmation → Acceptance. The owner will have the enforced ability to review the newOwner address secured by the pre-set buffer time. Also to reduce any operational mistakes or possible scams (e.g., address poisoning) the address is required as the parameter in each Initiation & Confirmation stage.

2. Foster on-chain reviewal practice for ownership transfer: We not only targets this as a contract interface and implementation methodology, but also hopes to foster an ecosystem-level awareness and security practice to thoroughly review, confirm the ownership transfer. The standard helps operators of Smart Contract to review newOwner address on-chain, and further confirm again if the address is indeed correct.

3. Ability to rollback ownership transfer, during the transfer process: Whether through an operational mistake or private key leak of owner account, or other reasons, the ability to rollback ownership transfer within the time buffer highly increases the security and operational burden.

Even in the extreme case of owner private key leak, if the buffer time is set enough, the original owner can earn time to evacuate the funds from the protocol, and possibly prohibit ownership transfer through DoS of ownership (attack → initiate , original owner(defender) → re initiate. which will reset the time back to 0).

4. Simplicity: MultistepOwnable is targeted to be a simple contract given the diverse use cases and scenarios it could be applied. The process for ownership transfer is concise but thorough enough to allow owners review each step. This is the rationale behind making the ownership transfer time buffer and buffer time update capability optional.

Specification

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.

A multi-step ownable contract MUST implement the following interface:

/// @title Multistep Ownership Standard
interface MultiStepOwnable {

	event OwnershipTransferInitiated(address indexed prevOwner, address indexed newOwner);	
	event OwnershipTransferConfirmed(address indexed prevOwner, address indexed newOwner);
	event OwnwershipTransferred(address indexed prevOwner, address indexed newOwner);

	/// @dev initiate the ownership transfer. First step of ownership transfer.
	/// moves the newOwner to the preConfirmed stage.
	/// 
	/// @param newOwner the address of the new owner of the contract.
	/// stored as preConfirmedOwner.
	function initiateOwnershipTransfer(address newOwner) external;
	
	/// @dev confirm the ownership transfer. Second step of ownership transfer.
	/// confirmation can only be done after the transfer-buffer period from initiation.
	/// newOwner should match with the initiation step's newOwner.
	/// To initiate ownership transfer to a different newOwner, initiation step should be re-conducted.
	///
	/// @param newOwner the address of the new owner of the contract.
	/// stored as pendingOwner.
	function confirmOwnershipTransfer(address newOwner) external;
	
	/// @dev cancels the pending ownership transfer. Before the final step of ownership transfer (acceptOwnershipTransfer()).
	/// This function should wipe out the pendingOwner.
	/// By calling this function, ownership transfer process is canceled and should be reinitiated from initiateOwnershipTransfer().
	function cancelPendingOwnershipTransfer() external;

	/// @dev accepts the ownership transfer. Final step of ownership transfer.
	/// This function can only be called by the newOwner that was confirmed in step 2.
	/// The contract should perform access control e.g.,
	/// msg.sender == pendingOwner()
	function acceptOwnershipTransfer() external;
	
	/// @notice only the address returned by owner() has authority as the owner.
	/// pendingOwner() and preConfirmedOwner() should not possess any
	/// authority/access/right.
	/// @dev returns the owner of the contract
	function owner() external view returns (address);
	
	/// @dev returns the pending owner of the account.
	/// pending owner should not have any authority/access/right.
	function pendingOwner() external view returns (address);
	
	/// @dev returns the pre-confirmed owner of the account.
	/// pre-confirmed owner should not have any authority/access/right.
	function preConfirmedOwner() external view returns (address);
	
	/// @dev returns the ownership transfer buffer time (in seconds).
	/// the buffer is enforced between initiation <> confirmation of ownership transfer. 
	/// the standard does not enforce the value range. it is highly recommended to be between 2 <> 14 days.
	function getOwnershipTransferBuffer() external view returns (uint256);
}

The MultiStepOwnable contract MAY implement the UpdateableOwnershipTransferBuffer interface to enable buffer period modification.

The contract MUST update the buffer period with a 2 step approach of initiation (initiateOwnershipBufferUpdate()) and then confirmation (confirmOwnershipBufferUpdate()) after the existing buffer period. The buffer period should be enforced between these 2 function calls. If this behavior is not enforced, the security of ownershipTransferBuffer could break during owner key compromise scenario.

/// @title UpdateableOwnershipTransferBuffer. Extension of MultiStepOwnable.
interface UpdateableOwnershipTransferBuffer {

	/// @dev initiates the update of ownership transfer time buffer.
	function initiateOwnershipBufferUpdate(uint256 newBuffer) external;
	
	/// @dev confirms the update of ownership transfer time buffer.
	/// confirmation SHOULD revert if existing time buffer did not pass since
	/// initiation of ownership transfer time buffer.
	function confirmOwnershipBufferUpdate(uint256 newBuffer) external;
}

Rationale

  1. A time buffer for ownership transfer is introduced to foster a process of reviewing the new owner address on-chain. However, this remains an optional behavior to allow flexibility in ownership management. Removing the optional time buffer would be similar to the implementation of Ownable2Step with an additional step for confirmation.

  2. Enforcing a time buffer to update the ownership transfer time buffer is crucial for maintaining the security of the MultiStepOwnable contract. When the owner key is compromised, this allows the original owner of the account to be able to DoS and prohibit the ownership transfer to the malicious entity when the ownership transfer buffer is sufficiently long enough.

  3. For compatibility with existing ownership mechanisms, the standard is designed to be compatible with the existing ownership mechanism for fetching the owner through owner().

Backwards Compatibility

TBD

Security Considerations

  1. Only owner should be available to call initiateOwnershipTransfer() & confirmOwnershipTransfer() & cancelPendingOwnershipTransfer().
  2. OwnershipTransferBuffer should be set together when owner is set. e.g., constructor(), initialize().
  3. If OwnershipTransferBuffer is set, it should be strictly enforced between initiation and confirmation.
  4. Before the new owner performs acceptOwnership(), the original, existing owner should still hold all rights as the owner. Because the owner is still unchanged.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

David Kim (@PowerStream3604), "ERC-8023: Multi-step Contract Ownership [DRAFT]," Ethereum Improvement Proposals, no. 8023, September 2025. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-8023.