Skip to content

fix: proxy upgrade + initialize should be atomic to prevent frontrunning #3842

@owenwahlgren

Description

@owenwahlgren

Summary

The permissioned L1 validator manager setup flow at /console/permissioned-l1s/validator-manager-setup/upgrade-proxy is vulnerable to proxy initialization frontrunning.

The current implementation calls ProxyAdmin.upgrade() and ValidatorManager.initialize() in two separate transactions, leaving a window where an attacker can front-run the initialize() call with malicious parameters (e.g., setting themselves as the owner).

Current Flow (Vulnerable)

TX 1: ProxyAdmin.upgrade(proxy, newImplementation)     // UpgradeProxy.tsx:154-161
                                                        // ⚠️ Window opens here — proxy is uninitialized
TX 2: ValidatorManager.initialize(settings)             // Initialize.tsx:148-155

Relevant files:

  • components/toolbox/console/permissioned-l1s/validator-manager-setup/UpgradeProxy.tsx — calls upgrade() only
  • components/toolbox/console/permissioned-l1s/validator-manager-setup/Initialize.tsx — separate initialize() tx

Proposed Fix

Use ProxyAdmin.upgradeAndCall() (already available in the ProxyAdmin ABI) to combine upgrade + initialization into a single atomic transaction:

TX 1: ProxyAdmin.upgradeAndCall(proxy, newImplementation, initializeCalldata)
      // No window — proxy is upgraded and initialized atomically

This eliminates the frontrunning window entirely. The initializeCalldata should be the ABI-encoded call to initialize(settings).

Steps to Reproduce

  1. Go to Console → Permissioned L1s → Validator Manager Setup
  2. Deploy validator manager + proxy
  3. Observe that "Upgrade Proxy" and "Initialize Manager" are separate steps with separate transactions
  4. Between TX 1 and TX 2, any actor could call initialize() on the proxy

Target Branch

Fix should be implemented in feature/console-blueprints-redesign.

References

  • OpenZeppelin ProxyAdmin.upgradeAndCall
  • The academy docs (content/academy/avalanche-l1/permissioned-l1s/04-validator-manager-deployment/03-upgrade-proxy.mdx) reference upgradeToAndCall() in the description but the implementation doesn't use it

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions