Skip to content

Feat/accesscontrol temporal facets#197

Merged
mudgen merged 5 commits into
Perfect-Abstractions:mainfrom
isihin-3:feat/accesscontrol-temporal-facets
Nov 14, 2025
Merged

Feat/accesscontrol temporal facets#197
mudgen merged 5 commits into
Perfect-Abstractions:mainfrom
isihin-3:feat/accesscontrol-temporal-facets

Conversation

@isihin-3

@isihin-3 isihin-3 commented Nov 11, 2025

Copy link
Copy Markdown
Contributor

Summary

Introduced temporal access control functionality with supporting facets, library helpers, and comprehensive tests to manage role expirations.

Changes Made

  • Added AccessControlTemporalFacet providing grant/revoke flows with expiry validation.
  • Added LibAccessControlTemporal exposing reusable storage helpers and events.
  • Created harness contracts for both facets to support isolated testing.
  • Wrote Forge tests covering expiry lookups, grant/revoke behaviour, and validation scenarios.

Checklist

Before submitting this PR, please ensure:

  • Code follows the Solidity feature ban - No inheritance, constructors, modifiers, public/private variables, external library functions, using for directives, or selfdestruct

  • Code follows Design Principles - Readable, uses diamond storage, favors composition over inheritance

  • Code matches the codebase style - Consistent formatting, documentation, and patterns (e.g. ERC20Facet.sol)

  • Code is formatted with forge fmt

  • Tests are included - All new functionality has comprehensive tests

  • All tests pass - Run forge test and ensure everything works

  • Documentation updated - If applicable, update relevant documentation

Additional Notes

No documentation updates were required

@netlify

netlify Bot commented Nov 11, 2025

Copy link
Copy Markdown

‼️ Deploy request for compose-diamonds rejected.

Name Link
🔨 Latest commit 9323621

@github-actions

github-actions Bot commented Nov 11, 2025

Copy link
Copy Markdown
Contributor

Coverage Report

Coverage

Metric Coverage Details
Lines 63% 1052/1664 lines
Functions 71% 208/295 functions
Branches 44% 121/273 branches

Last updated: Thu, 13 Nov 2025 05:14:10 GMT for commit 67cb321

@github-actions

github-actions Bot commented Nov 11, 2025

Copy link
Copy Markdown
Contributor

Gas Report

No gas usage changes detected between main and feat/accesscontrol-temporal-facets.

All functions maintain the same gas costs. ✅

Last updated: Thu, 13 Nov 2025 05:14:34 GMT for commit 67cb321

@farismln farismln left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey thanks for the good work. I notice unintended outcome on the revokeTemporalRole function in certain cases. please check it out

Comment on lines +144 to +150
bool _hasRole = acs.hasRole[_account][_role];
if (_hasRole) {
acs.hasRole[_account][_role] = false;
}

// Clear expiry timestamp
s.roleExpiry[_account][_role] = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are security risk here, if _hasRole is already false it would still clear the expiry timestamp, making the said _account became permanent in the roles.

consider to only clear the expiry timestamp if _hasRole is true

Comment on lines +134 to +140
// Revoke the role if it exists
if (_hasRole) {
acs.hasRole[_account][_role] = false;
}

// Clear expiry timestamp
s.roleExpiry[_account][_role] = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also have the same issue with the non Lib contract

@isihin-3

Copy link
Copy Markdown
Contributor Author

@farismln changes are made please check the new commit

@farismln

Copy link
Copy Markdown
Contributor

hey @isihin-3 the fix looks good!

also, I just realized for the revoke role that clearing the expiry is not necessary. what do you think? do you have any reason to clear the expiry time to 0 when revoking a role?
I think setting the AccessControlStorage for said account to false already sufficient, because there are checks whether the role is still true and there are checks if the expiry is not passed. maybe we can optimize gas cost that way? what is your opinion on this?

@isihin-3

isihin-3 commented Nov 13, 2025

Copy link
Copy Markdown
Contributor Author

Hey @farismln , that's a great point on the gas angle. I looked into it, and I think we must keep the expiry clear for two big reasons:

  • The "Re-Grant Bug": If we only set hasRole = false and leave the old expiry, a bug could happen. If an admin later grants a permanent role (using the base AccessControlFacet.grantRole), it will only set hasRole = true. The old "zombie" timestamp will still be there, accidentally turning the new permanent role into an expired one.

  • Gas Cost is Negligible: As you pointed out, gas is key. But setting a non-zero storage slot to zero (non-zero -> zero) costs 5,000 gas but issues a 4,800 gas refund. The net cost is only ~200 gas.

Given the low cost, I think it's safer to clear the slot to 0 to prevent the re-grant bug and keep the state clean for off-chain tools. What do you think?

@farismln

farismln commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

yeah fair point you mention @isihin-3

edit: maybe we can implement using 0 as expiry for it to be a permanent role. but maybe this would conflict with the normal access control behavior as there would be two way a permanent role can be granted when using the temporal one. but this would not be necessary if we think this temporal access control as extension of the normal facet.

@mudgen

mudgen commented Nov 13, 2025

Copy link
Copy Markdown
Contributor

@isihin-3 Thanks very much for this pull request!

@farismln Thanks very much for reviewing this pull request. @farismln when you are complete with your review we can merge this pull request.

@isihin-3

Copy link
Copy Markdown
Contributor Author

yeah fair point you mention @isihin-3

edit: maybe we can implement using 0 as expiry for it to be a permanent role. but maybe this would conflict with the normal access control behavior as there would be two way a permanent role can be granted when using the temporal one. but this would not be necessary if we think this temporal access control as extension of the normal facet.

The user has to check which facet is taken in use. According to that they can decide

@farismln farismln left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the changes fixed the issue from before. looks good!!

Comment on lines +147 to +155
if (_hasRole) {
// Revoke the role from AccessControl storage
acs.hasRole[_account][_role] = false;

// Clear expiry timestamp
s.roleExpiry[_account][_role] = 0;

emit TemporalRoleRevoked(_role, _account, msg.sender);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good. thanks for the fix

Comment on lines +125 to +133
bool _hasRole = acs.hasRole[_account][_role];
if (!_hasRole) {
return false;
}

acs.hasRole[_account][_role] = false;

// Clear expiry timestamp only when the role existed
s.roleExpiry[_account][_role] = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the approach different with the non lib ones where this is return early. but the issue are fixed on both cases. thanks

@farismln

Copy link
Copy Markdown
Contributor

hi @mudgen I already reviewed the fixes.

@mudgen mudgen merged commit 56b6e7f into Perfect-Abstractions:main Nov 14, 2025
4 checks passed
@mudgen

mudgen commented Nov 14, 2025

Copy link
Copy Markdown
Contributor

@farismln @isihin-3 Thanks, good job!

@isihin-3 isihin-3 deleted the feat/accesscontrol-temporal-facets branch November 14, 2025 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants