ethereum / EIPs

The Ethereum Improvement Proposal repository
https://eips.ethereum.org/
Creative Commons Zero v1.0 Universal
12.88k stars 5.27k forks source link

Discussion for EIP-2981: NFT Royalty Standard #2907

Closed VexyCats closed 2 years ago

VexyCats commented 4 years ago

TL;DR - ERC721s have a ton of creators and a few marketplaces, but no accepted means for transferring royalties from items being sold multiple times on secondary sales. This EIP is proposing a standard method that can be implemented by all marketplaces easily.

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC721Royalties {

    /**
    @notice This event is emitted when royalties are received.

    @dev The marketplace would call royaltiesRecieved() function so that the NFT contracts emits this event.

    @param creator The original creator of the NFT entitled to the royalties
    @param buyer The person buying the NFT on a secondary sale
    @param amount The amount being paid to the creator
  */
    event RecievedRoyalties (address indexed creator, address indexed buyer, uint256 indexed amount);

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

     /**
     * @dev Returns true if implemented
     * 
     * @dev this is how the marketplace can see if the contract has royalties, other than using the supportsInterface() call.
     */
    function hasRoyalties() external view returns (bool);

     /**
     * @dev Returns uint256 of the amount of percentage the royalty is set to. For example, if 1%, would return "1", if 50%, would return "50"
     * 
     * @dev Marketplaces would need to call this during the purchase function of their marketplace - and then implement the transfer of that amount on their end
     */
    function royaltyAmount() external view returns (uint256);

      /**
     * @dev Returns royalty amount as uint256 and address where royalties should go. 
     * 
     * @dev Marketplaces would need to call this during the purchase function of their marketplace - and then implement the transfer of that amount on their end
     */
    function royaltyInfo() external view returns (uint256, address);

      /**
     * @dev Called by the marketplace after the transfer of royalties has happened, so that the contract has a record 
     * @dev emits RecievedRoyalties event;
     * 
     * @param _creator The original creator of the NFT entitled to the royalties
     * @param _buyer The person buying the NFT on a secondary sale
     * @param _amount The amount being paid to the creator
     */
    function royaltiesRecieved(address _creator, address _buyer, uint256 _amount) external view;
}

Flow: (just suggestions, can be implemented however you like)

Constructor/deployment

Creator - the person who gets the royalties for secondary sales is set. Royalty Amount - the percentage amount that the creator gets on each sale, is set.

NFT sold on marketplace

Marketplace checks if the NFT being sold has royalties implemented - if so, call royaltyInfo() to get the amount and the creator's address.

Calculates the amount needed to be transferred and then executes that transfer.

Calls royaltiesRecieved() so that the NFT contract has a record of receiving the funds during a sale.


Thoughts? Anything that should be added or removed to make it easier to be implemented?

The logical code looks something like this:

abstract contract Royalties {
    /*
     * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
     */
    uint256 private royalty_amount;
    address private creator;

    /**
    @notice This event is emitted when royalties are transfered.

    @dev The marketplace would emit this event from their contracts. Or they would call royaltiesRecieved() function.

    @param creator The original creator of the NFT entitled to the royalties
    @param buyer The person buying the NFT on a secondary sale
    @param amount The amount being paid to the creator
  */

  event RecievedRoyalties (address indexed creator, address indexed buyer, uint256 indexed amount);
    constructor (uint256 _amount, address _creator) internal {
        royalty_amount = _amount;
        creator = _creator;
    }

    function hasRoyalties() public pure returns (bool) {
        return true;
    }

    function royaltyAmount() public view returns (uint256) {
        return royalty_amount;
    }

    function royaltyInfo() external view returns (uint256, address){
        return (royalty_amount, creator);
    }

    function royaltiesRecieved(address _creator, address _buyer, uint256 _amount) external {
        emit RecievedRoyalties(_creator, _buyer, _amount);
    }
}
wighawag commented 3 years ago

Thanks @seibelj for the update

I have some comments:

I am against the use of the term "NFT" as this standard can applies to tokens that are fungible which ERC1155 for exampele support. While NFT is a popular term, for the EIP to remain precise, we should use something more generic. I think "token" would work fine. We could still refers to NFT as the motivation for this standard though.

this sentence Implementers of this standard MUST NOT use the _data parameter and _royaltyPaymentData return value. These are intended for future EIPs that extend this ERC with additional features.

is in my opinion very confusing. This technically means extension would never be EIP-1981 compliant. We actually want extension to use the data fields and as I mentioned as part of my comment: https://github.com/ethereum/EIPs/issues/2907#issuecomment-840477931 , the way it is handled will be specified by the extension itself. No need to worry about it here.

Lenoftawa commented 3 years ago

For points 3 and 4 @lenifoti we cannot force implementation and that goes beyond the scope of the EIP anyways. How a marketplace chooses to deal with situations, is up to that contract and cannot be required.

Surely, that it what a standard does - constrain implementation to a strict protocol in order to allow interoperability between actors that have been build independently. We cannot force anyone to do anything, but we can define what is compliant behaviour, so that we don't get a bunch of incompatible implementations.

To me, the fact that @seibelj has already had to ask whether address 0x0 means 'burn it' or 'do nothing' demonstrates that this does need to be addressed #(https://github.com/ethereum/EIPs/issues/2907#issuecomment-838182234)

