-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFT (Re-Fungible Token) #1633
RFT (Re-Fungible Token) #1633
Changes from all commits
b7db763
c236454
285fd9e
3147ae6
fe54a5e
cc044b1
bf55304
82627f2
7b28d3f
a3905e6
d7e46d2
53bb849
602ae06
f3bac67
b77704e
0d9e6a4
2a9f423
cd1eaa5
df39bf3
82e50d7
5b54622
bdd6a1e
e774215
ca266ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,174 @@ | ||||||||||
--- | ||||||||||
eip: 1633 | ||||||||||
title: Re-Fungible Token Standard (RFT) | ||||||||||
author: Billy Rennekamp (@okwme), Dan Long <dan@artblx.com>, Kiryl Yermakou <kiryl@artblx.com>, Nate van der Ende <nate@artblx.com> | ||||||||||
discussions-to: https://github.com/ethereum/EIPs/issues/1634 | ||||||||||
status: Draft | ||||||||||
okwme marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
type: Standards Track | ||||||||||
category: ERC | ||||||||||
created: 2018-11-18 | ||||||||||
requires: 20, 165, 721 | ||||||||||
--- | ||||||||||
|
||||||||||
## Simple Summary | ||||||||||
[ERC-20](./eip-20.md) extension for proportional ownership of an [ERC-721](./eip-721.md) token. | ||||||||||
|
||||||||||
## Abstract | ||||||||||
The intention of this proposal, the Re-Fungible Token Standard, is to extend the ERC-20 Token Standard and utilize ERC-165 Standard Interface Detection in order to represent the shared ownership of an ERC-721 Non-Fungible Token. The ERC-20 Token Standard was modified as little as possible in order to allow this new class of token to operate in all of the ways and locations which are familiar to assets that follow the original ERC-20 specification. While there are many possible variations of this specification that would enable many different capabilities and scenarios for shared ownership, this proposal is focused on the minimal commonalities to enable as much flexibility as possible for various further extensions. This proposal makes it possible to verify, from the contract level or from an external query, whether a fungible token represents a form of shared ownership of a non-fungible token. The inclusion of ERC-165 makes it possible to verify, from the contract level or from an external query, whether a non-fungible token is owned by ERC-20 token representing shared ownership. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The abstract should be a human readable version of the specification. This abstract contains some good spec-like information, but it includes unrelated information that would be better suited for the
Suggested change
|
||||||||||
|
||||||||||
## Motivation | ||||||||||
Shared ownership occurs across many industries and for many reasons. As more assets are registered, regulated and/or represented by the ERC-721 Non-Fungible Token Standard there will be more instances where the need for shared ownership of these assets will arise. For example, ARTBLX Inc. is working towards facilitating a protocol for collective ownership of physical, digital and conceptual artworks. The fungible tokens created from this process will have a value attached to the non-fungible tokens which they represent. This will be useful for price discovery of the underlying asset, liquidity for shared owners and as a new class of asset which can be used as collateral for loans or other financial instruments like stable coins. Providing an interface to this special class of fungible tokens is necessary to allow third parties to recognize them as a special class of fungible token and to recognize when a non-fungible token is collectively owned. This might be useful in the case of a wallet who would want to utilize the metadata of the underlying NFT to show additional info next to an RFT, or on an exchange who might want to make that sort of info similarly available, or an NFT marketplace who may want to direct customers to a relevant exchange who wish to purchase shares in a NFT which is owned by an RFT. Anywhere an ERC-20 is applicable it would be useful for a user to know whether that token represents a shared NFT, and what attributes that NFT may have. | ||||||||||
|
||||||||||
## Specification | ||||||||||
At a minimum, third parties need two things: 1) to be able to distinguish re-fungible tokens from other token standards and 2) to determine when a non-fungible token is collectively owned. These two scenarios can be encountered from the perspective of initial contact with the non-fungible token or from the perspective of initial contact with the re-fungible token. | ||||||||||
|
||||||||||
#### Initial Contact with the Re-Fungible Token | ||||||||||
|
||||||||||
In order for a third party to confirm which non-fungible token is owned by the re-fungible token there needs to be a pointer from the RFT contract to the NFT contract and the relevant token id. This is possible with two public getters named `parentToken()` and `parentTokenId()`. The first getter returns a variable of type `address` and designates the contract address of the Non-Fungible Token contract. The second getter returns a variable of type `uint256` and designates the token ID of the Non-Fungible Token. With these getters, the identity of the Non-Fungible Token can be determined. Below is an example of the Re-Fungible Token Standard interface that includes these getter functions: | ||||||||||
|
||||||||||
```solidity | ||||||||||
pragma solidity ^0.4.20; | ||||||||||
|
||||||||||
/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2. | ||||||||||
interface RFT /* is ERC20, ERC165 */ { | ||||||||||
|
||||||||||
function parentToken() external view returns(address _parentToken); | ||||||||||
function parentTokenId() external view returns(uint256 _parentTokenId); | ||||||||||
|
||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
The validity of this claim can be confirmed from another contract (on-chain) or from interacting with an RPC endpoint (off-chain). Below is an example of the on-chain scenario: | ||||||||||
|
||||||||||
```solidity | ||||||||||
pragma solidity ^0.4.20; | ||||||||||
|
||||||||||
import './RFT.sol'; | ||||||||||
import './ERC721.sol'; | ||||||||||
|
||||||||||
contract ConfirmRFT { | ||||||||||
|
||||||||||
function confirmRFT(address _RFT) external view returns(bool) { | ||||||||||
address _NFT = RFT(_RFT).parentToken(); // returns address of NFT contract | ||||||||||
uint256 _tokenId = RFT(_RFT).parentTokenId(); // returns id of ID of NFT | ||||||||||
|
||||||||||
return | ||||||||||
NFT(_NFT).supportsInterface(0x80ac58cd) && // confirm it is ERC-721 | ||||||||||
NFT(_NFT).ownerOf(_tokenId) == _RFT; // confirm the owner of the NFT is the RFT contract address | ||||||||||
} | ||||||||||
|
||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
Below is an off-chain example using an instance of web3.js in javascript: | ||||||||||
```javascript | ||||||||||
async function confirmRFT(web3) { | ||||||||||
|
||||||||||
const ERC721ABI = [...] // abi for ERC721 | ||||||||||
const RFTABI = [...] // abi for RFT | ||||||||||
const RFTAddress = '0x0123456789abcdef0123456789abcdef' // address for the deployed RFT | ||||||||||
|
||||||||||
const RFTContract = new web3.eth.Contract(RFTABI, RFTAddress) // deployed RFT contract instance | ||||||||||
const ERC721Address = await RFTcontract.methods.parentToken().call() // returns address of NFT contract | ||||||||||
const ERC721TokenId = await RFTcontract.methods.parentTokenId().call() // returns id of ID of NFT | ||||||||||
|
||||||||||
const ERC721Contract = new web3.eth.Contract(ERC721ABI, ERC721Address) // deployed ERC721 (as reported by RFT) | ||||||||||
const isERC721 = await ERC721Contract.methods.supportsInterface('0x80ac58cd').call() // confirm it is ERC-721 | ||||||||||
const ownerOfAddress = await ERC721Contract.methods.ownerOf(ERC721TokenId).call() // get the owner of the NFT | ||||||||||
|
||||||||||
return ERC721Response.toLowerCase() === RFTAddress.toLowerCase() // confirm the owner of the NFT is the RFT contract | ||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
#### Initial Contact with the Non-Fungible Token | ||||||||||
|
||||||||||
When checking the owner of a specific non-fungible token it's important to be able to determine whether owner is in fact a re-fungible token contract. This is possible by utilizing ERC-165 Standard Interface Detection. In order to comply with that standard a contract must include the following getter function which returns `true` when passed the `bytes4` parameter `0x01ffc9a7`: | ||||||||||
``` | ||||||||||
function supportsInterface(bytes4 interfaceID) external view returns (bool); | ||||||||||
``` | ||||||||||
After establishing support for this interface it becomes useful in determining whether the contract adheres to the Re-Fungible Token Standard. To do so the `supportsInterface(bytes4 interfaceID)` getter function must return `true` when passed the `bytes4` parameter `0x5755c3f2` which is the result of `bytes4(keccak256('parentToken()')) ^ bytes4(keccak256('parentTokenId()'))` or `parentToken.selector ^ parentTokenId.selector`. This could be achieved with the following code: | ||||||||||
```solidity | ||||||||||
pragma solidity ^0.4.20; | ||||||||||
|
||||||||||
import "./ERC20.sol"; | ||||||||||
|
||||||||||
/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2. | ||||||||||
interface RFT is ERC20 /*, ERC165 */ { | ||||||||||
|
||||||||||
function supportsInterface(bytes4 interfaceID) external view returns(bool) { | ||||||||||
return | ||||||||||
interfaceID == this.supportsInterface.selector || // ERC165 | ||||||||||
interfaceID == this.parentToken.selector || // parentToken() | ||||||||||
interfaceID == this.parentTokenId.selector || // parentTokenId() | ||||||||||
interfaceID == this.parentToken.selector ^ this.parentTokenId.selector; // RFT | ||||||||||
} | ||||||||||
|
||||||||||
function parentToken() external view returns(address _parentToken); | ||||||||||
function parentTokenId() external view returns(uint256 _parentTokenId); | ||||||||||
|
||||||||||
} | ||||||||||
``` | ||||||||||
The flow of actually checking the status of a non-fungible token owner as a re-fungible token contract can be done from another contract (on-chain) as well as with an RPC endpoint (off-chain). Below is an example of the on-chain scenario: | ||||||||||
```solidity | ||||||||||
pragma solidity ^0.4.20; | ||||||||||
|
||||||||||
import './RFT.sol'; | ||||||||||
import './ERC721.sol'; | ||||||||||
|
||||||||||
contract ConfirmRFT { | ||||||||||
|
||||||||||
function confirmRFT(address _NFT, uint256 _tokenId) external view returns(bool) { | ||||||||||
address _RFT = ERC721(_NFT).ownerOf(_tokenId); // get the owner of the NFT | ||||||||||
|
||||||||||
return | ||||||||||
RFT(_RFT).supportsInterface(0x01ffc9a7) && // confirm it supports ERC-165 | ||||||||||
RFT(_RFT).supportsInterface(0x5755c3f2) // confirm it is RFT | ||||||||||
} | ||||||||||
|
||||||||||
} | ||||||||||
``` | ||||||||||
Below is an off-chain example using web3.js in javascript: | ||||||||||
```javascript | ||||||||||
async function confirmRFT(web3) { | ||||||||||
|
||||||||||
const ERC721ABI = [...] // abi for ERC721 | ||||||||||
const RFTABI = [...] // abi for RFT | ||||||||||
const ERC721Address = '0x0123456789abcdef0123456789abcdef' // address for the deployed NFT | ||||||||||
const ERC721TokenId = '7' // token Id of the NFT | ||||||||||
|
||||||||||
const ERC721Contract = new web3.eth.Contract(ERC721ABI, ERC721Address) // deployed ERC721 | ||||||||||
const RFTAddress = await ERC721Contract.methods.ownerOf(ERC721TokenId).call() // owner address of the NFT | ||||||||||
|
||||||||||
|
||||||||||
const RFTContract = new web3.eth.Contract(RFTABI, RFTAddress) // deployed RFT contract instance | ||||||||||
const isERC165 = await RFTContract.methods.supportsInterface('0x01ffc9a7').call() // confirm it is ERC-165 | ||||||||||
return isERC165 && await RFTContract.methods.supportsInterface('0x5755c3f2').call() // confirm it is RFT | ||||||||||
|
||||||||||
} | ||||||||||
``` | ||||||||||
## Rationale | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider indicating in the rationale section why you have two functions instead of one function with two return values: function parentToken() external returns (address, uint256); |
||||||||||
Most of the decisions made around the design of this standard were done in the hopes of keeping it as flexible as possible for as many use cases as possible. This includes making the standard 100% backwards compatible with ERC-20 Token Standard and able to interact with any previously deployed or future ERC-721 non-fungible token. This allows for each project to determine their own system for minting, burning and governing their re-fungible tokens depending on their specific use case. | ||||||||||
|
||||||||||
## Backwards Compatibility | ||||||||||
The Re-Fungible Token Standard is 100% backwards compatible with ERC-20 Token Standard. It is a small extension to the original specification and meant to be further extended for more specific use cases. Keeping the standard compatible with ERC-20 is important to allow for this token to benefit from the ecosystem that has grown around supporting the ubiquitous ERC-20 Token Standard. | ||||||||||
|
||||||||||
The Re-Fungible Token Standard is intended to interact with the ERC-721 Non-Fungible Token Standard. It is kept purposefully agnostic to extensions beyond the standard in order to allow specific projects to design their own token relationships such as governance over, rights to or permissions on each non-fungible token relative to the respective re-fungible token owners. | ||||||||||
Comment on lines
+153
to
+155
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Terse EIPs tend to do better than verbose ones, consider trimming this down to the essential bits:
Suggested change
THe rest of this content can go into |
||||||||||
|
||||||||||
## Implementation | ||||||||||
```solidity | ||||||||||
pragma solidity ^0.4.20; | ||||||||||
|
||||||||||
/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2. | ||||||||||
interface RFT /* is ERC20, ERC165 */ { | ||||||||||
|
||||||||||
function parentToken() external view returns(address _parentToken); | ||||||||||
function parentTokenId() external view returns(uint256 _parentTokenId); | ||||||||||
|
||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
## Security Considerations | ||||||||||
TBD | ||||||||||
|
||||||||||
## Copyright | ||||||||||
okwme marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.