EIP-4987 "held" token standard (NFTs + DeFi)

@SamWilsn your feedback on events brings up another good question. I think there’s a case to be made that this interface should not require any events to be emitted.

One of the main goals is for this interface to be extremely lightweight and non-intrusive. Additional events/gas doesn’t help with that

Plus, we could arguably get all the equivalent data by indexing underlying token Transfer events and filtering where to or from match our holder of interest

Agree… its good idea… very useful…

There are apps where users can deposit their NFTs…
But then its not visible in their wallet and marketplaces…

If this standard being adopted… then user can deposit his NFT to another contract, but it still remains visible in his wallet…

Particularly in our case, we want to allow users prove ownership of their NFTs… including those NFTs already staked into some contracts…

If this standard being supported by staking contract… then we can read heldOwnerOf to find actual owner… also we can listen Hold and Release events… to deterministically reduce database… I think events are important…

1 Like

and events are not so bad… they are not being saved into blockchain… I mean they are much cheaper than storage data in smart contract

1 Like

Thanks for the thoughts here @7flash, glad to hear this would be applicable for your use case

And a general update, the draft has been merged in and can be found published here:

An interface was included for each “held token” type, for example ERC721

/**
 * @notice the ERC721 holder standard provides a common interface to query
 * token ownership and balance information
 */
interface IERC721Holder is IERC165 {
  /**
   * @notice emitted when the token is transferred to the contract
   * @param owner functional token owner
   * @param tokenAddress held token address
   * @param tokenId held token ID
   */
  event Hold(
    address indexed owner,
    address indexed tokenAddress,
    uint256 indexed tokenId
  );

  /**
   * @notice emitted when the token is released back to the user
   * @param owner functional token owner
   * @param tokenAddress held token address
   * @param tokenId held token ID
   */
  event Release(
    address indexed owner,
    address indexed tokenAddress,
    uint256 indexed tokenId
  );

  /**
   * @notice get the functional owner of a held token
   * @param tokenAddress held token address
   * @param tokenId held token ID
   * @return functional token owner
   */
  function heldOwnerOf(address tokenAddress, uint256 tokenId)
    external
    view
    returns (address);

  /**
   * @notice get the held balance of the token owner
   * @param tokenAddress held token address
   * @param owner functional token owner
   * @return held token balance
   */
  function heldBalanceOf(address tokenAddress, address owner)
    external
    view
    returns (uint256);
}

I think this would be a great addition to the existing 721, especially in the use case of Meteverse access as in our scenario. Our 721 give instant access to token gated portals in our metaverse, a virtual meta mall, these access controllers can be staked through GYSR to earn our erc-20, this is how we are doing our initial token drop, exclusively through NFT access passes to the mall, whose rewards tokens are used to build and buy within. This new token standard would allow members to not have to own more than one, where the current contracts of staking require the custody thus the holder loses access. I would love to see this standard implemented.

1 Like

Hey folks! Wanted to share that this EIP has officially moved to “Review”

Would greatly appreciate any additional feedback or thoughts!
@MindfulFroggie @julesl23 @Daniel-K-Ivanov @flaskr @SamWilsn @7flash @TwompsonPlanetWEARTH

2 Likes

Great work designing this EIP. This is definitely something a lot of projects across different industries could benefit from enormously, whether it be DeFi protocols or consumer NFTs.

It’s worth mentioning that two other proposals try to tackle the same ownership attribution problem. One, which was already mentioned, is the already finalized EIP-4400, and the other is EIP-4799. EIP-4987, however, is the only proposal that does so in a way that is both simple and backwards-compatible.

I do think it would be helpful to include in the EIP a code snippet of the getOwner function you described, or at least a slightly more thorough explanation of how the chain of ownership validation would work, as this doesn’t seem to be too clearly laid out.

Another point to note is that until wallets / UIs adopt such a standard, for projects that care more about using this as a solution to mirror ownership (particularly consumer NFT projects), it seems like the best temporary solution may be for the originally owned NFT to proxy ownership calls to the owning contract. I’m curious @devinaconley if you might have any other ideas here.

Also, personally, I don’t like any of the suggested prefixes for the naming convention. My recommendation would be making it user (as in userOwnerOf or userBalanceOf) since in terms of semantics the functional owner is almost always also the user of the NFT.

Really excited to see this prop go through!

1 Like

Hey @leeren thanks for the thoughts and feedback!

Appreciate you digging into alternatives, great to hear. We put a major focus on the interface being lightweight and backwards compatible.

Good idea, will make an addition to include this.

The challenge there would needing to update the core ERC721 to do that. I think that individual projects can start by simply updating their own webapps to reflect held ownership of tokens.

Generally, I think adoption follows in 3 phases. First, of course, core protocols that hold tokens need to implement this interface. Then individual projects can consume this standard for their governance, access controls, etc. Finally, we can work with larger platforms for more universal adoption.

Tried adding this to the poll, but longer possible. I do like this suggestion as well, curious to hear other’s thoughts

Nice work on this standard. Wanted to comment on a possible use-case for the ERC20 side of things. I’m implementing a vault where some ERC20 tokens owned by the vault are reserved for distribution. I’m considering using something like hold(reservedAddress, this) to mark those tokens. I will need to know how many tokens are not reserved, so I wonder if this is something that should be added to the standard. Seems like there are other use-cases where querying the amount of held tokens is useful. I see two possible solutions. One just returns the unreserved balance:

function unheldBalanceOf(address owner) external view returns (uint256)

The other overrides heldBalanceOf with a different signature to indicate total held balance, and then I can subtract from balanceOf to get the unreserved balance.

function heldBalanceOf(address owner) external view returns (uint256)

To be honest I’m not sure if either of these are necessary for the standard, but one of them would be useful for my case.

1 Like

Hey @CareWater333 - thanks for the feedback/thoughts here.

First, agreed that the ERC20 distribution vault you are describing is a perfect use case for this standard. The held token interface would allow external system to include users in governance, membership, verification, etc. even before their tokens are vested.

Question on this, what kind of system will be consuming this data on “not reserved” tokens? Is it on-chain or off-chain?

On chain. This is an implementation of ERC4626, and the share price calculation needs to be based on the number of tokens not already reserved for distribution. I’ve already implemented it - it’s not hard. The question is if it’s useful enough to make part of the standard.

Just pushed an update to EIP-4987

  • more detailed revert specification
  • harden example implementation
  • add chained ownership lookup snippet (cc @leeren )
  • misc cleanup

Would welcome any additional feedback here!