56830 sc low broken admin ownership transfer logic acceptadminownership requires current admin instead of pending admin blocking role claim

Submitted on Oct 21st 2025 at 04:04:05 UTC by @akioniace for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #56830

  • 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

Description

Brief/Intro

AlchemistCurator::acceptAdminOwnership() is incorrectly restricted by onlyAdmin modifier. After the current Admin calls transferAdminOwnership(newAdmin), the pendingAdmin cannot call acceptAdminOwnership() to complete the transfer because accept function require caller to already to be as current Admin. As implemented, only the current admin can accept the pending admin, which defeats the two step handover flow.

Vulnerability Details

The Root cause is that AlchemistCurator::acceptAdminOwnership() function is declared as:

// FILE: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol?utm_source=immunefi#L31-L35

    function acceptAdminOwnership() external onlyAdmin { // @audit-issue : current admin, should be pendingAdmin
        admin = pendingAdmin;
        pendingAdmin = address(0);
        emit AdminChanged(admin);
    }

Because of onlyAdmin, only the existing admin can call acceptAdminOwnersup(). The intended two-step transfer pattern requires the pendingAdmin to be able to opt in and claim the role. The correct guard is that caller must be equal pendingAdmin, not admin.

Impact Details

The acceptAdminOwnership() function is incorrectly restricted to the current admin, preventing the pending admin from claiming ownership. This breaks the two-step ownership transfer logic.

References

To achieve two step ownership look at the openzeppelin Ownable2Step process: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable2Step.sol#L60-L67

Broken Logic Code Link - https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol?utm_source=immunefi#L31-L35

Proof of Concept

Proof of Concept

Copy Paste this function into v3-poc/src/test/AlchemistCurator.t.sol:

run with command:

Here is the Logs From the Test:

Was this helpful?