Skip to content
Last updated: February 06, 2026

SCWE-129: Single EOA Admin Without Rotation

Stable Version v1.0

This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

Relying on a single externally owned account (EOA) as contract admin creates a single point of failure. Key compromise, loss, or coercion can lead to irreversible upgrades, pauses, or fund drains with no recovery.

Remediation

  • Use multisig (>=2-of-3) with hardware keys and timelocks for admin roles.
  • Document and test key rotation and recovery procedures.
  • Limit admin powers by role (upgrade vs. ops) and enforce separation of duties.

Examples

Vulnerable

pragma solidity ^0.8.0;

contract Gov {
    address public admin; // single EOA — key loss or compromise = no recovery

    modifier onlyAdmin() { require(msg.sender == admin, "not admin"); _; }
    function upgrade(address impl) external onlyAdmin { /* ... */ }
}

Fixed

pragma solidity ^0.8.0;

contract Gov {
    IMultisig public admin;     // 2-of-3 multisig with hardware keys
    ITimelock public timelock;  // 48h delay for upgrades

    function proposeUpgrade(address impl) external {
        require(admin.isOwner(msg.sender), "not owner");
        timelock.schedule(impl);
    }
    function executeUpgrade() external {
        timelock.execute(); // only after delay; supports key rotation
    }
}