It sounds to me that you are suggesting that we opt out by saying that address 0x0 has undefined behaviour. If so, then let's say that. At least an NFT knows that it cannot make any assumptions about it (and therefore avoid using it).

Lenoftawa commented 3 years ago

this sentence 'Implementers of this standard MUST NOT use the _data parameter and _royaltyPaymentData return value. These are intended for future EIPs that extend this ERC with additional features.`

is in my opinion very confusing. This technically means extension would never be EIP-1981 compliant. We actually want extension to use the data fields and as I mentioned as part of my comment: #2907 (comment) , the way it is handled will be specified by the extension itself. No need to worry about it here.

We want to use these fields in an orderly way as part of a new standard, not this one. Until that standard exists, we do not want implementations using them.

Do you disagree with this? If not, then how would you word it?

VexyCats commented 3 years ago

@lenifoti if they want the payment burned, the royalty address can be 0x00.....001 or 0x000...000dead. But it depends on the marketplace still, I can write in my contract to take any 0x000....000XXX address and keep that payment for myself. So regardless of what is returned, the marketplace has the final say in this situation.

0x0 is the same as false in this sense, I think that would make most sense. Because it will be the default response for non-2981 contracts, so it needs to remain false

No issues changing NFT to tokens, not a big deal I think, so shouldn't be an issue to replace.

wighawag commented 3 years ago

We want to use these fields in an orderly way as part of a new standard, not this one. Until that standard exists, we do not want implementations using them.

Do you disagree with this? If not, then how would you word it?

I disagree. But more importantly if we keep that sentence, no extension will ever be compatible with EIP-2981

I would not say anything about its use. future extension will. If we really need to add something I would says something like

"the data fields can be used by future extension to give different meaning to royaltyAmount and royaltyReceiver"

seibelj commented 3 years ago

@wighawag

I am against the use of the term "NFT" as this standard can applies to tokens that are fungible which ERC1155 for exampele support. While NFT is a popular term, for the EIP to remain precise, we should use something more generic. I think "token" would work fine. We could still refers to NFT as the motivation for this standard though.

Strong disagree. This EIP was funded by the Ethereum foundation for the purpose of making a standard for NFT royalty payments. NFTs are primarily known for 721 although increasingly more supported by 1155. We need people able to google "NFT royalties" and find this. I'm happy to add "NFT and Token" to the title but NFTs are certainly the primary focus of this EIP and the problem we have been trying to solve, not a generic "Royalties for Everything" standard - that was a nice side effect but not the primary concern.

  1. To pick up on @cliffhall's concern, I think this is very misleading: " this EIP does not mandate a fixed-percentage fee model." Without an agreement on a unit of exchange, anything other than a fixed percentage is meaningless. The simplest case that demonstrates this is a percentage that reduces at 100 units and 1000 units.

    The reader of the spec will not have read the 250 or so messages in this thread, and if they did would still have difficulty understanding the issue surrounding percentages. It is partially addressed in the text, but leaves it up to the reader to work it out from first principles. We need to spell it out.

Someone who wants to meaningfully contribute to this EIP should be reading everything as we have discussed so many issues. I don't think spending a few hours reading comments to get the full context is a big ask considering the combined hundreds of hours we have all spent so far.

  • This EIP does not specify a unit of exchange or a way to negotiate it, that is for a future EIP. Therefore ...
  • Under this specification, returning something that is not a fixed percentage will result in royalties being underpaid or overpaid. The interface is designed to allow more flexible royalties to be specified in future extension EIPS.

@lenifoti

  1. We do not specify what 'unused' means. There may be a standard, but I cannot find one. If there is a standard, then we should reference it. I have been experimenting with passing various values in the _data field and it is difficult to find something that is unequivocal. This function could be called from a contract in Vyper or even in EVM byte code.

It's the same as in the C programming language - it's undefined behavior. Don't use it don't read it. If you do, anything can happen. I can change the language from "unused" to "undefined" if you prefer.

  1. We do not specify what should happen if the NFT returns 0x0 for _receiver. The fact that this has been raised in this thread is an indication that it should be documented. If I were a marketplace, I would be inclined to keep the royalty rather than burn it! If I were an NFT that wanted to burn it, I would give my own address and burn it myself, to prevent this.

If the NFT contract creator returns > 0 fee and says it should go to address 0x0, IMO it should be burned. But the marketplace will decide. I don't think spelling this edge case out in the EIP is necessarily a good thing, as it could result in restricting innovation by removing burn mechanics intended by the creator. But wasting money is also controversial. So I lean towards not specifying this.

  1. We do not specify what should happen if the NFT returns 0 for _value. It may be obvious to us that the marketplace should do nothing, but it is quite possible that someone will rely on marketplace to actually make the transfer of 0 tokens. Or that a marketplace does not check this and attempts to send 0 tokens to an address that doesn't expect it.

If the marketplace wants to waste gas sending 0 tokens I would be very surprised. I will add a note to the EIP that "no transfer should occur if value is 0" or something similar to be specific.

seibelj commented 3 years ago

