F-2024-0004·single-step-ownership-transfer

Single-Step Ownership Transfer Vulnerability in RibbonVault Constructor

Acknowledgedvaulthealthfipoints
TL;DR

RibbonVault inherits OpenZeppelin Ownable with a single-step transfer pattern. An incorrect new-owner address would permanently brick onlyOwner functions.

Severity
LOW
Impact
MEDIUM
Likelihood
LOW
Method
MManual review
CAT.
Complexity
LOW
Exploitability
LOW
02Section · Description

Description

Constructor vulnerability in the RibbonVault contract.

The constructor in the RibbonVault contract inherits from OpenZeppelin's Ownable contract, which utilizes a single-step ownership transfer pattern. This can lead to a situation where, if an incorrect address is provided for the new owner, none of the onlyOwner marked methods will be callable again. This single-step transfer pattern poses a risk of permanently locking out the admin from executing essential functions if an error is made during the ownership transfer.

03Section · Impact

Impact

High, because it bricks core protocol functionality. If an incorrect owner address is set, the admin will lose access to all functions protected by the onlyOwner modifier, preventing essential administrative tasks and potentially causing significant disruption to the contract's operations.

04Section · Recommendation

Recommendation

Use OpenZeppelin's Ownable2Step contract instead of Ownable. The Ownable2Step contract implements a two-step ownership transfer process, which requires the new owner to explicitly accept the ownership transfer. This approach significantly reduces the risk of errors during the transfer process and ensures that the new owner can take over the contract management without issues.

Replace the inheritance of Ownable with Ownable2Step in the RibbonVault contract. The constructor should be updated to initiate the two-step ownership transfer process, as demonstrated below:

solidity
import "@openzeppelin/contracts/access/Ownable2Step.sol";
constructor(address owner, string memory name, address paymentAddress, address pointsaddress)
Ownable2Step() EIP712(name, "1") {
vaultName = name;
depositFee = 10;
rate = 5000;
pointsMin = 10000 * 10 ** 18;
_Ipaymentcoin = IERC20T(paymentAddress);
_Ipointscoin = IERC20T(pointsaddress);
admin = owner;
onlyApprovedAdmin[owner] = true;
transferOwnership(owner);
}

By using Ownable2Step, the ownership transfer process becomes more secure, reducing the likelihood of administrative errors and ensuring the continued operability of the contract's core functionalities.

F-2024-0004

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx