diff --git a/EIPS/eip-6366.md b/EIPS/eip-6366.md index 1a1af11bb8076a..36407fceb129c9 100644 --- a/EIPS/eip-6366.md +++ b/EIPS/eip-6366.md @@ -8,12 +8,12 @@ status: Draft type: Standards Track category: ERC created: 2022-01-19 -requires: bit_based_permission +requires: 6617 --- ## Abstract -This EIP offers an alternative to Access Control Lists (ACLs) for granting authorization and enhancing security. An `uint256` is used to store permission of given address in a ecosystem. Each permission is represented by a single bit in `uint256` as described in [eip-bit_based_permission](./bit_based_permission.md). Bitwise operators and bitmasks are used to determine the access right which is much more efficient and flexible than `string` or `keccak256` comparison. +This EIP offers an alternative to Access Control Lists (ACLs) for granting authorization and enhancing security. An `uint256` is used to store permission of given address in a ecosystem. Each permission is represented by a single bit in `uint256` as described in [ERC-6617](./eip-6617.md). Bitwise operators and bitmasks are used to determine the access right which is much more efficient and flexible than `string` or `keccak256` comparison. ## Motivation @@ -31,67 +31,155 @@ _Note_ The following specifications use syntax from Solidity `0.8.7` (or above) Compliant contracts MUST implement `IEIP6366Core`. +It is RECOMMENDED to define each permission as a power of `2` so that we can check for the relationship between sets of permissions using [ERC-6617](./eip-6617.md). + ```solidity interface IEIP6366Core { - event Transfer(address indexed _from, address indexed _to, uint256 _value); - event Approval(address indexed _owner, address indexed _delegatee, uint256 _permission); - + /** + * MUST trigger when `_permission` are transferred, including `zero` permission transfers. + * @param _from Permission owner + * @param _to Permission receiver + * @param _permission Transferred subset permission of permission owner + */ + event Transfer(address indexed _from, address indexed _to, uint256 indexed _permission); + + /** + * MUST trigger on any successful call to `approve(address _delegatee, uint256 _permission)`. + * @param _owner Permission owner + * @param _delegatee Delegatee + * @param _permission Approved subset permission of permission owner + */ + event Approval(address indexed _owner, address indexed _delegatee, uint256 indexed _permission); + + /** + * Transfers a subset `_permission` of permission to address `_to`. + * The function SHOULD revert if the message caller’s account permission does not have the subset + * of the transferring permissions. The function SHOULD revert if any of transferring permissions are + * existing on target `_to` address. + * @param _to Permission receiver + * @param _permission Subset permission of permission owner + */ function transfer(address _to, uint256 _permission) external returns (bool success); + + /** + * Allows `_delegatee` to act for the permission owner's behalf, up to the `_permission`. + * If this function is called again it overwrites the current granted with `_permission`. + * `approve()` method SHOULD `revert` if granting `_permission` permission is not + * a subset of all available permission of permission owner. + * @param _delegatee Delegatee + * @param _permission Subset permission of permission owner + */ function approve(address _delegatee, uint256 _permission) external returns (bool success); + /** + * Returns the permissions of the given `_owner` address. + */ function permissionOf(address _owner) external view returns (uint256 permission); - function permissionRequire(uint256 _required, uint256 _permission) external view returns (bool isPermissioned); + + /** + * Returns `true` if `_required` is a subset of `_permission` otherwise return `false`. + * @param _permission Checking permission set + * @param _required Required set of permission + */ + function permissionRequire(uint256 _permission, uint256 _required) external view returns (bool isPermissioned); + + /** + * Returns `true` if `_required` permission is a subset of `_actor`'s permissions or a subset of his delegated + * permission granted by the `_owner`. + * @param _owner Permission owner + * @param _actor Actor who acts on behalf of the owner + * @param _required Required set of permission + */ function hasPermission(address _owner, address _actor, uint256 _required) external view returns (bool isPermissioned); + + /** + * Returns the subset permission of the `_owner` address were granted to `_delegatee` address. + * @param _owner Permission owner + * @param _delegatee Delegatee + */ function delegated(address _owner, address _delegatee) external view returns (uint256 permission); } ``` -1. It is RECOMMENDED to define each permission as a power of `2` so that we can check for the relationship between sets of permissions using [eip-bit_based_permission](./bit_based_permission.md). -2. `transfer(address _to, uint256 _permission)` transfers a subset of `_permission` permission to address `_to`, and MUST emit the `Transfer` event. The function SHOULD `revert` if the message caller's account permission does not have the subset of the transferring permission. The function SHOULD `revert` if any of transferring permission is existing on target `_to` address. Transfers of `0` permission MUST be treated as normal transfers and emit the `Transfer` event. -3. `approve(address _delegatee, uint256 _permission)` allows `_delegatee` to act for the permission owner's behalf, up to the `_permission` permission, and MUST emit the `Approval` event. If this function is called again it overwrites the current granted with `_permission`. `_permission` MUST be a subset of all available permission of permission owner. `approve()` method SHOULD `revert` if granting `_permission` permission is not a subset of all available permission of permission owner. -4. `permissionOf(address _owner)` returns the account permission of the given `_owner` address. -5. `permissionRequire(uint256 _required, uint256 _permission)` returns `true` if `_required` permission is a subset of `_permission` permission otherwise return `false`. -6. `hasPermission(address _owner, address _actor, uint256 _required)` returns `true` if `_required` permission is a subset of `_actor`'s permissions or a subset of his delegated permissions granted by the `_owner`. -7. `delegated(address _owner, address _delegatee)` returns the subset permission of the `_owner` address were granted to `_delegatee` address. -8. A token contract which creates new tokens SHOULD emit a `Transfer` event with the `_from` address set to `address(0x00)` when tokens are created. - ### Metadata Interface It is RECOMMENDED for compliant contracts to implement the optional extension `IEIP6366Meta`. +SHOULD define a description for the base permissions and main combinaison. + +SHOULD NOT define a description for every subcombinaison of permissions possible. + ```solidity interface IEIP6366Meta { + /** + * Structure of permission description + * @param _permission Permission + * @param _name Name of the permission + * @param _description Description of the permission + */ struct PermissionDescription { uint256 permission; string name; string description; } + /** + * MUST trigger when the description is updated. + * @param _permission Permission + * @param _name Name of the permission + * @param _description Description of the permission + */ event UpdatePermissionDescription(uint256 indexed _permission, string indexed _name, string indexed _description); + /** + * Returns the name of the token - e.g. `"OpenPermissionToken"`. + */ function name() external view returns (string memory); + + /** + * Returns the symbol of the token. E.g. `"OPT"`. + */ function symbol() external view returns (string memory); + + /** + * Returns the description of a given `_permission`. + * @param _permission Permission + */ function getDescription(uint256 _permission) external view returns (PermissionDescription memory description); - function setDescription(uint256 _permission, string memory _name, string memory _description) external returns (bool success); + /** + * Return `true` if the description was set otherwise return `false`. It MUST emit `UpdatePermissionDescription` event. + * @param _permission Permission + * @param _name Name of the permission + * @param _description Description of the permission + */ + function setDescription( + uint256 _permission, + string memory _name, + string memory _description + ) external returns (bool success); } ``` -1. `name()` returns the name of the token - e.g. `"OpenPermissionToken"`. -2. `symbol()` returns the symbol of the token. E.g. `"OPT"`. -3. `getDescription(uint256 _permission)` return the description of a permission. -4. `setDescription(uint256 _permission, string memory _name, string memory _description)` return `true` if the description was set otherwise return `false`. It MUST emit `UpdatePermissionDescription` event. -5. SHOULD define a description for the base permissions and main combinaison. -6. SHOULD NOT define a description for every subcombinaison of permissions possible. - ### Error Interface SHOULD NOT expected `IEIP6366Error` interface was implemented. ```solidity interface IEIP6366Error { + /** + * The owner or actor does not have the required permission + */ error AccessDenied(address _owner, address _actor, uint256 _permission); + + /** + * Conflict between permission set + */ error DuplicatedPermission(uint256 _permission); + + /** + * Data out of range + */ error OutOfRange(); } ``` diff --git a/assets/eip-6366/contracts/EIP6366Core.sol b/assets/eip-6366/contracts/EIP6366Core.sol index b3818e7fd324c3..91e54d64a954f7 100644 --- a/assets/eip-6366/contracts/EIP6366Core.sol +++ b/assets/eip-6366/contracts/EIP6366Core.sol @@ -57,10 +57,10 @@ contract EIP6366Core is IEIP6366Core { * @param _permission Checking permission set */ function permissionRequire( - uint256 _required, - uint256 _permission + uint256 _permission, + uint256 _required ) external view virtual override returns (bool isPermissioned) { - return _permissionRequire(_required, _permission); + return _permissionRequire(_permission, _required); } /** @@ -159,8 +159,8 @@ contract EIP6366Core is IEIP6366Core { } function _permissionRequire( - uint256 _required, - uint256 _permission + uint256 _permission, + uint256 _required ) internal pure returns (bool isPermissioned) { return _required == _permission & _required; } @@ -172,8 +172,8 @@ contract EIP6366Core is IEIP6366Core { ) internal view returns (bool isPermissioned) { return _permissionRequire( - _required, - _permissionOf(_actor) | _delegated(_owner, _actor) + _permissionOf(_actor) | _delegated(_owner, _actor), + _required ); } diff --git a/assets/eip-6366/contracts/interfaces/IEIP6366Core.sol b/assets/eip-6366/contracts/interfaces/IEIP6366Core.sol index a8d4143466b6fc..c9abc6a8533076 100644 --- a/assets/eip-6366/contracts/interfaces/IEIP6366Core.sol +++ b/assets/eip-6366/contracts/interfaces/IEIP6366Core.sol @@ -5,12 +5,16 @@ pragma solidity ^0.8.7; * @dev Defined the interface of the core of EIP6366 that MUST to be implemented */ interface IEIP6366Core { - event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Transfer( + address indexed _from, + address indexed _to, + uint256 indexed _permission + ); event Approval( address indexed _owner, address indexed _delegatee, - uint256 _permission + uint256 indexed _permission ); function transfer( @@ -28,8 +32,8 @@ interface IEIP6366Core { ) external view returns (uint256 permission); function permissionRequire( - uint256 _required, - uint256 _permission + uint256 _permission, + uint256 _required ) external view returns (bool isPermissioned); function hasPermission( diff --git a/assets/eip-6366/example/APermissionToken.sol b/assets/eip-6366/example/APermissionToken.sol index 2f37dfa8993261..c6b09c3c950d72 100644 --- a/assets/eip-6366/example/APermissionToken.sol +++ b/assets/eip-6366/example/APermissionToken.sol @@ -55,7 +55,7 @@ contract APermissionToken is EIP6366Core, EIP6366Meta { */ modifier allow(uint256 required) { address owner = msg.sender; - if (!_permissionRequire(required, _permissionOf(owner))) { + if (!_permissionRequire(_permissionOf(owner), required)) { revert IEIP6366Error.AccessDenied(owner, owner, required); } _; @@ -65,7 +65,7 @@ contract APermissionToken is EIP6366Core, EIP6366Meta { * @dev Deny blacklisted address */ modifier notBlacklisted() { - if (_permissionRequire(PERMISSION_DENIED, _permissionOf(msg.sender))) { + if (_permissionRequire(_permissionOf(msg.sender), PERMISSION_DENIED)) { revert IEIP6366Error.AccessDenied( msg.sender, msg.sender, @@ -79,27 +79,35 @@ contract APermissionToken is EIP6366Core, EIP6366Meta { * @dev Construct ERC-6366 */ constructor() EIP6366Meta("Ecosystem A Permission Token", "APT") { - _setDescription(PERMISSION_DENIED, "PERMISSION_DENIED", "Blacklisted address"); - _setDescription(PERMISSION_VOTE, "PERMISSION_VOTE", "Permission owner able to vote"); + _setDescription( + PERMISSION_DENIED, + "PERMISSION_DENIED", + "Blacklisted address" + ); + _setDescription( + PERMISSION_VOTE, + "PERMISSION_VOTE", + "Permission owner can vote" + ); _setDescription( PERMISSION_TRANSFER, "PERMISSION_TRANSFER", - "Permission owner able to transfer" + "Permission owner can transfer" ); _setDescription( PERMISSION_EXECUTE, "PERMISSION_EXECUTE", - "Permission owner able to execute" + "Permission owner can execute" ); _setDescription( PERMISSION_CREATE, "PERMISSION_CREATE", - "Permission owner able to create" + "Permission owner can create" ); _setDescription( PERMISSION_MASTER, "PERMISSION_MASTER", - "Permission owner able to mint and update description" + "Permission owner can mint and update description" ); _setDescription( ROLE_ADMIN, @@ -111,7 +119,7 @@ contract APermissionToken is EIP6366Core, EIP6366Meta { "ROLE_OPERATOR", "Operator role can execute and vote" ); - + // Assign master permission to deployer _mint(msg.sender, PERMISSION_MASTER); } diff --git a/assets/eip-6366/example/APermissioned.sol b/assets/eip-6366/example/APermissioned.sol index 409f2f9853cd58..ee05d69f4d8e63 100644 --- a/assets/eip-6366/example/APermissioned.sol +++ b/assets/eip-6366/example/APermissioned.sol @@ -55,7 +55,7 @@ contract APermissioned { * @dev Allow the actor who has required permission */ modifier allowOwner(uint256 _required) { - if (!opt.permissionRequire(_required, opt.permissionOf(msg.sender))) { + if (!opt.permissionRequire(opt.permissionOf(msg.sender), _required)) { revert IEIP6366Error.AccessDenied( msg.sender, msg.sender, @@ -71,8 +71,8 @@ contract APermissioned { modifier notBlacklisted() { if ( opt.permissionRequire( - PERMISSION_DENIED, - opt.permissionOf(msg.sender) + opt.permissionOf(msg.sender), + PERMISSION_DENIED ) ) { revert IEIP6366Error.AccessDenied(