On the percentage issue, if the NFT creator wants to return random values for the royalty payment each time someone calls royaltyInfo(), we explicitly discussed this and said it was OK. This was a compromise because the pro-fix-percentage faction was disagreed with by the pro-royalty-fee-innovation faction to leave it open, and no one wanted another boolean or parameter to functions to determine if it was fixed or not, when that was suggested as a compromise.

wighawag commented 3 years ago

Strong disagree. This EIP was funded by the Ethereum foundation for the purpose of funding NFT royalty payments. NFTs are primarily known for 721 although increasingly more supported by 1155. We need people able to google "NFT royalties" and find this. I'm happy to add "NFT and Token" to the title but NFTs are certainly the primary focus of this EIP and the problem we have been trying to solve, not a generic "Royalties for Everything" standard - that was a nice side effect but not the primary concern.

I am not going to fight over this but the reasoning here does not make sense. This EIP is a collaborative effort well beyond whatever funding was put in place. Is your argument solely the fact that you were paid to make an NFT standard and so NFT must be in the name ?

I wonder if the "NFT" wording in your funding proposal was used abusively as it is often the case in the community. that is fine, but for a standard we should aims for accuracy.

Plus, as discussed we want such standard to apply to fungible token too, so not mentioning that possibility feel lacking. Hence why I would advocate for making the standard clearer.

seibelj commented 3 years ago

The Title of this thread is Discussion for ERC-721 Royalties EIP. Feel free to make a new EIP for "Royalties for Everything" but clearly this was intended for NFTs from Day 1.

wighawag commented 3 years ago

The Title of this thread is Discussion for ERC-721 Royalties EIP. Feel free to make a new EIP for "Royalties for Everything" but clearly this was intended for NFTs from Day 1.

Not going to make a new standard, I do not even need it as we agreed that this standard can apply to ERC1155 and their fungible token. I just propose to make a clearer name and description

Lenoftawa commented 3 years ago

Regarding the 'NFT' issue - It is not a show-stopper for me, although I have been a supporter of leaving it as a more general royalty standard.

Lenoftawa commented 3 years ago

@wighawag I think we do need to say something about future extensions, although I'm happy for the wording that you disagree with to be replaced.

TLDR; https://github.com/ethereum/EIPs/issues/2907#issuecomment-840477931 proposed the use of 'no extra data' in _data and _royaltyPaymentData as a means of signaling capabilities between marketplace and token. As a result, we are forced to consider the extension mechanism in this EIP.

Here is my reasoning:

In https://github.com/ethereum/EIPs/issues/2907#issuecomment-840477931 (which I fully support), both the marketplace and token use 'no extra data' in _data and _royaltyPaymentData to determine that their counterpart is only capable of the basic ERC-2981 protocol and therefore they too must fall back to basic ERC-2981.

To make this work, more capable implementations that understand extensions need to be able to detect 'no extra data'. This in turn means that marketplaces and tokens that have been created without knowledge of future extensions must signal this fact using 'no extra data'.

It follows that implementations that do not include extensions to ERC-2981 must always:

And of course, 'no extra data' must be unambiguously defined.

@seibelj Following on from the above...

It's the same as in the C programming language - it's undefined behavior. Don't use it don't read it. If you do, anything can happen. I can change the language from "unused" to "undefined" if you prefer.

I agree with 'don't read it'. It is reserved for extensions and there are none under this EIP. When we have an EIP that defines extensions, then implementers that know about that EIP need to read it.

However, you must write a well-known value to it (0, false... whatever). This is so that counterparts know that you don't support any extensions. This ensures that they won't interpret it using an extended protocol. When we have an EIP that defines extensions, then implementers that know about that EIP need to write different values into it.

cliffhall commented 3 years ago

Just a quick reality check on the interface definition.

wighawag commented 3 years ago

@lenifoti

It follows that implementations that do not include extensions to ERC-2981 must always:

  • ignore any value sent to them in these fields
  • set these fields to a value that represents 'no extra data'.

EIP-2981 does not need to enforce this rule.

I mean if your implementation read the input data, it implictly means it uses an extension and will need to consider the input data according to that extension rules. If they return random data, it is their problem.

There is no need to enforce what an implementation must return. as mentioned in my comment, for cases A1 and A2, the return data will be ignored by marketplace that do not support extension. for case B, EIP-1982 implementer will have to follow the rules of the extension to be compatible there.

cliffhall commented 3 years ago

It follows that implementations that do not include extensions to ERC-2981 must always:

  • ignore any value sent to them in these fields
  • set these fields to a value that represents 'no extra data'.

You can't set a value representing 'no extra data' when the spec doesn't explain how to represent that. And you have to return something. I'm pretty sure the implementation should just return what's passed in. No sense trying to fabricate a 'nothing to see here' response when there's no definition of what that is.

Screen Shot 2021-05-27 at 4 42 06 PM
Lenoftawa commented 3 years ago

@wighawag @cliffhall Appreciate your responses.

@cliffhall

You can't set a value representing 'no extra data' when the spec doesn't explain how to represent that.

Precisely! If we assign meaning to 'no extra data' within the protocol then it must be something that can be represented consistently in some way. If there is a standard for doing this at the bytecode level, then we can use that.

However, @wighawag and others do not believe it is necessary to write anything specific into these fields. I'm not yet convinced that this is true if we accept the proposal: #https://github.com/ethereum/EIPs/issues/2907#issuecomment-840477931, but I am trying really, really hard. We'll see.

