Skip to content

[MerklPB] Migrate PoolBoosterFactoryMerkl to Beacon Proxy#2796

Open
clement-ux wants to merge 12 commits intomasterfrom
clement/improve-merklePB
Open

[MerklPB] Migrate PoolBoosterFactoryMerkl to Beacon Proxy#2796
clement-ux wants to merge 12 commits intomasterfrom
clement/improve-merklePB

Conversation

@clement-ux
Copy link
Collaborator

@clement-ux clement-ux commented Feb 12, 2026

Summary

Replaces EIP-1167 minimal proxies (Clones) with OpenZeppelin Beacon Proxy pattern so that upgrading a single GovernableBeacon automatically upgrades all existing Merkl pool boosters — no redeployment, no re-delegation.

Architecture

PoolBoosterFactoryMerkl (behind InitializeGovernedUpgradeabilityProxy)
    │
    ├── references → GovernableBeacon (IBeacon + Origin Governable)
    │                    │
    │                    └── points to → PoolBoosterMerklV2 implementation
    │
    └── deploys OZ BeaconProxy(beacon, initData) via CREATE2
         └── each proxy delegates to beacon.implementation()

Upgrading: governor calls GovernableBeacon.upgradeTo(newImpl) → all existing pool boosters instantly use the new implementation.

Changes

  • New: GovernableBeacon.sol — OZ IBeacon with Origin Governable access control
  • Refactored: PoolBoosterFactoryMerkl.sol — now Initializable behind InitializeGovernedUpgradeabilityProxy, deploys BeaconProxy via CREATE2, inlines pool booster tracking (no longer inherits AbstractPoolBoosterFactory)
  • New: PoolBoosterFactoryMerklProxy in Proxies.sol
  • Hardened: PoolBoosterMerklV2.sol — locked implementation against direct initialization, added campaignType > 0 validation
  • Factory guards — duplicate AMM pool check, removePoolBooster reverts on not found, storage gap for upgradeability
  • Removed unused oToken storage from factory
  • Updated deployment script for beacon + factory proxy setup
  • Updated fixture and fork tests (53 passing)

Test plan

  • All 53 mainnet fork tests passing
  • Beacon upgrade test: deploy new impl → beacon.upgradeTo() → existing proxies use new logic
  • Factory proxy initialization validation (zero governor, registry, beacon)
  • Duplicate AMM pool guard prevents double-creation
  • removePoolBooster reverts when address not found
  • computePoolBoosterAddress returns correct deterministic address
  • bribeAll() works across all pool boosters
  • All setter, bribe, and rescue tests pass unchanged

🤖 Generated with Claude Code

- PoolBoosterMerkl: refactor to initializable pattern for clone compatibility
- PoolBoosterFactoryMerkl: use Clones.cloneDeterministic instead of CREATE2
- Add implementation, strategist storage with governor setters
- Simplify computePoolBoosterAddress to only require salt
clement-ux and others added 2 commits February 12, 2026 16:28
Accept raw `bytes calldata _initData` instead of typed initialize params,
making the factory implementation-agnostic. Remove merklDistributor and
strategist storage/setters as they are no longer needed at factory level.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check implementation is set before validating other parameters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@clement-ux clement-ux marked this pull request as draft February 12, 2026 15:30
clement-ux and others added 9 commits February 12, 2026 17:06
Set implementation in factory constructor and add deploy script that
swaps old factory for new in the central registry and creates an
initial Pool Booster.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… & factory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove onlyGovernorOrStrategist from bribe() on PoolBoosterMerklV2 to
align with all other pool booster implementations (SwapxDouble, SwapxSingle,
Metropolis) and make bribeAll() on the factory work correctly. The previous
modifier caused bribeAll() to revert since msg.sender is the factory, not
the governor/strategist.

Test improvements:
- Use named constant MERKL_BOOSTER_TYPE instead of magic number 3
- Add comment explaining why init revert tests check "Initialization failed"
- Add withArgs check on TokensRescued event
- Add removePoolBooster auth test
- Add positive bribeAll test (executes bribes on funded boosters)
- Remove stale minAmount comment with wrong numbers
- Add note on createPoolBooster helper about || footgun

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l() to governor

Store factory address during initialize() and require bribe() caller to be
factory, governor, or strategist. Override bribeAll() on PoolBoosterFactoryMerkl
with onlyGovernor. Add corresponding tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop the V2 suffix — no longer needed since the old contract is gone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bribe() now requires governor/strategist/factory caller.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…with old sonic contract

The old PoolBoosterMerkl (non-clone, constructor-based) is still used on sonic.
Renaming to PoolBoosterMerkl breaks the sonic factory compilation. Also fix sonic
test to use strategist for bribe() and PoolBoosterMerklV2 artifact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@clement-ux clement-ux marked this pull request as ready for review February 12, 2026 21:56
@clement-ux clement-ux changed the title [MerklPB] Migrate PoolBoosterFactoryMerkl to Clones [MerklPB] Migrate PoolBoosterFactoryMerkl to Beacon Proxy Feb 13, 2026
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