Closed barnjamin closed 2 years ago
There are several considerations here so far that I see. 1) We need to know the royalty percent, I'm thinking to at least 2 decimals of precision. 2) There should be a way to identify not just the asset creator but a group of creators or collaborators who are also credited. 3) In the case of multiple creators we might want to also to enable weighted distribution of the royalty.
Obviously this would all be optional, and need some information gathering at the time of asset creation, as well as some smart contracts capable of consuming this data.
Im envisioning implementing metadata about people/groups for accreditation thusly: Person/Organization Schema
{
"title": "Person, Group or Organization",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the entity"
},
"description": {
"type": "string",
"description": "A simple description of the entity"
},
"address": {
"type": "string",
"description": "Optional blockchain wallet address for the entity"
}
}
}
And then somewhere in the Metadata:
...
"creator": {
"title": "Person, Group or Organization",
"description": "The entity"
},
"royalty": {
"type": "number",
"description": "This value indicates the percentage of sales of this asset which are required by the creator. Value should be set with up to two decimals in precision."
},
"contributors": {
"type": "array",
"items": {
"title": "Person, Group, or Organization"
},
"description": "List of all credited creators who are to be awarded credit for this work."
},
...
Good input, reformatting and commenting here
1) We need to know the royalty percent, I'm thinking to at least 2 decimals of precision.
Seems reasonable, and that is assumed to be for final sale price right? Preventing asset xfer unless the royalty txn that meets the requirement is attached atomically?
2) There should be a way to identify not just the asset creator but a group of creators or collaborators who are also credited. With the option for weighted distribution of the royalty.
This makes sense. Right now transactions are limited to 16 grouped atomically, so in the case that there are more collaborators than that it will need to go to some multisig or other account.
Also where would you expect the royalty info would be stored? The values will need to be encoded in whatever sc is validating the xfer to ensure the royalty is actually paid.
Also where would you expect the royalty info would be stored? The values will need to be encoded in whatever sc is validating the xfer to ensure the royalty is actually paid.
At the moment I think therefore an option is to create a stateful smart contract which wraps the asset and where each transaction must be also signed by the asset creator, if the asset should be manageable by him. ( else no multisig needed )
The wrapper contract should be the ManagerAddress to be able to prevent deletion of the asset or some other things the creator whouldnt want event after selling the asset. In the main them the rightful owner of the Asset should be able to freeze the asset and clawback the asset so he can freeze it and be the only one who can determine where it gets transferred (because he owns it).
The only way to enforce royaltie i can think about at the moment is using a multisignature contract when using a contract that should issue a royaltie payment for an asset with signatures required by at least the app and the wrapped contract containing the royaltie percentage
AlgoRealm game has been built exactly with the purpose of showing how you can embed decentralised royalty policies into a NFT, directly on Layer-1.
This being said, that solution was released before the ARC-4 proposal on ABI, so I think that if I were to propose a solution today, that takes this into account, I would done it differently.
I think we should collectively start thinking about general approaches about "ASA Transfer Control ABIs", whatever this "transfer control" means: it could be a royalty over an NFT, it could be a regulation policy in a given jurisdiction, it could be a decentralised KYC/whitelisting, it could be a policy on maximum transferable amounts, etc. Standardising this approach with ABIs would make also much more simple, in future, to interact with those ASAs directly form wallets.
So, in this case, royalty over NFTs would be just one particular class of ASA Transfer Control ABIs.
I think that for the ASA Royalty ABI:
"ASA Transfer Control ABIs"
Love it
The ASA subject to the royalty MUST be only transferred with Clawback.
Very interesting, hadn't thought about this. How could this be enforced? Would the ASA have to reside in a contract account?
maybe SHOULD
MIGHT?
It looks like you've thought a lot about this, would you be willing/have time to submit a draft proposal?
Very interesting, hadn't thought about this. How could this be enforced? Would the ASA have to reside in a contract account?
Previous to TEAL 5 the Clawback Address would be assigned to a Contract Account (AlgoRealm solution explains exactly this approach step by step). Form TEAL 5 you can also assign the Clawback Address to the Application Account. Since the ASA is issued as Default Frozen the only way to move it is asking to the Application to transfer the ASA on your behalf with an ABI call, so that you are implicitly enforcing all the Application logic over the ASA transferability.
MIGHT?
Yes, would be better using MAY to comply with RFC-2119.
Thanks for sharing your insights @cusma , this seems like a powerful strategy and I like how it keeps using ASAs
Previous to TEAL 5 the Clawback Address would be assigned to a Contract Account (AlgoRealm solution explains exactly this approach step by step). Form TEAL 5 you can also assign the Clawback Address to the Application Account. Since the ASA is issued as Default Frozen the only way to move it is asking to the Application to transfer the ASA on your behalf with an ABI call, so that you are implicitly enforcing all the Application logic over the ASA transferability.
@cusma Wow, very interesting and creative concept!
@cusma is there a timeline on ARC-4 / https://github.com/jannotti/ARCs/blob/6a9f91aed8068bbace872483a64cbf22d0e1c975/ARCs/arc-0004.md ?
AlgoRealm game has been built exactly with the purpose of showing how you can embed decentralised royalty policies into a NFT, directly on Layer-1.
This being said, that solution was released before the ARC-4 proposal on ABI, so I think that if I were to propose a solution today, that takes this into account, I would done it differently.
I think we should collectively start thinking about general approaches about "ASA Transfer Control ABIs", whatever this "transfer control" means: it could be a royalty over an NFT, it could be a regulation policy in a given jurisdiction, it could be a decentralised KYC/whitelisting, it could be a policy on maximum transferable amounts, etc. Standardising this approach with ABIs would make also much more simple, in future, to interact with those ASAs directly form wallets.
So, in this case, royalty over NFTs would be just one particular class of ASA Transfer Control ABIs.
I think that for the ASA Royalty ABI:
1. The ASA subject to the royalty **MUST** be created as Default Frozen, with no Freeze Address. 2. The Clawback Address of the ASA subject to the royalty **MUST** be assigned to an App (this is possible starting from TEAL 5). 3. The ASA subject to the royalty **MUST** be only transferred with Clawback. 4. The Manager Address of the ASA subject to the royalty **SHOULD** be delated (to avoid unforeseen modification of the royalty ex-post). 5. The ABI **MUST** allow the definition of royalty collectors (and maybe **SHOULD** allow to update them over time) 6. Given the royalty policy, known in advance, the ABI **MUST** calculate the royalty amounts on every transaction. 7. The ABI **MUST NOT** allow the modification of the royalty policy 8. The ABI **MAY** include a whitelisting
Whould it make sense to include: creating the asset from the application to enforce DefaultFrozen and no FreezeAddress via an application call on which the AssetConfigTxn gets issued via an InnerTxn on adequate ApplicationCall?
@JuWeber99 I think it would, as long as the App became aware of the newly generated ASA ID: this makes operations over the ASA restricted to a specific ASA ID and so more secure.
Please mind that my proposal was just a draft of me brainstorming with myself, but in principle the ASA Royalty ABI could expose, among the others, a proper mint
method with which you can define all the properties and configurations of the ASA subject to the royalty directly within the App.
I'm still thinking about whether the best approach would be having a complete ASA Royalty ABI with its own methods (maybe something like mint
, transferRoyalty
and optionals like burn
, revoke
, freezeAccount
, freezeAsa
and so on) or if would be better to modularise those single methods and combining them in a more composable and generic ASA Transfer ABI within which you may want to include the transferRoyalty
but not the revoke
one.
There are trade off between the two approaches, my gut feeling is that the second one could be over-engineered (while more modular).
In both cases the practical steps would be:
@cusma When discussing royalties, we have also come across the idea of "split gain" – it might make it more interesting to trade NFTs and thus increase the fluidity of the market.
An NFT that enforces a custom transaction when reselling it, the artist "win share" will calculate the amount of additional value generated (as the delta between the last and the current sale) and redirect a part of the additional value to the initial owner (aka artist).
The artist will only receive something if an NFT is sold for a higher price than the last recorded price. When the price is higher, she will get e.g. 20% of the additional value:
Perhaps, an architecture that has several ASA
I've also been thinking about this issue recently and want to share an idea I had.
An ASA that requires royalties is DefaultFrozen: True
and specifies a specific address in the FreezeAddress
field. This address is the address of a stateful application. The ASA creator opts in to this stateful app and stores a value in local state that represents their royalty percentage.
When making a trade involving the ASA the txn group must include a call to the app and a payment to the creator. The app call will only succeed and issue an inner unfreeze txn if the payment txn is to the correct address and for the correct amount, as determined by looking up the local state of the creator account.
The fully transaction group for a purchase of an ASA created by C from A by B would be:
This could be extended trivially to allow setting/updating a beneficiary for a creator so they do not have to be the same address.
This approach appeals to me because it potentially allows for a single well known royalty manager app that everyone could use simply by opting into and setting the freeze address of ASAs. I also like keeping the existing semantics of transaction groups rather than introducing new methods for this.
Any wallet/app that knows about this well-known app address would be able to determine that an ASA operates this way simply by looking at the freeze address. It would determine the royalty amount and beneficiary by looking up the local state of the ASA creator. Once it knows these it could prepare the appropriate transaction group.
Disadvantages
Hi, I have recently started to delve into this problem, and I agree with @cusma that it just makes much more sense to have a sort of ASA Transfer Control ABI. However, it is also hard to come up with a proposal that works for any scenario. Perhaps it would be better to create a new discussion? @barnjamin
Nonetheless, I am new to the community to Algorand and I would like to contribute somehow to the project.
My proposal is to create an object that is a JSON description of the list of transactions that are required to successfully transfer an ASA. This could help not only with the royalty fees, but also many other things.
In general, I believe each transaction SHALL only describe the necessary parameters for that transaction. If some transaction parameters are variable (e.g. a % royalty fee, and not a pre-defined fixed amount), an App will be called to compute the necessary parameters.
The JSON description would be
interface ASATransferControl {
assetId: number, // Perhaps redundant
listTransactionsGroup: Array<TransactionsGroup> // List of ordered transactions groups
}
where
interface TransactionsGroup {
buyerAsset: Array<number>, // Assets that may be used by the buyer to buy the original ASA
transactionsGroup: Array<Transaction> // Ordered group of transactions that defines how to buy the ASA
}
interface Transaction {
type: string, // "appl", "axfer", etc...
args: { field: string, value: string | number | ... | AppCall | AppRead } // Any argument that needs to be specified.
}
interface AppCall { // This is used to call an App method
appId: number,
method: Method, // See ARC-0004
appArgs: Array<string | number> // List of parameters
}
interface AppRead { // Read a global variable
appId: number,
variable: string,
type: string | number
}
listTransactionsGroup
is an array.TransactionsGroup
includes buyerAsset
. This is a list of ASA IDs that the buyer can use with that specific transactionsGroup
.field
may be AppCall
, which means that the value should be computed by calling the method defined in AppCall
. AppRead
is a simple read of a specific global variable in the App.For example, imagine the following 2 scenarios:
We first create an ASA (id 10) and assign the Clawback address to a specific App (id 123)
To buy the ASA the buyer must perform the following 3 transactions
We suppose the Clawback is also the App for simplicity, so I don't include a NoOp transaction.
To buy the ASA the buyer must perform the following 3 transactions
We assume the buyer's ASA id can be 11 or 12.
In the JSON description I make use of some variables that can be easily retrieved
The resulting JSON is as follows.
{
"assetId": 10,
"listTransactionsGroup": [
{ # SCENARIO 1
"buyerAsset": [0] # AssetID = 0 is ALGO
"transactionsGroup": [ # List of transactions
{
"type": "pay", # Royalty fee payment
"args": {
"rcv": "$CLAWBACK_ADDRESS", # The receiver of the fee is the Clawback address
"amount": { # Here we call the APP to compute the royalty fee
"appId": 123,
"method": {
"name": "computeFee",
"args": ["number"],
"returns": {"type": "number"}
},
"args": ["$AMOUNT"] # We call the "computeFee" method and pass the $AMOUNT parameter
}
}
},
{
"type": "pay", # Payment transaction, the receiver is the seller
"args":
{"rcv": "$SELLER", "amount": "$AMOUNT"} # No constant args
},
{
"type": "axfer", # Transaction to transfer the asset
"args":
{"asnd": "$SELLER",
"snd": "$CLAWBACK_ADDRESS"
"xaid": 10,
"aamt": 1
},
}
]
},
{ # SCENARIO 2
"buyerAsset": [11, 12], # buy the ASA using either ASA 11 or 12
"transactionsGroup": [ # List of transactions
{
"type": "pay", # Royalty fee payment of 1 algo
"args":
{"rcv": "$CLAWBACK_ADDRESS", "amount": 100000}
},
{
"type": "xfer", # Transfer to the seller 1 ASA id 11 or 12
"args":
{"snd": "$BUYER", "aamt": 1} # here the field "xaid" can only be 11 or 12 implicitly (the clawback should check this)
},
{
"type": "axfer", # Transaction to transfer the asset
"args":
{"asnd": "$SELLER",
"snd": "$CLAWBACK_ADDRESS"
"xaid": 10,
"aamt": 1
},
}
]
}
}
Comments:
assetId
in the ASATransferControl interface (it feels redundant)I'd have to agree with the approach suggested by @whereisrysmind here. It's similar to how Solana approaches this issue, and their NFT standards & markets are mature. Royalty information is embedded into the asset; that may not be possible - but we can put it into the metadata.
"sellerFeeBasisPoints": 500,
"creators": [
{"address": "", "share": 0},
{"address": "", "share": 100}
]
sellerFeeBasisPoints
is the secondary sale fee in basis points to pay out to the creators. In this case 5%
creators
is the list of creators & their respective cut.
address
is a creator wallet.
share
is the percent the of the seller fee each creator gets. The total share value must be equal to 100.
Markets would be expected to honor this. Their web2 can make a grouped transaction that can easily accommodate this. Additionally, this can work for assets that currently exist if the metadata can be updated.
You can encode some of it compactly in the Reserve Address. I also want to use the Reserve Address for some trait data. Maybe we can share. But the reason I suggest this is because stateful contracts are now Turing complete and very shortly get the ability to call one another so it seems very obvious to anticipate exponential growth in on chain capabilities and expectations. Maybe I am crazy but people are going to want to access some of that data. And remember algod does not do anything with Reserve Address. They should just call it Reserved Data for your Algorand App. To prevent confusion could specify encoding scheme in metadata. I guess I just wanted to say, I love you Reserve Address. Holds so many if my hopes and dreams for on-chain processing. But seriously is there an alternative I am missing for accessing relevant data in a smart contract?
Please review and comment here: https://github.com/algorandfoundation/ARCs/pull/70
@rssalessio It looks like you put a lot of thought into the above comment, please take a look at #70 if you have time.
Closing this, the discussion is now here ARC-18 Royalty Enforcement
This issue is to discuss requirements and possible specification for an enforceable Royalty system for Algorand NFTs.
Please comment with any relevant information or ideas.
If you're already doing this please describe the method you're using.