I'm pretty sure the implementation should just return what's passed in.

Expressions like pretty sure are not a good sign. In this case, the spec does state that the fields must not be used, so you should not do that. On the other hand, I can't see how we can achieve what we want without setting it to something known. But that's the other debate.

Lenoftawa commented 3 years ago

@wighawag

Still computing this..... I see my concern as analogous to a namespace issue - does that help explain it?

In #https://github.com/ethereum/EIPs/issues/2907#issuecomment-840477931 you make the following statement about future extensions:

It will be in their best interest to be simple and non-conflicting.

To be non-conflicting, implementers need to know what it is that they must not conflict with.

To avoid any possible conflict, we need to be absolutely sure that some rational interpretation of 'unused' (AKA 'no extra data') in a token implementation written in Solidity, Vyper or EVM bytecode etc. does not get interpreted by a marketplace as a valid response to an extension.

And conversely, that a rational interpretation of 'unused' by in a marketplace implementer writing in <pick your language/OS>, or a contract written in Solidity, Vyper or EVM bytecode etc. does not get used as a valid request to use an extension by a token.

To punt this into future EIPs is effectively saying: "Future EIPs must avoid all possible interpretations of 'unused' in all languages and OSs".

All this can be avoided by defining 'unused' as being some value that is not open to interpretation.

I just don't buy 'in 99.9% of cases it is OK'. Standards in an ecosystem such as this should be binary: 100% or 0%.

To put this in the context of your message:

I mean if your implementation read the input data, it implictly means it uses an extension and will need to consider the input data according to that extension rules.

Agree. If you only implement this EIP, which defines no extensions, then (to quote @seibelj) 'don't read it'.

If they return random data, it is their problem.

Agree with this too.

However... all this depends on being able to distinguish between 'random data' and 'unused'. That's all I (and I think @cliffhall) are calling for here.

In Case B, the marketplace will use its understanding of 'unused' to determine if the token is responding using the extended protocol (Case B2) or not (Cases B1 or B3). This is to allow the marketplace to be backward compatible with the many tokens that were released before that particular extension existed.

There is no need to enforce what an implementation must return. as mentioned in my comment, for cases A1 and A2, the return data will be ignored by marketplace that do not support extension.

In case A2, the token must correctly detect that the marketplace is not using an extension so that it behaves correctly. If the marketplace picks some interpretation of 'unused' that clashes with a future extension then the token will and serve a _royaltyAmount based on an extended protocol. The marketplace will assume that the return value is based on the non-extended protocol.

for case B, EIP-1982 implementer will have to follow the rules of the extension to be compatible there.

I am still happy to be wrong.

wighawag commented 3 years ago

@lenifoti I see 2 cases:

The EIP-2981 implementation

  1. read the input data and so will output data according to the extension it support, else (if the extension requested is not supported or no input data were present) it will have no reason to output data
  2. do not read the input data and will have no reason to output data

we could add a rule for the "no reason to output data" that if there is no data in input (data.length == 0) or if the input data signals an extension not supported, there should be no data in the output. This will signal the use of the original EIP-2981 interpretation of royaltyAmount and royaltyRecipient

I was thinking it was not necessary because as I mentioned, if the implementation output random data it is their problem.

But I see no harm in adding something about the output data:

it must be the empty bytes "" to signal marketplace to use the original EIP-2981 interpretation of royaltyAmount and royaltyRecipient, namely that the token/ETH to transfer is the same as the one used for purchase and that royaltyAmount represent the exact amount to send to royaltyRecipient

This remove the need to dictate what extension have to do while ensuring the original interpretation can be safely signaled when desired

cliffhall commented 3 years ago

Crazy question, but is this "reserved for future use" _data parameter more trouble than it's worth and needlessly complicating the spec?

Since extensions can easily add an overloaded royaltyInfo method of the same name but with different parameters that serve their purpose, I have a hard time understanding why this spec even concerns itself with how a future extension will work.

Arguably, this spec is already meddling with future extension specifications by defining a single _data argument with a pre-determined type which they are expected to use. IMHO, if the whole _data "feature" and all of it's confusing text were removed from this spec, it would make it much easier for implementers to understand.

wighawag commented 3 years ago

@cliffhall that's a very good question

I think the idea was to make it easier for extension to be accepted, as they would not need to implement 2 (or more) function and more ERC165 ids and their implementation would still fallback on a default one if the marketplace do not support it. Similarly for marketplace, it would be easier to adapt to new extension. Overall this would make it easier for extension like specific token payment to be supported by the ecosystem

And the addition of bytes data was supposed to be a simple addition to the standard, Something extension will have to define. Hence my argument here that we do not need to bother, except simply stating that to signal default behaviour, the empty bytes must be returned.

cliffhall commented 3 years ago

@wighawag Shouldn't the goal be adoption of this spec, and making the actual premise of it as solid as possible, rather than being concerned with how future extensions will be implemented?

All of the time spent hashing out this actually useless feature could have been spent sorting the whole "valueless units" issue, which still isn't right and will only work if the implementation does a percentage-based royalty calculation.

It's sad to see so much thought and spec verbiage going into how to properly "not use" a "feature reserved for future use" when the core proposition is kind of broken.

