69136 sc low missing revocation condition in setmigrationpermit prevents users from revoking stale migration permissions violating documented protocol guarantee

Submitted on Mar 13th 2026 at 01:31:04 UTC by @CaptSinbad for Audit Comp | Folks Finance: Staking Contracts

  • Report ID: #69136

  • Report Type: Smart Contract

  • Report severity: Low

  • Target: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

The setMigrationPermit function in Staking.sol applies a hasRole(MIGRATOR_ROLE, _migrator) check unconditionally — for both granting and revoking a permit. This means a user cannot call setMigrationPermit(migrator, false) to revoke a previously granted permit if the migrator's role has since been revoked by an admin.

The vulnerable code:

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 is appropriate when granting a permit — there is no reason to permit a non-existent migrator. However, the same check incorrectly blocks revocation. A user who wants to clear a stale permit cannot do so until the admin re-grants the role to that address, which may never happen.

The project's own known issues list acknowledges that migrationPermits can contain entries for migrators whose role has been revoked. That state is only reachable because this check blocks the user from clearing it themselves.

The README.md (line 140) explicitly states:

"The permission can be revoked at any time by calling setMigrationPermit(migratorAddress, false)."

This guarantee is false in the scenario the known issues list acknowledges as possible. The contract fails to deliver a documented and expected security property.

Recommended fix:

Apply the role check only when granting a permit, not when revoking one:

Proof of Concept

Terminal Output:

The test demonstrates that after a role revocation, a user's attempt to call setMigrationPermit(migrator, false) reverts with MigratorNotFound, leaving the permit permanently set to true with no user-accessible remedy. This directly contradicts the README guarantee that revocation is available at any time.

Was this helpful?