diff --git a/EIPS/eip-1203.md b/EIPS/eip-1203.md new file mode 100644 index 00000000..d4e30c9e --- /dev/null +++ b/EIPS/eip-1203.md @@ -0,0 +1,230 @@ +--- +eip: 1203 +title: ERC-1203 Multi-Class Token Standard (ERC-20 Extension) +author: Jeff Huang , Min Zu +discussions-to: https://github.com/ethereum/EIPs/issues/1203 +status: Draft +type: Standards Track +category: ERC +created: 2018-07-01 +--- + +## Simple Summary + +A standard interface for multi-class tokens (MCTs). + +## Abstract + +The following standard allows for the implementation of a standard API for MCTs within smart contracts. This standard provides basic functionality to track, transfer, and convert MCTs. + +## Motivation + +This standard is heavily inspired by ERC-20 Token Standard and ERC-721 Non-Fungible Token Standard. However, whereas these standards are chiefly concerned with representation of items/value in a single class, fungible or note, this proposed standard focus on that of a more complexed, multi-class system. It is fair to think of MCTs as a hybrid of fungible tokens (FT) and non-fungible tokens (NFTs), that is tokens are fungible within the same class but non-fungible with that from a different class. And conversions between classes may be optionally supported. + +MCTs are useful in representing various structures with heterogeneous components, such as: + +- **Abstract Concepts:** A company may have different classes of stocks (e.g. senior preferred, junior preferred, class A common, class B common) that together make up its outstanding equities. A shareholder's position of such company composites of zero or more shares in each class. + +- **Virtual Items:** A sandbox computer game may have many types of resources (e.g. rock, wood, berries, cows, meat, knife, etc.) that together make up that virtual world. A player's inventory has any combination and quantity of these resources + +- **Physical Items:** A supermarket may have many SKUs it has available for purchase (e.g. eggs, milk, beef jerky, beer, etc.). Things get added or removed from a shopper's cart as it moves down the aisle. + +It's sometimes possible, especially with regard to abstract concepts or virtual items, to convert from one class to another, at a specified conversion ratio. When it comes to physical items, such conversion essentially is the implementation of bartering. Though it might generally be easier to introduce a common intermediary class, i.e. money. + +## Specification + +```solidity +contract ERC20 { + function totalSupply() public view returns (uint256); + function balanceOf(address _owner) public view returns (uint256); + function transfer(address _to, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +} + +contract ERC1203 is ERC20 { + function totalSupply(uint256 _class) public view returns (uint256); + function balanceOf(address _owner, uint256 _class) public view returns (uint256); + function transfer(address _to, uint256 _class, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _class, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender, uint256 _class) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _class, uint256 _value) public returns (bool); + + function fullyDilutedTotalSupply() public view returns (uint256); + function fullyDilutedBalanceOf(address _owner) public view returns (uint256); + function fullyDilutedAllowance(address _owner, address _spender) public view returns (uint256); + function convert(uint256 _fromClass, uint256 _toClass, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _class, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _class, uint256 _value); + event Convert(uint256 indexed _fromClass, uint256 indexed _toClass, uint256 _value); +} +``` + +### ERC-20 Methods and Events (fully compatible) + +Please see [ERC-20 Token Standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) for detailed specifications. Do note that these methods and events only work on the "default" class of an MCT. + +```solidity + function totalSupply() public view returns (uint256); + function balanceOf(address _owner) public view returns (uint256); + function transfer(address _to, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +``` + +### Tracking and Transferring + +**totalSupply** + +Returns the total number of tokens in the specified `_class` + +```solidity + function totalSupply(uint256 _class) public view returns (uint256); +``` + +**balanceOf** + +Returns the number of tokens of a specified `_class` that the `_owner` has + +```solidity + function balanceOf(address _owner, uint256 _class) public view returns (uint256); +``` + +**transfer** + +Transfer `_value` tokens of `_class` to address specified by `_to`, return `true` if successful + +```solidity + function transfer(address _to, uint256 _class, uint256 _value) public returns (bool); +``` + +**approve** + +Grant `_spender` the right to transfer `_value` tokens of `_class`, return `true` if successful + +```solidity + function approve(address _spender, uint256 _class, uint256 _value) public returns (bool); +``` + +**allowance** + +Return the number of tokens of `_class` that `_spender` is authorized to transfer on the behalf of `_owner` + +```solidity + function allowance(address _owner, address _spender, uint256 _class) public view returns (uint256); +``` + +**transferFrom** + +Transfer `_value` tokens of `_class` from address specified by `_from` to address specified by `_to` as previously approved, return `true` if successful + +```solidity + function transferFrom(address _from, address _to, uint256 _class, uint256 _value) public returns (bool); +``` + +**Transfer** + +Triggered when tokens are transferred or created, including zero value transfers + +```solidity + event Transfer(address indexed _from, address indexed _to, uint256 _class, uint256 _value); +``` + +**Approval** + +Triggered on successful `approve` + +```solidity + event Approval(address indexed _owner, address indexed _spender, uint256 _class, uint256 _value); +``` + +### Conversion and Dilution + +**fullyDilutedTotalSupply** + +Return the total token supply as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedTotalSupply() public view returns (uint256); +``` + +**fullyDilutedBalanceOf** + +Return the total token owned by `_owner` as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedBalanceOf(address _owner) public view returns (uint256); +``` + +**fullyDilutedAllowance** + +Return the total token `_spender` is authorized to transfer on behalf of `_owner` as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedAllowance(address _owner, address _spender) public view returns (uint256); +``` + +**convert** + +Convert `_value` of `_fromClass` to `_toClass`, return `true` if successful + +```solidity + function convert(uint256 _fromClass, uint256 _toClass, uint256 _value) public returns (bool); +``` + +**Conversion** + +Triggered on successful `convert` + +```solidity + event Conversion(uint256 indexed _fromClass, uint256 indexed _toClass, uint256 _value); +``` + +## Rationale +This standard purposely extends ERC-20 Token Standard so that new MCTs following or existing ERC-20 tokens extending this standard are fully compatible with current wallets and exchanges. In addition, new methods and events are kept as closely to ERC-20 conventions as possible for ease of adoption. + +We have considered alternative implementations to support the multi-class structure, as discussed below, and we found current token standards incapable or inefficient in deal with such structures. + +**Using multiple ERC-20 tokens** + +It is certainly possible to create an ERC-20 token for each class, and a separate contract to coordinate potential conversions, but the short coming in this approach is clearly evident. The rationale behind this standard is to have a single contract to manage multiple classes of tokens. + +**Shoehorning ERC-721 token** + +Treating each token as unique, the non-fungible token standard offers maximum representational flexibility arguably at the expense of convenience. The main challenge of using ERC-721 to represent multi-class token is that separate logic is required to keep track of which tokens belongs to which class, a hacky and unnecessary endeavor. + +**Using ERC-1178 token** + +We came across ERC-1178 as we were putting final touches on our own proposal. The two ERCs look very similar on the surface but we believe there're a few key advantages this one has over ERC-1178. + +- ERC-1178 offers no backward compatibility whereas this proposal is an extension of ERC-20 and therefore fully compatible with all existing wallets and exchanges +- By the same token, existing ERC-20 contracts can extend themselves to adopt this standard and support additional classes without affecting their current behaviors +- This proposal introduces the concept of cross class conversion and dilution, making each token class integral part of a whole system rather than many silos + +## Backwards Compatibility +This EIP is fully compatible with the mandatory methods of ERC20 Token Standard so long as the implementation includes a "lowest common denominator" class, which may be class B common/gold coin/money in the abstract/virtual/physical examples above respectively. Where it is not possible to implement such class, then the implementation should specify a default class for tracking or transferring unless otherwise specified, e.g. US dollar is transferred unless other currency is explicitly specified. + +We find it contrived to require the optional methods of ERC20 Token Standard, `name()`, `symbol()`, and `decimals()`, but developers are certainly free to implement these as they wish. + +## Test Cases +The repository at [jeffishjeff/ERC-1203](https://github.com/jeffishjeff/ERC-1203) contains the [sample test cases](https://github.com/jeffishjeff/ERC-1203/blob/master/token.test.js). + +## Implementation +The repository at [jeffishjeff/ERC-1203](https://github.com/jeffishjeff/ERC-1203) contains the [sample implementation](https://github.com/jeffishjeff/ERC-1203/blob/master/token.sol). + +## References +- ERC-20 Token Standard. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md +- ERC-721 Non-Fungible Token Standard. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md +- ERC-1178 Multi-class Token Standard. https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1178.md + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).