This EIP offers a standard for building a bit-based permission and role system. Each permission is represented by a single bit. By using an uint256, up to $256$ permissions and $2^{256}$ roles can be defined. We are able to specify the importance of each permission based on the order of the bits.
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 and RFC 8174.
Note The following specifications use syntax from Solidity 0.8.7 (or above)
Permission and role MUST be defined as an uint256
Permission MUST be defined as a power of two
Permission MUST be unique
0 MUST be used for none permission
Rationale
Currently permission and access control is performed using a single owner (ERC-173) or with bytes32 roles (ERC-5982).
However, using bitwise and bitmask operations allows for greater gas-efficiency and flexibility.
Gas cost efficiency
Bitwise operations are very cheap and fast. For example, doing an AND bitwise operation on a permission bitmask is significantly cheaper than calling any number of LOAD opcodes.
Flexibility
With the 256 bits of the uint256, we can create up to 256 different permissions which leads to $2^{256}$ unique combinations (a.k.a. roles).
(A role is a combination of multiple permissions). Not all roles have to be predefined.
Since permissions are defined as unsigned integers, we can use the binary OR operator to create new role based on multiple permissions.
Ordering permissions by importance
We can use the most significant bit to represent the most important permission, the comparison between permissions can then be done easily since they all are uint256s.
Test Cases
pragmasolidity^0.8.7;import"EIP6617.sol";contractTest{usingEIP6617foruint256;uint256publicconstantPERMISSION_NONE=0;uint256publicconstantPERMISSION_READ=1;// 2⁰
uint256publicconstantPERMISSION_WRITE=2;// 2¹
uint256publicconstantPERMISSION_EXECUTE=4;// 2²
// Role operator = 1 | 2 = 3
uint256publicconstantROLE_OPERATOR=PERMISSION_READ|PERMISSION_WRITE;// Role admin = 1 | 2 | 4 = 7
uint256publicconstantROLE_ADMIN=PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXECUTE;functiontestPermissions()externalpurereturns(uint256){uint256userPermission;// adding read permission
userPermission=userPermission.permissionGrant(PERMISSION_READ);// adding admin role
userPermission=userPermission.permissionGrant(ROLE_ADMIN);// removing execute permission
userPermission=userPermission.permissionRevoke(PERMISSION_EXECUTE);// Checking permission
if(userPermission.permissionCheck(ROLE_ADMIN)){// Only admin can access this part
}returnuserPermission;}}
Reference Implementation
pragmasolidity^0.8.7;/**
@title EIP-6617 Bit Based Permission
@dev See https://eips.ethereum.org/EIPS/eip-6617
*/libraryEIP6617{/**
@notice Check if _permission is a superset of _requiredPermission
@param _permission The given permission
@param _requiredPermission The required permission
@return True if the _permission is a superset of the _requiredPermission else False
*/functionpermissionCheck(uint256_permission,uint256_requiredPermission)internalpurereturns(bool){return_permission&_requiredPermission==_requiredPermission;}/**
@notice Add permission
@param _permission The given permission
@param _permissionToAdd The permission that will be added
@return The new permission with the _permissionToAdd
*/functionpermissionGrant(uint256_permission,uint256_permissionToAdd)internalpurereturns(uint256){return_permission|_permissionToAdd;}/**
@notice Remove permission
@param _permission The given permission
@param _permissionToRemove The permission that will be removed
@return The new permission without the _permissionToRemove
*/functionpermissionRevoke(uint256_permission,uint256_permissionToRemove)internalpurereturns(uint256){return(_permission|_permissionToRemove)^_permissionToRemove;}}