Skip to content

feat: Adds a reinitializer(2) entrypoint to TopUpFactory#136

Open
luiscfaria wants to merge 1 commit into
masterfrom
audit/topup-factory-reinitializer
Open

feat: Adds a reinitializer(2) entrypoint to TopUpFactory#136
luiscfaria wants to merge 1 commit into
masterfrom
audit/topup-factory-reinitializer

Conversation

@luiscfaria

@luiscfaria luiscfaria commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a reinitializer(2) entrypoint to TopUpFactory so a reserved EtherFiPlaceholder proxy at the canonical CREATE3 factory address (0xF4e147…D8CF) can be upgraded into a working TopUpFactory. The placeholder already consumed OZ initializer v1, so TopUpFactory.initialize(also initializer) reverts with InvalidInitialization() on the upgrade. reinitialize(address) re-wires the beacon while reading roleRegistry() from existing storage.

This unblocks Asset Recovery on placeholder-reserved chains (Polygon, Plasma, and any future reserved chain).

Audit precedent

Byte-for-byte the same shape as the already-audited EtherFiSafeFactory.reinitialize:

  • Pattern audited as "Safe factory reinitializer (Scroll Migration)" — sc-cash-internal#2, live on OP mainnet today.
  • TopUpFactory.sol itself audited as "TopUp Contract Updates (Scroll Migration)" — sc-cash-internal#3.

This change is the 11-line combination of those two audited components. roleRegistry() is read from existing UpgradeableProxy storage (not re-supplied), so the upgrade cannot reassign it. Must be invoked atomically via upgradeToAndCall.

Changes

  • src/top-up/TopUpFactory.sol+reinitialize(address)
  • test/integration/fork/PolygonPlaceholderUpgradeFork.t.sol — fork test against real Polygon mainnet state: proves initialize reverts on the
    placeholder, reinitialize succeeds + preserves roleRegistry, and reinitialize is one-shot.
  • scripts/recovery/DeployPolygonTopUpFactory.s.sol — deploys the impls and emits the upgradeToAndCall(reinitialize) 3CP calldata.

Note

Medium Risk
Touches upgradeable factory initialization on a canonical production proxy address; mitigated by audited EtherFiSafeFactory pattern, preserved roleRegistry, and fork tests, but still a privileged on-chain upgrade path.

Overview
Adds TopUpFactory.reinitialize(address) (reinitializer(2)) so the reserved placeholder UUPS proxy at 0xF4e147…D8CF can be upgraded to a real factory without calling initialize again (placeholder already used OZ initializer v1). Reinit wires the beacon via __BeaconFactory_initialize using roleRegistry() from existing proxy storage, not a new argument—intended for a single atomic upgradeToAndCall(factoryImpl, reinitialize(topUpImpl)) from the RoleRegistry upgrader.

DeployPolygonTopUpFactory.s.sol deploys TopUpFactory + TopUp impls on Polygon and logs the 3CP upgrade calldata. PolygonPlaceholderUpgradeFork.t.sol forks Polygon mainnet to prove initialize reverts on the live placeholder, reinitialize succeeds (beacon, getDeterministicAddress, preserved RR), and reinit is one-shot.

Reviewed by Cursor Bugbot for commit 17c5111. Bugbot is set up for automated code reviews on this repo. Configure here.

@luiscfaria luiscfaria self-assigned this Jun 9, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@certora-run certora-run Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verification Results

  • Group ID: 5965f457-494e-4fc8-acc0-ba0b3a74362d
  • Commit: 17c5111
Job Result VERIFIED Link
EtherFiSafe.conf 14 Link
CashModuleCore.conf --rule "p03" 2 Link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant