57637 sc low acceptadminownership doesn t allow expected user approval

Submitted on Oct 27th 2025 at 19:46:04 UTC by @magtentic for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #57637

  • 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

acceptAdminOwnership is a function to transfer a pendingAdmin request as part of a 2 step process. As such, there is an expectancy that the pendingAdmin should be able to call the function without the function reverting.

Vulnerability Details

When pendingAdmin calls acceptAdminOwnership, the transaction reverts. This is unexpected, as the function is meant to be called by the pendingAdmin. The reason for this mainly resides with the function having the onlyAdmin modifier attached to it which then forces the admin to only call this function.

    function acceptAdminOwnership() external onlyAdmin {
...

onlyAdmin modifier :

This process just nullifies the 2 step process of calling transferAdminOwnerShip then the pendingAdmin calling acceptAdminOwnership as the current admin could've then just called setAdmin with the new admin details.

Impact Details

pendingAdmin cannot accept admin ownership, in the context of the naming, acceptance of a pendingAdmin to admin is done by the pendingAdmin and not the current admin.

References

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

  • PermissionedProxy : https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/utils/PermissionedProxy.sol?utm_source=immunefi

As a sidenote, the function transferAdminOwnerShip has an uppercase S in the name which should be lowercase for readability.

Proof of Concept

Proof of Concept

Steps below shows how a pendingAdmin cannot accept the admin ownership to themselves and needs to be done by the existing admin on their behalf.

  • Current admin configures a new admin by calling transferAdminOwnerShip

  • New admin tries to accept admin ownership by calling acceptAdminOwnership which then fails.

  • Current admin calls acceptAdminOwnership which now passes due to the onlyAdmin modifier.

  • Additional flows calling setAdmin to transfer ownership back to previous admin showing only a single step was required from the current admin

Was this helpful?