AlertSourceDiscuss
Skip to content
On this page

ERC-6366: Permission Token

A new token that held the permission of an address in an ecosystem

⚠️ DraftERC

Draft Notice

This EIP is in the process of being drafted. The content of this EIP is not final and can change at any time; this EIP is not yet suitable for use in production. Thank you!

AuthorsChiro (@chiro-hiro), Victor Dusart (@vdusart)
Created2022-01-19

Abstract

This EIP offers an alternative to Access Control Lists (ACLs) for granting authorization and enhancing security. Each permission is represented by a single bit in uint256 from which we can defined up to 256 permissions and 2²⁵⁶ roles. This approach use bitwise operator and bitmask to determine the access right which is much more efficient and flexible than string comparison or keccak(). We are able to specify the importance of permission based on the bit order.

Motivation

Special roles like Owner, Operator, Manager, Validator are common for many smart contracts because permissioned addresses are used to administer and manage them. It is difficult to audit and maintain these system since these permissions are not managed in a single smart contract.

Since permission and role are reflected by the permission token balance of the relevant account in the given ecosystem, cross-interactivity between many ecosystems will be made simpler.

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)

Core Interface

Compliant contracts MUST implement IEIP6366Core.

solidity
interface IEIP6366Core {
  event Transfer(address indexed _from, address indexed _to, uint256 _value);

  event Approval(address indexed _owner, address indexed _delegatee, uint256 _permission);

  function transfer(address _to, uint256 _permission) external returns (bool success);

  function approve(address _delegatee, uint256 _permission) external returns (bool success);

  function permissionOf(address _owner) external view returns (uint256 permission);

  function permissionRequire(uint256 _required, uint256 _permission) external view returns (bool isPermissioned);

  function hasPermission(address _owner, address _actor, uint256 _required) external view returns (bool isPermissioned);

  function delegated(address _owner, address _delegatee) external view returns (uint256 permission);
}

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 the bitwise operator and bitmask.

Example

text
const PERMISSION_NONE = 0;
const PERMISSION_READ = 1; // 2⁰
const PERMISSION_WRITE = 2; // 2¹
const PERMISSION_EXECUTE = 4; // 2²

// Role admin = 4 | 2 | 1 = 7
const ROLE_ADMIN = PERMISSION_READ | PERMISSION_WRITE | PERMISSION_EXECUTE;
// Role operator = 1 | 2 = 3
const ROLE_OPERATOR = PERMISSION_READ | PERMISSION_WRITE;

The most important permission SHOULD be greater than the lesser one.

transfer

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.

Note Transfers of 0 permission MUST be treated as normal transfers and emit the Transfer event.

solidity
function transfer(address _to, uint256 _permission) external returns (bool success);

approve

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.

Note _permission MUST be a subset of all available permission of permission owner.

Note approve() method SHOULD revert if granting _permission permission is not a subset of all available permission of permission owner.

solidity
function approve(address _delegatee, uint256 _permission) external returns (bool success);

permissionOf

Returns the account permission of the given _owner address.

solidity
function permissionOf(address _owner) external view returns (uint256 permission);

permissionRequire

Returns true if _required permission is a subset of _permission permission otherwise return false.

solidity
function permissionRequire(uint256 _required, uint256 _permission) external view returns (bool isPermissioned);

hasPermission

Returns true if _required permission is a subset of _actor's permissions or a subset of his delegated permissions granted by the _owner.

solidity
function hasPermission(address _owner, address _actor, uint256 _required) external view returns (bool isPermissioned);

delegated

Returns the subset permission of the _owner address were granted to _delegatee address.

solidity
function delegated(address _owner, address _delegatee) external view returns (uint256 permission);

Transfer

MUST trigger when permission are transferred, including zero permission transfers.

A token contract which creates new tokens SHOULD emit a Transfer event with the _from address set to address(0x00) when tokens are created.

solidity
event Transfer(address indexed _from, address indexed _to, uint256 _value)

Approval

MUST trigger on any successful call to approve(address _delegatee, uint256 _permission).

solidity
event Approval(address indexed _owner, address indexed _delegatee, uint256 _permission)

Metadata Interface

It is RECOMMENDED for compliant contracts to implement the optional extension IEIP6366Meta.

solidity
interface IEIP6366Meta {
  struct PermissionDescription {
    uint256 index;
    uint256 permission;
    string name;
    string description;
  }

  event UpdatePermissionDescription(uint256 indexed _permission, string indexed _name, string indexed _description);

  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function getDescription(uint256 _index) external view returns (PermissionDescription memory description);

  function setDescription(uint256 _index, string memory _name, string memory _description) external returns (bool success);
}

name

Returns the name of the token - e.g. "OpenPermissionToken".

solidity
function name() external view returns (string memory);

symbol

Returns the symbol of the token. E.g. "OPT".

solidity
function symbol() external view returns (string memory);

getDescription

Return a description of a permission, at a given _index.

solidity
function getDescription(uint256 _index) external view returns (PermissionDescription memory description);

setDescription

Return true if the description was set otherwise return false. It MUST emit UpdatePermissionDescription event.

solidity
function setDescription(uint256 _index, string memory _name, string memory _description) external returns (bool success);

SHOULD define a relationship between index field and permission field in PermissionDescription. It is RECOMMENDED to define permission is 2 to the power of index.

UpdatePermissionDescription

MUST trigger when description is updated.

solidity
event UpdatePermissionDescription(uint256 indexed _permission, string indexed _name, string indexed _description);

Error Interface

SHOULD NOT expected IEIP6366Error interface was implemented.

solidity
interface IEIP6366Error {
  error AccessDenied(address _owner, address _actor, uint256 _permission);

  error DuplicatedPermission(uint256 _permission);

  error OutOfRange();
}

Rationale

Needs discussion.

Reference Implementation

First implementation could be found here:

Security Considerations

Need more discussion.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Chiro, Victor Dusart, "ERC-6366: Permission Token[DRAFT]," Ethereum Improvement Proposals, no. 6366, 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6366.