EIP-6047: Extend ERC-721 to Support balance counting via Transfer event

Thanks @frangio

Define “compatible”

There is no way to retroactively add a “MUST” requirement to EIP-721, so it could never be anything more than a recommendation. The Backwards Compatibility section is inaccurately saying that this is fully backwards compatible… it clearly isn’t.

I have a feeling when we use the term EIP Foo is compatible with EIP Bar we mean different things.

Let me clarify what I mean when using this term with you to ensure we are talking the same language in this discussion so then we can determine if we have different views or not.

In the context of EIP-6047there could be other case when we used the term differently, When I say EIP-6047 (Mandate ERC721 Transfer) is Compatible with EIP-721, what I really mean is that any compliant contract confirming to EIP-6047 will also be a compliant contract with EIP-721. Or more mathematically described as:

The whole set of EIP-6047 compliant contracts is a (strict) subset of the whole set of EIP-721

Let me use other examples to demonstrate this relationship

  1. EIP-712 (Typed Data Signing) is compatible with EIP-191 (General Signing) because the whole set of EIP-712 compliant contracts is a (strict) subset of EIP-191. Contract compliant with EIP-712 is always compliant with EIP-191 but not all contract compliant with EIP-191 is a EIP-712 compliant contract
  2. EIP-2612 is compatible with EIP-20 because the whole set of EIP-2612 compliant contract is a (strict) subset of EIP-20 but not all EIP-20 compliant contract is compliant with EIP-2612.

With that clarification let me address your second comment about EIP-2309

That’s good suggestion. Some considerations here.

Supporting EIP-2309-alike interface is a good idea. But it seems EIP-2309 cannot be supported as of its current version. Since it’s final, a new (competing) EIP probably need to be proposed due to EIP-2309’s incompatibility with EIP-721, let me explain why I say EIP-2309 is not compatible with EIP-721

Specification of EIP-2309

Contracts that implement the ConsecutiveTransfer event MAY still use the original Transfer event, however when emitting the ConsecutiveTransfer event the Transfer event MUST NOT be emitted.

If EIP-721 is intended to mandate*see note a Transfer event

Specification of EIP-721
(Transfer event): This emits when ownership of any NFT changes by any mechanism. Exception…

The mandate to emit ConsecutiveTransfer instead of Transfer in some cases from EIP-2309 actually breaks the mandate of EIP-721. EIP-721 mandates a Transfer event MUST be emitted. EIP-2309 mandates a Transfer event MUST NOT be emitted in some cases but instead use ConsecutiveTransfer in these cases.

Any compliant contract of EIP-2309 when not always emitting Transfer event but emitting ConsecutiveTransfer, is not conforming to EIP-721. Therefore the whole set of conforming contracts of EIP-2309 is not subset of the whole set of conforming contracts of EIP-721.

If you could agree with the assertion that EIP-2309 is not EIP-721 compatible, while EIP-2309 could not be supported, we shall probably support something like EIP-1155’s TransferBatch event.

Note for @fulldecent

Hey @fulldecent could you clarify whether it’s mandated by EIP-721 to emit a Transfer event always except for creation? . It seems EIP-721’s current snapshot is not strictly following RFC 2119 such as “Every ERC-721 compliant contract must (lower case must) implement the ERC721 and ERC165 interfaces”

A contract that emits EIP-2309 ConsecutiveTransfer in the constructor instead of EIP-721 Transfer, is both EIP-721 and EIP-2309 compliant. In order to remain EIP-721 compliant it must not emit ConsecutiveTransfer events outside of the constructor (unless they are accompanied by Transfer events, in which case there is no reason to just emit Transfer).

1 Like

@frangio Just to clarify, do you mean, in your opinion, EIP-721 doesnt mandate Transfer event?

EIP-721 doesn’t mandate Transfer during contract creation, as clearly stated in the present EIP draft.

Maybe this is better way to frame it: Consider IP-6047 extends EIP-721 to make token balance totally accountable merely via counting Transfer events, just like EIP-1155.

I agree with the goal of making token balance totally accountable by events, but I think this EIP should allow EIP-2309 ConsecutiveTransfer as an alternative to EIP-721 Transfer during contract construction. If you think this defeats the purpose of the EIP you were trying to propose then feel free to ignore my suggestion.

I agree with the new title for the EIP. The contents of this post should be updated as well since they still say “Mandate … for EIP-721”.

@frangio
Great, we agree on the general goal of making token balance totally accountable by events. :heart:

Let’s debate one of the design decision here

Design Choices for which “transfer event” to use for batching

  • Option 1. Use EIP-2309 ConsecutiveTransfer instead of Transfer when transferring multiple tokens with consecutive ids, and use Transfer otherwise.
  • Option 1a, [See update]Use ConsecutiveTransfer in addition to Transfers
  • Option 1b, [See update]Use ConsecutiveTransfer only at contract creation.
  • Option 2. Use ERC-721 Transfer for all cases, emit Transfer event multiple times for multiple token ids, regardless of consecutive token ids.
  • Option 3. use something like TransferBatch with unit256[] tokenIds that allows specifying uint256 tokenIds. (similar to EIP-1155)

