EIP-4671: Non-tradable Token


Haha I really like that application ! :smiley:

The standard itself doesn’t really need to describe how to “mint” a token. You can take a look at ERC20, ERC721 for reference. But implementations of the standard will contain that function. Ideally people don’t implement directly the standard but extend from an existing implementation (like the ones of OpenZeppelin). I made an implementation myself: Non-Tradable-Token/contracts at master · OmarAflak/Non-Tradable-Token · GitHub

You can check the EIPCreatorBadge to see what you would actually need to do to create a NTT: Non-Tradable-Token/EIPCreatorBadge.sol at master · OmarAflak/Non-Tradable-Token · GitHub

I’ll read your EIP idea and get back to you, I wanted to answer the first part of your message first :smiley:

I think returning a string should cover the majority of use cases.

Of course, there also should be setAttr(string attr_name, string attr_value)

and maybe getAttrs() (if there are no attributes/metadata it return a empty array)

In case someone need to store the hash of the NTT, he can use this funcionality…

Another use case: Identifying and marking synthetic EOAs

Synthetic EOAs are EOAs whose public address has been derived by computing a single TX and without the private key via the method described here (see proffered solution 1).


And it is also possible, if you know the address, digest and value of “c”, for a smart contract to know if an EOA is synthetic (i.e. if it was created with the above method).


Without EIP-4671, you’d need to run this computation each time you needed to check if the EOA is synthetic. This is relatively gas-expensive and gas-variable because you may need to cycle through a few values of r and run ecrecover multiple times. Not great.

With EIP-4671, a NTT could be created that checks if an account is synthetic, then issues the EOA a token identifying it as such. Now any smart contract wishing to identify synthetic EOAs and coded to assume the presence (or absence) of this NTT can simply check the address in question for the NTT.

Three benefits:

  1. Synthetic-ness can now be checked w/o the digest and “c”
  2. Less gas usage
  3. Predictable gas usage

As I said before, the uses cases for this EIP will be myriad and impactful. IMO, it should be finalized and adopted ASAP.

This is really cool @omaraflak! I’ve been interested in this idea of NTTs and been working on some proposals for it that I just published: https://github.com/ethereum/EIPs/issues/1238#issuecomment-1029055365

As mentioned, I think this EIP is a duplicate of EIP-1238 but let’s combine our efforts? :slightly_smiling_face:

@omaraflak I love this concept, and find it very relevant.

I see instances where it would make sense to make this pull-oriented and push-oriented. Curious if you’re envisioning the implementation of both scenarios?

Also I see this potentially (eventually) tying into Self Sovereign Identity (SSI) and Decentralized ID (DID) use cases. Not sure if this has been considered.

Hi @saginawj, I’m not sure what you mean by pull/push oriented. Could you expand a bit ? :slight_smile:

What about options for revoking when minting, and a corresponding revoke function.

  • function mint(address owner, bool revokable) 
  • function mint(address owner, uint256 expiryDate) 
  • function revoke(uint256 tokenId)  external

I think there are no situations where the token should not be revokable. A certificate can always be delivered by mistake, and the authority who delivers should be able to revoke it. Although the record should show that you once owned a token from that contract, so it must not “disappear”.

This is why each token has a isValid(tokenId) returns (bool) property. But I didn’t specify how the token should be invalidated (or revoked) because it seems as an implementation detail. It’s more general to have a boolean that states if the token is valid or not. Different implementation could choose how exactly to invalidate the token, e.g. based on an expiry date as you suggested, or anything else.

1 Like

@calvbore I talked to people from Ethereum about this particular subject. I ended up implementing this idea of pulling the token by providing a signed message from the owner :slight_smile:


Yes sorry- I envision both self-mints and airdrops being relevant use cases. So I’m just curious if there’s an expectation to assign a token that can’t be traded, but maybe can be burned. For example, maybe a professor assigns a NTT to a student. The student can’t sell/trade it, but can burn.

I feel that it depends how you see these NTT in general.

If you want to burn a token it means you’re not happy that it’s been assigned to you publicly. For instance if it’s a criminal record (even though I don’t think this would ever be on the blockchain). There is no good solution:

1- If you allow people to burn tokens, then it doesn’t fit the use-case of the criminal record example or any other where you’re labeled against your will (which is a good thing I think, we don’t want to end up with a social score on the blockchain…).
2- If you don’t allow to burn tokens, you can be assigned tokens from random contracts you might not want to be associated with.