wighawag commented 3 years ago

@cliffhall I kind of agree, but that is easy to say after the fact. As I said, my thinking was that the extra bytes was a simple addition, common with other standard by the way.

Now removing the data from the standard does not help necesssarely. it is good to talk about future extension to ensure we remove potential burden.

And for basic implementation, they really do not need to do anything just return an empty data anyway. So I still think it is better to leave it as is to keep the standard flexible.

The only thing that we could add as I said, is to say that an empty output data signal the use of the basic interpretation. This should remove @lenifoti 's concern

cliffhall commented 3 years ago

@cliffhall I kind of agree, but that is easy to say after the fact.

But until the standard is accepted, it isn't after the fact, is it? It's still in the discussion phase, and presumably open for change. I've worked to implement it on two marketplaces now, and so I'm not just playing armchair architect. I really want it to succeed at solving the problem at hand, and think if it does, there could be very little need for future extensions anyway.

Mentioning the "valueless units" issue elicited nothing but "there's been lots of discussion about this" in response, but notwithstanding, it's still broken.

Consider this extremely likely scenario, should a marketplace not use a percentage calculation for royalties as this spec is going out of its way to support:

If a percentage-based calculation is used, then it always works out correctly no matter what token the sale was made in. It seems like if anything, support for non-percentage based schemes ought to be left to extensions, should anyone feel the need for that.

As @seibelj said in his response

I believe > 99% of contracts will use fixed-percentage royalties, as that is the model already supported in existing marketplaces like OpenSea, Rarible, and Mintable.

If that is the case, why are we even muddying the waters with talk of supporting non-percentage based calculations, when that is the fatal flaw with this spec as it stands?

The only thing that would need to happen to remediate this issue is to reword this section:

Royalty payment amounts over percentages This EIP does not specify how the _royaltyAmount is calculated from the sale price _value. Many NFT contracts may follow a fixed-percentage royalty that is the same regardless of price. However, EIP-2981 implementers may choose whatever logic they want to calculate the _royaltyAmount - this EIP does not mandate a fixed-percentage fee model.

...like...

Royalty payment amounts over percentages This EIP does not specify how the _royaltyAmount is calculated from the sale price _value. However, only a fixed-percentage approach will guarantee that regardless of which token the sale was made in, the creator will receive their expected share. Other royalty approaches such as a expecting a fixed amount of a specific token or its equivalent may be supported by future extensions to this spec.

We're still returning a value and not a percentage, so we've done all that needs to be done to support the "Royalty payment amounts over percentages" philosophy, even though only percentages will work with this spec.

Just my two gwei.

Lenoftawa commented 3 years ago

If it is the responsibility of the token to calculate the royalty from _value, then we should define _value a bit more tightly. Does it include marketplace fees etc.. or is it net of any additions?

Is the royalty related to the amount that the seller received? Or the amount that the purchaser paid?

wighawag commented 3 years ago

But until the standard is accepted, it isn't after the fact, is it? It's still in the discussion phase,

of course, my reply was to your statement : "All of the time spent hashing out this actually useless feature could have been spent sorting the whole "valueless units" issue" which I felt unfair. we are indeed still discussing it

It seems you are discussing 2 issues now and conflating them. The bytes data was for future extension, to make them easier to build, to support the scenario you mention for example

But the scenario you mention is not possible without extension. As we mentioned earlier, the unit of value is the same as the one used for the purchase unless input data state otherwise (but that is handled by extenion)

The goal of not using percentage is different: it is to allow different curve or max cap, etc... and I think it has its value on its own

VexyCats commented 3 years ago
  • Marketplace A wants to receive a fixed .2 ETH as royalty for NFT number 1
  • Marketplace B has sold that NFT for 200 USDC on its secondary market.
  • Marketplace B calls royaltyInfo(1, 200, "0x0")
  • Marketplace A responds with .2 in _royaltyAmount
  • Marketplace B sends the _recipient .2 USDC - twenty cents!

If a percentage-based calculation is used, then it always works out correctly no matter what token the sale was made in. It seems like if anything, support for non-percentage based schemes ought to be left to extensions, should anyone feel the need for that.

I agree here - we cannot cater to every possible scenario and it would be foolhardy to try to do so.

Btw @cliffhall what two marketplaces are going to support 2981? Just so we know when we try to get other marketplaces on board.

@lenifoti _value is the transactional value the buyer has paid. Most marketplaces would not send the value to calculate royalties AFTER they have taken their fee, wouldn't make mathematical sense.

I don't see any problem with the additional tet @wighawag suggested related to the _data field. Does anyone see an issue with this:

But I see no harm in adding something about the output data:

it must be the empty bytes "" to signal marketplace to use the original EIP-2981 interpretation of royaltyAmount and >royaltyRecipient, namely that the token/ETH to transfer is the same as the one used for purchase and that royaltyAmount >represent the exact amount to send to royaltyRecipient

cliffhall commented 3 years ago

@VexyCats

we cannot cater to every possible scenario and it would be foolhardy to try to do so.

Absolutely. The only one we can cater to is percentage-based calculation of royalties. To even infer that another calculation method would work guarantees there will be problems.

Btw @cliffhall what two marketplaces are going to support 2981? Just so we know when we try to get other marketplaces on board.

KnownOrigin and Seen.Haus will both be sporting 2981 support in their new contract suites.

@wighawag

But the scenario you mention is not possible without extension.

That is exactly my point. To even suggest that non-percentage based calculations are allowed is wrong. The scenario is what happens if someone reads the spec and thinks they can return anything other than a percentage of the _value amount and have it actually work out to what they were intending. That's why I suggested rewording of that spec section to be absolutely clear that only percentage based calculations will work in this specification.

wighawag commented 3 years ago

That is exactly my point. To even suggest that non-percentage based calculations are allowed is wrong. The scenario is what happens if someone reads the spec and thinks they can return anything other than a percentage of the _value amount and have it actually work out to what they were intending. That's why I suggested rewording of that spec section to be absolutely clear that only percentage based calculations will work in this specification.

To me you are again conflating: percentage vs value and the "which token to pay" issue

The later can indeed be made more clear in the spec, but we do not need to go with strict percentage and forego the more flexible "value", which again allow for different curve and cap.

cliffhall commented 3 years ago

To me you are again conflating: percentage vs value and the "which token to pay" issue

Nope. Not talking about the "which token to pay" non-issue. Callers are told they must pay in the same token as the sale. No problem, makes perfect sense.

What I am saying is that expectations on the part of the callee about how to calculate royalties are not being set properly.

If anything other than percentage-based calculations are used, there will be problems. However, if they use a percentage based royalty calculation, it will always work, regardless of the coin of the sale itself. 12% is 12% no matter what you're applying it to.

If there are other types of calculations that would work as well as percentage in all cases, then they should be mentioned. Otherwise the current wording may lead implementers to think a fixed amount is something they can return, not realizing that the coin of the sale might not be the same as the coin they'd like a fixed amount of.

we do not need to go with strict percentage and forego the more flexible "value", which again allow for different curve and cap.

unless the curve and cap algorithms are related to percentage, (i.e, the percentage used to calculate the royalty shrinks from resale to resale on a curve), then there will be problems because the only other thing they can refer to is amounts of a particular coin, which cannot be known in this spec.

wighawag commented 3 years ago

@cliffhall what problem are you talking about ?

With a value, it is even simpler, the royalty amount is that value and the token is the same as the one used for payment, like for percentage. Marketplace do not even need to handle rounding error, they get told what to pay.

cliffhall commented 3 years ago

@wighawag

@cliffhall what problem are you talking about ? With a value, it is even simpler, the royalty amount is that value and the token is the same as the one used for payment, like for percentage. Marketplace do not even need to handle rounding error, they get told what to pay.

We're passing in valueless units. A fixed value for royalty makes no sense in the absence of knowledge of the coin.

Can you not see how this makes no sense? You cannot pass back a fixed value and expect royalties to work in anything other than a completely random way based on the token of the sale.

wighawag commented 3 years ago

You are assuming the contract implementation is not thinking in term of percentage. THe point is to allow flexible percentage, on a curve, capped, etc...

As I said the token is the one use fo purchase, the EIP-2981 implementation do not care.

For those that want to care, the idea is to use extension where the token info is passed in

wighawag commented 3 years ago

You right that curve and cap cannot be implemented without knowledge of the token, but it was quick example I gave. the idea of this, coming from @lenifoti if I remember correctly is that we want to allow innovative idea to take place.

wighawag commented 3 years ago

hmm, it is true that I am not sure I can come up with an amount that is not the result of a percentage calculation and so we could as well return a percentage.

But I still think a value is more elegant as we do not force the rounding mechanism etc...

And of course, it ties well to the extension mechanism we have

cliffhall commented 3 years ago

@wighawag

hmm, it is true that I am not sure I can come up with an amount that is not the result of a percentage calculation and so we could as well return a percentage. But I still think a value is more elegant as we do not force the rounding mechanism etc...

I am on board with returning an amount and not a percentage. By doing that we allow for the possibility of extensions that calculate that amount differently given extra information we do not have in this spec. That's why I said:

We're still returning a value and not a percentage, so we've done all that needs to be done to support the "Royalty payment amounts over percentages" philosophy, even though only percentages will work with this spec.

I merely suggest being 100% clear about the potential pitfalls of using non-percentage based calculations with this spec by rewording that section like this:

Royalty payment amounts over percentages This EIP does not specify how the _royaltyAmount is calculated from the sale price _value. However, only a percentage-based approach will guarantee that regardless of which token the sale was made in, the creator will receive their expected share. Other royalty approaches such as a expecting a fixed amount of a specific token or its equivalent may be supported by future extensions to this spec.

Lenoftawa commented 3 years ago

In the hope of coming to a consensus (and not inflaming things)....

1. I don't think anyone is questioning the use of: "_value input and _royaltyAmount output".

Can we agree on that?

Other than the possible future uses, it is simpler for a marketplace and eliminates the need to describe the percentage format (which was also discussed at some length).

2. @cliffhall's proposal is simply to remove wording that gives any hint that this EIP promotes non-percentage calculations.

Can we agree on that approach and focus our discussion on that?

We may need to say something about payments in a future EIP. But we can remove mention of:

