Minimalistic transferable interface

Since an ERC20 is fungible, it can be either transferable or not transferable, if this interface would be applied.

If that is not the case, for example a token can be vested and there can be cases where the owner cannot transfer it or the recipient cannot receive it, this interface doesn’t work.
We need a more complex function, like the one I proposed a few comments above:

function isTransferable(
  address from, 
  address to, 
  uint tokenIdOrAmount) 
external view returns(bool);

This would support also ERC20 and any other asset that is transferable between two addresses. The problem here is if to keep the interface minimalistic and simple, or make it capable of managing any possible scenario. It looks like the tendency is towards the first case.

Cool. Thanks for the feedback. That’s what I use that in my protocols, I’ll try to push it forwards a bit.

as for a simple solution for finding out if a token is transferable, I think it should be enough for us all to agree on an error message (like ‘Token-Not-transferable’) in case it’s soulbound. Then if a contract doesn’t have a transfer function, or returns such an error, we can deduce it’s soulbound. Seems simpler and more lightweight.

Honestly, NFT standards only leave you about 18kb to work with. I would have a hard time supporting any additions to them. It seems to make much more sense to just remove the transfer functions. Wouldn’t that serve the same purpose with less code?

If the token is non-transferable, despite if we agree or not on a specific error, that transaction will revert consuming at least 40000 gas. It is better to allow a marketplace to check that before calling it. If not, we don’t really solve the issue.

From my experience if transactions are going to fail metamask will tell you beforehand. You can also simulate the transaction before sending it to the chain to check if it might fail and that’s free.

Maybe you can expand more about the specific situation where a revert incur a cost?

1 Like

I think that that is exactly what MetaMask does. But it is a work around in absence of a clear solution that is valid for everyone and everyone can verify. Anyway, I got your point and it is valid.

Gotcha. Thanks, and good luck! :pray:

1 Like

I am working on a new project where, for security reasons, the tokens are transferable only by the owner and cannot be approved without the owner make the contract approvable.

So, I am thinking that it could make sense to have a just-a-bit-less-minimalistic interface like

interface IERCxxx {
  function isTransferable(uint tokenId) external view returns (bool);
  function isApprovable(uint tokenId) external view returns (bool);
}

Of course, we may suggest a new interface only for the approvability, but any token who would implement both, would spend more gas because it has to check the two interfaceId.

What do you think?

I’m strongly against it, it defeats the purpose of minimalism. I have created 4-5 use cases using the minimal version and in non of them there was approvable in any way.

I would do a different EIP for that. It’s true implementers of both will spend a bit more gas on the check (unless you use a mapping, in which case the extra gas is only paid once, and by the issuer). The alternative is to force all implementers of only soulbound to have an extra unused method, it’s a clear choice IMO.

I’m feeling conflicted. While I agree with the principle, I’m currently working on several projects related to escrows and secure vaults, and in none of them can I use the IERC6454 function. By default, the tokens in these projects are non-tradeable for security reasons, which means they can’t be approved. The scenario where you only need to check the transferability of a token and don’t have to worry about its approvability seems rare.

A token that is non-transferable can either be approved for future transferability or cannot be approved at all. As an exchange, it’s challenging to decide whether to try to approve the spending or not. The easy solution is to have a second interface that checks only for approvability, but I don’t find it exciting.
I believe that whoever wrote the ERC721 proposal overlooked these two views. They should have been there from the beginning.

Anyway, I have implemented an IERC721Approvable interface inside the protector-protected-protocol. I wonder if it would make sense to make a standard proposal of it.

I think I will start a new discussion about it.

In eip6147, by querying the guard information, it is already possible to know whether the token can be transferred.

1 Like

Nice proposal. But it would make no sense for someone to implement it without implementing the entire protocol. Here (as in ERC6454) we are talking about the minimal effort necessary to let an exchange know it the token is transferable or not.

I guess you’re biased here by your projects. Let me give you few simple cases where this makes sense on it’s own:

  1. Reputational NFTs: They’re bound to an address, no need for approval. We have a version with Social Recovery where holders can vote to help someone recover their reputation NFT after lost keys. We could force approvals there, but it would be really forced as the approval is not by one party but by the community together.
  2. Nested Non-transferable NFTs: We introduced Nestable tokens in ERC-6059. We have quite a few collections where once you mint and NFT into another, it becomes attached to it for good. No approval needed at all. This has many use cases in gaming.
  3. Non-transferable depending on context: I have a project where you can transfer an NFT out depending on the balance of an specific ERC20 token. An approve scheme is not practical there, it depends on the balance at the time of execution.

This can of course be any other condition, like number of certain emojis on token (introduced in ERC-6381), non-transferable after certain number of blocks or after certain number of transfers, and I’m sure many others we’re not seeing just yet. So I insist we should not limit them.

I agree with you on the points you list. What I realized is that we are restricting the field of application, while, ideally, we want to have an interface that is applicable in most cases, i.e., that covers what is missed in the original ERC721 proposal.
Thus said, I will stick with the minimalistic approach, because as you say makes sense in many scenarios, while I will propose an interface for the approvability. Whoever needs both, can implement both.

1 Like

This is rather late to the game, but negated booleans are really annoying for programmers. I’d recommend using isTransferable instead of isNonTransferable.

1 Like

Thank you for the note @SamWilsn! While we agree that negated booleans are annoying, we feel that, in this case, you are actually checking if the token is non-transferrable (as this is the focus of the proposal), so we feel that this better represents the state of the token. Much like we explained in the rationale of the proposal:

ERC-721 tokens are usually transferable, but this interface focuses on the use case where NFTs may not be transferable. The method name was chosen to reflect this.

I’m just thinking usability here, and trying to avoid philosophic discussion. For example, I find this much more non-understandable:

if (!someContract.isNonTransferable()) {
    // <code that renders the transfer UI elements>
}
1 Like

Personally, I agree that this would be better.

From what we’ve experienced, the most common use case is to check if the token is non-transferrable and revert if it is, so something like:

if (someContract.isNonTransferrable(tokenId)) {
    revert TokenIsNonTransferrable();
}

Won’t every wallet ever have to check if the token is transferable to know whether or not to display the UI for doing so?