56418 sc low two step owner transfer is broken and can lead to unforseen damages
Submitted on Oct 15th 2025 at 19:24:07 UTC by @Josh4324 for Audit Comp | Alchemix V3
Report ID: #56418
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Protocol insolvency
Ownership at risk
Description
Brief/Intro
The two-step ownership system is designed to protect against accidental ownership transfer or loss of ownership. However, with the way it is implemented in https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol?utm_source=immunefi#L27-L35, ownership can easily be lost, renounced (transferred to address zero) or transferred to an inactive address (i.e being lost)
Vulnerability Details
The way traditional two-step ownership transfer works is that the admin transfers ownership to a new admin by calling transferAdminOwnerShip, and that new admin accepts the ownership by calling acceptAdminOwnership. This system prevents the accidental loss of admin privileges.
However, with the way the admin transfer system is designed, calling acceptAdminOwnership by the current admin when pendingAdmin is address(0) will lead to loss of admin rights.
See OpenZeppelin implementation here https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable2Step.sol
Impact Details
Accidental renouncing of admin rights: If
acceptAdminOwnershipis called when pendingAdmin is equal to address(0), admin rights will be lost. This can easily happen accidentally if the transaction is sent twice.Loss of admin rights to an invalid address: While not likely to occur, the original
acceptAdminOwnershiprequires that the new owner is the "mes.sender"; this condition ensures that the new admin is an active wallet on that chain.Finally, it breaks the whole purpose or design of the two-step ownership transfer, because only one address is involved in the whole process.
References
https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol?utm_source=immunefi#L27-L35
Recommendation
Use the original openzepplin implementation: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable2Step.sol
Proof of Concept
Proof of Concept
I will be providing two POCs. Please add the following test to test/AlchemistCurator.t.sol
Accidental Renounce
Output
You can see that the admin has been accidentally renouced and set to address zero
New Admin cannot accept ownership
Output A new admin or a pending admin cannot accept ownership, thereby breaking the core design of the two-step ownership transfer.
Was this helpful?