3. @cliffhall suggested removing the extra data and add it back (or use a different function) in a future EIP. I'm assuming that we would then use ERC-165 introspection on the new signature/function to test for extensions. I think this makes good sense.

This may be a little more contentions, but it makes the standard so much crisper. What does the community think?

I don't think that @wighawag was suggesting that the spec is frozen, simply explaining how we got here. In fact, even the signature change is trivial to an implementer.

dievardump commented 3 years ago

Btw @cliffhall what two marketplaces are going to support 2981? Just so we know when we try to get other marketplaces on board.

I'm already supporting 2981 on my ERC721 contract and the market contract will be soon update to support it. (on BeyondNFT)

2981 as it is now is also already implemented in NFT and Market contracts we have on an upcoming platform (Wallkanda)

Lenoftawa commented 3 years ago

You right that curve and cap cannot be implemented without knowledge of the token, but it was quick example I gave. the idea of this, coming from @lenifoti if I remember correctly is that we want to allow innovative idea to take place.

Mea culpa - it was I.

I did think that there is a place for things like minims, caps and also change of percentage when the price goes ballistic. I was also listening to a podcast with Mark Cuban discussing using NFTs for tickets where the idea was to discourage scalping by changing the rate over time (and potentially over the amount), and also turning them in to POAPs after the game was over.

It occurred to me that the reason that a fixed percentage was the "99% case" is likely because it was the only rational approach that a marketplace could take given the absence of this EIP.

I had not worked through the currency problem at that stage and when it became clear, I did propose explicitly signaling a percentage.

cliffhall commented 3 years ago

@lenifoti

  1. I don't think anyone is questioning the use of: "_value input and _royaltyAmount output". Can we agree on that?

yep.

  1. @cliffhall's proposal is simply to remove wording that gives any hint that this EIP promotes non-percentage calculations. Can we agree on that approach and focus our discussion on that?

If so, I provided a suggested wording for the appropriate section. I think it just comes down to making sure that's agreeable to all.

  1. @cliffhall suggested removing the extra data and add it back (or use a different function) in a future EIP. I'm assuming that we would then use ERC-165 introspection on the new signature/function to test for extensions. I think this makes good sense. This may be a little more contentions, but it makes the standard so much crisper. What does the community think?

Crisper is a good term. It means as a developer, I focus only on what I need to do in order to meet the spec with an implementation or to interact with implementers of the spec. A parallel is ERC-1155's safeTransferFrom and its _data parameter. I was just writing a unit test 5 minutes ago and found in the spec nothing about what to pass it if I don't want to pass anything. It's not "" or "0x0", it's 0, I just had to do trial and error to see what 'empty argument' for the bytes type is when using ethers.js. All for a "feature" that will almost always not be used. That was just extra friction that makes me not like that call very much. I fear this _data param on royaltyInfo will engender the same disdain.

cliffhall commented 3 years ago

@lenifoti

I did think that there is a place for things like minims, caps and also percentage if the price goes ballistic.

That was very forward thinking and future EIPs can totally implement those things with extra data being passed to them. They just won't work with this EIP.

Lenoftawa commented 3 years ago

Btw @cliffhall what two marketplaces are going to support 2981? Just so we know when we try to get other marketplaces on board.

I'm already supporting 2981 on my ERC721 contract and the market contract will be soon update to support it. (on BeyondNFT)

2981 as it is now is also already implemented in NFT and Market contracts we have on an upcoming platform (Wallkanda)

@dievardump I think I saw some code on Etherscan. What hash value are you registering for EIP-165? I seem to remember that it did not include the extra bytes - right?

It seems a bit dangerous to put it out on Miannet before this EIP is finalised.

Lenoftawa commented 3 years ago

@lenifoti _value is the transactional value the buyer has paid. Most marketplaces would not send the value to calculate royalties AFTER they have taken their fee, wouldn't make mathematical sense.

Won't it depend on who the marketplace thinks is paying their fee, who they think is paying the royalty and whether the fee is added or subtracted? I can envision an implementer doing the calculations in a sequence and calling royaltyInfo() at some point, I'm happy to be convinced there is only one way.

I'm not a marketplace implementer, so I'm merely thinking about how I express exactly what I want to happen as a token for sale. The idea that the marketplace can decide on what is meant by something seems to defeat the goal of this EIP.

I do concede that this is not significant in the scheme of things. But it does not hurt to require what you believe is what "Most markets" will do.

VexyCats commented 3 years ago

@seibelj Why did we remove the contract address from the royaltyInfo() function for passing in the ERC20 address that is being used to transact in value?

I thought we had discussed a while back that it was pretty critical - I thought it was in the EIP as it stands but had to double check to realize it was not.

Does anyone @wighawag @lenifoti @cliffhall @jamesmorgan see objections to having the following:

 function royaltyInfo(uint256 _tokenId, uint256 _value, **address _currencyContract**, bytes calldata _data) external returns (address _receiver, uint256 _royaltyAmount, bytes memory _royaltyPaymentData);

I don't see any reason why this would be bad, restricting, or harmful to add.

If there are no objections to this - it should be added 100%.

This will allow for fixed percentage, as well as more unique mechanisms of royalty calculation to be done via the NFT contract. It does not impose hardships on the marketplace contract, and it does not increase gas costs significantly.

