diff --git a/.gitignore b/.gitignore index b386776c..7e172432 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ gcloud-md5-ingress.yml collator-data relay-data .vscode/settings.json +lcov.info diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 97c635ce..0921b140 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -10,6 +10,8 @@ "args": [ "run", "--release", + "--bin", + "hashed", "--", "--dev" ], diff --git a/pallets/fruniques/Cargo.toml b/pallets/fruniques/Cargo.toml index dd4bcbd9..1b2c6154 100644 --- a/pallets/fruniques/Cargo.toml +++ b/pallets/fruniques/Cargo.toml @@ -43,6 +43,7 @@ std = [ "frame-system/std", "frame-benchmarking/std", "sp-runtime/std", + "pallet-rbac/std", ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] diff --git a/pallets/fruniques/src/functions.rs b/pallets/fruniques/src/functions.rs index 4e4c001b..8ed643da 100644 --- a/pallets/fruniques/src/functions.rs +++ b/pallets/fruniques/src/functions.rs @@ -1,9 +1,9 @@ use super::*; use crate::types::*; -use frame_system::pallet_prelude::*; - +use frame_support::sp_io::hashing::blake2_256; use frame_support::traits::tokens::nonfungibles::Inspect; +use frame_system::pallet_prelude::*; use scale_info::prelude::string::String; use pallet_rbac::types::*; @@ -82,7 +82,63 @@ impl Pallet { } pub fn do_initial_setup() -> DispatchResult { - let _pallet_id = Self::pallet_id(); + + let pallet: IdOrVec = Self::pallet_id(); + + let owner_role_ids = T::Rbac::create_and_set_roles( + pallet.clone(), + FruniqueRole::get_owner_roles())?; + + for owner_role in owner_role_ids { + T::Rbac::create_and_set_permissions( + pallet.clone(), + owner_role, + Permission::owner_permissions())?; + } + + let admin_role_ids = T::Rbac::create_and_set_roles( + pallet.clone(), + FruniqueRole::get_admin_roles())?; + + for admin_role in admin_role_ids { + T::Rbac::create_and_set_permissions( + pallet.clone(), + admin_role, + Permission::admin_permissions())?; + } + + let collaborator_role_ids = T::Rbac::create_and_set_roles( + pallet.clone(), + FruniqueRole::get_collaborator_roles())?; + + for collaborator_role in collaborator_role_ids { + T::Rbac::create_and_set_permissions( + pallet.clone(), + collaborator_role, + Permission::collaborator_permissions())?; + } + + let collector_role_ids = T::Rbac::create_and_set_roles( + pallet.clone(), + FruniqueRole::get_collector_roles())?; + + for collector_role in collector_role_ids { + T::Rbac::create_and_set_permissions( + pallet.clone(), + collector_role, + Permission::collector_permissions())?; + } + + let holder_role_ids = T::Rbac::create_and_set_roles( + pallet.clone(), + FruniqueRole::get_holder_roles())?; + + for holder_role in holder_role_ids { + T::Rbac::create_and_set_permissions( + pallet.clone(), + holder_role, + Permission::holder_permissions())?; + } Ok(()) } @@ -150,6 +206,11 @@ impl Pallet { ) -> DispatchResult { let owner = T::CreateOrigin::ensure_origin(origin.clone(), &class_id)?; + let scope_id = class_id.using_encoded(blake2_256); + T::Rbac::create_scope(Self::pallet_id(), scope_id)?; + + Self::insert_auth_in_frunique_collection(owner.clone(), class_id, FruniqueRole::Owner)?; + pallet_uniques::Pallet::::do_create_collection( class_id, owner.clone(), @@ -197,12 +258,18 @@ impl Pallet { origin: OriginFor, collection: T::CollectionId, item: T::ItemId, - owner: ::Source, + owner: T::AccountId, metadata: CollectionDescription, attributes: Option>, ) -> DispatchResult { ensure!(Self::collection_exists(&collection), >::CollectionNotFound); - pallet_uniques::Pallet::::mint(origin.clone(), collection, item, owner)?; + let user: T::AccountId = ensure_signed(origin.clone())?; + Self::is_authorized(user, collection, Permission::Mint)?; + + // pallet_uniques::Pallet::::do_mint(collection, item, owner, |_| Ok(()))?; + pallet_uniques::Pallet::::do_mint(collection, item, owner, |_| { + Ok(()) + })?; pallet_uniques::Pallet::::set_metadata( origin.clone(), @@ -227,7 +294,37 @@ impl Pallet { Ok(()) } + /// Helper functions to interact with the RBAC module pub fn pallet_id() -> IdOrVec { IdOrVec::Vec(Self::module_name().as_bytes().to_vec()) } + + pub fn insert_auth_in_frunique_collection( + user: T::AccountId, + class_id: T::CollectionId, + role: FruniqueRole, + ) -> DispatchResult { + T::Rbac::assign_role_to_user( + user, + Self::pallet_id(), + &class_id.using_encoded(blake2_256), + role.id(), + )?; + + Ok(()) + } + + fn is_authorized( + user: T::AccountId, + collection_id: T::CollectionId, + permission: Permission, + ) -> DispatchResult { + let scope_id = collection_id.using_encoded(blake2_256); + ::Rbac::is_authorized( + user, + Self::pallet_id(), + &scope_id, + &permission.id(), + ) + } } diff --git a/pallets/fruniques/src/lib.rs b/pallets/fruniques/src/lib.rs index d62e87c2..e90dfcee 100644 --- a/pallets/fruniques/src/lib.rs +++ b/pallets/fruniques/src/lib.rs @@ -20,6 +20,8 @@ pub mod pallet { use crate::types::*; use frame_support::{pallet_prelude::*, transactional}; use frame_system::pallet_prelude::*; + + use pallet_rbac::types::RoleBasedAccessControl; /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] pub trait Config: frame_system::Config + pallet_uniques::Config { @@ -30,6 +32,8 @@ pub mod pallet { /// Maximum number of children a Frunique can have #[pallet::constant] type ChildMaxLen: Get; + + type Rbac : RoleBasedAccessControl; } #[pallet::pallet] @@ -47,6 +51,8 @@ pub mod pallet { FruniqueDivided(T::AccountId, T::AccountId, T::CollectionId, T::ItemId), // A frunique has been verified. FruniqueVerified(T::AccountId, CollectionId, ItemId), + // A user has been invited to collaborate on a collection. + InvitedToCollaborate(T::AccountId, T::AccountId, T::CollectionId), // Counter should work? NextFrunique(u32), } @@ -158,7 +164,7 @@ pub mod pallet { #[pallet::weight(10_000 + T::DbWeight::get().writes(10))] pub fn initial_setup(origin: OriginFor) -> DispatchResult { T::RemoveOrigin::ensure_origin(origin.clone())?; - // Self::do_initial_setup()?; + Self::do_initial_setup()?; Ok(()) } @@ -263,7 +269,6 @@ pub mod pallet { } let owner: T::AccountId = ensure_signed(origin.clone())?; - let account_id = Self::account_id_to_lookup_source(&owner); let instance_id: ItemId = >::try_get(class_id).unwrap_or(0); >::insert(class_id, instance_id + 1); @@ -282,7 +287,7 @@ pub mod pallet { >::insert(class_id, instance_id, Some(child_info)); } - Self::do_spawn(origin, class_id, instance_id, account_id, metadata, attributes)?; + Self::do_spawn(origin, class_id, instance_id, owner.clone(), metadata, attributes)?; Self::deposit_event(Event::FruniqueCreated( owner.clone(), @@ -317,6 +322,30 @@ pub mod pallet { Ok(()) } + /// ## Invite a user to become a collaborator in a collection. + /// ### Parameters: + /// - `origin` must be signed by the owner of the frunique. + /// - `class_id` must be a valid class of the asset class. + /// - `invitee` must be a valid user. + /// ### Considerations: + /// This functions enables the owner of a collection to invite a user to become a collaborator. + /// The user will be able to create NFTs in the collection. + /// The user will be able to add attributes to the NFTs in the collection. + + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn invite( + origin: OriginFor, + class_id: CollectionId, + invitee: T::AccountId, + ) -> DispatchResult { + + let owner: T::AccountId = ensure_signed(origin.clone())?; + Self::insert_auth_in_frunique_collection(invitee.clone(), class_id, FruniqueRole::Collaborator)?; + + Self::deposit_event(Event::InvitedToCollaborate(owner, invitee, class_id)); + Ok(()) + } + /// ## Force set counter /// ### Parameters: /// `origin` must be signed by the Root origin. @@ -391,7 +420,7 @@ pub mod pallet { let _ = >::clear(1000, None); let _ = >::clear(1000, None); let _ = >::clear(1000, None); - + T::Rbac::remove_pallet_storage(Self::pallet_id())?; Ok(()) } } diff --git a/pallets/fruniques/src/mock.rs b/pallets/fruniques/src/mock.rs index ddb59b34..59901fdf 100644 --- a/pallets/fruniques/src/mock.rs +++ b/pallets/fruniques/src/mock.rs @@ -22,6 +22,7 @@ construct_runtime!( Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Fruniques: pallet_fruniques::{Pallet, Call, Storage, Event}, + RBAC: pallet_rbac::{Pallet, Call, Storage, Event}, } ); parameter_types! { @@ -60,6 +61,7 @@ impl pallet_fruniques::Config for Test { type Event = Event; type RemoveOrigin = EnsureRoot; type ChildMaxLen = ChildMaxLen; + type Rbac = RBAC; } parameter_types! { @@ -112,7 +114,39 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; } +parameter_types! { + pub const MaxScopesPerPallet: u32 = 2; + pub const MaxRolesPerPallet: u32 = 6; + pub const RoleMaxLen: u32 = 25; + pub const PermissionMaxLen: u32 = 25; + pub const MaxPermissionsPerRole: u32 = 11; + pub const MaxRolesPerUser: u32 = 2; + pub const MaxUsersPerRole: u32 = 2; +} +impl pallet_rbac::Config for Test { + type Event = Event; + type MaxScopesPerPallet = MaxScopesPerPallet; + type MaxRolesPerPallet = MaxRolesPerPallet; + type RoleMaxLen = RoleMaxLen; + type PermissionMaxLen = PermissionMaxLen; + type MaxPermissionsPerRole = MaxPermissionsPerRole; + type MaxRolesPerUser = MaxRolesPerUser; + type MaxUsersPerRole = MaxUsersPerRole; +} // Build genesis storage according to the mock runtime. // pub(crate) fn new_test_ext() -> sp_io::TestExternalities { // frame_system::GenesisConfig::default().build_storage::().unwrap().into() // } +// Build genesis storage according to the mock runtime. + + +pub fn new_test_ext() -> sp_io::TestExternalities { + let balance_amount = 1_000_000 as u64; + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(1, balance_amount), (2, balance_amount), (3, balance_amount)], + }.assimilate_storage(&mut t).expect("assimilate_storage failed"); + let mut t: sp_io::TestExternalities = t.into(); + t.execute_with(|| Fruniques::do_initial_setup().expect("Error on configuring initial setup")); + t +} diff --git a/pallets/fruniques/src/tests.rs b/pallets/fruniques/src/tests.rs index a201bd1c..08ed8570 100644 --- a/pallets/fruniques/src/tests.rs +++ b/pallets/fruniques/src/tests.rs @@ -1,6 +1,6 @@ use crate::{mock::*, Error}; +use codec::Encode; use core::convert::TryFrom; -use codec::{Encode}; use frame_support::{assert_noop, assert_ok, BoundedVec}; @@ -40,16 +40,17 @@ fn dummy_description() -> BoundedVec { fn dummy_attributes() -> Vec<(BoundedVec, BoundedVec)> { vec![( - BoundedVec::::try_from(b"dummy key".encode()).expect("Error on encoding key to BoundedVec"), - BoundedVec::::try_from(b"dummy value".encode()).expect("Error on encoding value to BoundedVec"), - )] + BoundedVec::::try_from(b"dummy key".encode()) + .expect("Error on encoding key to BoundedVec"), + BoundedVec::::try_from(b"dummy value".encode()) + .expect("Error on encoding value to BoundedVec"), + )] } fn dummy_empty_attributes() -> Vec<(BoundedVec, BoundedVec)> { vec![] } - #[test] fn create_collection_works() { ExtBuilder::default().build().execute_with(|| { @@ -61,23 +62,49 @@ fn create_collection_works() { fn spawn_extrinsic_works() { ExtBuilder::default().build().execute_with(|| { // A collection must be created before spawning an NFT - assert_noop!(Fruniques::spawn(Origin::signed(1), 0, None, dummy_description(), None), Error::::CollectionNotFound); + assert_noop!( + Fruniques::spawn(Origin::signed(1), 0, None, dummy_description(), None), + Error::::CollectionNotFound + ); // Create a collection assert_ok!(Fruniques::create_collection(Origin::signed(1), dummy_description())); // The first item can not be a child - assert_noop!(Fruniques::spawn(Origin::signed(1), 0, Some((0, false, 10)), dummy_description(), None), Error::::ParentNotFound); + assert_noop!( + Fruniques::spawn(Origin::signed(1), 0, Some((0, false, 10)), dummy_description(), None), + Error::::ParentNotFound + ); // A NFT can be created with empty data assert_ok!(Fruniques::spawn(Origin::signed(1), 0, None, dummy_description(), None)); // A NFT can be created with attributes - assert_ok!(Fruniques::spawn(Origin::signed(1), 0, None, dummy_description(), Some(dummy_attributes()))); + assert_ok!(Fruniques::spawn( + Origin::signed(1), + 0, + None, + dummy_description(), + Some(dummy_attributes()) + )); // A NFT can be hierarchical - assert_ok!(Fruniques::spawn(Origin::signed(1), 0, Some((0, false, 10)), dummy_description(), None)); + assert_ok!(Fruniques::spawn( + Origin::signed(1), + 0, + Some((0, false, 10)), + dummy_description(), + None + )); // The parent must exist - assert_noop!(Fruniques::spawn(Origin::signed(1), 0, Some((100, false, 10)), dummy_description(), None), Error::::ParentNotFound); - + assert_noop!( + Fruniques::spawn( + Origin::signed(1), + 0, + Some((100, false, 10)), + dummy_description(), + None + ), + Error::::ParentNotFound + ); }) } @@ -85,16 +112,37 @@ fn spawn_extrinsic_works() { fn set_attributes_works() { ExtBuilder::default().build().execute_with(|| { // A collection must be created before spawning an NFT - assert_noop!(Fruniques::spawn(Origin::signed(1), 0, None, dummy_description(), None), Error::::CollectionNotFound); + assert_noop!( + Fruniques::spawn(Origin::signed(1), 0, None, dummy_description(), None), + Error::::CollectionNotFound + ); // Create a collection assert_ok!(Fruniques::create_collection(Origin::signed(1), dummy_description())); // Attributes can be added only to existing NFTs - assert_noop!(Fruniques::set_attributes(Origin::signed(1), 0, 0, dummy_attributes()), Error::::FruniqueNotFound); + assert_noop!( + Fruniques::set_attributes(Origin::signed(1), 0, 0, dummy_attributes()), + Error::::FruniqueNotFound + ); // A NFT can be created with empty data assert_ok!(Fruniques::spawn(Origin::signed(1), 0, None, dummy_description(), None)); // Attributes can not be empty - assert_noop!(Fruniques::set_attributes(Origin::signed(1), 0, 0, dummy_empty_attributes()), Error::::AttributesEmpty); - + assert_noop!( + Fruniques::set_attributes(Origin::signed(1), 0, 0, dummy_empty_attributes()), + Error::::AttributesEmpty + ); }) } + +#[test] +fn invite_collaborator_works() { + new_test_ext().execute_with(|| { + // Create a collection + assert_ok!(Fruniques::create_collection(Origin::signed(1), dummy_description())); + assert_ok!(Fruniques::invite( + Origin::signed(1), + 0, + 2 + )); + }); +} diff --git a/pallets/fruniques/src/types.rs b/pallets/fruniques/src/types.rs index d8ef186a..60ab39a0 100644 --- a/pallets/fruniques/src/types.rs +++ b/pallets/fruniques/src/types.rs @@ -3,8 +3,8 @@ use super::*; use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; -use sp_runtime::Permill; use sp_runtime::sp_std::vec::Vec; +use sp_runtime::Permill; pub type AttributeKey = BoundedVec::KeyLimit>; pub type AttributeValue = BoundedVec::ValueLimit>; @@ -20,6 +20,10 @@ pub type CollectionDescription = StringLimit; // (ParentId, Hierarchical, Percentage) pub type HierarchicalInfo = (ItemId, bool, u8); +// pub type RoleId = + +// pub type RoleIds = BoundedVec<[u8; 32], <::Rbac as RoleBasedAccessControl<::AccountId>>::MaxRolesPerPallet>; + #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen)] pub struct ChildInfo { pub collection_id: CollectionId, @@ -44,6 +48,7 @@ pub enum FruniqueRole { Admin, Collaborator, Collector, + Holder, } impl Default for FruniqueRole { @@ -59,6 +64,7 @@ impl FruniqueRole { Self::Admin => "Admin".as_bytes().to_vec(), Self::Collaborator => "Collaborator".as_bytes().to_vec(), Self::Collector => "Collector".as_bytes().to_vec(), + Self::Holder => "Holder".as_bytes().to_vec(), } } @@ -66,9 +72,31 @@ impl FruniqueRole { self.to_vec().using_encoded(blake2_256) } + pub fn get_owner_roles() -> Vec> { + [Self::Owner.to_vec()].to_vec() + } + + pub fn get_admin_roles() -> Vec> { + [Self::Admin.to_vec()].to_vec() + } + + pub fn get_collaborator_roles() -> Vec> { + [Self::Collaborator.to_vec()] + .to_vec() + } + + pub fn get_collector_roles() -> Vec> { + [Self::Collector.to_vec()].to_vec() + } + + pub fn get_holder_roles() -> Vec> { + [Self::Holder.to_vec()].to_vec() + } + pub fn enum_to_vec() -> Vec> { use crate::types::FruniqueRole::*; - [Owner.to_vec(), Admin.to_vec(), Collaborator.to_vec(), Collector.to_vec()].to_vec() + [Owner.to_vec(), Admin.to_vec(), Collaborator.to_vec(), Collector.to_vec(), Holder.to_vec()] + .to_vec() } } @@ -77,26 +105,23 @@ impl FruniqueRole { Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy, )] pub enum Permission { - /// No authorization required + /// Not a permission None, - /// Authorization required - Required, /// Authorization required and must be approved by the owner Mint, /// Authorization required and must be approved by a holder / collector Transfer, /// Allow a user to collaborate on a collection - Collaborate, + InviteCollaborator, } impl Permission { pub fn to_vec(self) -> Vec { match self { Self::None => "None".as_bytes().to_vec(), - Self::Required => "Required".as_bytes().to_vec(), Self::Mint => "Mint".as_bytes().to_vec(), Self::Transfer => "Transfer".as_bytes().to_vec(), - Self::Collaborate => "Collaborate".as_bytes().to_vec(), + Self::InviteCollaborator => "InviteCollaborator".as_bytes().to_vec(), } } @@ -104,21 +129,21 @@ impl Permission { self.to_vec().using_encoded(blake2_256) } + pub fn get_permissions() -> Vec> { + use crate::types::Permission::*; + [None.to_vec(), Mint.to_vec(), Transfer.to_vec(), InviteCollaborator.to_vec()] + .to_vec() + } + pub fn owner_permissions() -> Vec> { use crate::types::Permission::*; - [None.to_vec(), Required.to_vec(), Mint.to_vec(), Transfer.to_vec()].to_vec() + [Mint.to_vec(), Transfer.to_vec(), InviteCollaborator.to_vec()].to_vec() } pub fn admin_permissions() -> Vec> { use crate::types::Permission::*; - let mut admin_permissions = [ - None.to_vec(), - Required.to_vec(), - Mint.to_vec(), - Transfer.to_vec(), - Collaborate.to_vec(), - ] - .to_vec(); + let mut admin_permissions = + [Mint.to_vec(), InviteCollaborator.to_vec()].to_vec(); admin_permissions.append(&mut Permission::holder_permissions()); admin_permissions } @@ -128,8 +153,13 @@ impl Permission { [Mint.to_vec()].to_vec() } + pub fn collector_permissions() -> Vec> { + use crate::types::Permission::*; + [None.to_vec()].to_vec() + } + pub fn holder_permissions() -> Vec> { use crate::types::Permission::*; - [None.to_vec(), Required.to_vec(), Transfer.to_vec()].to_vec() + [Transfer.to_vec()].to_vec() } } diff --git a/pallets/fund-admin/src/functions.rs b/pallets/fund-admin/src/functions.rs index edda1254..dacee7c7 100644 --- a/pallets/fund-admin/src/functions.rs +++ b/pallets/fund-admin/src/functions.rs @@ -11,10 +11,10 @@ use crate::types::*; impl Pallet { // M A I N F U N C T I O N S // -------------------------------------------------------------------------------------------- - + // I N I T I A L S E T U P // -------------------------------------------------------------------------------------------- - + pub fn do_initial_setup() -> DispatchResult{ // Create a global scope for the administrator role let pallet_id = Self::pallet_id(); @@ -25,7 +25,7 @@ impl Pallet { //Admin rol & permissions let administrator_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [ProxyRole::Administrator.to_vec()].to_vec())?; T::Rbac::create_and_set_permissions(pallet_id.clone(), administrator_role_id[0], ProxyPermission::administrator_permissions())?; - + //Builder rol & permissions let builder_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [ProxyRole::Builder.to_vec()].to_vec())?; T::Rbac::create_and_set_permissions(pallet_id.clone(), builder_role_id[0], ProxyPermission::builder_permissions())?; @@ -47,50 +47,50 @@ impl Pallet { } pub fn do_sudo_add_administrator( - admin: T::AccountId, + admin: T::AccountId, name: FieldName, ) -> DispatchResult{ // create a administrator user account & register it in the rbac pallet Self::sudo_register_admin(admin.clone(), name)?; - + Self::deposit_event(Event::AdministratorAssigned(admin)); Ok(()) } pub fn do_sudo_remove_administrator( - admin: T::AccountId, + admin: T::AccountId, ) -> DispatchResult{ // remove administrator user account & remove it from the rbac pallet Self::sudo_delete_admin(admin.clone())?; - + Self::deposit_event(Event::AdministratorRemoved(admin)); Ok(()) } - + // P R O J E C T S // -------------------------------------------------------------------------------------------- - + /// Create a new project /// - only administrator can create a new project /// Expenditures: (name, type, amount, naics code, jobs multiplier, CUDAction, expenditure_id) /// users = (accountid, role) pub fn do_create_project( - admin: T::AccountId, + admin: T::AccountId, title: FieldName, description: FieldDescription, - image: CID, + image: Option, address: FieldName, - creation_date: u64, - completion_date: u64, + creation_date: CreationDate, + completion_date: CompletionDate, expenditures: BoundedVec<( - Option>, + Option, Option, - Option, - Option>, - Option, + Option, + Option, + Option, CUDAction, - Option<[u8;32]>, + Option, ), T::MaxRegistrationsAtTime>, users: Option Pallet { AssignAction, ), T::MaxRegistrationsAtTime>>, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::CreateProject)?; - //Add timestamp + //Add timestamp let timestamp = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; //Create project_id //TOREVIEW: We could use only name as project_id or use a method/storagemap to check if the name is already in use - let project_id = (title.clone()).using_encoded(blake2_256); + let project_id: ProjectId = (title.clone()).using_encoded(blake2_256); //ensure completion_date is in the future ensure!(completion_date > creation_date, Error::::CompletionDateMustBeLater); - + //Create project data let project_data = ProjectData:: { builder: Some(BoundedVec::::default()), @@ -121,12 +121,15 @@ impl Pallet { description, image, address, - status: ProjectStatus::default(), + status: ProjectStatus::default(), inflation_rate: None, registration_date: timestamp, creation_date, completion_date, updated_date: timestamp, + construction_loan_drawdown_status: DrawdownStatus::None, + developer_equity_drawdown_status: DrawdownStatus::None, + eb5_drawdown_status: DrawdownStatus::None, }; // create scope for project_id @@ -156,17 +159,17 @@ impl Pallet { pub fn do_edit_project( admin: T::AccountId, - project_id: [u8;32], - title: Option>, - description: Option>, - image: Option>, - address: Option>, - creation_date: Option, - completion_date: Option, + project_id: ProjectId, + title: Option, + description: Option, + image: Option, + address: Option, + creation_date: Option, + completion_date: Option, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::EditProject)?; - + //Ensure project exists ensure!(ProjectsInfo::::contains_key(project_id), Error::::ProjectNotFound); @@ -179,22 +182,15 @@ impl Pallet { //Mutate project data >::try_mutate::<_,_,DispatchError,_>(project_id, |project| { let project = project.as_mut().ok_or(Error::::ProjectNotFound)?; - + if let Some(title) = title { - let mod_title = title.into_inner(); - project.title = mod_title[0].clone(); + project.title = title; } if let Some(description) = description { - let mod_description = description.into_inner(); - project.description = mod_description[0].clone(); - } - if let Some(image) = image { - let mod_image = image.into_inner(); - project.image = mod_image[0].clone(); + project.description = description; } if let Some(address) = address { - let mod_address = address.into_inner(); - project.address = mod_address[0].clone(); + project.address = address; } if let Some(creation_date) = creation_date { project.creation_date = creation_date; @@ -205,9 +201,10 @@ impl Pallet { project.completion_date = completion_date; } //TOREVIEW: Check if this is working + project.image = image; project.updated_date = current_timestamp; - Ok(()) + Ok(()) })?; //Ensure completion_date is later than creation_date @@ -216,13 +213,13 @@ impl Pallet { // Event Self::deposit_event(Event::ProjectEdited(project_id)); Ok(()) - } + } pub fn do_delete_project( admin: T::AccountId, - project_id: [u8;32], + project_id: ProjectId, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::DeleteProject)?; //Ensure project exists & get project data @@ -278,21 +275,21 @@ impl Pallet { // Deletes all drawdowns from DrawdownsByProject storagemap >::remove(project_id); - //Event + //Event Self::deposit_event(Event::ProjectDeleted(project_id)); Ok(()) } pub fn do_execute_assign_users( admin: T::AccountId, - project_id: [u8;32], + project_id: ProjectId, users: BoundedVec<( - T::AccountId, + T::AccountId, ProxyRole, AssignAction, ), T::MaxRegistrationsAtTime>, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::AssignUser)?; //Ensure project exists @@ -320,7 +317,7 @@ impl Pallet { } fn do_assign_user( - project_id: [u8;32], + project_id: ProjectId, user: T::AccountId, role: ProxyRole, ) -> DispatchResult { @@ -333,7 +330,7 @@ impl Pallet { // Ensure user is not assigened to the selected scope (project_id) with the selected role ensure!(!T::Rbac::has_role(user.clone(), Self::pallet_id(), &project_id, [role.id()].to_vec()).is_ok(), Error::::UserAlreadyAssignedToProject); - + // Update project data depending on the role assigned Self::add_project_role(project_id, user.clone(), role)?; @@ -352,15 +349,15 @@ impl Pallet { // Insert user into scope rbac pallet T::Rbac::assign_role_to_user(user.clone(), Self::pallet_id(), &project_id, role.id())?; - //Event + //Event Self::deposit_event(Event::UsersAssignationCompleted(project_id)); Ok(()) } fn do_unassign_user( - project_id: [u8;32], + project_id: ProjectId, user: T::AccountId, - role: ProxyRole, + role: ProxyRole, ) -> DispatchResult { //Ensure user is registered ensure!(>::contains_key(user.clone()), Error::::UserNotRegistered); @@ -399,12 +396,12 @@ impl Pallet { admin: T::AccountId, users: BoundedVec<( T::AccountId, // 0:account id - Option>, // name + Option, // name Option, // 2:role CUDAction, // 3:action ), T::MaxRegistrationsAtTime>, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::RegisterUser)?; for user in users{ @@ -412,7 +409,7 @@ impl Pallet { CUDAction::Create => { // Create user only needs: account id, name and role Self::do_create_user( - user.0.clone(), + user.0.clone(), user.1.clone().ok_or(Error::::UserNameRequired)?, user.2.clone().ok_or(Error::::UserRoleRequired)?, )?; @@ -445,7 +442,7 @@ impl Pallet { fn do_create_user( user: T::AccountId, - name: BoundedVec, + name: FieldName, role: ProxyRole, ) -> DispatchResult { //Get current timestamp @@ -459,12 +456,12 @@ impl Pallet { match role { ProxyRole::Administrator => { - Self::do_sudo_add_administrator(user.clone(), name[0].clone())?; + Self::do_sudo_add_administrator(user.clone(), name.clone())?; }, _ => { // Create user data let user_data = UserData:: { - name: name[0].clone(), + name: name.clone(), role, image: CID::default(), date_registered: current_timestamp, @@ -483,7 +480,7 @@ impl Pallet { fn do_update_user( user: T::AccountId, - name: Option>, // name + name: Option, // name role: Option, ) -> DispatchResult { // Ensure user is registered @@ -494,7 +491,7 @@ impl Pallet { let user_info = user_data.as_mut().ok_or(Error::::UserNotRegistered)?; if let Some(mod_name) = name { - user_info.name = mod_name.into_inner()[0].clone(); + user_info.name = mod_name.clone(); } if let Some(mod_role) = role { // If user has assigned projects cannot update role @@ -537,7 +534,7 @@ impl Pallet { Ok(()) })?; } - + Self::deposit_event(Event::UserDeleted(user.clone())); }, } @@ -547,11 +544,11 @@ impl Pallet { } pub fn do_edit_user( - user: T::AccountId, - name: Option>, - image: Option>, - email: Option>, - documents: Option>, + user: T::AccountId, + name: Option, + image: Option, + email: Option, + documents: Option>, ) -> DispatchResult { //Ensure user is registered ensure!(>::contains_key(user.clone()), Error::::UserNotRegistered); @@ -562,16 +559,13 @@ impl Pallet { //TODO: evaluate this inner method, optimize it if let Some(name) = name { - let mod_name = name.into_inner(); - user_info.name = mod_name[0].clone(); + user_info.name = name.clone(); } if let Some(image) = image { - let mod_image = image.into_inner(); - user_info.image = mod_image[0].clone(); + user_info.image = image.clone(); } if let Some(email) = email { - let mod_email = email.into_inner(); - user_info.email = mod_email[0].clone(); + user_info.email = email.clone(); } if let Some(documents) = documents { // Ensure user is an investor @@ -588,22 +582,22 @@ impl Pallet { // B U D G E T E X P E N D I T U R E // -------------------------------------------------------------------------------------------- - + // Expenditures: (name, type, amount, naics code, jobs multiplier, CUDAction, expenditure_id) pub fn do_execute_expenditures( admin: T::AccountId, - project_id: [u8;32], + project_id: ProjectId, expenditures: BoundedVec<( - Option>, // 0: name + Option, // 0: name Option, // 1: type - Option, // 2: amount - Option>, // 3: naics code - Option, // 4: jobs multiplier + Option, // 2: amount + Option, // 3: naics code + Option, // 4: jobs multiplier CUDAction, // 5: CUDAction - Option<[u8;32]>, // 6: expenditure_id - ), T::MaxRegistrationsAtTime>, + Option, // 6: expenditure_id + ), T::MaxRegistrationsAtTime>, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::Expenditures)?; // Ensure project exists @@ -653,12 +647,12 @@ impl Pallet { /// Create a new budget expenditure - /// + /// /// # Arguments - /// + /// /// * `admin` - The admin user that creates the budget expenditure /// * `project_id` - The project id where the budget expenditure will be created - /// + /// /// Then we add the budget expenditure data /// * `name` - The name of the budget expenditure /// * `type` - The type of the budget expenditure @@ -666,12 +660,12 @@ impl Pallet { /// * `naics code` - The naics code of the budget expenditure /// * `jobs_multiplier` - The jobs multiplier of the budget expenditure fn do_create_expenditure( - project_id: [u8;32], - name: BoundedVec, + project_id: [u8;32], + name: FieldName, expenditure_type: ExpenditureType, - expenditure_amount: u64, - naics_code: Option>, - jobs_multiplier: Option, + expenditure_amount: ExpenditureAmount, + naics_code: Option, + jobs_multiplier: Option, ) -> DispatchResult { //Ensure project exists ensure!(>::contains_key(project_id), Error::::ProjectNotFound); @@ -686,24 +680,24 @@ impl Pallet { ensure!(!name.is_empty(), Error::::EmptyExpenditureName); // Create expenditure id - let expenditure_id = (project_id, name.clone(), expenditure_type, timestamp).using_encoded(blake2_256); + let expenditure_id: BudgetExpenditureId = (project_id, name.clone(), expenditure_type, timestamp).using_encoded(blake2_256); // NAICS code let get_naics_code = match naics_code { Some(mod_naics_code) => { - Some(mod_naics_code.into_inner()[0].clone()) + Some(mod_naics_code.clone()) }, None => None, }; - + // Create expenditurte data let expenditure_data = ExpenditureData { project_id, - name: name.into_inner()[0].clone(), + name: name.clone(), expenditure_type, expenditure_amount, naics_code: get_naics_code, - jobs_multiplier, + jobs_multiplier, }; // Insert expenditure data into ExpendituresInfo @@ -722,12 +716,12 @@ impl Pallet { } fn do_update_expenditure( - project_id: [u8;32], - expenditure_id: [u8;32], - name: Option>, - expenditure_amount: Option, - naics_code: Option>, - jobs_multiplier: Option, + project_id: ProjectId, + expenditure_id: BudgetExpenditureId, + name: Option, + expenditure_amount: Option, + naics_code: Option, + jobs_multiplier: Option, ) -> DispatchResult { //Ensure project exists ensure!(ProjectsInfo::::contains_key(project_id), Error::::ProjectNotFound); @@ -735,7 +729,7 @@ impl Pallet { // Ensure project is not completed Self::is_project_completed(project_id)?; - // Ensure expenditure_id exists + // Ensure expenditure_id exists ensure!(>::contains_key(expenditure_id), Error::::ExpenditureNotFound); // Mutate expenditure data @@ -748,13 +742,13 @@ impl Pallet { //TODO: ensure name is unique if let Some(mod_name) = name { - expenditure.name = mod_name.into_inner()[0].clone(); + expenditure.name = mod_name.clone(); } if let Some(mod_expenditure_amount) = expenditure_amount { expenditure.expenditure_amount = mod_expenditure_amount; } if let Some(mod_naics_code) = naics_code { - expenditure.naics_code = Some(mod_naics_code.into_inner()[0].clone()); + expenditure.naics_code = Some(mod_naics_code.clone()); } if let Some(mod_jobs_multiplier) = jobs_multiplier { expenditure.jobs_multiplier = Some(mod_jobs_multiplier); @@ -768,7 +762,7 @@ impl Pallet { } fn do_delete_expenditure( - expenditure_id: [u8;32], + expenditure_id: BudgetExpenditureId, ) -> DispatchResult { // Ensure expenditure_id exists & get expenditure data let expenditure_data = >::get(expenditure_id).ok_or(Error::::ExpenditureNotFound)?; @@ -790,9 +784,9 @@ impl Pallet { // -------------------------------------------------------------------------------------------- // For now drawdowns functions are private, but in the future they may be public fn do_create_drawdown( - project_id: [u8;32], + project_id: ProjectId, drawdown_type: DrawdownType, - drawdown_number: u32, + drawdown_number: DrawdownNumber, ) -> DispatchResult { // TOOD: Ensure builder permissions //Self::is_superuser(admin.clone(), &Self::get_global_scope(), ProxyRole::Administrator.id())?; @@ -831,15 +825,16 @@ impl Pallet { Ok(()) })?; + Self::do_edit_drawdown_status_in_project_info(project_id, drawdown_id, DrawdownStatus::default())?; //TOREVIEW: Check if an event is needed Ok(()) } fn do_initialize_drawdowns( admin: T::AccountId, - project_id: [u8;32], + project_id: ProjectId, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::Expenditures)?; // Ensure project exists @@ -858,8 +853,8 @@ impl Pallet { } pub fn do_submit_drawdown( - project_id: [u8;32], - drawdown_id: [u8;32], + project_id: ProjectId, + drawdown_id: DrawdownId, ) -> DispatchResult { // Ensure project exists & is not completed Self::is_project_completed(project_id)?; @@ -897,6 +892,8 @@ impl Pallet { Ok(()) })?; + Self::do_edit_drawdown_status_in_project_info(project_id, drawdown_id, DrawdownStatus::Submitted)?; + //Event Self::deposit_event(Event::DrawdownSubmitted(drawdown_id)); @@ -905,10 +902,10 @@ impl Pallet { pub fn do_approve_drawdown( admin: T::AccountId, - project_id: [u8;32], - drawdown_id: [u8;32], + project_id: ProjectId, + drawdown_id: DrawdownId, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::Expenditures)?; // Get drawdown data & ensure drawdown exists @@ -919,7 +916,7 @@ impl Pallet { // Ensure drawdown has transactions ensure!(>::contains_key(project_id, drawdown_id), Error::::DrawdownHasNoTransactions); - + // Get drawdown transactions let drawdown_transactions = TransactionsByDrawdown::::try_get(project_id, drawdown_id).map_err(|_| Error::::DrawdownNotFound)?; @@ -950,6 +947,8 @@ impl Pallet { Ok(()) })?; + Self::do_edit_drawdown_status_in_project_info(project_id, drawdown_id, DrawdownStatus::Approved)?; + // Generate the next drawdown Self::do_create_drawdown(project_id, drawdown_data.drawdown_type, drawdown_data.drawdown_number + 1)?; @@ -961,12 +960,12 @@ impl Pallet { pub fn do_reject_drawdown( admin: T::AccountId, - project_id: [u8;32], - drawdown_id: [u8;32], - transactions_feedback: Option>, - drawdown_feedback: Option>, + project_id: ProjectId, + drawdown_id: DrawdownId, + transactions_feedback: Option>, + drawdown_feedback: Option, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::Expenditures)?; // Get drawdown data & ensure drawdown exists @@ -1043,7 +1042,7 @@ impl Pallet { // Update drawdown feedback >::try_mutate::<_,_,DispatchError,_>(drawdown_id, |drawdown_data| { let drawdown_data = drawdown_data.as_mut().ok_or(Error::::DrawdownNotFound)?; - drawdown_data.feedback = Some(mod_drawdown_feedback[0].clone()); + drawdown_data.feedback = Some(mod_drawdown_feedback.clone()); Ok(()) })?; }, @@ -1056,6 +1055,8 @@ impl Pallet { Ok(()) })?; + Self::do_edit_drawdown_status_in_project_info(project_id, drawdown_id, DrawdownStatus::Rejected)?; + //Event Self::deposit_event(Event::DrawdownRejected(drawdown_id)); @@ -1067,14 +1068,14 @@ impl Pallet { // -------------------------------------------------------------------------------------------- // For now transactions functions are private, but in the future they may be public pub fn do_execute_transactions( - project_id: [u8;32], - drawdown_id: [u8;32], + project_id: ProjectId, + drawdown_id: DrawdownId, transactions: BoundedVec<( - Option<[u8;32]>, // expenditure_id - Option, // amount + Option, // expenditure_id + Option, // amount Option>, //Documents CUDAction, // Action - Option<[u8;32]>, // transaction_id + Option, // transaction_id ), T::MaxRegistrationsAtTime>, ) -> DispatchResult { @@ -1103,10 +1104,10 @@ impl Pallet { )?; }, CUDAction::Update => { - // Update transaction needs (amount, documents, transaction_id) + // Update transaction needs (amount, documents, transaction_id) Self::do_update_transaction( transaction.1, - transaction.2, + transaction.2, transaction.4.ok_or(Error::::TransactionIdNotFound)?, )?; }, @@ -1128,10 +1129,10 @@ impl Pallet { } fn do_create_transaction( - project_id: [u8;32], - drawdown_id: [u8;32], - expenditure_id: [u8;32], - amount: u64, + project_id: ProjectId, + drawdown_id: DrawdownId, + expenditure_id: BudgetExpenditureId, + amount: Amount, documents: Option>, ) -> DispatchResult { // Ensure amount is valid @@ -1181,9 +1182,9 @@ impl Pallet { } fn do_update_transaction( - amount: Option, + amount: Option, documents: Option>, - transaction_id: [u8;32], + transaction_id: TransactionId, ) -> DispatchResult { // Ensure transaction exists ensure!(TransactionsInfo::::contains_key(transaction_id), Error::::TransactionNotFound); @@ -1203,11 +1204,11 @@ impl Pallet { // Try mutate transaction data >::try_mutate::<_,_,DispatchError,_>(transaction_id, |transaction_data| { - let mod_transaction_data = transaction_data.as_mut().ok_or(Error::::TransactionNotFound)?; - + let mod_transaction_data = transaction_data.as_mut().ok_or(Error::::TransactionNotFound)?; + // Ensure expenditure exists ensure!(ExpendituresInfo::::contains_key(mod_transaction_data.expenditure_id), Error::::ExpenditureNotFound); - + if let Some(mod_amount) = amount { mod_transaction_data.amount = mod_amount; } @@ -1218,7 +1219,7 @@ impl Pallet { mod_transaction_data.updated_date = timestamp; Ok(()) })?; - + //TOREVIEW: Check if this event is needed Self::deposit_event(Event::TransactionEdited(transaction_id)); @@ -1226,7 +1227,7 @@ impl Pallet { } fn do_delete_transaction( - transaction_id: [u8;32] + transaction_id: TransactionId ) -> DispatchResult { // Ensure transaction exists and get transaction data let transaction_data = TransactionsInfo::::get(transaction_id).ok_or(Error::::TransactionNotFound)?; @@ -1256,13 +1257,13 @@ impl Pallet { pub fn do_up_bulk_upload( user: T::AccountId, - project_id: [u8;32], - drawdown_id: [u8;32], + project_id: ProjectId, + drawdown_id: DrawdownId, description: FieldDescription, - total_amount: u64, + total_amount: TotalAmount, documents: Documents, ) -> DispatchResult { - // Ensure builder permissions + // Ensure builder permissions Self::is_authorized(user, &project_id, ProxyPermission::UpBulkupload)?; // Ensure project is not completed @@ -1298,6 +1299,8 @@ impl Pallet { Ok(()) })?; + Self::do_edit_drawdown_status_in_project_info(project_id, drawdown_id, DrawdownStatus::Submitted)?; + Ok(()) } @@ -1305,9 +1308,9 @@ impl Pallet { // -------------------------------------------------------------------------------------------- pub fn do_execute_inflation_adjustment( admin: T::AccountId, - projects: BoundedVec<([u8;32], Option, CUDAction), T::MaxRegistrationsAtTime>, + projects: BoundedVec<(ProjectId, Option, CUDAction), T::MaxRegistrationsAtTime>, ) -> DispatchResult { - // Ensure admin permissions + // Ensure admin permissions Self::is_authorized(admin.clone(), &Self::get_global_scope(), ProxyPermission::Expenditures)?; // Ensure projects is not empty @@ -1348,7 +1351,7 @@ impl Pallet { // H E L P E R S // -------------------------------------------------------------------------------------------- - + /// Get the current timestamp in milliseconds fn get_timestamp_in_milliseconds() -> Option { let timestamp:u64 = T::Timestamp::now().into(); @@ -1371,7 +1374,7 @@ impl Pallet { fn _change_project_status( admin: T::AccountId, - project_id: [u8;32], + project_id: ProjectId, status: ProjectStatus ) -> DispatchResult { //ensure admin permissions @@ -1387,14 +1390,14 @@ impl Pallet { >::try_mutate::<_,_,DispatchError,_>(project_id, |project| { let project = project.as_mut().ok_or(Error::::ProjectNotFound)?; project.status = status; - Ok(()) + Ok(()) })?; Ok(()) } fn is_project_completion_date_later( - project_id: [u8;32], + project_id: ProjectId, ) -> DispatchResult { // Get project data & ensure project exists let project_data = ProjectsInfo::::get(project_id).ok_or(Error::::ProjectNotFound)?; @@ -1405,7 +1408,7 @@ impl Pallet { } fn add_project_role( - project_id: [u8;32], + project_id: ProjectId, user: T::AccountId, role: ProxyRole, ) -> DispatchResult { @@ -1431,7 +1434,7 @@ impl Pallet { devs.try_push(user.clone()).map_err(|_| Error::::MaxBuildersPerProjectReached)?; } } - Ok(()) + Ok(()) })?; }, ProxyRole::Investor => { @@ -1448,7 +1451,7 @@ impl Pallet { investors.try_push(user.clone()).map_err(|_| Error::::MaxInvestorsPerProjectReached)?; } } - Ok(()) + Ok(()) })?; }, ProxyRole::Issuer => { @@ -1465,7 +1468,7 @@ impl Pallet { issuers.try_push(user.clone()).map_err(|_| Error::::MaxIssuersPerProjectReached)?; } } - Ok(()) + Ok(()) })?; }, ProxyRole::RegionalCenter => { @@ -1482,7 +1485,7 @@ impl Pallet { regional_centers.try_push(user.clone()).map_err(|_| Error::::MaxRegionalCenterPerProjectReached)?; } } - Ok(()) + Ok(()) })?; }, } @@ -1491,7 +1494,7 @@ impl Pallet { } pub fn remove_project_role( - project_id: [u8;32], + project_id: ProjectId, user: T::AccountId, role: ProxyRole, ) -> DispatchResult { @@ -1515,7 +1518,7 @@ impl Pallet { return Err(Error::::UserNotAssignedToProject.into()); } } - Ok(()) + Ok(()) })?; }, ProxyRole::Investor => { @@ -1531,7 +1534,7 @@ impl Pallet { return Err(Error::::UserNotAssignedToProject.into()); } } - Ok(()) + Ok(()) })?; }, ProxyRole::Issuer => { @@ -1547,7 +1550,7 @@ impl Pallet { return Err(Error::::UserNotAssignedToProject.into()); } } - Ok(()) + Ok(()) })?; }, ProxyRole::RegionalCenter => { @@ -1563,16 +1566,16 @@ impl Pallet { return Err(Error::::UserNotAssignedToProject.into()); } } - Ok(()) + Ok(()) })?; }, } Ok(()) } - - + + /// This functions performs the following checks: - /// + /// /// 1. Checks if the user is registered in the system /// 2. Checks if the user has the required role from UsersInfo storage /// 3. Checks if the user is trying to assign an admin role @@ -1586,9 +1589,9 @@ impl Pallet { // Check if the user role trying to be assigned matches the actual user role from UsersInfo storage if user_data.role != role { return Err(Error::::UserCannotHaveMoreThanOneRole.into()); - } + } - // Can't assign an admin to a project, admins exists globally + // Can't assign an admin to a project, admins exists globally if role == ProxyRole::Administrator { return Err(Error::::CannotAddAdminRole.into()); } @@ -1597,7 +1600,7 @@ impl Pallet { } fn is_project_completed( - project_id: [u8;32], + project_id: ProjectId, ) -> DispatchResult { // Get project data & ensure project exists let project_data = ProjectsInfo::::get(project_id).ok_or(Error::::ProjectNotFound)?; @@ -1610,7 +1613,7 @@ impl Pallet { #[allow(dead_code)] fn is_drawdown_editable( - drawdown_id: [u8;32], + drawdown_id: DrawdownId, ) -> DispatchResult { // Get drawdown data & ensure drawdown exists let drawdown_data = DrawdownsInfo::::get(drawdown_id).ok_or(Error::::DrawdownNotFound)?; @@ -1647,7 +1650,7 @@ impl Pallet { } fn is_transaction_editable( - transaction_id: [u8;32], + transaction_id: TransactionId, ) -> DispatchResult { // Get transaction data let transaction_data = TransactionsInfo::::get(transaction_id).ok_or(Error::::TransactionNotFound)?; @@ -1668,7 +1671,7 @@ impl Pallet { pub fn is_authorized( authority: T::AccountId, project_id: &[u8;32], permission: ProxyPermission ) -> DispatchResult{ T::Rbac::is_authorized( authority, - Self::pallet_id(), + Self::pallet_id(), project_id, &permission.id(), ) @@ -1678,7 +1681,7 @@ impl Pallet { fn is_superuser( authority: T::AccountId, scope_global: &[u8;32], rol_id: RoleId ) -> DispatchResult{ T::Rbac::has_role( authority, - Self::pallet_id(), + Self::pallet_id(), scope_global, vec![rol_id], ) @@ -1690,7 +1693,7 @@ impl Pallet { ) -> DispatchResult{ // check if user is already registered ensure!(!>::contains_key(admin.clone()), Error::::UserAlreadyRegistered); - + //Get current timestamp let current_timestamp = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; @@ -1720,7 +1723,7 @@ impl Pallet { fn sudo_delete_admin( admin: T::AccountId ) -> DispatchResult{ // check if user is already registered ensure!(>::contains_key(admin.clone()), Error::::UserNotRegistered); - + //Remove user from UsersInfo storage map >::remove(admin.clone()); @@ -1772,4 +1775,43 @@ impl Pallet { Ok(()) } -} + fn do_edit_drawdown_status_in_project_info( + project_id: ProjectId, + drawdown_id: DrawdownId, + drawdown_status: DrawdownStatus + ) -> DispatchResult { + let drawdown_data = DrawdownsInfo::::get(drawdown_id).ok_or(Error::::DrawdownNotFound)?; + + // Match drawdown type + match drawdown_data.drawdown_type { + DrawdownType::EB5 => { + // Update EB5 drawdown status in project info + >::try_mutate::<_,_,DispatchError,_>(project_id, |project_data| { + let project_data = project_data.as_mut().ok_or(Error::::ProjectNotFound)?; + project_data.eb5_drawdown_status = drawdown_status; + Ok(()) + })?; + Ok(()) + }, + DrawdownType::ConstructionLoan => { + // Update Construction Loan drawdown status in project info + >::try_mutate::<_,_,DispatchError,_>(project_id, |project_data| { + let project_data = project_data.as_mut().ok_or(Error::::ProjectNotFound)?; + project_data.construction_loan_drawdown_status = drawdown_status; + Ok(()) + })?; + Ok(()) + }, + DrawdownType::DeveloperEquity => { + // Update Developer Equity drawdown status in project info + >::try_mutate::<_,_,DispatchError,_>(project_id, |project_data| { + let project_data = project_data.as_mut().ok_or(Error::::ProjectNotFound)?; + project_data.developer_equity_drawdown_status = drawdown_status; + Ok(()) + })?; + Ok(()) + }, + } + } + +} diff --git a/pallets/fund-admin/src/lib.rs b/pallets/fund-admin/src/lib.rs index d9760cbb..d98b7d34 100644 --- a/pallets/fund-admin/src/lib.rs +++ b/pallets/fund-admin/src/lib.rs @@ -36,7 +36,7 @@ pub mod pallet { pub trait Config: frame_system::Config { //TODO: change all accounts names for users type Event: From> + IsType<::Event>; - + type Moment: Parameter + Default + Scale @@ -49,7 +49,7 @@ pub mod pallet { type Rbac : RoleBasedAccessControl; - type RemoveOrigin: EnsureOrigin; + type RemoveOrigin: EnsureOrigin; #[pallet::constant] type MaxDocuments: Get; @@ -106,8 +106,8 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn users_info)] pub(super) type UsersInfo = StorageMap< - _, - Blake2_128Concat, + _, + Blake2_128Concat, T::AccountId, // Key account_id UserData, // Value UserData OptionQuery, @@ -116,9 +116,9 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn projects_info)] pub(super) type ProjectsInfo = StorageMap< - _, - Identity, - [u8;32], // Key project_id + _, + Identity, + ProjectId, // Key project_id ProjectData, // Value ProjectData OptionQuery, >; @@ -126,9 +126,9 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn users_by_project)] pub(super) type UsersByProject = StorageMap< - _, - Identity, - [u8;32], // Key project_id + _, + Identity, + ProjectId, // Key project_id BoundedVec, // Value users ValueQuery, >; @@ -136,8 +136,8 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn projects_by_user)] pub(super) type ProjectsByUser = StorageMap< - _, - Blake2_128Concat, + _, + Blake2_128Concat, T::AccountId, // Key account_id BoundedVec<[u8;32], T::MaxProjectsPerUser>, // Value projects ValueQuery, @@ -146,9 +146,9 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn expenditures_info)] pub(super) type ExpendituresInfo = StorageMap< - _, - Identity, - [u8;32], // Key expenditure_id + _, + Identity, + BudgetExpenditureId, // Key expenditure_id ExpenditureData, // Value ExpenditureData OptionQuery, >; @@ -156,9 +156,9 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn expenditures_by_project)] pub(super) type ExpendituresByProject = StorageMap< - _, - Identity, - [u8;32], // Key project_id + _, + Identity, + ProjectId, // Key project_id BoundedVec<[u8;32], T::MaxExpendituresPerProject>, // Value expenditures ValueQuery, >; @@ -166,29 +166,29 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn drawdowns_info)] pub(super) type DrawdownsInfo = StorageMap< - _, - Identity, - [u8;32], // Key drawdown id + _, + Identity, + DrawdownId, // Key drawdown id DrawdownData, // Value DrawdownData OptionQuery, >; - + #[pallet::storage] #[pallet::getter(fn drawdowns_by_project)] pub(super) type DrawdownsByProject = StorageMap< - _, - Identity, - [u8;32], // Key project_id - BoundedVec<[u8;32], T::MaxDrawdownsPerProject>, // Value Drawdowns + _, + Identity, + ProjectId, // Key project_id + BoundedVec, // Value Drawdowns ValueQuery, >; #[pallet::storage] #[pallet::getter(fn transactions_info)] pub(super) type TransactionsInfo = StorageMap< - _, - Identity, - [u8;32], // Key transaction id + _, + Identity, + TransactionId, // Key transaction id TransactionData, // Value TransactionData OptionQuery, >; @@ -196,14 +196,14 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn transactions_by_drawdown)] pub(super) type TransactionsByDrawdown = StorageDoubleMap< - _, - Identity, - [u8;32], //K1: project id - Identity, - [u8;32], //K2: drawdown id - BoundedVec<[u8;32], T::MaxTransactionsPerDrawdown>, // Value transactions + _, + Identity, + ProjectId, //K1: project id + Identity, + DrawdownId, //K2: drawdown id + BoundedVec, // Value transactions ValueQuery - >; + >; // E V E N T S // ------------------------------------------------------------------------------------------------------------ @@ -212,15 +212,15 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Project was created successfully - ProjectCreated(T::AccountId, [u8;32]), + ProjectCreated(T::AccountId, ProjectId), /// Proxy initial setup completed ProxySetupCompleted, /// User registered successfully UserAdded(T::AccountId), /// Project was edited - ProjectEdited([u8;32]), + ProjectEdited(ProjectId), /// Project was deleted - ProjectDeleted([u8;32]), + ProjectDeleted(ProjectId), /// Administrator added AdministratorAssigned(T::AccountId), /// Administrator removed @@ -236,17 +236,17 @@ pub mod pallet { /// Expenditure was created successfully ExpenditureCreated, /// Expenditure was edited successfully - ExpenditureEdited([u8;32]), + ExpenditureEdited(BudgetExpenditureId), /// Expenditure was deleted successfully - ExpenditureDeleted([u8;32]), + ExpenditureDeleted(BudgetExpenditureId), /// Trasactions was completed successfully TransactionsCompleted, /// Transaction was created successfully - TransactionCreated([u8;32]), + TransactionCreated(TransactionId), /// Transaction was edited successfully - TransactionEdited([u8;32]), + TransactionEdited(TransactionId), /// Transaction was deleted successfully - TransactionDeleted([u8;32]), + TransactionDeleted(TransactionId), /// Users extrinsic was completed successfully UsersExecuted, /// Assign users extrinsic was completed successfully @@ -289,7 +289,7 @@ pub mod pallet { MaxProjectsPerUserReached, /// User is not assigned to the project UserNotAssignedToProject, - /// Can not register administrator role + /// Can not register administrator role CannotRegisterAdminRole, /// Max number of builders per project reached MaxBuildersPerProjectReached, @@ -318,7 +318,7 @@ pub mod pallet { /// Drowdown id is not found DrawdownNotFound, /// Invalid amount - InvalidAmount, + InvalidAmount, /// Documents field is empty DocumentsIsEmpty, /// Transaction id is not found @@ -342,7 +342,7 @@ pub mod pallet { /// User does not have the specified role UserDoesNotHaveRole, /// Transactions vector is empty - EmptyTransactions, + EmptyTransactions, /// Transaction ID was not found in do_execute_transaction TransactionIdNotFound, /// Drawdown can not be submitted if does not has any transactions @@ -402,12 +402,12 @@ pub mod pallet { // ------------------------------------------------------------------------------------------------------------ #[pallet::call] impl Pallet { - // I N I T I A L + // I N I T I A L // -------------------------------------------------------------------------------------------- /// Initialize the pallet by setting the permissions for each role - /// & the global scope - /// - /// # Considerations: + /// & the global scope + /// + /// # Considerations: /// - This function can only be called once /// - This function can only be called usinf the sudo pallet #[transactional] @@ -421,17 +421,17 @@ pub mod pallet { } /// Adds an administrator account to the site - /// + /// /// # Parameters: /// - origin: The sudo account /// - admin: The administrator account to be added /// - name: The name of the administrator account - /// + /// /// # Considerations: /// - This function can only be called using the sudo pallet /// - This function is used to add the first administrator to the site /// - If the user is already registered, the function will return an error: UserAlreadyRegistered - /// - This function grants administator permissions to the user from the rbac pallet + /// - This function grants administator permissions to the user from the rbac pallet /// - Administator role have global scope permissions #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(10))] @@ -446,19 +446,19 @@ pub mod pallet { } /// Removes an administrator account from the site - /// + /// /// # Parameters: /// - origin: The sudo account /// - admin: The administrator account to be removed - /// + /// /// # Considerations: /// - This function can only be called using the sudo pallet /// - This function is used to remove any administrator from the site /// - If the user is not registered, the function will return an error: UserNotFound /// - This function removes administator permissions of the user from the rbac pallet - /// + /// /// # Note: - /// WARNING: Administrators can remove themselves from the site, + /// WARNING: Administrators can remove themselves from the site, /// but they can add themselves back #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(10))] @@ -474,41 +474,41 @@ pub mod pallet { // U S E R S // -------------------------------------------------------------------------------------------- - /// This extrinsic is used to create, update, or delete a user account - /// + /// This extrinsic is used to create, update, or delete a user account + /// /// # Parameters: /// - origin: The administrator account - /// - user: The target user account to be registered, updated, or deleted. + /// - user: The target user account to be registered, updated, or deleted. /// It is an array of user accounts where each entry it should be a tuple of the following: /// - 0: The user account /// - 1: The user name /// - 2: The user role /// - 3: The CUD operation to be performed on the user account. CUD action is ALWAYS required. - /// + /// /// # Considerations: /// - Users parameters are optional because depends on the CUD action as follows: /// * **Create**: The user account, user name, user role & CUD action are required /// * **Update**: The user account & CUD action are required. The user name & user role are optionals. - /// * **Delete**: The user account & CUD action are required. + /// * **Delete**: The user account & CUD action are required. /// - This function can only be called by an administrator account - /// - Multiple users can be registered, updated, or deleted at the same time, but + /// - Multiple users can be registered, updated, or deleted at the same time, but /// the user account must be unique. Multiple actions over the same user account /// in the same call, it could result in an unexpected behavior. /// - If the user is already registered, the function will return an error: UserAlreadyRegistered /// - If the user is not registered, the function will return an error: UserNotFound - /// + /// /// # Note: /// WARNING: It is possible to register, update, or delete administators accounts using this extrinsic, /// but administrators can not delete themselves. - /// WARNING: This function only registers, updates, or deletes users from the site. + /// WARNING: This function only registers, updates, or deletes users from the site. /// DOESN'T grant or remove permissions from the rbac pallet. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn users( - origin: OriginFor, + origin: OriginFor, users: BoundedVec<( T::AccountId, // account id - Option>, // name + Option, // name Option, // role CUDAction, // action ), T::MaxRegistrationsAtTime>, @@ -519,16 +519,16 @@ pub mod pallet { } /// Edits an user account - /// + /// /// # Parameters: /// - origin: The user account which is being edited /// - name: The name of the user account which is being edited /// - image: The image of the user account which is being edited /// - email: The email of the user account which is being edited /// - documents: The documents of the user account which is being edited. - /// ONLY available for the investor role. - /// - /// + /// ONLY available for the investor role. + /// + /// /// # Considerations: /// - If the user is not registered, the function will return an error: UserNotFound /// - This function can only be called by a registered user account @@ -538,21 +538,21 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn users_edit_user( - origin: OriginFor, - name: Option>, - image: Option>, - email: Option>, - documents: Option> + origin: OriginFor, + name: Option, + image: Option, + email: Option, + documents: Option> ) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_edit_user(who, name, image, email, documents) - } + } // P R O J E C T S // -------------------------------------------------------------------------------------------- /// Registers a new project. - /// + /// /// # Parameters: /// - origin: The administrator account /// - title: The title of the project @@ -575,7 +575,7 @@ pub mod pallet { /// * 0: The user account /// * 1: The user role /// * 2: The AssignAction to be performed on the user. - /// + /// /// # Considerations: /// - This function can only be called by an administrator account /// - For users assignation, the user account must be registered. If the user is not registered, @@ -584,30 +584,30 @@ pub mod pallet { /// flow, the expenditures are always created. The naics code & the jobs multiplier /// can be added later by the administrator. /// - Creating a project will automatically create a scope for the project. - /// + /// /// # Note: /// WARNING: If users are provided, the function will assign the users to the project, granting them /// permissions in the rbac pallet. #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn projects_create_project( - origin: OriginFor, - title: FieldName, - description: FieldDescription, - image: CID, + origin: OriginFor, + title: FieldName, + description: FieldDescription, + image: Option, address: FieldName, - creation_date: u64, - completion_date: u64, + creation_date: CreationDate, + completion_date: CompletionDate, expenditures: BoundedVec<( - Option>, + Option, Option, - Option, - Option>, - Option, + Option, + Option, + Option, CUDAction, - Option<[u8;32]>, + Option ), T::MaxRegistrationsAtTime>, users: Option>, @@ -618,7 +618,7 @@ pub mod pallet { } /// Edits a project. - /// + /// /// # Parameters: /// - origin: The administrator account /// - project_id: The selected project id that will be edited @@ -628,14 +628,14 @@ pub mod pallet { /// - address: The address of the project to be edited /// - creation_date: The creation date of the project to be edited /// - completion_date: The completion date of the project to be edited - /// + /// /// # Considerations: /// - This function can only be called by an administrator account /// - ALL parameters are optional because depends on what is being edited /// - The project id is required because it is the only way to identify the project - /// - The project id must be registered. If the project is not registered, + /// - The project id must be registered. If the project is not registered, /// the function will return an error: ProjectNotFound - /// - It is not possible to edit the expenditures or the users assigned to the project + /// - It is not possible to edit the expenditures or the users assigned to the project /// through this function. For that, the administrator must use the extrinsics: /// * expenditures /// * projects_assign_user @@ -644,14 +644,14 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn projects_edit_project( - origin: OriginFor, - project_id: [u8;32], - title: Option>, - description: Option>, - image: Option>, - address: Option>, - creation_date: Option, - completion_date: Option, + origin: OriginFor, + project_id: ProjectId, + title: Option, + description: Option, + image: Option, + address: Option, + creation_date: Option, + completion_date: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; // origin need to be an admin @@ -659,25 +659,25 @@ pub mod pallet { } /// Deletes a project. - /// + /// /// # Parameters: /// - origin: The administrator account /// - project_id: The selected project id that will be deleted - /// + /// /// # Considerations: /// - This function can only be called by an administrator account /// - The project id is required because it is the only way to identify the project /// - The project id must be registered. If the project is not registered, /// the function will return an error: ProjectNotFound - /// + /// /// # Note: /// - WARNING: Deleting a project will delete ALL stored information associated with the project. /// BE CAREFUL. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn projects_delete_project( - origin: OriginFor, - project_id: [u8;32], + origin: OriginFor, + project_id: ProjectId, ) -> DispatchResult { let who = ensure_signed(origin)?; // origin need to be an admin @@ -685,34 +685,34 @@ pub mod pallet { } /// Assigns a user to a project. - /// + /// /// # Parameters: /// - origin: The administrator account /// - project_id: The selected project id where user will be assigned - /// - users: The users to be assigned to the project. This is a vector of tuples + /// - users: The users to be assigned to the project. This is a vector of tuples /// where each entry is composed by: /// * 0: The user account id /// * 1: The user role /// * 2: The AssignAction to be performed. (Assign or Unassign) - /// + /// /// # Considerations: /// - This function can only be called by an administrator account /// - This extrinsic allows multiple users to be assigned/unassigned at the same time. /// - The project id is required because it is the only way to identify the project /// - This extrinsic is used for both assigning and unassigning users to a project - /// depending on the AssignAction. + /// depending on the AssignAction. /// - After a user is assigned to a project, the user will be able to perform actions /// in the project depending on the role assigned to the user. /// - After a user is unassigned from a project, the user will not be able to perform actions /// in the project anymore. /// - If the user is already assigned to the project, the function will return an erro. - /// + /// /// # Note: /// - WARNING: ALL provided users needs to be registered in the site. If any of the users /// is not registered, the function will return an error. /// - Assigning or unassigning a user to a project will add or remove permissions to the user - /// from the RBAC pallet. - /// - Warning: Cannot assign a user to a project with a different role than the one they + /// from the RBAC pallet. + /// - Warning: Cannot assign a user to a project with a different role than the one they /// have in UsersInfo. If the user has a different role, the function will return an error. /// - Warning: Cannot unassign a user from a project with a different role than the one they /// have in UsersInfo. If the user has a different role, the function will return an error. @@ -721,10 +721,10 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn projects_assign_user( - origin: OriginFor, - project_id: [u8;32], + origin: OriginFor, + project_id: ProjectId, users: BoundedVec<( - T::AccountId, + T::AccountId, ProxyRole, AssignAction, ), T::MaxRegistrationsAtTime>, @@ -734,10 +734,10 @@ pub mod pallet { Self::do_execute_assign_users(who, project_id, users) } - // B U D G E T E X P E N D I T U R E + // B U D G E T E X P E N D I T U R E // -------------------------------------------------------------------------------------------- /// This extrinsic is used to create, update or delete expenditures. - /// + /// /// # Parameters: /// - origin: The administrator account /// - project_id: The selected project id where the expenditures will be created/updated/deleted @@ -750,7 +750,7 @@ pub mod pallet { /// * 4: The jobs multiplier of the expenditure /// * 5: The expenditure action to be performed. (Create, Update or Delete) /// * 6: The expenditure id. This is only used when updating or deleting an expenditure. - /// + /// /// # Considerations: /// - Naics code and jobs multiplier are always optional. /// - This function can only be called by an administrator account @@ -767,17 +767,17 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn expenditures( - origin: OriginFor, - project_id: [u8;32], + origin: OriginFor, + project_id: ProjectId, expenditures: BoundedVec<( - Option>, // name + Option, // name Option, // type - Option, // amount - Option>, // naics code - Option, // jobs multiplier + Option, // amount + Option, // naics code + Option, // jobs multiplier CUDAction, // action - Option<[u8;32]>, // expenditure_id - ), T::MaxRegistrationsAtTime>, + Option, // expenditure_id + ), T::MaxRegistrationsAtTime>, ) -> DispatchResult { let who = ensure_signed(origin)?; // origin need to be an admin @@ -789,7 +789,7 @@ pub mod pallet { /// This extrinsic is used to create, update or delete transactions. /// Transactions status can be saved as draft or submitted. - /// + /// /// # Parameters: /// - origin: The user account who is creating the transactions /// - project_id: The selected project id where the transactions will be created @@ -801,9 +801,9 @@ pub mod pallet { /// * 2: Documents associated to the transaction /// * 3: The transaction action to be performed. (Create, Update or Delete) /// * 4: The transaction id. This is only used when updating or deleting a transaction. - /// - submit: If true, the transactions will be submitted. + /// - submit: If true, the transactions will be submitted. /// If false, the transactions will be saved as draft. - /// + /// /// # Considerations: /// - This function can only be called by a builder role account /// - This extrinsic allows multiple transactions to be created/updated/deleted at the same time. @@ -819,24 +819,24 @@ pub mod pallet { /// - If a drawdown is submitted, all transactions must be submitted too. If the drawdown do not contain /// any transaction, it will be returned an error. /// - After a drawdown is submitted, it can not be updated or deleted. - /// - After a drawdown is rejected, builders will use this extrinsic to update the transactions. + /// - After a drawdown is rejected, builders will use this extrinsic to update the transactions. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn submit_drawdown( - origin: OriginFor, - project_id: [u8;32], - drawdown_id: [u8;32], + origin: OriginFor, + project_id: ProjectId, + drawdown_id: DrawdownId, transactions: Option, // expenditure_id - Option, // amount + Option, // expenditure_id + Option, // amount Option>, //Documents CUDAction, // Action - Option<[u8;32]>, // transaction_id + Option, // transaction_id ), T::MaxRegistrationsAtTime>>, submit: bool, ) -> DispatchResult { let who = ensure_signed(origin)?; // origin need to be an admin - // Ensure builder permissions + // Ensure builder permissions Self::is_authorized(who, &project_id, ProxyPermission::SubmitDrawdown)?; match submit{ @@ -852,11 +852,17 @@ pub mod pallet { // Check if there's transactions to execute if let Some(transactions) = transactions { // Do execute transactions - Self::do_execute_transactions( - project_id, - drawdown_id, - transactions, - )?; + if transactions.len() > 0 { + Self::do_execute_transactions( + project_id, + drawdown_id, + transactions)?; + } + // Self::do_execute_transactions( + // project_id, + // drawdown_id, + // transactions, + // )?; } // Do submit drawdown @@ -867,13 +873,13 @@ pub mod pallet { } /// Approves a drawdown - /// + /// /// # Parameters: /// ### For EB5 drawdowns: /// - origin: The administator account who is approving the drawdown /// - project_id: The selected project id where the drawdown will be approved /// - drawdown_id: The selected drawdown id to be approved - /// + /// /// ### For Construction Loan & Developer Equity (bulk uploads) drawdowns: /// - origin: The administator account who is approving the drawdown /// - project_id: The selected project id where the drawdown will be approved @@ -887,10 +893,10 @@ pub mod pallet { /// * 2: Documents associated to the transaction /// * 3: The transaction action to be performed. (Create, Update or Delete) /// * 4: The transaction id. This is only used when updating or deleting a transaction. - /// + /// /// # Considerations: /// - This function can only be called by an administrator account - /// - This extrinsic allows multiple transactions to be created/updated/deleted at the same time + /// - This extrinsic allows multiple transactions to be created/updated/deleted at the same time /// (only for Construction Loan & Developer Equity drawdowns). /// - Transaction parameters are optional because depends on the action to be performed: /// * **Create**: Expenditure id, Amount, Documents & Action are required. @@ -905,16 +911,16 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn approve_drawdown( - origin: OriginFor, - project_id: [u8;32], - drawdown_id: [u8;32], + origin: OriginFor, + project_id: ProjectId, + drawdown_id: DrawdownId, bulkupload: Option, transactions: Option, // expenditure_id + Option, // expenditure_id Option, // amount Option>, //Documents CUDAction, // Action - Option<[u8;32]>, // transaction_id + Option, // transaction_id ), T::MaxRegistrationsAtTime>>, ) -> DispatchResult { let who = ensure_signed(origin)?; // origin need to be an admin @@ -965,31 +971,31 @@ pub mod pallet { } /// Rejects a drawdown - /// + /// /// # Parameters: /// - origin: The administator account who is rejecting the drawdown /// - project_id: The selected project id where the drawdown will be rejected /// - drawdown_id: The selected drawdown id to be rejected - /// + /// /// Then the next two feedback parameters are optional because depends on the drawdown type: /// #### EB5 drawdowns: - /// - transactions_feedback: Administrator will provide feedback for each transaction + /// - transactions_feedback: Administrator will provide feedback for each transaction /// that is wrong. This is a vector of tuples where each entry is composed by: /// * 0: The transaction id /// * 1: The transaction feedback - /// + /// /// #### Construction Loan & Developer Equity drawdowns: /// - drawdown_feedback: Administrator will provide feedback for the WHOLE drawdown. - /// + /// /// # Considerations: /// - This function can only be called by an administrator account - /// - This extrinsic allows multiple transactions to be rejected at the same time + /// - This extrinsic allows multiple transactions to be rejected at the same time /// (only for EB5 drawdowns). - /// - For EB5 drawdowns, the administrator can provide feedback for each transaction + /// - For EB5 drawdowns, the administrator can provide feedback for each transaction /// that is wrong. - /// - For Construction Loan & Developer Equity drawdowns, the administrator can provide + /// - For Construction Loan & Developer Equity drawdowns, the administrator can provide /// feedback for the WHOLE drawdown. - /// - After a builder re-submits a drawdown, the administrator will have to review + /// - After a builder re-submits a drawdown, the administrator will have to review /// the drawdown again. /// - After a builder re-submits a drawdown, the feedback field will be cleared automatically. /// - If a single EB5 transaction is wrong, the administrator WILL reject the WHOLE drawdown. @@ -997,11 +1003,11 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn reject_drawdown( - origin: OriginFor, - project_id: [u8;32], - drawdown_id: [u8;32], - transactions_feedback: Option>, - drawdown_feedback: Option>, + origin: OriginFor, + project_id: ProjectId, + drawdown_id: DrawdownId, + transactions_feedback: Option>, + drawdown_feedback: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; // origin need to be an admin @@ -1009,7 +1015,7 @@ pub mod pallet { } /// Bulk upload drawdowns. - /// + /// /// # Parameters: /// - origin: The administator account who is uploading the drawdowns /// - project_id: The selected project id where the drawdowns will be uploaded @@ -1017,7 +1023,7 @@ pub mod pallet { /// - description: The description of the drawdown provided by the builder /// - total_amount: The total amount of the drawdown /// - documents: The documents provided by the builder for the drawdown - /// + /// /// # Considerations: /// - This function can only be called by a builder account /// - This extrinsic allows only one drawdown to be uploaded at the same time. @@ -1025,17 +1031,17 @@ pub mod pallet { /// - Only available for Construction Loan & Developer Equity drawdowns. /// - After a builder uploads a drawdown, the administrator will have to review it. /// - After a builder re-submits a drawdown, the feedback field will be cleared automatically. - /// - Bulkuploads does not allow individual transactions. - /// - After a builder uploads a drawdown, the administrator will have to + /// - Bulkuploads does not allow individual transactions. + /// - After a builder uploads a drawdown, the administrator will have to /// insert each transaction manually. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn up_bulkupload( - origin: OriginFor, - project_id: [u8;32], - drawdown_id: [u8;32], + origin: OriginFor, + project_id: ProjectId, + drawdown_id: DrawdownId, description: FieldDescription, - total_amount: u64, + total_amount: TotalAmount, documents: Documents, ) -> DispatchResult { let who = ensure_signed(origin)?; // origin need to be a builder @@ -1044,7 +1050,7 @@ pub mod pallet { } /// Modifies the inflation rate of a project. - /// + /// /// # Parameters: /// - origin: The administator account who is modifying the inflation rate /// - projects: The projects where the inflation rate will be modified. @@ -1052,22 +1058,22 @@ pub mod pallet { /// * 0: The project id /// * 1: The inflation rate /// * 2: The action to be performed (Create, Update or Delete) - /// + /// /// # Considerations: /// - This function can only be called by an administrator account /// - This extrinsic allows multiple projects to be modified at the same time. - /// - The inflation rate can be created, updated or deleted. + /// - The inflation rate can be created, updated or deleted. /// - The inflation rate is optional because depends on the CUDAction parameter: /// * **Create**: The inflation rate will be created. Project id, inflation rate and action are required. /// * **Update**: The inflation rate will be updated. Project id, inflation rate and action are required. /// * **Delete**: The inflation rate will be deleted. Project id and action are required. /// - The inflation rate can only be modified if the project is in the "started" status. - /// + /// #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn inflation_rate( - origin: OriginFor, - projects: BoundedVec<([u8;32], Option, CUDAction), T::MaxRegistrationsAtTime>, + origin: OriginFor, + projects: BoundedVec<(ProjectId, Option, CUDAction), T::MaxRegistrationsAtTime>, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -1075,13 +1081,13 @@ pub mod pallet { } /// Kill all the stored data. - /// + /// /// This function is used to kill ALL the stored data. /// Use it with caution! - /// + /// /// ### Parameters: - /// - `origin`: The user who performs the action. - /// + /// - `origin`: The user who performs the action. + /// /// ### Considerations: /// - This function is only available to the `admin` with sudo access. #[transactional] @@ -1107,4 +1113,4 @@ pub mod pallet { } } -} \ No newline at end of file +} diff --git a/pallets/fund-admin/src/tests.rs b/pallets/fund-admin/src/tests.rs index a1a1b430..3562c26a 100644 --- a/pallets/fund-admin/src/tests.rs +++ b/pallets/fund-admin/src/tests.rs @@ -20,7 +20,7 @@ fn pallet_name()-> pallet_rbac::types::IdOrVec { fn return_field_name(name: &str) -> FieldName { let name: BoundedVec> = name.as_bytes().to_vec().try_into().unwrap_or_default(); name -} +} fn return_field_description(description: &str) -> FieldDescription { let description: BoundedVec> = description.as_bytes().to_vec().try_into().unwrap_or_default(); @@ -37,13 +37,13 @@ fn register_administrator() -> DispatchResult { Ok(()) } -fn return_user(user_account:u64, user_name: Option<&str>, user_role: Option, action: CUDAction) -> BoundedVec<(u64, Option>, +fn return_user(user_account:u64, user_name: Option<&str>, user_role: Option, action: CUDAction) -> BoundedVec<(u64, Option, Option, CUDAction), MaxRegistrationsAtTime> { - let mut users: BoundedVec<(u64, Option>, + let mut users: BoundedVec<(u64, Option, Option, CUDAction), MaxRegistrationsAtTime> = bounded_vec![]; - let field_name: BoundedVec = BoundedVec::try_from(vec![return_field_name(user_name.unwrap_or_default())]).unwrap_or_default(); + let field_name = return_field_name(user_name.unwrap_or_default()); users.try_push((user_account, Some(field_name), user_role, action)).unwrap_or_default(); users } @@ -64,7 +64,7 @@ fn field_description_to_string(boundedvec: &BoundedVec>) -> St s } -// I N I T I A L +// I N I T I A L // ----------------------------------------------------------------------------------------- #[test] fn cannon_initialize_pallet_twice_shouldnt_work() { diff --git a/pallets/fund-admin/src/types.rs b/pallets/fund-admin/src/types.rs index 037a87f2..03ccf655 100644 --- a/pallets/fund-admin/src/types.rs +++ b/pallets/fund-admin/src/types.rs @@ -9,6 +9,36 @@ pub type FieldDescription = BoundedVec>; pub type CID = BoundedVec>; pub type Documents = BoundedVec<(FieldName,CID), ::MaxDocuments>; +// Projects +pub type ProjectId = [u8; 32]; +pub type CreationDate = u64; +pub type CompletionDate = u64; +pub type UpdatedDate = u64; +pub type RegistrationDate = u64; + +// Users +pub type DateRegistered = u64; + +// Transactions +pub type TransactionId = [u8; 32]; +pub type Amount = u64; + +// Drawdowns +pub type DrawdownId = [u8; 32]; +pub type DrawdownNumber = u32; + +// Budget expenditures +pub type BudgetExpenditureId = [u8; 32]; +pub type ExpenditureAmount = u64; +pub type Balance = u64; +pub type NAICSCode = BoundedVec>; +pub type JobsMultiplier = u32; +pub type InflationRate = u32; + +// Miscellaneous +pub type CreatedDate = u64; +pub type CloseDate = u64; +pub type TotalAmount = u64; #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] @@ -20,14 +50,17 @@ pub struct ProjectData { pub regional_center: Option>, pub title: FieldName, pub description: FieldDescription, - pub image: CID, - pub address: FieldName, + pub image: Option, + pub address: FieldName, pub status: ProjectStatus, - pub inflation_rate: Option, - pub creation_date: u64, - pub completion_date: u64, - pub registration_date: u64, - pub updated_date: u64, + pub inflation_rate: Option, + pub creation_date: CreationDate, + pub completion_date: CompletionDate, + pub registration_date: RegistrationDate, + pub updated_date: UpdatedDate, + pub eb5_drawdown_status: DrawdownStatus, + pub construction_loan_drawdown_status: DrawdownStatus, + pub developer_equity_drawdown_status: DrawdownStatus, } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] @@ -49,7 +82,7 @@ pub struct UserData { pub name: FieldName, pub role: ProxyRole, pub image: CID, - pub date_registered: u64, + pub date_registered: DateRegistered, pub email: FieldName, pub documents: Option>, } @@ -66,19 +99,19 @@ pub enum ProxyRole { #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen)] pub struct ExpenditureData { - pub project_id: [u8;32], + pub project_id: ProjectId, pub name: FieldName, pub expenditure_type: ExpenditureType, - pub expenditure_amount: u64, + pub expenditure_amount: ExpenditureAmount, pub naics_code: Option, - pub jobs_multiplier: Option, + pub jobs_multiplier: Option, } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] pub enum ExpenditureType { - HardCost, + HardCost, SoftCost, - Operational, + Operational, Others, } @@ -91,10 +124,10 @@ impl Default for ExpenditureType { #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen)] pub struct BudgetData { - pub expenditure_id: [u8;32], - pub balance: u64, - pub created_date: u64, - pub updated_date: u64, + pub expenditure_id: BudgetExpenditureId, + pub balance: Balance, + pub created_date: CreatedDate, + pub updated_date: UpdatedDate, } @@ -102,21 +135,21 @@ pub struct BudgetData { #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct DrawdownData { - pub project_id: [u8;32], - pub drawdown_number: u32, + pub project_id: ProjectId, + pub drawdown_number: DrawdownNumber, pub drawdown_type: DrawdownType, - pub total_amount: u64, + pub total_amount: TotalAmount, pub status: DrawdownStatus, pub documents: Option>, pub description: Option, pub feedback: Option, - pub created_date: u64, - pub close_date: u64, + pub created_date: CreatedDate, + pub close_date: CloseDate, } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] pub enum DrawdownType { - EB5, + EB5, ConstructionLoan, DeveloperEquity, } @@ -129,7 +162,8 @@ impl Default for DrawdownType { #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] pub enum DrawdownStatus { - Draft, + None, + Draft, Submitted, Approved, Rejected, @@ -145,21 +179,21 @@ impl Default for DrawdownStatus { #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct TransactionData { - pub project_id: [u8;32], - pub drawdown_id: [u8;32], - pub expenditure_id: [u8;32], - pub created_date: u64, - pub updated_date: u64, - pub closed_date: u64, + pub project_id: ProjectId, + pub drawdown_id: DrawdownId, + pub expenditure_id: BudgetExpenditureId, + pub created_date: CreatedDate, + pub updated_date: UpdatedDate, + pub closed_date: CloseDate, pub feedback: Option, - pub amount: u64, + pub amount: ExpenditureAmount, pub status: TransactionStatus, pub documents: Option>, } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] pub enum TransactionStatus { - Draft, + Draft, Submitted, Approved, Rejected, @@ -227,7 +261,7 @@ pub enum ProxyPermission { Inflation, // inflation } -impl ProxyPermission { +impl ProxyPermission { pub fn to_vec(self) -> Vec{ match self{ Self::RegisterUser => "RegisterUser".as_bytes().to_vec(), @@ -252,7 +286,7 @@ impl ProxyPermission { pub fn administrator_permissions() -> Vec>{ use crate::types::ProxyPermission::*; let administrator_permissions = [ - RegisterUser.to_vec(), + RegisterUser.to_vec(), EditUser.to_vec(), CreateProject.to_vec(), EditProject.to_vec(), @@ -293,4 +327,4 @@ impl ProxyPermission { } -} \ No newline at end of file +} diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index b4e67c81..ed5cc522 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -16,17 +16,17 @@ impl Pallet { pub fn do_initial_setup()->DispatchResult{ let pallet_id = Self::pallet_id(); let super_roles = vec![MarketplaceRole::Owner.to_vec(), MarketplaceRole::Admin.to_vec()]; - let super_role_ids = T::Rbac::create_and_set_roles(pallet_id.clone(), super_roles)?; + let super_role_ids = ::Rbac::create_and_set_roles(pallet_id.clone(), super_roles)?; for super_role in super_role_ids{ - T::Rbac::create_and_set_permissions(pallet_id.clone(), super_role, Permission::admin_permissions())?; + ::Rbac::create_and_set_permissions(pallet_id.clone(), super_role, Permission::admin_permissions())?; } // participant role and permissions - let participant_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::Participant.to_vec()].to_vec())?; - T::Rbac::create_and_set_permissions(pallet_id.clone(), participant_role_id[0], Permission::participant_permissions() )?; + let participant_role_id = ::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::Participant.to_vec()].to_vec())?; + ::Rbac::create_and_set_permissions(pallet_id.clone(), participant_role_id[0], Permission::participant_permissions() )?; // appraiser role and permissions - let _appraiser_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::Appraiser.to_vec()].to_vec())?; + let _appraiser_role_id = ::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::Appraiser.to_vec()].to_vec())?; // redemption specialist role and permissions - let _redemption_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; + let _redemption_role_id = ::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; Self::deposit_event(Event::MarketplaceSetupCompleted); Ok(()) @@ -38,7 +38,7 @@ impl Pallet { // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); //Insert on marketplaces and marketplaces by auth - T::Rbac::create_scope(Self::pallet_id(), marketplace_id)?; + ::Rbac::create_scope(Self::pallet_id(), marketplace_id)?; Self::insert_in_auth_market_lists(owner.clone(), MarketplaceRole::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceRole::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); @@ -125,7 +125,7 @@ impl Pallet { //Self::can_enroll(authority, marketplace_id)?; Self::is_authorized(authority, &marketplace_id,Permission::AddAuth)?; //ensure the account is not already an authority - // handled by T::Rbac::assign_role_to_user + // handled by ::Rbac::assign_role_to_user //ensure!(!Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AlreadyApplied); match authority_type{ MarketplaceRole::Owner => { @@ -148,7 +148,7 @@ impl Pallet { //Self::can_enroll(authority.clone(), marketplace_id)?; Self::is_authorized(authority.clone(), &marketplace_id,Permission::RemoveAuth)?; //ensure the account has the selected authority before to try to remove - // T::Rbac handles the if role doesnt hasnt been asigned to the user + // ::Rbac handles the if role doesnt hasnt been asigned to the user //ensure!(Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AuthorityNotFoundForUser); match authority_type{ @@ -494,7 +494,7 @@ impl Pallet { fn insert_in_auth_market_lists(authority: T::AccountId, role: MarketplaceRole, marketplace_id: [u8;32])->DispatchResult{ - T::Rbac::assign_role_to_user(authority, Self::pallet_id(), + ::Rbac::assign_role_to_user(authority, Self::pallet_id(), &marketplace_id, role.id())?; Ok(()) @@ -529,7 +529,7 @@ impl Pallet { fn remove_from_market_lists(account: T::AccountId, author_type: MarketplaceRole , marketplace_id : [u8;32])->DispatchResult{ - T::Rbac::remove_role_from_user(account, Self::pallet_id(), + ::Rbac::remove_role_from_user(account, Self::pallet_id(), &marketplace_id, author_type.id())?; Ok(()) @@ -557,17 +557,17 @@ impl Pallet { Self::insert_in_applicants_lists(applicant.clone(), next_status,marketplace_id)?; if prev_status == ApplicationStatus::Approved{ - T::Rbac::remove_role_from_user(applicant.clone(), Self::pallet_id(), &marketplace_id, MarketplaceRole::Participant.id())?; + ::Rbac::remove_role_from_user(applicant.clone(), Self::pallet_id(), &marketplace_id, MarketplaceRole::Participant.id())?; } if next_status == ApplicationStatus::Approved{ - T::Rbac::assign_role_to_user(applicant, Self::pallet_id(), &marketplace_id, MarketplaceRole::Participant.id())? + ::Rbac::assign_role_to_user(applicant, Self::pallet_id(), &marketplace_id, MarketplaceRole::Participant.id())? } Ok(()) } fn is_authorized( authority: T::AccountId, marketplace_id: &[u8;32], permission: Permission ) -> DispatchResult{ - T::Rbac::is_authorized( + ::Rbac::is_authorized( authority, Self::pallet_id(), marketplace_id, @@ -579,7 +579,7 @@ impl Pallet { ///Lets us know if the selected user is an admin. /// It returns true if the user is an admin, false otherwise. fn is_admin(account: T::AccountId, marketplace_id: [u8;32]) -> bool{ - T::Rbac::has_role(account, Self::pallet_id(), + ::Rbac::has_role(account, Self::pallet_id(), &marketplace_id, [MarketplaceRole::Admin.id()].to_vec()).is_ok() } @@ -604,7 +604,7 @@ impl Pallet { // }; //owners.len() == 1 - T::Rbac::get_role_users_len(Self::pallet_id(), + ::Rbac::get_role_users_len(Self::pallet_id(), &marketplace_id, &MarketplaceRole::Owner.id()) == 1 } @@ -657,7 +657,7 @@ impl Pallet { // remove from Marketplaces list >::remove(marketplace_id); - T::Rbac::remove_scope(Self::pallet_id(), marketplace_id)?; + ::Rbac::remove_scope(Self::pallet_id(), marketplace_id)?; Ok(()) } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 725ca5de..0c8b8c6d 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -670,7 +670,7 @@ pub mod pallet { let _ = >::clear(1000, None); let _ = >::clear(1000, None); let _ = >::clear(1000, None); - T::Rbac::remove_pallet_storage(Self::pallet_id())?; + // T::Rbac::remove_pallet_storage(Self::pallet_id())?; Ok(()) } diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index f22786e9..931d54b3 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -100,6 +100,7 @@ impl pallet_fruniques::Config for Test { type Event = Event; type RemoveOrigin = EnsureRoot; type ChildMaxLen = ChildMaxLen; + type Rbac = RBAC; } parameter_types! { diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index 2e252042..2e0397ff 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -52,7 +52,7 @@ fn _create_file(name: &str, cid: &str, create_custodian_file: bool) -> Applicati } // due to encoding problems with polkadot-js, the custodians_cid generation will be done in another function -fn create_application_fields( n_files: u32) -> +fn create_application_fields( n_files: u32) -> BoundedVec<(BoundedVec >,BoundedVec> ), MaxFiles> { let mut files = Vec::<(BoundedVec >,BoundedVec> )>::default(); for i in 0..n_files{ @@ -63,7 +63,7 @@ fn create_application_fields( n_files: u32) -> BoundedVec::<(BoundedVec >,BoundedVec> ), MaxFiles>::try_from( files).unwrap_or_default() } -fn create_custiodian_fields( custodian_account: u64, n_files: u32, ) -> +fn create_custodian_fields( custodian_account: u64, n_files: u32, ) -> Option<( u64,BoundedVec>, MaxFiles>) >{ let cids: Vec>> = (0..n_files).map(|n|{ let cid = format!("cid_custodian{}",n.to_string()); @@ -71,7 +71,7 @@ fn create_custiodian_fields( custodian_account: u64, n_files: u32, ) -> }).collect(); - Some( + Some( (custodian_account,BoundedVec::>, MaxFiles>::try_from(cids).unwrap_or_default()) ) } @@ -83,7 +83,7 @@ fn create_marketplace_works() { // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert!(GatedMarketplace::marketplaces(m_id).is_some() ); + assert!(GatedMarketplace::marketplaces(m_id).is_some() ); }); } @@ -103,8 +103,8 @@ fn exceeding_max_roles_per_auth_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, m_label.clone() )); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 2, MarketplaceRole::Appraiser, m_label.using_encoded(blake2_256))); assert_noop!( - GatedMarketplace::add_authority(Origin::signed(1), 2, MarketplaceRole::RedemptionSpecialist, m_label.using_encoded(blake2_256) ), - RbacErr::ExceedMaxRolesPerUser + GatedMarketplace::add_authority(Origin::signed(1), 2, MarketplaceRole::RedemptionSpecialist, m_label.using_encoded(blake2_256) ), + RbacErr::ExceedMaxRolesPerUser ); }); @@ -128,7 +128,7 @@ fn apply_with_custodian_works() { // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custiodian_fields(4,2) )); + assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custodian_fields(4,2) )); assert!( GatedMarketplace::applicants_by_marketplace(m_id, ApplicationStatus::Pending).len() ==1); assert!(GatedMarketplace::custodians(4, m_id).pop().is_some() ); @@ -142,7 +142,7 @@ fn apply_with_same_account_as_custodian_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_noop!( - GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custiodian_fields(3,2) ), + GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custodian_fields(3,2) ), Error::::ApplicantCannotBeCustodian ); }); @@ -154,10 +154,10 @@ fn exceeding_max_applications_per_custodian_shouldnt_work() { // Dispatch a signed extrinsic. assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custiodian_fields(6,2) )); - assert_ok!(GatedMarketplace::apply(Origin::signed(4),m_id, create_application_fields(2), create_custiodian_fields(6,2) )); + assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custodian_fields(6,2) )); + assert_ok!(GatedMarketplace::apply(Origin::signed(4),m_id, create_application_fields(2), create_custodian_fields(6,2) )); assert_noop!( - GatedMarketplace::apply(Origin::signed(5),m_id, create_application_fields(2), create_custiodian_fields(6,2) ), + GatedMarketplace::apply(Origin::signed(5),m_id, create_application_fields(2), create_custodian_fields(6,2) ), Error::::ExceedMaxApplicationsPerCustodian ); }); @@ -464,10 +464,10 @@ fn update_marketplace_marketplace_not_found_shouldnt_work(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert!(GatedMarketplace::marketplaces(m_id).is_some()); - let m_id_2 = create_label("not the first marketplace").using_encoded(blake2_256); + let m_id_2 = create_label("not the first marketplace").using_encoded(blake2_256); assert_noop!(GatedMarketplace::update_label_marketplace(Origin::signed(1), m_id_2, create_label("my marketplace 2")), Error::::MarketplaceNotFound); }); - + } #[test] @@ -486,9 +486,9 @@ fn update_label_marketplace_works(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert!(GatedMarketplace::marketplaces(m_id).is_some()); + assert!(GatedMarketplace::marketplaces(m_id).is_some()); assert_ok!(GatedMarketplace::update_label_marketplace(Origin::signed(1), m_id, create_label("my marketplace 2"))); - assert!(GatedMarketplace::marketplaces(m_id).is_some() ); + assert!(GatedMarketplace::marketplaces(m_id).is_some() ); }); } @@ -504,7 +504,7 @@ fn remove_marketplace_marketplace_not_found_shouldnt_work(){ let m_id_2 = create_label("not the first marketplace").using_encoded(blake2_256); assert_noop!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id_2), Error::::MarketplaceNotFound); }); - + } @@ -538,7 +538,7 @@ fn remove_marketplace_deletes_storage_from_marketplaces_by_authority_works(){ assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::RedemptionSpecialist, m_id)); - + //assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![MarketplaceRole::Owner]); assert_ok!(::Rbac::has_role(1, GatedMarketplace::pallet_id(), &m_id, vec![MarketplaceRole::Owner.id()])); //assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![MarketplaceRole::Admin]); @@ -602,7 +602,7 @@ fn remove_marketplace_deletes_storage_from_custodians_works(){ let m_id = create_label("my marketplace").using_encoded(blake2_256); assert!(GatedMarketplace::marketplaces(m_id).is_some()); - assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custiodian_fields(4,2) )); + assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), create_custodian_fields(4,2) )); assert!(GatedMarketplace::custodians(4, m_id) == vec![3]); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); @@ -804,12 +804,12 @@ fn enlist_sell_offer_works() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().offer_type, OfferType::SellOrder); @@ -842,12 +842,12 @@ fn enlist_sell_offer_item_already_enlisted_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); assert_noop!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000), Error::::OfferAlreadyExists); @@ -862,11 +862,11 @@ fn enlist_sell_offer_not_owner_tries_to_enlist_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - + assert_noop!(GatedMarketplace::enlist_sell_offer(Origin::signed(2), m_id, 0, 0, 10000), Error::::NotOwner); }); } @@ -878,11 +878,11 @@ fn enlist_sell_offer_price_must_greater_than_zero_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - + assert_noop!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 0), Error::::PriceMustBeGreaterThanZero); }); } @@ -894,12 +894,12 @@ fn enlist_sell_offer_price_must_greater_than_minimun_amount_works() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - let minimum_amount = 1001; + + let minimum_amount = 1001; assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, minimum_amount)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); @@ -913,11 +913,11 @@ fn enlist_sell_offer_is_properly_stored_works() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); @@ -938,13 +938,13 @@ fn enlist_sell_offer_two_marketplaces(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace2"))); let m_id2 = create_label("my marketplace2").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id2, 0, 0, 11000)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id2, 0, 0, 11000)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 2); assert_eq!(GatedMarketplace::offers_by_account(1).len(), 2); @@ -962,12 +962,12 @@ fn enlist_buy_offer_works() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); @@ -986,12 +986,12 @@ fn enlist_buy_offer_item_is_not_for_sale_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); @@ -1008,20 +1008,20 @@ fn enlist_buy_offer_owner_cannnot_create_buy_offers_for_their_own_items_shouldnt assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - + assert_noop!(GatedMarketplace::enlist_buy_offer(Origin::signed(1), m_id, 0, 0, 1100), Error::::CannotCreateOffer); }); } -#[test] +#[test] fn enlist_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); @@ -1030,12 +1030,12 @@ fn enlist_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); @@ -1052,12 +1052,12 @@ fn enlist_buy_offer_price_must_greater_than_zero_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); @@ -1076,12 +1076,12 @@ fn enlist_buy_offer_an_item_can_receive_multiple_buy_offers(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); @@ -1107,15 +1107,15 @@ fn take_sell_offer_works(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); assert_ok!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id)); @@ -1130,15 +1130,15 @@ fn take_sell_offer_owner_cannnot_be_the_buyer_shouldnt_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(1), offer_id), Error::::CannotTakeOffer); @@ -1150,15 +1150,15 @@ fn take_sell_offer_id_does_not_exist_shouldnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); let offer_id2 = offer_id.using_encoded(blake2_256); @@ -1172,15 +1172,15 @@ fn take_sell_offer_buyer_does_not_have_enough_balance_shouldnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1100); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id), Error::::NotEnoughBalance); @@ -1192,22 +1192,22 @@ fn take_buy_offer_works(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - - assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); @@ -1219,20 +1219,20 @@ fn take_buy_offer_only_owner_can_accept_buy_offers_shouldnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - - assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); @@ -1245,19 +1245,19 @@ fn take_buy_offer_id_does_not_exist_shouldnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - - assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); @@ -1272,22 +1272,22 @@ fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - - assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - + Balances::make_free_balance_be(&2, 0); assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2), Error::::NotEnoughBalance); }); @@ -1298,18 +1298,18 @@ fn remove_sell_offer_works(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - + assert_ok!(GatedMarketplace::remove_offer(Origin::signed(1), offer_id)); assert_eq!(GatedMarketplace::offers_by_account(1).len(), 0); assert!(GatedMarketplace::offers_info(offer_id).is_none()); @@ -1321,22 +1321,22 @@ fn remove_buy_offer_works(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1001)); + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1001)); let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id2).is_some()); - + assert_ok!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2)); assert_eq!(GatedMarketplace::offers_by_account(2).len(), 0); assert!(GatedMarketplace::offers_info(offer_id2).is_none()); @@ -1348,18 +1348,18 @@ fn remove_offer_id_does_not_exist_sholdnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - + let offer_id2 = offer_id.using_encoded(blake2_256); assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2), Error::::OfferNotFound); }); @@ -1370,18 +1370,18 @@ fn remove_offer_creator_doesnt_match_sholdnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - + assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id), Error::::CannotRemoveOffer); }); } @@ -1391,27 +1391,27 @@ fn remove_offer_status_is_closed_shouldnt_work(){ new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 1300); - + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); let m_id = create_label("my marketplace").using_encoded(blake2_256); - + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); assert_eq!(Uniques::owner(0, 0).unwrap(), 1); - assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - - assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2), Error::::CannotDeleteOffer); }); - -} \ No newline at end of file + +} diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index bc21c4b5..d08a0c5e 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -10,7 +10,7 @@ use crate::types::*; impl RoleBasedAccessControl for Pallet{ /*---- Basic Insertion of individual storage maps ---*/ /// Scope creation - /// + /// /// Creates a scope within a external pallet using the pallet index. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -25,7 +25,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Scope removal - /// + /// /// Removes a scope within a external pallet using the pallet index. /// Executing this function will delete all registered role users. /// ### Parameters: @@ -53,14 +53,14 @@ impl RoleBasedAccessControl for Pallet{ }); // remove on users by scope let _ = >::clear_prefix((pallet_id, scope_id),1000 ,None); - + Ok(()) } /// External pallet storage removal - /// + /// /// Removes all storage associated to a external pallet. - /// + /// /// Executing this function will delete all role lists and permissions linked /// to that pallet. /// ### Parameters: @@ -90,13 +90,13 @@ impl RoleBasedAccessControl for Pallet{ } /// Role creation and coupling with pallet. - /// + /// /// Creates the specified roles if needed and adds them to the pallet. /// Recommended first step to enable RBAC on a external pallet. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `roles`: A list of roles to create, encoded in bytes. - fn create_and_set_roles(pallet: IdOrVec, roles: Vec>) -> + fn create_and_set_roles(pallet: IdOrVec, roles: Vec>) -> Result, DispatchError>{ let mut role_ids= Vec::<[u8;32]>::new(); for role in roles{ @@ -109,7 +109,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Role creation. - /// + /// /// Creates a role and returns its identifier, if its already created, /// the function will return the preexisting one. /// ### Parameters: @@ -124,12 +124,12 @@ impl RoleBasedAccessControl for Pallet{ } /// Role coupling with pallet. - /// + /// /// Assigns a previously created role to a pallet. - /// + /// /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. - /// - `role_id`: The unique role identifier. + /// - `role_id`: The unique role identifier. fn set_role_to_pallet(pallet: IdOrVec, role_id: RoleId )-> DispatchResult{ ensure!(>::contains_key(role_id), Error::::RoleNotFound); >::try_mutate(pallet.to_id(), |roles|{ @@ -140,11 +140,11 @@ impl RoleBasedAccessControl for Pallet{ } /// Multiple role coupling with pallet. - /// + /// /// Assigns multiple, previously created roles to a pallet. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. - /// - `roles`: A list of unique role identifiers. + /// - `roles`: A list of unique role identifiers. fn set_multiple_pallet_roles(pallet: IdOrVec, roles: Vec)->DispatchResult{ let pallet_id = pallet.to_id(); // checks for duplicates: @@ -161,7 +161,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Role assignation to a user - /// + /// /// Assigns a role to a user in a scope context. /// ### Parameters: /// - `user`: The account which the role will be granted. @@ -186,7 +186,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Role removal from the user. - /// + /// /// Removes the specified role from a user in a scope context. the user will no longer /// be able to enforce the removed role and its permissions. /// ### Parameters: @@ -218,13 +218,13 @@ impl RoleBasedAccessControl for Pallet{ } /// Permission creation and coupling with a role. - /// + /// /// Creates the specified permissions if needed and assigns them to a role. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role identifier to which the permissions will /// be linked to. - /// - `permissions`: A list of permissions to create and link, + /// - `permissions`: A list of permissions to create and link, /// encoded in bytes. fn create_and_set_permissions(pallet: IdOrVec, role_id: RoleId, permissions: Vec>)-> Result, DispatchError> { @@ -241,7 +241,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Permission creation - /// + /// /// Creates the specified permission in the specified pallet.. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -260,7 +260,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Permission linking to role. - /// + /// /// Assigns a previously created permission to a role. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -281,8 +281,8 @@ impl RoleBasedAccessControl for Pallet{ } /// Multiple permissions assignation to a role - /// - /// Assigns multiple, previously created permissions + /// + /// Assigns multiple, previously created permissions /// to a role in a pallet context. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -294,7 +294,7 @@ impl RoleBasedAccessControl for Pallet{ let pallet_id_enum = pallet.to_id_enum(); let pallet_id = pallet_id_enum.to_id(); Self::is_role_linked_to_pallet(pallet_id_enum, &role_id )?; - + let role_permissions = >::get(&pallet_id, role_id); for id in permissions.clone(){ ensure!(!role_permissions.contains(&id), Error::::PermissionAlreadyLinkedToRole ); @@ -308,7 +308,7 @@ impl RoleBasedAccessControl for Pallet{ /*---- Helper functions ----*/ /// Authorization function - /// + /// /// Checks if the user has a role that includes the specified permission. /// ### Parameters: /// - `user`: The account to validate. @@ -329,7 +329,7 @@ impl RoleBasedAccessControl for Pallet{ } /// User role validation function - /// + /// /// Checks if the user has at least one of the specified roles. /// ### Parameters: /// - `user`: The account to validate. @@ -346,9 +346,9 @@ impl RoleBasedAccessControl for Pallet{ ); Ok(()) } - + /// Scope validation - /// + /// /// Checks if the scope exists in that pallet. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -359,18 +359,18 @@ impl RoleBasedAccessControl for Pallet{ } /// Permission validation. - /// - /// Checks if the permission exists in a pallet context. + /// + /// Checks if the permission exists in a pallet context. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `permission_id`: The permission to validate. fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId)->DispatchResult{ ensure!(>::contains_key(pallet.to_id(), permission_id), Error::::PermissionNotFound); - Ok(()) + Ok(()) } /// Role validation - /// + /// /// Checks if the role is linked to the pallet. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -383,7 +383,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Permission linking validation - /// + /// /// Checks if the permission is linked to the role in the pallet context. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -396,7 +396,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Role list length - /// + /// /// Returns the number of user that have the specified role in a scope context. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. @@ -431,4 +431,4 @@ impl Pallet{ filtered_vec.dedup(); vec.len() == filtered_vec.len() } -} \ No newline at end of file +} diff --git a/parachain-runtime/src/lib.rs b/parachain-runtime/src/lib.rs index b4113974..71627e84 100644 --- a/parachain-runtime/src/lib.rs +++ b/parachain-runtime/src/lib.rs @@ -973,6 +973,7 @@ impl pallet_fruniques::Config for Runtime { type Event = Event; type RemoveOrigin = RootOrThreeFifthsOfCouncil; type ChildMaxLen = ChildMaxLen; + type Rbac = RBAC; } parameter_types! { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 09ce3d65..166a8332 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -106,7 +106,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 127, + spec_version: 128, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -549,7 +549,7 @@ impl pallet_fruniques::Config for Runtime { EnsureRoot, pallet_collective::EnsureProportionAtLeast, >; - + type Rbac = RBAC; type ChildMaxLen = ChildMaxLen; }