EIP-7531 Resolving Staked ERC-721 Ownership Recognition

I see.

Imo, you shouldn’t target the existing pools with this, or at least it shouldn’t be a concern for the event:
Pools that would take the time to update to implement this can easily also add a function that allows users to emit all the events for all their already staked items (and offer an “upgrade” button on their interface for users to call this function)

I’ll again come with the “Indexer POV”, but as an indexer, when I index and I do introspection, I cache the results in my DB. So the first time I see a pool I will introspect for the different interfaces I’m looking for, but not later. So I would miss this EIP if it was only added after the first time I encountered the contract.

1 Like

I didn’t think about this. It makes sense. If I want to take advantage of it, I call the function and pay the gas to emit the event. Still, the pool could emit the reverse event when the assets are unstaked.


I added an event in the interface after studying the problem to avoid unwanted effects (for example, fake events emitted by fraudulent contracts). Here is the updated doc in the pull request:

What do you think?

I like the addition of the event. Also great thinking on the timing.

What do you think of replacing mentions of “staked” to “held”. I think held is more broad, and in the use case I will use it for NFTs are not really staked. You are already using “hold” for the event and method so it seems appropriate.

1 Like

Good point, thanks.
I updated the EIP, leaving staked only where it was specifically talking about staking pools.

Bit of bikeshedding, but I’d recommend something like RightsHolderChange instead of HolderOfRightsFor for the event, to match the naming convention of other events in ERC-20.

1 Like

Good point.
Consistently, the function would sound better as rightsHolderOf instead of holderOfRightsFor.
I made a change in the PR at

I think we may move to review. Any opinion about it?

Working on an NFT lending plugin for the Cruna protocol, I realized that we have another scenario that we may address in this ERC.

Let’s assume that Bob owns an NFT called jBall that can be used to play a game called jBaller.

Bob stakes the NFT in a pool contract, which

emit Transfer(bobAddress, poolAddress, tokenId);
emit RightsHolderChange(jBallAddress, tokenId, bobAddress); 

Now, let’s imagine that Alice wants to borrow Bob’s jBall.
The owner of the token is the pool, but the pool may

emit UsageRightsHolderChange(jBallAddress, tokenId, aliceAddress);

If jBaller listen to the UsageRightsHolderChange event, of calls

usageRightsHolderOf(jBallAddress, tokenId)

they can let Alice play the game, while still Bob is the one that has rights to airdrops and the pool, having the ownership, can manage the transfer.

The logic would change this way:

If a Transfer event is emitted and no rights’ event is emitted at the same time, the receiver of the NFT holds all the rights.

If only a RightsHolderChange event is emitted, the holder has rights on ownership and usage of the NFT.
If only a UsageRightsHolderChange event is emitted, the holder has only rights to use the NFT, but the owner of the NFT keeps any other right.
If both are emitted, we have the scenario described above.

Does it make sense?

I realized that the proposal can keep track of the two modes (rights over ownership and rights of usage) adding a parameter for the rights type. This also allows to add more rights types in the future, if needed, without altering the interface.

interface IERC7531 {

    enum Rights {

    event RightsHolderChange(address indexed tokenAddress, uint256 indexed tokenId, address indexed holder, Rights rights);

    function rightsHolderOf(
        address tokenAddress,
        uint256 tokenId,
        Rights rights
    ) external view returns (address);


I made a PR to address that change at

Hey @sullof, we are resuming our AllERCDevs, wanna join next time?

Hi authors of ERC-7531, this is Victor, an EIP editor and current operator of AllERCDevs.

I like to invite you to our next AllERCDevs meeting (online) to present for 10min of your ERCs if you are interested!

AllERCDevs is a bi-weekly meeting for ERC authors, builders and editors to meet and help the drafting and adoption of an ERC. The next one is 2024-03-21 UTC 1600, let us know if this time works for you, I can put this ERC in the agenda, or you can add a response directly at 2024 1st AllERCDevs Agenda Thursday (EMEA friendly time) · Issue #17 · ercref/AllERCDevs · GitHub

1 Like

I realized that in most cases the contract receiving the token will emit more events at the same time, like

emit RightsHolderChange(tokenAddress, tokenId, ownershipHolder, 0x399d2b36);
emit RightsHolderChange(tokenAddress, tokenId, usageHolder, 0x230a5961);

It would be more efficient to emit just

emit RightsHolderChange(
  [ownershipHolder, usageHolder], 
  [0x399d2b36, 0x230a5961]

and this becomes even more relevant if the token/pool adopts more rights.

What do you think?

Never mind, I realized that, counterintuitively, the second will cost more gas and, most important, won’t allow to index the holders.