69141 sc low setmigrationpermit revocation silently blocked for de listed migrators contradicting documented guarantee
Submitted on Mar 13th 2026 at 02:52:01 UTC by @VinayVig for Audit Comp | Folks Finance: Staking Contracts
Report ID: #69141
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol
Description
Brief/Intro
The README explicitly guarantees that migration permission "can be revoked at any time", but the implementation of setMigrationPermit unconditionally checks that the target address still holds MIGRATOR_ROLE before allowing any update — including revocations. If a migrator's role is revoked by the admin, users holding a permit for that address lose their ability to revoke it, directly violating the protocol's own documented invariant. When combined with the fact that permits are never cleared after migration completes, a revoked-then-re-granted migrator can silently re-inherit stale permits from users who had no mechanism to clean up after the original role removal.
Vulnerability Details
The README states under the Migration User Flow section:
"The permission can be revoked at any time by calling setMigrationPermit(migratorAddress, false)."
The implementation does not fulfill this guarantee:
// Staking.sol:77-82
function setMigrationPermit(address _migrator, bool _isMigrationPermitted) external {
if (!hasRole(MIGRATOR_ROLE, _migrator)) revert MigratorNotFound(_migrator);
migrationPermits[_migrator][msg.sender] = _isMigrationPermitted;
emit MigrationPermitUpdated(_migrator, msg.sender, _isMigrationPermitted);
}The role check runs unconditionally regardless of whether _isMigrationPermitted is true or false. When a migrator's MIGRATOR_ROLE is revoked — for example, due to compromise, decommission, or a protocol upgrade — any user who previously granted that migrator a permit cannot call setMigrationPermit(migrator, false). The call reverts with MigratorNotFound.
Impact Details
No direct fund loss occurs — migrated tokens go to V2, not an attacker. However, the impact is a violation of the protocol's core user consent guarantee:
Users cannot exercise the revocation right that the documentation explicitly promises them
Stale permits from decommissioned migrators accumulate permanently with no in-protocol cleanup path
A migrator that is revoked and later re-granted the role inherits all historical permits without user re-consent, silently bypassing the opt-in mechanism the protocol is designed around
The README's Key Properties section states: "No migration can happen without the user's active approval." The combination of missing permit cleanup and blocked revocation means this property can be violated in a realistic admin key rotation or migrator upgrade scenario.
Recommended fix
Allow revocation regardless of current role status by gating the role check only on grants:
References
https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol#L77-L82
Proof of Concept
Add this test in test/Staking.t.sol
Results
Was this helpful?