For the complete documentation index, see llms.txt. This page is also available as Markdown.

69345 sc low migration permits cannot be revoked after migrator role is revoked despite readme claiming revocation is possible at any time

Submitted on Mar 14th 2026 at 11:09:40 UTC by @Razkky for Audit Comp | Folks Finance: Staking Contracts

  • Report ID: #69345

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

Description

The README states that a user can revoke a migrator permission at any time by calling:

staking.setMigrationPermit(migratorAddress, false);

Source:

  • https://github.com/Folks-Finance/folks-staking-contracts/blob/main/README.md#L140

However, the implementation of setMigrationPermit first checks that the target address currently holds MIGRATOR_ROLE:

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

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);
}

Because of this, once a migrator loses MIGRATOR_ROLE, users can no longer clear the stored approval for that address. The old approval remains in migrationPermits, and if the same address later regains MIGRATOR_ROLE, the prior approval becomes active again.

This appears to be an informational / documentation mismatch rather than a direct asset-loss issue, since exploitation depends on privileged role management. Still, the current behavior is weaker than the user-facing statement that revocation is possible "at any time".

Impact

Informational.

The issue does not directly cause theft or denial of funds on its own, but it means:

  • users cannot actually revoke a previously granted migration approval after role revocation, despite documentation saying they can;

  • stale approvals remain stored and can become effective again if the same address is re-granted MIGRATOR_ROLE.

Proof of Concept

1

Add the following test cases to test/Staking.t.sol.

2

Run the following command:

3

Output

4

Observe the results:

  • test_CannotRevokePermitAfterMigratorRoleRevoked shows that once the migrator loses MIGRATOR_ROLE, the user can no longer revoke the stored approval because setMigrationPermit(migrator, false) reverts with MigratorNotFound.

  • test_StalePermitBecomesActiveAgainIfRoleReGranted shows that the stale approval remains stored and becomes active again if the same address later regains MIGRATOR_ROLE.

Was this helpful?