Skip to content
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

Merged
merged 24 commits into from
Mar 11, 2021
Merged

RFT (Re-Fungible Token) #1633

merged 24 commits into from
Mar 11, 2021

Conversation

okwme
Copy link
Contributor

@okwme okwme commented Dec 2, 2018


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: #1634
status: Draft
type: Standards Track
category: ERC
created: 2018-11-18
requires: 20, 165, 721

Simple Summary

This improvement proposal outlines an extension to the ERC-20 Token Standard and utilization of ERC-165 Standard Interface Detection. The purpose is to enable the ability to distinguish when an ERC-20 token represents shared ownership of an ERC-721 non-fungible token (NFT) and by extension any potentially underlying asset therein. This is made possible by a re-fungible token (RFT) contract assuming ownership of a non-fungible 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.

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.

Inital 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:

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:

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:

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
}

Inital 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:

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:

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:

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

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.

There are a number of other ERCs which have similarities to this proposal however they are often overly opinionated and restict many valid variations which could arise in different scenarios. Many of them break ERC-20 or ERC-721 in the process, or are at their core solving a different problem. We believe a token standard should cover only the required commonality between an otherwise diverse set of scenarios in order for them to behave as expected where necessary but allow them to achieve their individual goals elsewhere. Below are a list of the proposals considered due to some similarities as well as why they may not fulfill the requirements of this standard.

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.

Implementation

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);

}

Copyright

Copyright and related rights waived via CC0.

@okwme okwme changed the title Re-Fungible Token RFT (Re-Fungible Token) Mar 29, 2019
@axic
Copy link
Member

axic commented May 9, 2019

Without fully reading the proposal, this seems to be similar to #1513. Would it be possible to merge efforts?

@axic axic mentioned this pull request May 9, 2019
@okwme
Copy link
Contributor Author

okwme commented May 10, 2019

hi @axic
i tried to get the conversation started with @tlxsam here: #1634 (comment) but never got a response.

@tlxsam
Copy link

tlxsam commented May 10, 2019

hi @okwme the proposals are under review, will discuss further soon.

@axic axic added the ERC label May 20, 2019
@github-actions
Copy link

There has been no activity on this pull request for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Sep 15, 2020
@tlxsam
Copy link

tlxsam commented Sep 15, 2020

We are upgrading the implementation based on the latest contract definitions and will resubmit for review.

@github-actions github-actions bot removed the stale label Sep 15, 2020
@lightclient
Copy link
Member

@tlxsam do you plan to use this PR or close?

@JamesDoe
Copy link

JamesDoe commented Oct 5, 2020

@okwme, thanks so much for sending me a link to this Git!
As mentioned in my previous post, you guys have an amazing proposal on your hands!

It aligns with my current necessity to find an applicable standard for NFTs that can have several, non-owner participants associated with it. In my use case, I'm creating a 'living' securities strategy, whereby the author sets the parameters of the strategy (exit points, risk thresholds - such as 'theta' and 'IV' limits, minimum input thresholds for ERC20 participation, etc.) and then unleashes the NFT into the wild.

Once the NFT hits the main net, its internal parameters become immutable. The trick is that the NFT can only 'survive' by
being 'fed' a particular ERC20 from other addresses... i.e. 'participants', who basically act as investors.

The participants are rewarded if/when the decentralized securities play exits with a profit, and the author in return, is rewarded a nominal commission that is applied as value against the NFT token. The author is also mandated to hold a set stake in the NFT.

Lastly, while participants can not exit prior to the pre-programmed thresholds/conditions, they are free to 'sell' or 'trade' their vestments in the NFT.

So, there a lot of moving pieces that benefit from less stricture on the ERC-standard side of things.
I'm looking forward to presenting my build-out to you all, and getting feedback on how applicable it may or may not be to the standard you guys are creating. Optimally, I intend to stay as closely aligned with your initial proposal's ideas as possible.

The beauty in your concept, is the careful attention paid to a separation of concerns.

In my use case, that separation will be vital — as the author needs some maneuverability when dictating the behavior of the NFT, and I have to carefully weigh that customizability against security concerns (preventing rogue actions that would break securities laws in my country).

@github-actions
Copy link

github-actions bot commented Dec 4, 2020