My thoughts

Here is my thoughts.

First, let me re-iterate this and make sure if we are on the same page:

Both Option 1 and Option 3 are NOT backward compatible with ERC-721, because let’s say Coinbase, OpenSea and Etherscan are already watching Transfer of ERC721 hoping to learn all transfers and do something upon a Transfer. Now, if a new contract start adopting Option 1 or Option 2, start using a transfer event different from what’s already in ERC-721. In this case, without updating the logic Coinbase, OpenSea or Etherscan will none be able adopt to Option 1 or Option 2. This is why I say Option 1 and Option 3 are not backward compatible.

On the other-hand, for any new contract using Option 2, despite being gas-inefficient, is backward compatible with ERC721: that is to say, Coinbase, OpenSea or Etherscan or Subgraphs of TheGraphProtocol will not need to make change to their logic to also watch for a new transfer event with different name. Their old logic will still work.

Let me know if I explain myself clearly :slight_smile: If my explanation still lacks clarity, I am happy to write some reference implementation code of both contract and indexer/tests to demonstrate what I mean.


Update after more research.

Context:

  • EIP-2309 admits it’s not compatible with ERC-721 and suggest either using both ERC-721 Transfer

For platforms that wish to support the ConsecutiveTransfer event it would be best to support both the original Transfer event and the ConsecutiveTransfer event to track token ownership.

We hereby have two other new options as variant of Option 1:

  • Option 1a, Conforming contract to use ConsecutiveTransfer in addition to
    Transfers, this defeats the purpose of gas efficiency and adds more gas cost. and this is incompatible with When emitting the ConsecutiveTransfer event the Transfer event MUST NOT be emitted in EIP-2309
  • Option 1b, Conforming contract to use ConsecutiveTransfer only at contract creation. This is limited use-case and also it makes it impossible to support indexers who only watches Transfer, defeating purpose of maximizing compatibility with ERC-721s.

Also, I have doubt of how much gas cost EIP-2309 really saves overtime for Option 1.b. Here is why:

While one transfer will change gas cost from O(N) to O(1) with N = number of tokenIds, If one address is going to receive a large amount of tokens, my unverified probably uneducated hypothesis is that such batch holding is for the purpose of distributing the NFTs down the road. Since they will only distribute tokens one by one given EIP-721 doesn’t support batchTransfer, if we only use ConsecutiveTransfer for creation, when the holding is doing secondary distribution, the long term gas cost is still cost of O(N). Therefore the overall gas saving, if only adopting at creation time, is very limited.

Therefore, so far I find Option 2 still the most preferred design option for this decision question.

Yes, I should clarify, I’m aware that full EIP-2309 is incompatible with EIP-721. This is why we have only implemented the subset of EIP-2309 that can be made compliant: Option 1b. This corresponds with OpenZeppelin’s ERC721Consecutive. It’s compliant because the only time that Transfer events are not emitted is during contract construction, as allowed by EIP-721.

Indexers that only watch Transfer will miss the initial batch mint. This is true, but developers can make an informed decision about this and choose that route. (In reality, it is those indexers that are making a mistake by assuming that all transfers will be logged in an event, which is not guaranteed by EIP-721.)

Yes, of course, but the goal is to reduce the cost of initial mint, i.e. the launch of a project. As you mentioned that is reduced from O(N) to O(1).

So my suggestion is to make this EIP recommend Option 1b.

1 Like

I see what you saying. We are in full sync now, @frangio
I now understand that you recommend Option 1b as preferred design choice in closing which event to emit.

With respect to my own opinion, I am open to choose Option 1b as recommendation. Since Option 2 will still be what people support out of box, I lean towards at least recommend Option 2, and consider also recommend Option 1b.

For whether to also support Option 1b, we could take input of opinion and views from major indexers. If they support / are committed to support Option 1b, I think we could also recommend Option 1b.

Do you have some recommendation who are also important ecosystem players that we shall also solicit feedback from?

I’m sure NFT marketplaces will have a lot of experience with indexing and may have opinions. I don’t know which particular ones.

I’m sure NFT marketplaces will have a lot of experience with indexing and may have opinions. I don’t know which particular ones.

Sounds good.

Thank you @frangio

Emitting ERC-2309 in the constructor of an ERC721 contract works quite well in my opinion. There are plenty of actual use cases of this in the wild. Major marketplaces already support this standard (opensea, looksrare, sudoswap). I don’t see the issue that this new standard will solve.

Thank you @pizzarob for the response.

Two questions

Major marketplaces already support this standard (opensea, looksrare, sudoswap)

Do you refer to their indexing side? Or do you only referring to their contracts? I love to do more research on these, can you kindly share some point of reference?

I don’t see the issue that this new standard will solve.

Which issue, could you ellaborate?

