Skip to content

Commit 227260d

Browse files
authored
Merge pull request #229 from hashed-io/fruniques/update_tests
Update tests
2 parents b80a224 + c97f91a commit 227260d

File tree

6 files changed

+117
-121
lines changed

6 files changed

+117
-121
lines changed

pallets/fruniques/README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
### **FR**actional **Uniques**
33
> This is WIP - just being spec'd out
44
5-
This pallet is being developed **tightly coupled** to both [`pallet_assets`](https://paritytech.github.io/substrate/latest/pallet_assets/) and [`pallet_uniques`](https://paritytech.github.io/substrate/latest/pallet_uniques/index.html). These are the default [Statemine](https://github.com/paritytech/cumulus/tree/master/polkadot-parachains/statemine) pallets for fungible and non-fungible tokens.
5+
This pallet is being developed **tightly coupled** to both [`pallet_assets`](https://paritytech.github.io/substrate/latest/pallet_assets/) and [`pallet_uniques`](https://paritytech.github.io/substrate/latest/pallet_uniques/index.html). These are the default [Statemine](https://github.com/paritytech/cumulus/tree/master/polkadot-parachains/statemine) pallets for fungible and non-fungible tokens.
66

77
A Frunique is a type of Non-Fungible Token (NFT)
88

@@ -19,20 +19,20 @@ This pallet provides functionality that allows NFT holders to mint a fungible to
1919

2020
The non-fungible token is created and minted using the Statemine `pallet_uniques`.
2121

22-
The fungible token is created and minted using the Statemine `pallet_assets`.
22+
The fungible token is created and minted using the Statemine `pallet_assets`.
2323

2424
The NFT/Unique can be unlocked and released if and only if a single origin holds all of the corresponding fungible token.
2525

2626
![basket-of-fungibles](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/hashed-io/hashed-substrate/main/docs/fungible-basket-frunique.iuml)
2727

2828
## NFT <--> NFTs
29-
An NFT as a trie of NFTs.
29+
An NFT as a trie of NFTs.
3030

3131
### Use cases
3232
#### Tax credit marketplace
3333
A credit is a single NFT, with an `amount`, state of redemption, expiration year, and other metadata. However, that owner can sell less than the `amount`, in which case the newly created credit NFT has all of the same associated data. The sum of the children `amount` values must be equal to the parent.
3434

35-
To support this, we'll create a `AggregatedFrunique` type that enforces the aggregation rules.
35+
To support this, we'll create a `AggregatedFrunique` type that enforces the aggregation rules.
3636

3737
#### Cannabis compliance
3838
For the NY state cannabis compliance program, all yield from all plants must be tracked. This aligns to a very similar data structure as above. Each mother plant is an NFT, each clone as an NFT, each package of flower an NFT, etc. Auditing a specific item is fairly easy via traversing all of its ancestors and descendants through to the harvest and dispensary.
@@ -58,7 +58,7 @@ yarn install
5858
```bash
5959
yarn run:api tx.fruniques.create 1 1 1 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 1 100 --seed "//Alice"
6060
```
61-
## Check NFT
61+
## Check NFT
6262
```bash
6363
yarn run:api query.uniques.class 1
6464
yarn run:api query.uniques.asset 1 1
@@ -71,9 +71,16 @@ yarn run:api query.assets.asset 1
7171
yarn run:api query.assets.account 1 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
7272
```
7373

74+
## Tests of the pallet
75+
76+
To run the test you simply run the following command
77+
```bash
78+
cargo test --package pallet-fruniques
79+
```
80+
7481
# Similar Projects
7582
### [Fractional Art](https://fractional.art/)
76-
83+
7784
### [Detailed Process Explainer with screenshots](https://medium.com/fractional-art/how-to-use-fractional-to-fractionalize-nfts-84da1a465b6d)
7885

7986
License: MIT

pallets/fruniques/src/functions.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ impl<T: Config> Pallet<T> {
3030
u32::from_ne_bytes(input.try_into().unwrap())
3131
}
3232

33+
pub fn percent_to_permill(input: u8) -> Permill {
34+
Permill::from_percent(input as u32)
35+
}
36+
3337
pub fn bytes_to_string(input: Vec<u8>) -> String {
3438
let mut s = String::default();
3539
for x in input {
@@ -196,7 +200,6 @@ impl<T: Config> Pallet<T> {
196200
collection: T::CollectionId,
197201
item: T::ItemId,
198202
owner: <T::Lookup as sp_runtime::traits::StaticLookup>::Source,
199-
_numeric_value: Option<Permill>,
200203
attributes: Option<Vec<(BoundedVec<u8, T::KeyLimit>, BoundedVec<u8, T::ValueLimit>)>>,
201204
) -> DispatchResult {
202205

pallets/fruniques/src/lib.rs

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ pub mod pallet {
2121
use frame_support::{pallet_prelude::*, transactional, BoundedVec};
2222
use frame_system::pallet_prelude::*;
2323
use scale_info::prelude::vec::Vec;
24-
use sp_runtime::Permill;
2524
/// Configure the pallet by specifying the parameters and types on which it depends.
2625
#[pallet::config]
2726
pub trait Config: frame_system::Config + pallet_uniques::Config {
@@ -41,22 +40,25 @@ pub mod pallet {
4140
#[pallet::event]
4241
#[pallet::generate_deposit(pub(super) fn deposit_event)]
4342
pub enum Event<T: Config> {
44-
// A frunique and asset class were succesfully created!
43+
// A frunique and asset class were successfully created!
4544
FruniqueCollectionCreated(T::AccountId, T::CollectionId),
46-
// A frunique and asset class were succesfully created!
45+
// A frunique and asset class were successfully created!
4746
FruniqueCreated(T::AccountId, T::AccountId, T::CollectionId, T::ItemId),
48-
// A frunique/unique was succesfully divided!
47+
// A frunique/unique was successfully divided!
4948
FruniqueDivided(T::AccountId, T::AccountId, T::CollectionId, T::ItemId),
5049
// Counter should work?
5150
NextFrunique(u32),
5251
}
5352

5453
#[pallet::error]
5554
pub enum Error<T> {
56-
NoneValue,
55+
// The user does not have permission to perform this action
5756
NoPermission,
57+
// Only the owner of the Frunique can perform this action
5858
NotAdmin,
59+
// The storage is full
5960
StorageOverflow,
61+
// A feature not implemented yet
6062
NotYetImplemented,
6163
// Too many fruniques were minted
6264
FruniqueCntOverflow,
@@ -70,6 +72,10 @@ pub mod pallet {
7072
AttributesEmpty,
7173
// The collection doesn't exist
7274
CollectionNotFound,
75+
// The parent doesn't exist
76+
ParentNotFound,
77+
// The frunique doesn't exist
78+
FruniqueNotFound,
7379
}
7480

7581
#[pallet::storage]
@@ -118,7 +124,7 @@ pub mod pallet {
118124
Blake2_128Concat,
119125
CollectionId, // Parent collection id
120126
Blake2_128Concat,
121-
ItemId, // Parent item id
127+
ItemId, // Parent item id
122128
Option<ChildInfo>, // ParentId and flag if it inherit attributes
123129
ValueQuery,
124130
>;
@@ -152,7 +158,6 @@ pub mod pallet {
152158
Self::account_id_to_lookup_source(&admin),
153159
)?;
154160

155-
156161
Self::deposit_event(Event::FruniqueCollectionCreated(admin, new_collection_id));
157162

158163
<NextCollection<T>>::put(Self::next_collection() + 1);
@@ -178,12 +183,12 @@ pub mod pallet {
178183

179184
/// ## Set multiple attributes to a frunique.
180185
/// `origin` must be signed by the owner of the frunique.
186+
/// - `class_id` must be a valid class of the asset class.
187+
/// - `instance_id` must be a valid instance of the asset class.
181188
/// - `attributes` must be a list of pairs of `key` and `value`.
182189
/// `key` must be a valid key for the asset class.
183190
/// `value` must be a valid value for the asset class.
184191
/// `attributes` must not be empty.
185-
/// - `instance_id` must be a valid instance of the asset class.
186-
/// - `class_id` must be a valid class of the asset class.
187192
188193
#[pallet::weight(10_000 + T::DbWeight::get().writes(1))]
189194
pub fn set_attributes(
@@ -192,6 +197,8 @@ pub mod pallet {
192197
instance_id: T::ItemId,
193198
attributes: Vec<(BoundedVec<u8, T::KeyLimit>, BoundedVec<u8, T::ValueLimit>)>,
194199
) -> DispatchResult {
200+
ensure!(Self::item_exists(&class_id, &instance_id), <Error<T>>::FruniqueNotFound);
201+
195202
// ! Ensure the admin is the one who can add attributes to the frunique.
196203
let admin = Self::admin_of(&class_id, &instance_id);
197204
let signer = core::prelude::v1::Some(ensure_signed(origin.clone())?);
@@ -224,59 +231,51 @@ pub mod pallet {
224231
///
225232
/// ### Parameters needed in order to divide a unique:
226233
/// - `class_id`: The type of NFT that the function will create, categorized by numbers.
227-
/// - `numeric_value`: The value of the NFT that the function will create.
228234
/// - `parent_info`: Information of the parent NFT and a flag to indicate the child would inherit their attributes.
229235
/// - `attributes`: Generates a list of attributes for the new NFT.
230236
///
231237
#[pallet::weight(10_000 + T::DbWeight::get().writes(4))]
232238
pub fn spawn(
233239
origin: OriginFor<T>,
234240
class_id: CollectionId,
235-
numeric_value: Option<Permill>,
236241
parent_info: Option<HierarchicalInfo>,
237242
attributes: Option<Vec<(BoundedVec<u8, T::KeyLimit>, BoundedVec<u8, T::ValueLimit>)>>,
238243
) -> DispatchResult {
244+
ensure!(Self::collection_exists(&class_id), <Error<T>>::CollectionNotFound);
245+
246+
if let Some(parent_info) = parent_info {
247+
ensure!(Self::item_exists(&class_id, &parent_info.0), <Error<T>>::ParentNotFound);
248+
}
249+
239250
let owner: T::AccountId = ensure_signed(origin.clone())?;
240251
let account_id = Self::account_id_to_lookup_source(&owner);
241252

242253
let instance_id: ItemId = <NextFrunique<T>>::try_get(class_id).unwrap_or(0);
243254
<NextFrunique<T>>::insert(class_id, instance_id + 1);
244255

245-
Self::do_spawn(
246-
origin.clone(),
247-
class_id,
248-
instance_id,
249-
account_id,
250-
numeric_value,
251-
attributes,
252-
)?;
253-
254-
Self::deposit_event(Event::FruniqueCreated(
255-
owner.clone(),
256-
owner,
257-
class_id,
258-
instance_id,
259-
));
260-
261-
262256
if let Some(parent_info) = parent_info {
263-
ensure!(
264-
Self::item_exists(&class_id, &parent_info.0),
265-
<Error<T>>::CollectionNotFound
266-
);
257+
ensure!(Self::item_exists(&class_id, &parent_info.0), <Error<T>>::ParentNotFound);
267258
<FruniqueParent<T>>::insert(class_id, instance_id, Some(parent_info));
268259

269260
let child_info = ChildInfo {
270261
collection_id: class_id,
271262
child_id: instance_id,
272263
is_hierarchical: parent_info.1,
273-
weight: numeric_value.unwrap()
264+
weight: Self::percent_to_permill(parent_info.2),
274265
};
275266

276267
<FruniqueChild<T>>::insert(class_id, instance_id, Some(child_info));
277-
278268
}
279269

270+
Self::do_spawn(origin.clone(), class_id, instance_id, account_id, attributes)?;
271+
272+
Self::deposit_event(Event::FruniqueCreated(
273+
owner.clone(),
274+
owner,
275+
class_id,
276+
instance_id,
277+
));
278+
280279
Ok(())
281280
}
282281

0 commit comments

Comments
 (0)