69717 sc low users are unable to revoke migration permits for deprecated or demoted migrators
Submitted on Mar 16th 2026 at 13:16:19 UTC by @OxAnmol for Audit Comp | Folks Finance: Staking Contracts
Report ID: #69717
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol
Impacts:
Description
Brief/Intro
The Staking contract allows users to approve specific migrator addresses to move their funds via setMigrationPermit(). However, the function enforces that the target address currently holds the MIGRATOR_ROLE. If a migrator is deprecated and has its role revoked by the admin, users are permanently blocked from revoking their approval for that specific migrator.
Vulnerability Details
In Staking.sol, the setMigrationPermit function includes a strict access control check on the target migrator address before updating the user's permit state:
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);
}If an active migrator is deprecated (maybe, if it experiences a security incident?), the protocol admin will naturally revoke its MIGRATOR_ROLE.
If a user wanted to delist such a migrator by manually clearing their approval (_isMigrationPermitted = false) for the now-defunct migrator, the transaction will revert because hasRole(MIGRATOR_ROLE, _migrator) evaluates to false. This forcibly locks the user's storage state into an approved status for a deprecated address.
Impact Details
While the immediate risk is low because a demoted migrator cannot call migratePositionsFrom() anyway, this breaks the expected allowance flow. Users should always retain their ability to revoke permissions to migrators, regardless of that migrator's current standing with the protocol admin.
Proof of Concept
Add the following to the Staking.t.sol:
Recommendation:
Modify the function so that the MIGRATOR_ROLE check is only enforced when a user is granting permission, allowing users to freely revoke permission from any address at any time.
Was this helpful?