I have a struct declaration in Substrate Pallet as follows
Method implementations are inside Uniques pallet
The flow
- an NFT gets minted
- Sets Attributes for the minted NFT
- attribute value passed as a JSON
- Inside the split method, requires to get the attribute for the NFT
- modify current_fraction to another value
- Save the attribute
Uniques pallet's set_attribute function
pub fn set_attribute(
origin: OriginFor<T>,
collection: T::CollectionId,
maybe_item: Option<T::ItemId>,
key: BoundedVec<u8, T::KeyLimit>,
value: BoundedVec<u8, T::ValueLimit>,
)
So if I pass a JSON like { current_fraction: 1 }
from polkadot App, the value of value: BoundedVec<u8, T::ValueLimit>,
is [123, 34, 99, 117, 114, 114, 101, 110, 116, 95, 102, 114, 97, 99, 116, 105, 111, 110, 34, 58, 49, 125]
,
So this is the value I need to retrieve , reconstruct the object, modify its value and saves it
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)]
pub struct RealEstateValues {
pub current_fraction: u32,
}
I have a function to split an Asset written in the same substrate pallet as follows
#[pallet::call_index(26)]
#[pallet::weight(T::WeightInfo::split())]
pub fn split(
sender_origin: OriginFor<T>,
collection_id: T::CollectionId,
item_id: T::ItemId,
key: BoundedVec<u8, T::KeyLimit>,
num_of_splits: u32,
) -> DispatchResult {
// TODO: Move this to config or storage
let property_key = "realEstateData";
let maximum_splits: u32 = 52u32;
// ensure!(num_of_splits.le(&maximum_splits), Error::<T, I>::ExceedsMaxSplits);
let property_key: BoundedVec<u8, ConstU32<32>> =
BoundedVec::try_from(property_key.as_bytes().to_vec()).map_err(|_| Error::<T, I>::ErrorConvertingKey)?;
ensure!(key == property_key, Error::<T, I>::InvalidKey);
let origin = ensure_signed(sender_origin.clone())?;
let item_metadata = ItemMetadataOf::<T, I>::get(collection_id, item_id).ok_or(Error::<T, I>::MetadataMissingForItem)?;
let item_attribute = Attribute::<T, I>::get((collection_id, Some(item_id), &key)).ok_or(Error::<T, I>::AttributesMissingForItem)?;
let real_estate_value = item_attribute.0.clone();
let item_attribute_vec = real_estate_value.to_vec();
let real_estate_value_json: RealEstateValues = Decode::decode(&mut &item_attribute_vec[..]).map_err(|_| Error::<T, I>::InvalidPropertyAttributes)?;
// ***********************************************************************************
// ***********************************************************************************
ensure!(num_of_splits.saturating_mul(real_estate_value_json.current_fraction).le(&maximum_splits), Error::<T, I>::ExceedsMaxSplits);
let new_real_estate_values = RealEstateValues {
current_fraction: num_of_splits,
};
let new_real_estate_values_bounded_vec: BoundedVec<u8, T::ValueLimit> = Encode::encode(&new_real_estate_values).try_into().unwrap();//new_real_estate_values.encode().try_into().unwrap();
// let p = serde_json::to_string(&new_real_estate_values).unwrap();
// // let new_real_estate_values_bounded_vec: BoundedVec<u8, T::ValueLimit> = p.as_bytes();
let mut current_item_id = CurrentItemItByCollection::<T, I>::get(collection_id).ok_or(Error::<T, I>::UnknownItem)?;
let parent_item_id = current_item_id;
let mut new_item_ids = Vec::with_capacity(num_of_splits as usize);
for _ in 0..num_of_splits {
let next_nft_id = <T as Config<I>>::ItemId::from(current_item_id.into() + 1);
new_item_ids.push(next_nft_id);
Self::do_mint(collection_id, next_nft_id, origin.clone(), |collection_details| {
if collection_details.issuer != origin {
return Err(Error::<T, I>::NoPermission.into());
}
Ok(())
})?;
Self::deposit_event(Event::NewItemMinted { collection_id, item_id: next_nft_id, owner:origin.clone() });
Self::set_metadata(
sender_origin.clone(),
collection_id,
next_nft_id,
item_metadata.data.clone(),
false,
)?;
Self::set_attribute(
sender_origin.clone(),
collection_id,
Some(next_nft_id),
key.clone(),
new_real_estate_values_bounded_vec.clone(),
)?;
current_item_id = next_nft_id;
}
CurrentItemItByCollection::<T, I>::set(collection_id, Some(current_item_id));
Self::deposit_event(Event::ItemSplit {
parent_collection_id: collection_id,
parent_item_id: item_id,
num_of_splits,
new_item_ids,
owner: origin.clone(),
});
// Burn parent NFT on success
Self::do_burn(collection_id, parent_item_id, |collection_details, details| {
let is_permitted = collection_details.admin == origin || details.owner == origin;
ensure!(is_permitted, Error::<T, I>::NoPermission);
Ok(())
})?;
Self::deposit_event(Event::Burned { collection: collection_id, item: parent_item_id, owner: origin });
Ok(())
}
This is the corresponding test
#[test]
fn split_should_work() {
new_test_ext().execute_with(|| {
Balances::make_free_balance_be(&1, 100_000);
let key_str = "realEstateData";
let key_bounded_vec: BoundedVec<u8, ConstU32<50>> =key_str.as_bytes().to_vec().try_into().unwrap();
let mut item_id = 1u32;
let collection_id = 1u32;
println!("real_estate_values: {:?}", real_estate_values);
let json_data = serde_json::json!({
"current_fraction": 1,
});
// Serialize the JSON data to bytes
let serialized_json = serde_json::to_vec(&json_data).unwrap();
let real_estate_values_bounded_vec: BoundedVec<u8, ConstU32<50>> = serialized_json.try_into().unwrap();
// let real_estate_values_bounded_vec: BoundedVec<u8, ConstU32<50>> = serde_json::to_string(&real_estate_values).unwrap().as_bytes().to_vec().try_into().unwrap();
// let real_estate_values_bounded_vec: BoundedVec<u8, ConstU32<50>> = Encode::encode(&real_estate_values).try_into().unwrap();
println!("real_estate_values_bounded_vec: {:?}", real_estate_values_bounded_vec);
let real_estate_value_json: RealEstateValues = Decode::decode(&mut &real_estate_values_bounded_vec[..]).unwrap();
println!("real_estate_value_json: {:?}", real_estate_value_json);
assert_ok!(Uniques::create(RuntimeOrigin::signed(1), collection_id, 1));
assert_ok!(Uniques::mint(RuntimeOrigin::signed(1), collection_id, item_id, 1));
// Sets Metadata
assert_ok!(Uniques::set_metadata(
RuntimeOrigin::signed(1),
collection_id,
item_id,
bvec![0, 0],
false
));
// Sets Attributes
assert_ok!(Uniques::set_attribute(
RuntimeOrigin::signed(1),
collection_id,
Some(item_id),
key_bounded_vec.clone(),
real_estate_values_bounded_vec
));
assert_ok!(Uniques::set_price(RuntimeOrigin::signed(1), collection_id, item_id, Some(400), None));
assert_ok!(Uniques::set_metadata(RuntimeOrigin::signed(1), collection_id, item_id, bvec![42, 42], false));
assert_ok!(Uniques::split(RuntimeOrigin::signed(1), collection_id, item_id, key_bounded_vec.clone(), 4));
item_id += 1;
assert_ok!(Uniques::set_metadata(
RuntimeOrigin::signed(1),
collection_id,
item_id,
bvec![0, 0],
false
));
assert_noop!(
Uniques::split(RuntimeOrigin::signed(1), collection_id, item_id, key_bounded_vec, 14),
Error::<Test>::ExceedsMaxSplits
);
});
}
So when I do
let real_estate_value_json: RealEstateValues = Decode::decode(&mut &item_attribute_vec[..]).map_err(|_| Error::<T, I>::InvalidPropertyAttributes)?;
when I try to use serde or serde_json dependency it throws an error when Im compiling substrate
it gives this error
error[E0152]: duplicate lang item in crate `std` (which `serde_json` depends on): `panic_impl`.
|
= note: the lang item is first defined in crate `sp_io` (which `sp_application_crypto` depends on)
= note: first definition in `sp_io`
If I use this method in serde
let real_estate_value_json: RealEstateValues = from_slice(&item_attribute_vec)
.map_err(|_| Error::<T, I>::InvalidPropertyAttributes)?;
```
It incorrectly sets current_fraction value
How to fix this issue?