Context
On 12th of November 2025, Superfluid Foundation uncovered an issue related to FluidLockerFactory contract.
Severity
This is considered a medium-low severity issue.
No funds are at risk, but it may cause some inconvenience to some users.
Impact
A user that have created a Reserve before the previous `FluidLockerFactory` upgrade may now be able to create a new Reserve
When this occurs, the below mapping is then overridden.
mapping(address user => address locker) private _lockers;
This create malfunctions of other system components such as : claim-app only showing the user his new Reserve, subgraph having two-entries for a given user
Reason
This issue root cause is coming from a miss-use of Create2 feature. When deploying new Reserve contracts, the current FluidLockerFactory implementation relies on create2 to ensure avoiding Reserve duplication per users.
lockerInstance = address(
new BeaconProxy{ salt: keccak256(abi.encode(lockerOwner)) }
(
address(LOCKER_BEACON),
""
)
);
However, the BeaconProxy init code having changed (Openzeppelin dependency update between the initial deployment and previous FluidLockerFactory upgrade), the “salt” used for Create2 is no longer unique per user.
Resolution
Technical
Perform an upgrade that contain the following changes :
In FluidLockerFactory contract :
-
revise
_createLockerContract(address lockerOwner)function -
add input validation to check if lockerOwner has already a Reserve
-
add
updateUserLockerAddress(address lockerOwner, address lockerInstance) -
write the
user -> lockermapping to set back the previous user’s Reserve address
The new implementation logic can be inspected here.
The pull-request containing the fix can be inspected here
Operational
-
Get the Security Council to review and approve this upgrade
-
Perform the
FluidLockerFactoryupgrade (requires DAO Signature instructed by Security Council) -
Perform
updateUserLockerAddresscall for the impacted users (requires DAO Signature instructed by Security Council)