I think the way to see this is that non-tradable tokens are not meant to represent anything too sensitive that you don’t want to expose. They’re only a proof of (good) achievement(s). Something that you’re willing to display publicly. And the importance tied to a NTT should come from the authority that delivered it. If some random guy gives you a jail-token, it shouldn’t matter because that person is nobody. Also, this is why the standard outlines a NTT Store, which is a way for you to expose in one place a subset (possibly all) of our tokens, and share them with an external service.

The Non-Tradable Token standard has been merged as a draft!

I’m planning on changing a few things. Mainly:

  • Currently the NTT Store allows you to make a display/collection of NTTs coming from different contracts but that only belong to one address. I think in practice people will have their tokens delivered to different addresses. So I want to make it possible to build that collection from multiple sources addresses.

  • I want to add a hash of the data associated to tokenURI

  • (to be discussed) I want to add a key/value store to a token contract. The hash could be one of the keys…

I think it’d be better if EIP-4671 referenced ERC721’s and implemented its interface. I also gave soulbound tokens a shot, and I think that’s the better approach as it’d allow ERC721 wallets to be instantly compatible with Non-tradable tokens.

I’ve outlined that idea here: EIPs/eip-4966.md at 9754700b44b8e63d5503b563d16c99a2552b8c2e · ethereum/EIPs · GitHub

Thanks for the prompt Tim! I think that’s a good idea. I’ll probably update Metadata as well as Enumerable :slight_smile:

What is being defined in ERC-4671 is beyond the scope of what a standardized Solidity interface can guarantee. For me, I’d have no issue just standardizing the “Non-Tradable Token” as defined in the “Specification” section.

However, I think most of the extension code isn’t useful to have in an EIP. Ultimately, we’d like machines to be able to rely on an interface that doesn’t change and where we can safely expect a reasonable behavior implemented below. For E.g. it’d be outrageous if ERC20's transfer function would do something else but transfer tokens. However, with e.g. IERC4671Consensus I doubt every other implementer is capable of replicating a compatible behavior or usefulness from this interface definition.

My suggestion is simply to standardize the first “Non-Tradable Token” section.

1 Like

Untradable and absolutely bound to the wallet is based on the assumption that one person, one wallet. In fact, wallets will be changed for various reasons, such as private key leaks, and I think it should be transferable, limited to non-exchangeable tokens, and better only to be sent. But this led to a new problem, the private key was leaked and my “graduate certificate” was transferred.

To simulate a graduation certificate, can we change our thinking, keep the graduation certificate information on the contract address, and use it to prove that it belongs to me by binding the query word or query code. Or use the connection wallet address to query.In this way, even if my private key is leaked, the tokens in it can be transferred while keeping “my diploma”. The graduation certificate should contain all relevant information, such as name and ID card. Key privacy information such as ID card number can be verified by password. The password is similar to the private key and needs to be recorded by the owner.

@TimDaub thanks for making the call for compatibility.

So far I am aware of #ERC-1238#ERC4671#ERC-4966 which all provide different perspectives to the implementation of NTTs or Soulbound Tokens.

However I feel like the core features of the ERC-721 should be baked in


So that we can use existing tooling that can recognize + index NFT tokens to display NTT to users

1 Like

Hi guys!
@omaraflak, thkx for great work on this EIP.

I am seeing potential in this and find this great point to jump in this magical forum to join the conversation

Proposal for this standard:

Replace isValid/hasValid and revoke() with burn()


  • Structure is similar to ERC721Burnable without transfer methods, meaning less adaption friction for the standard.
  • Checking ownership of the token becomes easier (checking side can just make sure that token is not listed in enumeration).
  • Token standard heads toward Non-Tradability, however one should not put limitations on ability to non-tradable transfer scenario. Which burn is - I assume it is true.
  • Revoking and re-issuing token takes form mint-burn-mint which is convenient approach which many developers use already today
  • More secure. Token could be flash-toggled and contracts relying on this must assume that state of the token can toggle multiple times over a block. Resulting tradability of token state. In Mint/Burn scenario checking token ownership by id allows to rely on that state can change only once - to burn.

Also what is purpose use of holdersCount/emittedCount and why Enumerable does not has totalSupply() interface?

Thats my first post on the forum, if this discussion fits more in github or elsewhere just let me know,

1 Like

Also I started EIP-1753: Smart Contract Interface for Licences. We should all work together!

Checkout eip-1753 too :slight_smile: