Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 111 additions & 23 deletions EIPS/eip-6366.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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();
}
```
Expand Down
14 changes: 7 additions & 7 deletions assets/eip-6366/contracts/EIP6366Core.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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;
}
Expand All @@ -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
);
}

Expand Down
12 changes: 8 additions & 4 deletions assets/eip-6366/contracts/interfaces/IEIP6366Core.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand Down
26 changes: 17 additions & 9 deletions assets/eip-6366/example/APermissionToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
_;
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions assets/eip-6366/example/APermissioned.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(
Expand Down