ERC-6617 Bit-based permission standard (Bit Based Permission)
Review · Other · created 2023-02-27
A standard that represents a single smart contract permission as a single bit of a number. One uint256 can define up to 256 permissions and 2^256 possible roles.
Why it is needed
Permission management for smart contracts, which determines who can do what, has mainly been handled in two ways so far: giving a contract a single owner (ERC-173), or assigning each role a 32-byte identifier (bytes32) (ERC-5982). However, these approaches require a storage read operation to check permissions, which is relatively expensive in gas, and they lack flexibility for combining permissions into new roles. This standard represents each permission as a single bit of a 256-bit number (uint256), so permissions can be checked and combined using only cheap bitwise operations. This improves gas efficiency and allows roles that were not predefined to be freely created as combinations of permissions.
What to implement
event PermissionGranted(address indexed _grantor, uint256 indexed _permission, address indexed _user);: An event that must be emitted when a permission is granted. Records the granter, the permission granted, and the user who received it.event PermissionRevoked(address indexed _revoker, uint256 indexed _permission, address indexed _user);: An event that must be emitted when a permission is revoked. Records the revoker, the permission revoked, and the user who lost it.function hasPermission(address _user, uint256 _requiredPermission) external view returns (bool);: Checks whether a user has the required permission. Returns true if the user's permissions include all of the required permission.function grantPermission(address _user, uint256 _permissionToAdd) external returns (bool);: Adds a permission to a user.function revokePermission(address _user, uint256 _permissionToRevoke) external returns (bool);: Revokes a permission from a user.event UpdatePermissionDescription(uint256 indexed _permission, string indexed _name, string indexed _description);: An event that must be emitted when a permission description is updated.function getPermissionDescription(uint256 _permission) external view returns (PermissionDescription memory description);: Returns the name and description of a given permission.function setPermissionDescription(uint256 _permission, string memory _name, string memory _description) external returns (bool success);: Sets a name and description for a permission. Returns true on success and must emit an UpdatePermissionDescription event.
In depth
Permission representation rules
A compliant contract must implement the IEIP6617 interface. Permissions are represented as uint256, and since each permission must occupy exactly one bit of the uint256, it must be a power of two. Each permission must be unique, and 0 must be used to mean no permission. These rules allow a single uint256 to hold up to 256 permissions.
Inclusion check in hasPermission
hasPermission(_user, _requiredPermission) returns true if the user's permissions are a superset of the required permission, and false otherwise. In other words, every bit set in the required permission must also be set in the user's permissions for the check to pass. Since permissions are all uint256 values, this check is handled with bitwise operations.
Granting, revoking, and event obligations
grantPermission(_user, _permissionToAdd) adds a permission to a user, and revokePermission(_user, _permissionToRevoke) revokes a permission. A PermissionGranted event must be emitted when a permission is granted, and a PermissionRevoked event must be emitted when it is revoked. Both events record the granter or revoker, the target permission, and the target user as indexed arguments.
Metadata extension IEIP6617Meta
An optional extension that addresses the weakness that permissions as numbers are not immediately meaningful. Implementation is RECOMMENDED, and names and descriptions SHOULD be defined for base permissions and major combinations, but descriptions SHOULD NOT be defined for every possible subcombination. The PermissionDescription struct has permission, name, and description fields, and setPermissionDescription must return true on success and must emit an UpdatePermissionDescription event.
Why the bitwise approach was chosen
Access control was previously done with a single owner (ERC-173) or bytes32 roles (ERC-5982), but using bits and bitmask operations gives better gas efficiency and flexibility. A single AND operation on a permission bitmask is much cheaper than multiple calls to the LOAD opcode. Also, 256 permissions allow for 2^256 possible combinations (roles), and new roles can be created by combining multiple permissions with a binary OR operation without needing to predefine every role.
Ordering permissions by importance
Bit order can encode the importance of permissions, for example by placing the most important permission at the most significant bit. Since permissions are all uint256 values, comparing their magnitude is as simple as comparing numbers.
A proposal still under review
This proposal's status is Review. It is not yet a finalized standard, and its content may change during review. The source document provides a basic implementation example as a reference implementation.