--- eip: 8083 title: Time-Bound Access Control description: Provides secure time-bound roles with automatic expiration, eliminating manual revocation risks. author: Jeff Fei (@77eff) , Kenny Kung (@kennyk10) , Shulei (@baishuo13) discussions-to: https://ethereum-magicians.org/t/eip-draft-time-bound-access-control-interface/26516 status: Draft type: Standards Track category: ERC created: 2025-11-11 requires: 165 --- ## Abstract This ERC introduces a minimal interface for enforcing time-bound role permissions in smart contracts. A "time-bound role permission" is a role grant whose validity is constrained to a specific expiration timestamp, after which the role is automatically treated as inactive without any on-chain state mutation. The proposal defines three functional modules: an expiration management function for assigning and updating the expiration timestamp associated with a given role-account pair, an expiration query function for retrieving the stored expiration timestamp, and an active-role evaluation function that determines whether a role is currently valid by comparing the current block timestamp against the stored expiration. Together, these functions enable automatic deactivation of expired roles at the point of permission evaluation, removing the need for manual revocation transactions or off-chain monitoring services. ## Motivation The permission system of smart contracts is crucial for the operation and management. Role-based access control (RBAC) systems are widely adopted in smart contracts to manage permissions effectively. However, the absence of a standardized mechanism for automatically revoking roles after predefined durations introduces significant security challenges, particularly in dynamic environments such as complex organizational structures and multi-party business scenarios. Permanent role assignments exacerbate these risks in common use cases, including: - Temporary access needs, such as third-party supplier or vendors integrations. - Project- or task-specific permissions that should not persist beyond their scope. - Employee offboarding or role changes. In these cases, permanent roles create unnecessary attack surfaces. To address these systematic issues, this ERC introduced verifiable time constraints directly into permission management. ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). Every contract compliant with this ERC MUST implement the ITimeBoundAccessControl interface. Contracts SHOULD also implement [ERC-165](./eip-165.md) to support interface detection. ```solidity pragma solidity ^0.8.13; /// @title Time-Bound Access Control Interface interface ITimeBoundAccessControl /* is ERC165 */ { /// @dev Emitted when role expiration is changed event RoleExpirationChanged( bytes32 indexed role, address indexed account, uint256 previousExpiryTimestamp, uint256 expiryTimestamp ); /// @dev set role expiration at specified timestamp function setRoleExpiration( bytes32 role, address account, uint256 expiryTimestamp ) external; /// @dev Query the expiry timestamp of the role function getRoleExpiration(bytes32 role, address account) external view returns (uint256); /// @dev Checks if role is active at current timestamp function hasActiveRole(bytes32 role, address account) external view returns (bool); } ``` - The setRoleExpiration function MUST restrict callers to accounts that hold an administrative role over the target role, enforced either through an on-chain role-admin mapping or an equivalent access-control mechanism, so that only authorized administrators can set or update expiration timestamps. - The expiryTimestamp parameter MUST be represented as seconds since the Unix epoch, denoting the role expiration time. - All operations involving changes to role expiration MUST emit the RoleExpirationChanged event. ## Rationale - **Timestamp-Only**: A single timestamp parameter simplifies implementation while supporting calendar-based expiration that aligns with real-world use cases. - **Minimal surface**: The design enforces a clear security boundary with the function `hasActiveRole`, avoids auxiliary functions to reduce the attack surface, and provides a fully self-contained specification. ## Backwards Compatibility No backward compatibility issues are introduced. This proposal is fully backward-compatible with existing access control systems and supports [ERC-165](./eip-165.md) interface detection. ## Reference Implementation ```solidity pragma solidity 0.8.13; import { ITimeBoundAccessControl } from "./ITimeBoundAccessControl.sol"; /** * @title ERC-8083 Implementation * @dev Implementation of the ITimeBoundAccessControl Time-Bound Access Control Interface */ contract ERC8083Impl is ITimeBoundAccessControl { mapping(bytes32 => bytes32) private _roleAdmin; mapping(bytes32 => mapping(address => uint256)) private _roleExpiryTimestamps; error NotActiveRole(bytes32 role, address account); event RoleAdminChanged(bytes32 indexed role, bytes32 indexed prevRole, bytes32 adminRole); /** * @dev Sets the admin role for a given role * @param role The role to set admin for * @param adminRole The admin role to set */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roleAdmin[role] = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Returns the admin role for a given role * @param role The role to query * @return The admin role */ function getRoleAdmin(bytes32 role) public view returns(bytes32) { return _roleAdmin[role]; } /** * @dev Modifier to check if an account has an active role * @param role The role to check * @param account The account to check */ modifier onlyActiveRole(bytes32 role, address account) { if (!hasActiveRole(role, account)) { revert NotActiveRole(role, account); } _; } /** * @dev Sets the expiration timestamp for a role-account pair * @param role The role to set expiration for * @param account The account to set expiration for * @param expiryTimestamp The expiration timestamp */ function setRoleExpiration( bytes32 role, address account, uint256 expiryTimestamp ) external onlyActiveRole(getRoleAdmin(role), msg.sender) { uint256 lastExpiryTimestamp = _roleExpiryTimestamps[role][account]; _roleExpiryTimestamps[role][account] = expiryTimestamp; emit RoleExpirationChanged(role, account, lastExpiryTimestamp, expiryTimestamp); } /** * @dev Returns the expiration timestamp for a role-account pair * @param role The role to query * @param account The account to query * @return The expiration timestamp */ function getRoleExpiration(bytes32 role, address account) external view returns(uint256) { return _roleExpiryTimestamps[role][account]; } /** * @dev Checks if an account has an active role (not expired) * @param role The role to check * @param account The account to check * @return Whether the role is active */ function hasActiveRole(bytes32 role, address account) public view returns (bool) { return block.timestamp < _roleExpiryTimestamps[role][account]; } /** * @dev Checks if the contract supports a given interface * @param interfaceId The interface identifier, as specified in ERC-165 * @return true if the contract supports the interface, false otherwise */ function supportsInterface(bytes4 interfaceID) external pure returns (bool) { return interfaceID == this.supportsInterface.selector || interfaceID == type(ITimeBoundAccessControl).interfaceId; } } ``` ## Security Considerations - **Timestamp Variability and Safety Margins**: Ethereum block timestamps are determined by validators and can deviate from real-world time. Malicious or accidental manipulation could lead to premature role expirations or unintended delays in revocation. Contracts managing high-value permissions or time-sensitive roles are advised to incorporate safety margins to mitigate timestamp variances. For short-duration roles, larger margins are recommended to account for potential network congestion or delays. - **Temporal Consistency**: Role permissions are expected to remain valid until their exact expiry time and be automatically invalidated immediately thereafter, ensuring precise alignment between granted duration and effective access period. - **Enforcement of Active Role Checks**: Expired roles are not actively monitored or processed after expiry. Instead, all permission validations should rely strictly on the hasActiveRole function. - **Mandatory Pre-Action Validation**: Every permission check is expected to call hasActiveRole prior to executing any privileged operation, irrespective of previous role grants, to maintain consistent and real-time enforcement. - **Permanent Root Roles**: Root or default admin roles are advised to be assigned infinite expiry times (e.g., type(uint256).max). This ensures these roles remain permanent and prevents the risk of irreversible lockout from the entire role-based access control system. - **Transaction Order Dependency Attacks**: Due to miner extractable value (MEV) and transaction reordering within blocks, attackers may front-run role renewals or extensions to execute privileged actions just before expiry updates. Time-sensitive roles are advised to implement safeguards to mitigate transaction ordering risks. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).