FINAL EIP-5192 - Minimal Soulbound NFTs

Recording of PEEPanEIP #89: EIP-5192: Minimal Soulbound NFTs with @TimDaub

2 Likes

Is there some sample code?

yeah here FINAL EIP-5192 - Minimal Soulbound NFTs - #14 by TimDaub

Thank you.
Is this completed?
I can’t understand how the Lock function works.

1 Like

That implementation does not follow the spec because locked doesn’t throw when passing a tokenId assigned to the zero address, or am i missing something?

Your code:

/*
 *  EIP-5192 Functions
 */
function locked(uint256 tokenId) external view returns (bool) {
  return true;
}

The spec:

/// @notice Returns the locking status of an Soulbound Token
/// @dev SBTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param tokenId The identifier for an SBT.
function locked(uint256 tokenId) external view returns (bool);
1 Like

you are correct it should throw

1 Like

I am looking for something such as this standard, but for 1155’s as well. Couldn’t this be achieved? Soulbound 1155’s (with fungibility)?

EDIT: this is the closest I have found EIP-5727: Semi-Fungible Soulbound Token - #11 by 0xTimepunk

I submitted an EIP that is the same simple interface EIP-5192. Please take a look.

1 Like

I just posted a similar solution (Minimalistic non-transferable interface - #4 by sullof) and discovered this one thanks to a comment. The issue with proposal 5192 is that it assumes that there is a specific point at which the token switches from being transferable to non-transferable. In my work in the gaming industry, while it is necessary to know if a token is locked or not, the reason for this lock is often dependent on other contracts. There is no event that switches the status of the NFT, but the NFT can be locked for various reasons such as the owner having a locked balance, only a certain number of tokens being unlocked at the same time, or the owner having to own a second specific token in order to unlock the first. In all these cases, the NFT is monitoring other contracts and determining if it is transferable or not based on their status. If we were to implement a system where the NFT switches its transferability through direct transactions in a game, it would happen dozens of times a day and would be unsustainable. I believe that the token should simply return its status without emitting any events.

Interesting, use case!

If you don’t want to emit events every time, it can make sense that you implement EIP-5192’s function locked(), and then upon status changes, you don’t emit the Locked and Unlock events. It’s not ideal but at least you have partial standard support then. In that case, although you’d have to notify indexers, at least all wallets etc. could check if your token was locked by calling the function locked.

1 Like

Maybe I wrongly assumed that your proposal was requiring the emission of events at status change.
It looks like it is not Add EIP-5192 - Minimal Soulbound NFTs by TimDaub · Pull Request #5192 · ethereum/EIPs · GitHub
Actually, if it just is

interface IERC5192 {
  function locked(uint256 tokenId) external view returns (bool);
}

it looks good to me. I will remove mine and will support this.
It will force me to find a new naming for ERC721Lockable but it is ok :smiley:

Never mind, I realized that was an old implementation and the new one is here

Exchanges listen to events to update their databases. That means that OpenSea is not going to call the locked view, most likely it will assume that if no event has been emitted, the status has not changed. For this reason I suggest that the events MUST be emitted if someone implements the interface.

  /// @notice Emitted when the locking status is changed to locked.
  /// @dev If a token is minted and the status is locked, this event MUST be emitted.
  /// @param tokenId The identifier for a token.
  event Locked(uint256 tokenId);

Leaving the implementer the freedom of deciding if emitting an event or not risks to create confusion.

1 Like

Just for confirmation, the actual valid specification is always here (and it won’t change anymore because it has been marked “final”): EIP-5192: Minimal Soulbound NFTs

Exchanges listen to events to update their databases. That means that OpenSea is not going to call the locked view, most likely it will assume that if no event has been emitted, the status has not changed. For this reason I suggest that the events MUST be emitted if someone implements the interface.

Yes, sadly, true. Would you mind expanding on why sometimes emitting the Locked and Unlocked events aren’t in your interest? I understand that it’d increase your gas costs to emit those events. But e.g., for an indexer, I honestly see no better way than working with events and emitting them on every state change.

If a transaction causes a change in status, it may make sense to emit an event. In games, things are more dynamic. A token may be transferable or not depending on what happens in the game. For example, a token can only be transferred if the owner has enough rewards in the game’s ERC20 token or if they also own another token. Additionally, only one token can be transferred at a time if the owner has more assets in the same family. These issues are often resolved by staking tokens in a pool and giving up ownership. However, the current trend is to allow the owner to keep ownership while locking the token in different ways. In this case, the feature that is most affected is the transferability. I believe that emitting events makes sense with Soulbound NFTs, but not with NFTs used in games or other dynamic environments. That is why I suggest a minimalistic interface that only tells the caller if the token is transferable or not, ignoring the nature of the token. In my opinion, a Soulbound token is just one example of a non-transferable token and we should not create a standard for this sub-case. Of course, this is just my opinion and I may not be correct.

1 Like

That is true as long as the indexer can count on the event, i.e., if the event is mandatory.
However, if the standard does not require events and just exposes a view, an exchange is forced to call the view. If not all the tokens emit the events, of course, the exchange is anyway forced to call the view, but that would be a failure.

I’ve been working on a project called ERC721Lockable. The goal is to allow individuals to stake their NFTs in a pool without losing ownership of the NFTs. I created it for a specific use case in the game Everdragons2, where players can stake their dragons to earn rewards, but still maintain the ability to vote on the dragon as a governance token. I’ve recently updated the project to extend IERC5192. I would appreciate your input on the project, particularly on the naming. If it makes sense for you, you can also list it as an implementation of your ERC in a scenario different than Soulbound tokens.

I apologize for joining this discussion at such a late stage. However, as the issue has not yet been finalized, I would like to propose a solution to the problem of lockable tokens that cannot emit events.

If we were to have two separate interfaces:

interface IERC5192 {
  function locked(uint tokenId) external view returns(bool);
}

and

interface IERC5192Extended is IERC5192 {
  // events MUST be emitted
  event Locked(uint tokenId);
  event Unlocked(uint tokenId);
}

This would solve the problem at hand. A contract that cannot emit events would implement the first interface, while a contract that can do so would implement the second. The main benefit of this approach is that in the second interface, the emission of events can be made mandatory, which would resolve the dilemma faced by marketplaces (not being able to trust the events, if they “should” be emitted, but may not).

From the EIP’s Security Concerns section: There are no security considerations related directly to the implementation of this standard.

It might be worth mentioning some critical, relevant security concerns in the EIP. For instance:

  • The contract should have proper access controls to ensure that only authorized parties (e.g. the contract owner) can lock or unlock tokens.
  • Care should be taken to ensure that the locking function cannot be called multiple times on the same token, as this could potentially lead to the token being permanently locked by an attacker.
  • The contract should have a mechanism to handle emergency situations, such as a way to unlock the token in case the original owner loses access to their account.
  • It’s important to also consider the security of the EIP-165 and EIP-721 that this EIP is based on.

I get that these mostly aren’t “directly” related to the functionality of the extension, but the extension only works if there’s a locking function added to the NFT contract. That locking function is probably getting written at the same time as this extension is getting implemented, so it might be worth calling out the key security concerns.

1 Like

I wrote a blog post about why Lockable Tokens are needed.

The Red Whale that Killed the Ocean DAO