LSP18 Royalties
Hey cool @lykhonis ! Not sure about the number tho as @CallumGrindle and I have a draft PR for LSP15 too https://github.com/lukso-network/LIPs/pull/133 😊😅🤭
Not that familiar with the NFts ecosystem but looks pretty cool 🚀 .
However I am a bit confused about LSP18RoyaltiesMap, you mention that there are two types:
- type 0 where royalty will be a “percentage”
- type 1 where it will be a fix value in LYX (gwei)
However when looking at the value type I only see (bytes8,bytes2,bytes4) where I guess it should be (bytes8,bytes2,bytes4) or (bytes8,bytes2,bytes32) ?
Btw I don't think you need bytes32 for the fixed amount value. It could fit into a bytes20 without problem since it would be more than the total amount of LYX in circulation so if you decide to allow fix amount you could have a value type of (bytes8,bytes2,bytes20) (fits into one 32 bytes slot)
Also your implementation code currently only works with LSP18RoyaltiesMap of type 0?
A few notes I would probably add:
- You could have royalties percentages that add up to > 100% making the NFTs not transferable not sure that is something you want to look for (same is true for fixed amount > NFT price)
- Having a mix of percentage and fix amount royaltors could get messy
- Adding too many royalty recipient could DoS NFTs transfer
Possible optimization:
Change value content of LSP18Royalties[] to bytes20:bytes2:bytes10 where
- bytes20 = address of royalty recipient
- Bytes(2) = type
- bytes(10) = percentage or uint80 (which would limit max royalty fee to 1.2 millions LYX)
Pros: won’t need to read, store and maintain at two different storage locations
Cons: can’t look at royalty amount by address and will need to read entire LSP18Royalties[] - Not a problem if a dApp is looking for that trough getData
Quick question, as I said I am not super familiar with the NFT environment but wouldn’t it make sense to have the LSP18RoyaltiesEnforceAtLoss relative to the recipient address instead of the NFT smart contract? For example, I have a marketer that only gets royalties when NFTs are not sold at loss. If so I guess it could be added to the LSP18RoyaltiesMap key by appending a 1 byte bool.
Hi all, updated the standard with new details and addressed previous comments. Would appreciate a fresh look at this. Thanks.
Looks good. The only thing is that we might face some real challenges with the notion of "enforcing" royalties. Whether we use a percentage or LYX amount, they can always be bypassed imo.
Honestly not sure how to start answering about "enforcement" part. This is a standard describing metadata.
Looks good. The only thing is that we might face some real challenges with the notion of "enforcing" royalties. Whether we use a percentage or LYX amount, they can always be bypassed imo.
Honestly not sure how to start answering about "enforcement" part. This is a standard describing metadata.
Ah ok sorry thought it was a standard that intended to be implemented at the smart contract level
Looks good. The only thing is that we might face some real challenges with the notion of "enforcing" royalties. Whether we use a percentage or LYX amount, they can always be bypassed imo.
Honestly not sure how to start answering about "enforcement" part. This is a standard describing metadata.
Ah ok sorry thought it was a standard that intended to be implemented
Yes, we do implement on Universal Page. This is updated standard to how to provide information to marketplaces by creators. This is not a standard or system design that ensures enforcement of royalties on chain. I am confused by your comments honestly.
THIS STANDARD IS NOT ABOUT ENFORCING ROYALTIES ON CHAIN! Please do read simple summary it is one sentence.
Since this goes nowhere and the team declines understand why we propose this standard, what it's actually about, we will move forward with it to implement and launch as it's blocking progress of UniversalPage. Closing.
@lykhonis the focus on the team is the network and up launch right now. Additional standards have less importance internally yet.
Tho this doesn’t mean you need to close this. Standard debates can happen over months and years and closing and implement as a single project is likely not leading to adoption.
I personal think the buyable nft standard is the better approach to royalties. But I had no time to look at this proposal in the mean time.
Also please note. Standard adoption is not determined by a LSP being added to the repo, but by people using it. I would suggest you reopen it and start using it as you see fit. And I’ll give my option after the network launch up process is over and I got more mental space for additional ideas. (but like I said standards don’t need the lukso team approval, or mine to be used by anyone)
After re-reading the standard, I'm just wondering the need of 3 different keys for that, I think one would be more than enough.
Firstly, I think LSP18RoyaltiesEnforcePayment doesn't make sense, as if the other royalty keys are filled, then it means you want marketplaces to enforce royalties. I won't create a NFT, add royalties on it but then say "no do not apply those royalties", if that value is on false, then why adding royalties to the contract ? So basically, marketplaces should assume that if the royalty keys are filled, then creators want them to be applied.
Secondly, the two other keys are really redondant. Is the tandardInterfaceId really important?
Let's say it is, why not having only the LSP18RoyaltiesRecipients[] key with the following format:
{
"name": "LSP18RoyaltiesRecipients[]",
"key": "0xfdd4e98ba62fdcf79cfde4cfe031a71195ae21ff3d0e29f79db24f2fe9ceb59b",
"keyType": "Array",
"valueType": "(address,bytes4,uint16,uint32)",
"valueContent": "(Address,Bytes4,Number,Number)"
}
Using this key only you would have all the info you need to add royalties (or am I missing something?). And the value would be packed in less than 32 bytes
@samuel-videau The Map data key is needed because otherwise to get the royalty settings of an address, you would have to loop through the entire array in storage. Which can be big a uses a lot of gas.
You could put everything in one data key and allow it. But the standard needs to specify this aspect and make developers aware of it.
@samuel-videau The Map data key is needed because otherwise to get the royalty settings of an address, you would have to loop through the entire array in storage. Which can be big a uses a lot of gas.
You could put everything in one data key and allow it. But the standard needs to specify this aspect and make developers aware of it.
But the thing is that here you still need to loop through the array to know the addresses that should have royalties anyway.
I don t see the use case where you would only need to query the royalties for only a specific address
That's actually a pretty good point. I agree about this.
- LSP18RoyaltiesRecipientsMap is only defined here because of alignment with other standards on how values are stored in arrays. Indeed, this key-set may be excessive as it maps recipients in the array, where array may never be large enough not to iterate over. I agree, to remove this key-set to simplify this.
- Standard bytes4 is defined to align with the rest of standards where addresses are mapped and type of address is indicated in the value
- LSP18RoyaltiesEnforcePayment is needed as it instructs a marketplace to enforce royalties regardless whether a user is selling it at a profit or at a loss. If this key is set to false, and user is selling at a loss, a marketplace should not enforce royalties and allow sale without them.
- LSP18RoyaltiesEnforcePayment is needed as it instructs a marketplace to enforce royalties regardless whether a user is selling it at a profit or at a loss. If this key is set to false, and user is selling at a loss, a marketplace should not enforce royalties and allow sale without them.
How will the concept of "selling at a loss" will work cross-marketplaces since the value is not part of any events? For example, I buy my NFT on opensea and sell it on looksrare. How will Looksrare know about the price I purchased my nft for? Should looksrare rely on opensea api 👀
@lykhonis sorry for the long wait, we now had a proper look internally and the standard looks great, as separating issuers and royalties makes sense.
We would have a few points that I will comment on the doc.
After additional thinking.
- doing automatic push payments of NFT sales will require the payout smart contract to iterate over the array keys, which can be very costly.
- Using
ArraykeyType was probably chosen so that it supports many royalty receivers.
I see two scenarios
- a few royalty receivers. -> Then an compact array as
valueContentis better and keyTypeSingleton. As this is one call, and easy to iterate over. - have many royalty receivers -> Its better to point to a LSP7 contract, where each balance references the percentage e.g. 1000 tokens available, and balance means a percentage of this.
For small amount of royalty receivers its easy todo a "push" payment distribution. (just look up the royalties array)
For large royalty receivers group, you need to use a "pull" contract. e.g. on NFT purchase, the royalties get put into a claim contract and based on the LSP7 royalty balances, users can withdraw their part.
To make both of these use cases possible i would suggest:
{
"name": "LSP18RoyaltiesRecipients",
"key": "0xxxxxxxxx",
"keyType": "Singleton",
"valueType": "(bytes4,address,uint32)[CompactBytesArray]",
"valueContent": "(Bytes4,Number,Address)"
}
Compact byte array allows for more simple encoded arrays.
basically you get back in one single smart contract call [(reciver1, percentage), (reciver2, percentage), ...]
Small number of royalties receivers Simply put an array of UPs or EOAs in there
Large number of royalties receivers
Just put the address of an LSP7 contract with claim/withdraw function, that distributes based on tokens held in the LSP7.
you can even have a combination of both now.
Bytes4is the interfaceId, of the linked contract (TBD if that's necessary)Numberis the percentage of royalties that get distributed to the addressAddressThe address of receiver(), which could also be a LSP7 royalty claim contract(s) or any other contract, that contains the royalty distribution system
Benefits
- allows for cheaper sort contract calls for small amount of royalties receivers
- allows for very large group where royalties can even be changing over time, as its could be a LSP7
side note: i would remove the dependency on LSP4, that's necessary
-> Concerning enforcement data key, what's your thinking here? @lykhonis
The biggest issue i am seeing is for LSP8 NFTs individual token IDs. We would need a way that each token ID gets a different royalty distribution.
one way of doing this could be with an additional map data Key
{
"name": "LSP18RoyaltiesRecipients:<tokenID>",
"key": "0xxxxxxxxx000<tokenID>",
"keyType": "Singleton",
"valueType": "(bytes4,address,uint32)[CompactBytesArray]",
"valueContent": "(Bytes4,Number,Address)"
}
So you first check if LSP18RoyaltiesRecipients:<tokenID>" is set, if not then use the global one LSP18RoyaltiesRecipients
After additional thinking.
- doing automatic push payments of NFT sales will require the payout smart contract to iterate over the array keys, which can be very costly.
- Using
ArraykeyType was probably chosen so that it supports many royalty receivers.I see two scenarios
- a few royalty receivers. -> Then an compact array as
valueContentis better and keyTypeSingleton. As this is one call, and easy to iterate over.- have many royalty receivers -> Its better to point to a LSP7 contract, where each balance references the percentage e.g. 1000 tokens available, and balance means a percentage of this.
For small amount of royalty receivers its easy todo a "push" payment distribution. (just look up the royalties array)
For large royalty receivers group, you need to use a "pull" contract. e.g. on NFT purchase, the royalties get put into a claim contract and based on the LSP7 royalty balances, users can withdraw their part.
To make both of these use cases possible i would suggest:
{ "name": "LSP18RoyaltiesRecipients", "key": "0xxxxxxxxx", "keyType": "Singleton", "valueType": "(bytes4,address,uint32)[CompactBytesArray]", "valueContent": "(Bytes4,Number,Address)" }Compact byte array allows for more simple encoded arrays.
basically you get back in one single smart contract call
[(reciver1, percentage), (reciver2, percentage), ...]Small number of royalties receivers Simply put an array of UPs or EOAs in there
Large number of royalties receivers Just put the address of an LSP7 contract with
claim/withdrawfunction, that distributes based on tokens held in the LSP7.you can even have a combination of both now.
Bytes4is the interfaceId, of the linked contract (TBD if that's necessary)Numberis the percentage of royalties that get distributed to the addressAddressThe address of receiver(), which could also be a LSP7 royalty claim contract(s) or any other contract, that contains the royalty distribution systemBenefits
- allows for cheaper sort contract calls for small amount of royalties receivers
- allows for very large group where royalties can even be changing over time, as its could be a LSP7
side note: i would remove the dependency on LSP4, that's necessary
-> Concerning enforcement data key, what's your thinking here? @lykhonis
I think indeed this is a good change. We can start slowly with a small number of receivers (most likely the most common case anyway) as a single key/value.
Enforcing part was more marketplace dependent feature. Rational was for a creator to opt-in to not charge royalties if a NFT is sold at a loss. If I to buy nft at $10 and sell at $5, I will not pay 10% royalties thus loosing even more.
@frozeman I guess we can always use the Buyable NFT standard in the future as an alternative way around Royalties, so that people have options.
@CJ42 For the buyable NFT standard. Will there be a way to enforce a fee for the curator? Enforceable royalties for NFT creators are great, but without enforceable royalties/fees for platform creators (like Universal Page), there is no incentive to create the products to discover and trade these assets. This lack of incentive could hurt the NFT creators (and overall ecosystem) even more so I'm interested to hear what you think about this.
@frozeman I guess we can always use the Buyable NFT standard in the future as an alternative way around Royalties, so that people have options.
@CJ42 For the buyable NFT standard. Will there be a way to enforce a fee for the curator? Enforceable royalties for NFT creators are great, but without enforceable royalties/fees for platform creators (like Universal Page), there is no incentive to create the products to discover and trade these assets. This lack of incentive could hurt the NFT creators (and overall ecosystem) even more so I'm interested to hear what you think about this.
@frozeman @CJ42 Any thoughts?
We addressed compact array solution in the LSP and implemented it locally in solidity and typescript. I believe it is a good baseline and start.
@jakeprins sorry for the late response. In the future feel free to ping me in DM on discord. So I see it and answer faster.
In my opinion, the “en force” part should be in the marketplace itself. Not the standard. In my opinion, it should be by the fault always requiring royalties. But if it’s on the marketplace level, if anyway depends on the marketplace implementation . If we later add the ability to buy and sell the NFT directly over the nft smart contract, then royalties will be always enforced, or, however, the order book contract attached to the NFT decides to do it. The entries here in the NFT are purely information as of now, and it depends on the marketplace or future standards to use it in whatever way they want anyway.
@jakeprins sorry for the late response. In the future feel free to ping me in DM on discord. So I see it and answer faster.
In my opinion, the “en force” part should be in the marketplace itself. Not the standard. In my opinion, it should be by the fault always requiring royalties. But if it’s on the marketplace level, if anyway depends on the marketplace implementation . If we later add the ability to buy and sell the NFT directly over the nft smart contract, then royalties will be always enforced, or, however, the order book contract attached to the NFT decides to do it. The entries here in the NFT are purely information as of now, and it depends on the marketplace or future standards to use it in whatever way they want anyway.
@frozeman Yes indeed. However, the original question was more about the "buyable" NFT standard and how it could enforce "royalties" for the creators of NFT platforms (known as platform fees). I think it's important to incentivize the creation and maintenance of these platforms, especially for applications that require a lot of work and money to build and run. Otherwise, the "royalty" problem would simply be shifted from NFT creators to app creators.
@jakeprins from my perspective the buyable standard doesn't need to enforce royalties for curators/middleman since this is already built in to the overall infrastructure of blockchains. What I mean by this is that end users still needs to pay gas/fees to be able to use the blockchain therefore as a curator/platform they inherently benefit from this feature. So they either abstract it away from end users via ads or simply pay for user's gas by taking a fee. And in my humble opinion, curators should play the game of AND not OR for the best business results ;)
@jakeprins sorry for the late response. In the future feel free to ping me in DM on discord. So I see it and answer faster.
In my opinion, the “en force” part should be in the marketplace itself. Not the standard. In my opinion, it should be by the fault always requiring royalties. But if it’s on the marketplace level, if anyway depends on the marketplace implementation . If we later add the ability to buy and sell the NFT directly over the nft smart contract, then royalties will be always enforced, or, however, the order book contract attached to the NFT decides to do it. The entries here in the NFT are purely information as of now, and it depends on the marketplace or future standards to use it in whatever way they want anyway.
I also see this enforcable part in the Marketplace itself, so that different market places can come up with different solutions for enforcing royalties, which would create different business models that NFT creators could choose from.
The Marketplace could create a LSP17 Extension contract in the form of the order book that contains the royalty enforcement logic (that the Marketplace itself has defined). This way, we can differentiate between the storage layer (the royalty distribution settings, percentage, etc...), and the logic layer (in the LSP17 extension itself). An NFT collection could then decide to move overtime from one royalty distribution medium to another, if it finds a new one more efficient, with additional benefits.
@frozeman Yes indeed. However, the original question was more about the "buyable" NFT standard and how it could enforce "royalties" for the creators of NFT platforms (known as platform fees). I think it's important to incentivize the creation and maintenance of these platforms, especially for applications that require a lot of work and money to build and run. Otherwise, the "royalty" problem would simply be shifted from NFT creators to app creators.
The buyable NFT standard would have in the buy() function an address as parameter, where websites/marketplaces can add themselves to receive a portion of the royalty, that incentivices marketplaces or websites add the buy buttons themselves.
But this dicussion is outside of this standard.
@CJ42 @lykhonis lets get thsi standard merges asap, it seems you guys took my suggestions in already.
Super! First community standard 🎉