We discussed this very early on and I'm pretty sure we came to this conclusion a long time ago see comments around https://github.com/ethereum/EIPs/issues/2907#issuecomment-807177914

Anyways - this will solve the valueless issue from @cliffhall.

Finally I think we have finished the discussion related to the the extension EIPs and what our role is there for this EIP - so I don't think we have any more serious discussions going on? We are good to move to draft then final call

Lenoftawa commented 3 years ago

@VexyCats Interestingly, your message arrived as I was trying to figure out the consequences of changing the function signature per @cliffhall's suggestion.

The EIP is in Draft so I don't see why this is not possible, but I was disappointed to find that there are live contracts claiming to implement EIP-2981 with different interface IDs.

A search of the Mainnet on Etherscan shows about 16 contracts that appear to use royaltyInfo(...). I believe that most of them are claiming to be ERC-2981.

Several have no arguments and possibly do not claim to be EIP-2981 - these are also the only ones that has have significant transactions. The rest have had very few transactions, especially those that include the extra bytes argument.

I count 4 different interface IDs, suggesting 4 different signatures for royaltyInfo(...).

Several claim to use the extra data but calculate 2 different Ids. Only one of the contracts matches the latest value (0xc155531d).

VexyCats commented 3 years ago

@lenifoti Alot of those are my own contracts from Mintable that supported EIP-2981 as the version I original proposed on the EIP, they were live back in december 2020. Mintable lets users create smart contracts, we have created over 200+ and they all use the early version of 2981.

I don't see why thats important though? Are you worried about etherscan and the function signature in someway? Etherscan is always willing to work with people, including for EIPs and can easily make changes on their end to support it.

But you can explain a bit on why its an issue?

Regardless, the function sig should be changed.

Lenoftawa commented 3 years ago

@VexyCats I was getting the impression that there was resistance to changing the signature (and therefore the interface ID) because there are contracts that are in the wild. That's all.

It could create some confusion, but I personally think we should do the right thing here.

Lenoftawa commented 3 years ago

@VexyCats Regarding the token-contract parameter, I think you are referring to this: https://github.com/ethereum/EIPs/issues/2907#issuecomment-831650224

These exotic use-cases should be handled in an EIP that builds on this one, which the arbitrary data fields enable.

However, if people feel this is absolutely crucial, we could add:

royaltyInfo(
    uint256 tokenId,
    address tokenPaid,
    uint256 value,
    bytes calldata data
)
external view returns (
    address receiver,
    uint256 royaltyAmount,
    bytes memory royaltyPaymentData
);

address tokenPaid would be the contract address for the ERC20-compliant token paid, or set to null for native asset (ETH).

So the intent is that the token receives the currency from the marketplace and can use it to calculate the amount using a non-percentage scheme, as long as it is able to access the value of that token with respect to its reference currency. I guess the main issue I see is that it might be hard to do the calculation in a gasless way, but that's the token's concern to work around if it want to represent non -percentages.

It seems to have been mentioned in the height of some other 'discussion' and there was no explicit consensus as far as I can see. I think the sentiment of the group may have been summed up in the first line:

These exotic use-cases should be handled in an EIP that builds on this one, which the arbitrary data fields enable.

It has merit as an extension - do we want it in this EIP?

VexyCats commented 3 years ago

I think it has no negative side effects by being in this EIP. It only improves the EIP functionality, and future extensions would need/rely on it as well.

I don't think its an exotic use case either - as a lot of NFTs can be purchased with ERC20s and its fairly common.

Unless there is some serious side effect of having it included - I don't see any reason it should not be included.

This solves the royalty fixed/percentage issue, this improves flexibility down the road for future extensions and it can be useful for reporting/accounting of royalties.

Adding this final change - should be looked at as the final change and then we are good to go I believe

Lenoftawa commented 3 years ago

This assumes that the transaction is in a tokenised currency - realistically a stablecoin in the future. So, we still need to consider things like:

I'm still in favour of removing the extra data and pushing into an extension.

We need to think seriously about the extension anyway because we have not considered how payments and the event happen (push/pull/batches...). We cannot declare victory until we have at least a statement on how payments happen and what the _receiver needs to be capable of. I'm keen to draw a line under this EIP and move on to that.

cliffhall commented 3 years ago

I think it has no negative side effects by being in this EIP. It only improves the EIP functionality, and future extensions would need/rely on it as well. I don't think its an exotic use case either - as a lot of NFTs can be purchased with ERC20s and its fairly common. Unless there is some serious side effect of having it included - I don't see any reason it should not be included.

I would say that the downside to including _tokenPaid and keeping _data is that 99.99% of royalties will be percentage based. In those cases, it is not necessary to know the token of the sale, nor to pass in extra data to guide a more complicated royalty scheme. Nearly every call made to a 2981 implementation would have to pass two unused parameters. It makes the spec more complicated and harder to understand for implementers.

Future EIPs that want to do something other than percentage based calculations can easily overload the method with whatever params they need. Meanwhile this spec could have a crystal clear method signature that serves nearly every marketplace's need. The documentation for this is lighter and friction to adoption is minimized.

royaltyInfo(
    uint256 _tokenId,
    uint256 _value
)
external view returns (
    address receiver,
    uint256 royaltyAmount
);