-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add pallet-api/nonfungibles
#432
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
@@ Coverage Diff @@
## main #432 +/- ##
==========================================
+ Coverage 71.28% 73.93% +2.64%
==========================================
Files 72 76 +4
Lines 13626 15237 +1611
Branches 13626 15237 +1611
==========================================
+ Hits 9713 11265 +1552
- Misses 3646 3711 +65
+ Partials 267 261 -6
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking very good! Left a few comments based on a high level review. Ready to finalise everything as I didn't find anything concerning!
CI also failing which would be good to resolve for final review. |
df829e7
to
13434d3
Compare
8af4673
to
ac93544
Compare
// Measured: `606` | ||
// Estimated: `68341938741594600 + a * (3168 ±44_956_152_011_428) + i * (2663 ±729_819_634_434_010)` | ||
// Minimum execution time: 13_000_000 picoseconds. | ||
Weight::from_parts(25_055_102, 68341938741594600) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonder why this is unexpectedly high
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stopped reviewing after the events in mod.rs
because I have found too many deviations from the spec without any explanation. Could you please give the PR a final review yourself while looking at the spec (especially found different comments) and explain any changes made to prevent confusion :)
pallets/api/src/nonfungibles/mod.rs
Outdated
/// The creator of the collection. | ||
creator: AccountIdOf<T>, | ||
/// The administrator of the collection. | ||
admin: AccountIdOf<T>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for deviating from the owner
parameter in pallet-nfts
or is this forgotten in the copy paste of the fungibles event?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it on purpose. I don't think the owner
field in the pallet-nfts
makes sense because it is not the owner but the admin. And this also makes it similar to the one we have in pallet-api/fungibles
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't that be confusing with:
pop-node/pallets/nfts/src/types.rs
Line 100 in 8b2438b
pub(super) owner: AccountId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally see that field is incorrect. Maybe it could be changed in pallet-nfts
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up to you, we can also create an issue and adapt later as it delays finishing this PR
df27276
to
ea59ba3
Compare
/// The events that can be emitted. | ||
#[pallet::event] | ||
#[pallet::generate_deposit(pub(super) fn deposit_event)] | ||
pub enum Event<T: Config> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Primary change made to the events that deviate from the spec is the introduction of collection
field. The PSP34 standard events are designed for a single non-fungible token. Hence, to support multiple tokens, we need the collection
field to specify the collection.
} | ||
|
||
#[pallet::call] | ||
impl<T: Config> Pallet<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the events, to support multiple tokens, we also need to accept collection
as parameters to dispatchables related to a non-fungible token.
}, | ||
/// Details of a specified collection. | ||
#[codec(index = 9)] | ||
Collection(CollectionIdOf<T>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following Read
operations, along with the others listed below, are additional reads not specified in the standard or spec. However, I believe they are essential to the design of the Pop API's nonfungible feature. Another potential Read to consider is Item(CollectionIdOf<T>, ItemIdOf<T>)
, which queries the details of a collection item.
pallets/api/src/nonfungibles/mod.rs
Outdated
/// - `item` - The item of the collection of whose approvals will be cleared. | ||
#[pallet::call_index(5)] | ||
#[pallet::weight(NftsWeightInfoOf::<T>::clear_all_transfer_approvals())] | ||
pub fn clear_all_transfer_approvals( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional dispatchables added that are not mentioned in the spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to hear why
Also, can we follow the order of the dispatchables defined in the spec. Makes it easier to review. The more unknown / not explained changes in a PR the more question marks arise for the reviewer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are implemented for the pallet while the spec mainly stick to the PSP34 standard. And we did agree that we should not limit the capabilities of the pallet. Hence, as we already provided this in the fork pallet nfts, implement it for the nonfungibles pallet will enrich the pallet features
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does however make the PR bigger, more to review, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solid PR! Most of the functionality is correct so well done!
Most of the comments are related to differences with the spec which caused confusion as deviating from it means that it needs to be reviewed again which takes time. In addition, having the order of functions in the same order makes the review process streamlined, same counts for the order of tests with the code (i.e. dispatachables order, etc).
Moreover, the additional functions that have been added create review overload (because it is already a very big PR) because this requires much more than reviewing the logic, tests and benchmarks. This is also why I haven't reviewed those functions.
pallets/api/src/nonfungibles/mod.rs
Outdated
/// The creator of the collection. | ||
creator: AccountIdOf<T>, | ||
/// The administrator of the collection. | ||
admin: AccountIdOf<T>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't that be confusing with:
pop-node/pallets/nfts/src/types.rs
Line 100 in 8b2438b
pub(super) owner: AccountId, |
@@ -46,7 +52,7 @@ impl frame_system::Config for Test { | |||
type BlockHashCount = BlockHashCount; | |||
type BlockLength = (); | |||
type BlockWeights = (); | |||
type DbWeight = (); | |||
type DbWeight = RocksDbWeight; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requires for testing with <Test as frame_system::Config>::DbWeight::get()::reads(1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just skimmed over and found some improvements in the impl.rs
. I will do a proper review monday.
…elease separately (#433)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to see how clean this and simple the mapping is to the pallet.
I haven't reviewed everything, but there are several comments I left.
The biggest one is that in several of these functions you are doing reads before calling into pallet-nfts. However, these values are only needed for the event. It also has the side-effect of double charging weight. By moving these reads to AFTER the pallet-nft call we get a few things:
- pallet-nfts does the error handling for us
- pallet-nfts pulls the storage items into the memory overlay -- hence allowing us to remove the double charged weight
item: ItemIdOf<T>, | ||
to: AccountIdOf<T>, | ||
) -> DispatchResultWithPostInfo { | ||
let owner = NftsOf::<T>::owner(collection, item) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend we move this let owner ...
to AFTER the pallet-nfts::transfer and do NOT charge for the read. The reason we don't charge for the read is because the queried item is already in the memory overlay and won't be read from storage. Additionally, we can simplify to use a .expect(...)
because pallet-nfts will handle the error checking for us.
/// - `item` - The item. | ||
/// - `to` - The recipient account. | ||
#[pallet::call_index(3)] | ||
#[pallet::weight(NftsWeightInfoOf::<T>::transfer() + T::DbWeight::get().reads(1))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similarly, this extra read should not be needed as the Item is already in the memory overlay (hence we are double charging for the item).
If you ran a benchmark with and without the let owner = ...
you should see the number of reads stays the same.
to: AccountIdOf<T>, | ||
collection: CollectionIdOf<T>, | ||
item: ItemIdOf<T>, | ||
witness: MintWitness<ItemIdOf<T>, ItemPriceOf<T>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would we be able to modify this parameter to something like:
- Change name to
price
- Set type to
ItemPriceOf
only - If the witness is optional (as is in the actual pallet-nfts::mint), we should do
maybe_price
The item id is already a parameter, so we can create the MintWitness instance ourselves in this function.
/// - `collection` - The collection identifier. | ||
/// - `item` - The item to burn. | ||
#[pallet::call_index(8)] | ||
#[pallet::weight(NftsWeightInfoOf::<T>::burn() + T::DbWeight::get().reads(1))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to transfer, we are double charging a read here due to the memory overlay.
collection: CollectionIdOf<T>, | ||
item: ItemIdOf<T>, | ||
) -> DispatchResultWithPostInfo { | ||
let owner = NftsOf::<T>::owner(collection, item) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, would recommend simplifying this by moving it after pallet-nfts::burn, use .expect(), and remove the error mapping with weight read.
/// - `admin` - The admin of this collection. | ||
/// - `config` - The configuration of the collection. | ||
#[pallet::call_index(12)] | ||
#[pallet::weight(NftsWeightInfoOf::<T>::create() + T::DbWeight::get().reads(1))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
double-charging
config: CollectionConfigOf<T>, | ||
) -> DispatchResultWithPostInfo { | ||
let creator = ensure_signed(origin.clone())?; | ||
let id = NextCollectionIdOf::<T>::get() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would recommend moving after nfts::create, use .expect.
It's cleaner to let pallet-nfts handle the errors for us and pull the storage into the memory overlay.
witness.item_configs, | ||
witness.attributes, | ||
))] | ||
pub fn destroy( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Preface: I haven't reviewed the destroy witness PR). One thought, why don't we expand the standard a little bit and have functionality similar to:
- destroy_without_witness // max weight until post-dispatch
- destroy_with_witness // optional choice to provide the witness for accurate weight
Note: I am not saying to use those names, but just representing the functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see here for more context: #445 (comment)
collection, | ||
item, | ||
key: key.to_vec(), | ||
data: value.to_vec(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are currently cloning each of these twice. Once when you do key.clone()
, and once when you do key.to_vec()
I recommend defining two vars at the top of the fn: key_vec = key.to_vec();
and data_vec = data.to_vec()
. This then allows you to do only one clone (which occurs with the .to_vec()).
The non-fungibles pallet offers a streamlined interface for interacting with non-fungible assets. The goal is to provide a simplified, consistent API that adheres to standards in the smart contract space. Built on top of the optimized version of the
pallet-nfts
(See #387).[sc-1242]