The way I see it this ERC would simply be documenting standard best practice given that EIP-721 by itself technically allows the bad practice of not emitting events in the constructor.

2 Likes

Hi. I am referring to the indexing side. I’m sure querying the chain for ERC-2309 events would yield a lot of results. That being said I’m not sure this standard you are proposing would fix anything. It would be a new standard, yes, with a new rule, but people are already emitting 2309 in the constructor and it will remain valid under ERC-721. I don’t think people will stop emitting the event if they have a use case where they need to mint many NFTs to one or several wallets (e.g. team mint). In addition, 2309 is already indexed by major marketplaces. So this new standard exists and then what? I’m not sure I’m sold that it’s a best standard to only emit ERC-721 transfer events in the constructor. The flexibility is nice and has proven use cases.

@pizzarob,
EIP-6047’s goal is to make balance of token account able by merely using event.
EIP-2309 introduces ConsecutiveTransfer but solves a different problem than EIP-6047 trying to solve.

I appreciate your feedback. I couldn’t find OpenSea documenting it’s indexing EIP-2309. If anyone could provide some links of documentation, that would be great. Or if someone could provide a public deployment of EIP-2309-based ERC721, that would also help me do t he research.

For indexers, we continue to ask for help if there is any documentation / feedback about your indexing of EIP-2309. Otherwise I think I so far still lean towards just choose Option 2 so all indexer will work out of box.

Here’s one https://etherscan.io/address/0x38930aae699c4cd99d1d794df9db41111b13092b#code - see line 87. It uses ERC721A

I’m not sure if there’s any docs about opensea/looksrare indexers but both have properly indexed the above collection and tokens 1 - 1000 were minted using consecutive transfer.

Thanks, that helps a lot.

Hi everyone. Just registered for this site to contribute to the discussion here (only get two links in the post). To briefly introduce: I just launched GigaMart, an NFT marketplace and had previously done some research into 2309 support. To summarize what I learned here:

  • I discovered 2309 when my indexer failed to detect most of the items minted in the GoonzSchoolPhotos https://etherscan.io/address/0x36e5913bf48ccbd361875d09c0a20a4c144da896 collection, which emitted ConsecutiveTransfer events in its minting process.

  • I’d previously viewed the fact that ERC-721 and ERC-1155 could not mint an NFT without emitting an event for each unique token or (in the case of ERC-1155) type of token as a sort of backstop against denial-of-service attacks. If someone wanted to flood my indexer with bogus NFTs, they would need to at least pay proportional gas to do so.

  • Honoring EIP-2309 without limits feels impossible: for the gas cost of a single event, a collection could signal that it is minting a full 2^256 - 1 items. Conventionally, my marketplace would then try to collect the metadata for each newly-minted token that it sees. To prevent the ConsecutiveTransfer event from being abused to fill my metadata collection queue with 2^256 - 1 pending calls, the only reasonable solution I see is for a marketplace to truncate overly-large token ID ranges.

  • The popular Chiru Labs implementation https://github.com/chiru-labs/ERC721A/issues/310 of ERC-721 has a limit of 5,000 on the size of an EIP-2309 range. That GoonzSchoolPhotos collection that brought this to my attention minted things in batches of 1,000. The OpenSea token ID range size limit is 5,000, determined empirically by Chiru Labs.

Guilty as charged; I’m one of those who wrongly believed this to be the case.

Yes, this is something that marketplaces need to be aware of and support already given that some contracts already use it in the wild.

Neither could I when I went looking; the limit that they respect for 2309 is 5,000 just determined via testing. I think it’s incredibly unfortunate that 2309 itself doesn’t dictate any kind of limit on the number of tokens supported; 5,000 is an arbitrary and rather small range when compared to the “typical” drop of 10,000 in an NFT collection.

TL;DR I did a lot of recent research into supporting the situation caused by EIP-2309, am generally dissatisfied with it as an EIP, agree with @frangio on most of these points, and see option 1b as the only viable solution. That also means I see little value in this EIP beyond clarifying the 2309/721 relationship and maybe dictating a range limit on 2309.

2 Likes

@_Enoch thank you I think you bring up a very good point that ERC2309 without a proper limit provides an opportunity for flooding indexer’s account with very little gas cost.

The goal of this EIP-6047 is to mandate the event-emitting behavior for all EIP-6047 compliant contract. Like any EIP, this EIP will have no effect in any contract whose developer choose to not comply with this EIP.


Whether to require ConsecutiveTransfer or only require Transfer is a decision we need to make for EIP-6047.

I hear loud and clear from you that GigaMart supports ConsecutiveTransfer as well as Transfer and it’s one data point.

To re-iterate: we are choosing between the following options for 6047, and its consequence:

  • Option 2: Support Transfer: then all indexers (regardless of adopting 2309 or not) are compatible with 6047, but 2309 contracts are incompatible with 6047
  • Option 1b: Support Transfer and ConsecutiveTransfer: any indexers not adopting 2309 yet are incompatible with 6047, but all 2309 contracts are compatible with 6047