There has been no activity on this pull request for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Dec 4, 2020
@okwme
Copy link
Contributor Author

okwme commented Dec 5, 2020

glad to hear it @JamesDoe
I'll change the status to last call if there are no other changes requested

EIPS/eip-1633.md Show resolved Hide resolved
@github-actions github-actions bot removed the stale label Dec 5, 2020
Copy link
Contributor

@MicahZoltu MicahZoltu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minimum changes to merge as Draft:
Change status to Draft, remove template comments, add Security Considerations section.

The rest of the feedback should be considered prior to submitting for Review status.

EIPS/eip-1633.md Outdated Show resolved Hide resolved
EIPS/eip-1633.md Outdated Show resolved Hide resolved
EIPS/eip-1633.md Outdated Show resolved Hide resolved
EIPS/eip-1633.md Outdated Show resolved Hide resolved
EIPS/eip-1633.md Outdated Show resolved Hide resolved
Comment on lines +180 to +182
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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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 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.
This EIP is backward compatible with the [ERC-20](./eip-20.md) token standard. It is designed to interface with existing [ERC-721](./eip-721.md) tokens.

THe rest of this content can go into ## Rationale or ## Motivation.

EIPS/eip-1633.md Outdated
Comment on lines 157 to 176
There are a number of other ERCs which have similarities to this proposal however they are often overly opinionated and restict many valid variations which could arise in different scenarios. Many of them break ERC-20 or ERC-721 in the process, or are at their core solving a different problem. We believe a token standard should cover only the required commonality between an otherwise diverse set of scenarios in order for them to behave as expected where necessary but allow them to achieve their individual goals elsewhere. Below are a list of the proposals considered due to some similarities as well as why they may not fulfill the requirements of this standard.

* [ERC-864: Divisible non-fungible tokens](https://github.com/ethereum/EIPs/issues/864)
* Proposes a replacement for ERC-20, not ERC-20 compatible
* Not a complete proposal
* [ERC-1155: Multi Token Standard](https://github.com/ethereum/EIPs/issues/1155)
* Combines ERC-20 and ERC-721 into a single contract
* Not ERC-20 backwards compatible
* [EIP-1178: Multi-class Token Standard](https://github.com/ethereum/EIPs/pull/1178)
* Solves a different problem (multiple classes of ERC-20 in one contract)
* [ERC-1410: Partially Fungible Token Standard](https://github.com/ethereum/EIPs/issues/1410)
* Solves a different problem (multiple classes of ERC-20 in one contract)
* [ERC-1528: Refungible ERC721 Asset with Fungible ERC20](https://github.com/ethereum/EIPs/issues/1528)
* Combines ERC-20 and ERC-721 into a single contract
* Not ERC-721 backwards compatibile
* Limits the types of assets which can become re-fungible
* [ERC-1553: Asset Token Standard](https://github.com/ethereum/EIPs/issues/1553)
* Standard for defining real world assets and ownership via ERC-20
* Only for real world assets
* Primarily concerned with a different objective (correlating real world and on chain assets)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
There are a number of other ERCs which have similarities to this proposal however they are often overly opinionated and restict many valid variations which could arise in different scenarios. Many of them break ERC-20 or ERC-721 in the process, or are at their core solving a different problem. We believe a token standard should cover only the required commonality between an otherwise diverse set of scenarios in order for them to behave as expected where necessary but allow them to achieve their individual goals elsewhere. Below are a list of the proposals considered due to some similarities as well as why they may not fulfill the requirements of this standard.
* [ERC-864: Divisible non-fungible tokens](https://github.com/ethereum/EIPs/issues/864)
* Proposes a replacement for ERC-20, not ERC-20 compatible
* Not a complete proposal
* [ERC-1155: Multi Token Standard](https://github.com/ethereum/EIPs/issues/1155)
* Combines ERC-20 and ERC-721 into a single contract
* Not ERC-20 backwards compatible
* [EIP-1178: Multi-class Token Standard](https://github.com/ethereum/EIPs/pull/1178)
* Solves a different problem (multiple classes of ERC-20 in one contract)
* [ERC-1410: Partially Fungible Token Standard](https://github.com/ethereum/EIPs/issues/1410)
* Solves a different problem (multiple classes of ERC-20 in one contract)
* [ERC-1528: Refungible ERC721 Asset with Fungible ERC20](https://github.com/ethereum/EIPs/issues/1528)
* Combines ERC-20 and ERC-721 into a single contract
* Not ERC-721 backwards compatibile
* Limits the types of assets which can become re-fungible
* [ERC-1553: Asset Token Standard](https://github.com/ethereum/EIPs/issues/1553)
* Standard for defining real world assets and ownership via ERC-20
* Only for real world assets
* Primarily concerned with a different objective (correlating real world and on chain assets)

I recommend leaving this out. Several of these aren't actually EIPs, just ideas, and others are still drafts. EIPs should only link to final EIPs, so unless you want this EIP to be blocked on all of these I recommend removing those at least. Also, in general the rationale section shouldn't be used to argue against any particular standard and instead should just present the reasoning for specific decisions. Your opening paragraph in this section does a good job of this IMO and so I recommend just keeping that only.


## Abstract
<!--A short (~200 word) description of the technical issue being addressed.-->
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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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 ## Motivation section.

Suggested change
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.
A contract interface 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.


}
```
## Rationale
Copy link
Contributor

Choose a reason for hiding this comment

The 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);

EIPS/eip-1633.md Outdated
Comment on lines 27 to 152
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
}
```

#### Inital 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

}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently written more like an article or tutorial than a specification. Specifications are generally better when they are short and to the point. While this is all good content, I think it is more appropriate for a different medium than a standards repository.

Suggested change
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.
#### Inital 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
}
```
#### Inital 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
}
```
```solidity
pragma solidity ^0.4.20;
/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2.
interface RFT /* is ERC20, ERC165 */ {
/// @dev Returns the ERC-721 token that that shares of this token represent ownership of.
function parentToken() external view returns(address _parentToken);
/// @dev Returns the ERC-721 token ID that shares of this token represent ownership of.
function parentTokenId() external view returns(uint256 _parentTokenId);
}
```

I think the intent here is that parentToken returns an ERC-721 token, in which case that should be made explicit.

You probably also want to specify that _parentToken.ownerOf(_parentTokenId) == this MUST be true.

I believe that EIP-165 already clearly specifies how to calculate selectors so it is not necessary to redefine that here. Also, consider dropping EIP-165 from this specification and leaving it up to the contract author to decide whether they want to implement 165 or not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that the intent with this EIP is that share of ownership is rft.balanceOf(user) / rft.totalSupply(), but that isn't specified anywhere. If that is a requirement, then I recommend asserting that. If it isn't, then consider clarifying why this EIP doesn't standardize the mechanism for calculating ownership share.

EIPS/eip-1633.md Outdated Show resolved Hide resolved
okwme and others added 9 commits January 6, 2021 16:50
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
EIPS/eip-1633.md Outdated Show resolved Hide resolved
EIPS/eip-1633.md Outdated Show resolved Hide resolved
EIPS/eip-1633.md Outdated Show resolved Hide resolved
@github-actions
Copy link

github-actions bot commented Mar 7, 2021

There has been no activity on this pull request for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Mar 7, 2021
okwme and others added 4 commits March 8, 2021 13:08
Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com>
Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com>
Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com>
EIPS/eip-1633.md Outdated Show resolved Hide resolved
Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com>
@MicahZoltu
Copy link
Contributor

grumble spell checker is failing on someone's name... will need to research how to bypass the spellchecker here.

@github-actions github-actions bot removed the stale label Mar 9, 2021
---
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>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
author: Billy Rennekamp (@okwme), Dan Long <dan@artblx.com>, Kiryl Yermakou <kiryl@artblx.com>, Nate van der Ende <nate@artblx.com>
author: Billy Rennekamp (@okwme), Dan Long <dan@artblx.com>, Kiryl Yermakou <kiryl@artblx.com>, Nate van der Ende (@van-der-ende)

@MicahZoltu MicahZoltu closed this Mar 11, 2021
@MicahZoltu MicahZoltu reopened this Mar 11, 2021
@MicahZoltu MicahZoltu merged commit 11885ee into ethereum:master Mar 11, 2021
phi-line pushed a commit to phi-line/EIPs that referenced this pull request Apr 29, 2021
[ERC-20](./eip-20.md) extension for proportional ownership of an [ERC-721](./eip-721.md) token.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants