use super::*;
use crate::types::*;
use frame_support::pallet_prelude::*;
use frame_support::sp_io::hashing::blake2_256;
use frame_support::traits::Time;
use frame_system::pallet_prelude::*;
use frame_system::RawOrigin;
use pallet_rbac::types::*;
use scale_info::prelude::vec; use sp_runtime::sp_std::vec::Vec; use sp_runtime::traits::StaticLookup;
use sp_runtime::Permill;
impl<T: Config> Pallet<T> {
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 as pallet::Config>::Rbac::create_and_set_roles(pallet_id.clone(), super_roles)?;
for super_role in super_role_ids {
<T as pallet::Config>::Rbac::create_and_set_permissions(
pallet_id.clone(),
super_role,
Permission::admin_permissions(),
)?;
}
let participant_role_id = <T as pallet::Config>::Rbac::create_and_set_roles(
pallet_id.clone(),
[MarketplaceRole::Participant.to_vec()].to_vec(),
)?;
<T as pallet::Config>::Rbac::create_and_set_permissions(
pallet_id.clone(),
participant_role_id[0],
Permission::participant_permissions(),
)?;
let _appraiser_role_id = <T as pallet::Config>::Rbac::create_and_set_roles(
pallet_id.clone(),
[MarketplaceRole::Appraiser.to_vec()].to_vec(),
)?;
let _redemption_role_id = <T as pallet::Config>::Rbac::create_and_set_roles(
pallet_id,
[MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec(),
)?;
Self::deposit_event(Event::MarketplaceSetupCompleted);
Ok(())
}
pub fn do_create_marketplace(
origin: OriginFor<T>,
admin: T::AccountId,
marketplace: Marketplace<T>,
) -> DispatchResult {
let owner = ensure_signed(origin.clone())?;
let marketplace_id = marketplace.using_encoded(blake2_256);
ensure!(!<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceAlreadyExists);
<T as pallet::Config>::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)?;
<Marketplaces<T>>::insert(marketplace_id, marketplace);
Self::deposit_event(Event::MarketplaceStored(owner, admin, marketplace_id));
Ok(())
}
pub fn do_apply(
applicant: T::AccountId,
custodian: Option<T::AccountId>,
marketplace_id: [u8; 32],
application: Application<T>,
) -> DispatchResult {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
ensure!(!Self::is_user_blocked(applicant.clone(), marketplace_id), Error::<T>::UserIsBlocked);
ensure!(
!<ApplicationsByAccount<T>>::contains_key(applicant.clone(), marketplace_id),
Error::<T>::AlreadyApplied
);
let app_id = (marketplace_id, applicant.clone(), application.clone()).using_encoded(blake2_256);
ensure!(!<Applications<T>>::contains_key(app_id), Error::<T>::AlreadyApplied);
if let Some(c) = custodian {
ensure!(applicant.ne(&c), Error::<T>::ApplicantCannotBeCustodian);
Self::insert_custodian(c, marketplace_id, applicant.clone())?;
}
Self::insert_in_applicants_lists(
applicant.clone(),
ApplicationStatus::default(),
marketplace_id,
)?;
<ApplicationsByAccount<T>>::insert(applicant, marketplace_id, app_id);
<Applications<T>>::insert(app_id, application);
Self::deposit_event(Event::ApplicationStored(app_id, marketplace_id));
Ok(())
}
pub fn do_invite(
authority: T::AccountId,
marketplace_id: [u8; 32],
new_user: T::AccountId,
fields: Fields<T>,
custodian_fields: Option<CustodianFields<T>>,
) -> DispatchResult {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
ensure!(!Self::is_user_blocked(new_user.clone(), marketplace_id), Error::<T>::UserIsBlocked);
ensure!(
!<ApplicationsByAccount<T>>::contains_key(new_user.clone(), marketplace_id),
Error::<T>::AlreadyApplied
);
Self::is_authorized(authority.clone(), &marketplace_id, Permission::Enroll)?;
let (custodian, fields) = Self::set_up_application(fields, custodian_fields);
let application = Application::<T> {
status: ApplicationStatus::default(),
fields,
feedback: BoundedVec::<u8, T::MaxFeedbackLen>::default(),
};
Self::do_apply(new_user.clone(), custodian, marketplace_id, application)?;
Self::do_enroll(
authority,
marketplace_id,
AccountOrApplication::Account(new_user),
true,
BoundedVec::<u8, T::MaxFeedbackLen>::try_from(
b"User enrolled by the marketplace admin".to_vec(),
)
.unwrap(),
)?;
Ok(())
}
pub fn do_enroll(
authority: T::AccountId,
marketplace_id: [u8; 32],
account_or_application: AccountOrApplication<T>,
approved: bool,
feedback: BoundedVec<u8, T::MaxFeedbackLen>,
) -> DispatchResult {
Self::is_authorized(authority, &marketplace_id, Permission::Enroll)?;
let next_status = match approved {
true => ApplicationStatus::Approved,
false => ApplicationStatus::Rejected,
};
let applicant = match account_or_application.clone() {
AccountOrApplication::Account(acc) => acc,
AccountOrApplication::Application(application_id) => <ApplicationsByAccount<T>>::iter()
.find_map(|(acc, m_id, app_id)| {
if m_id == marketplace_id && app_id == application_id {
return Some(acc);
}
None
})
.ok_or(Error::<T>::ApplicationNotFound)?,
};
ensure!(!Self::is_user_blocked(applicant.clone(), marketplace_id), Error::<T>::UserIsBlocked);
Self::change_applicant_status(applicant, marketplace_id, next_status, feedback)?;
Self::deposit_event(Event::ApplicationProcessed(
account_or_application,
marketplace_id,
next_status,
));
Ok(())
}
pub fn do_authority(
authority: T::AccountId,
account: T::AccountId,
authority_type: MarketplaceRole,
marketplace_id: [u8; 32],
) -> DispatchResult {
Self::is_authorized(authority, &marketplace_id, Permission::AddAuth)?;
ensure!(!Self::is_user_blocked(account.clone(), marketplace_id), Error::<T>::UserIsBlocked);
match authority_type {
MarketplaceRole::Owner => {
ensure!(!Self::owner_exist(marketplace_id), Error::<T>::OnlyOneOwnerIsAllowed);
Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?;
},
_ => {
Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?;
},
}
Self::deposit_event(Event::AuthorityAdded(account, authority_type));
Ok(())
}
pub fn self_enroll(account: T::AccountId, marketplace_id: [u8; 32]) -> DispatchResult {
ensure!(
!Self::has_any_role(account.clone(), &marketplace_id),
Error::<T>::UserAlreadyParticipant
);
ensure!(!Self::is_user_blocked(account.clone(), marketplace_id), Error::<T>::UserIsBlocked);
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
Self::insert_in_auth_market_lists(
account.clone(),
MarketplaceRole::Participant,
marketplace_id,
)?;
Self::deposit_event(Event::AuthorityAdded(account, MarketplaceRole::Participant));
Ok(())
}
pub fn do_remove_authority(
authority: T::AccountId,
account: T::AccountId,
authority_type: MarketplaceRole,
marketplace_id: [u8; 32],
) -> DispatchResult {
Self::is_authorized(authority.clone(), &marketplace_id, Permission::RemoveAuth)?;
match authority_type {
MarketplaceRole::Owner => {
ensure!(Self::owner_exist(marketplace_id), Error::<T>::OwnerNotFound);
return Err(Error::<T>::CantRemoveOwner.into());
},
MarketplaceRole::Admin => {
ensure!(authority != account, Error::<T>::AdminCannotRemoveItself);
ensure!(!Self::is_admin(authority, marketplace_id), Error::<T>::CannotDeleteAdmin);
Self::remove_from_market_lists(account.clone(), authority_type, marketplace_id)?;
},
_ => {
Self::remove_from_market_lists(account.clone(), authority_type, marketplace_id)?;
},
}
Self::deposit_event(Event::AuthorityRemoved(account, authority_type));
Ok(())
}
pub fn do_update_label_marketplace(
authority: T::AccountId,
marketplace_id: [u8; 32],
new_label: BoundedVec<u8, T::LabelMaxLen>,
) -> DispatchResult {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
Self::is_authorized(authority, &marketplace_id, Permission::UpdateLabel)?;
Self::update_label(marketplace_id, new_label)?;
Self::deposit_event(Event::MarketplaceLabelUpdated(marketplace_id));
Ok(())
}
pub fn do_remove_marketplace(
authority: T::AccountId,
marketplace_id: [u8; 32],
) -> DispatchResult {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
Self::is_authorized(authority, &marketplace_id, Permission::RemoveMarketplace)?;
Self::remove_selected_marketplace(marketplace_id)?;
Self::deposit_event(Event::MarketplaceRemoved(marketplace_id));
Ok(())
}
pub fn do_enlist_sell_offer(
authority: T::AccountId,
marketplace_id: [u8; 32],
collection_id: T::CollectionId,
item_id: T::ItemId,
price: T::Balance,
percentage: u32,
) -> Result<[u8; 32], DispatchError> {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
Self::is_authorized(authority.clone(), &marketplace_id, Permission::EnlistSellOffer)?;
if let Some(a) = pallet_uniques::Pallet::<T>::owner(collection_id, item_id) {
ensure!(a == authority, Error::<T>::NotOwner);
} else {
return Err(Error::<T>::CollectionNotFound.into());
}
Self::is_the_offer_valid(price, Permill::from_percent(percentage))?;
let creation_date = Self::get_timestamp_in_milliseconds().ok_or(Error::<T>::TimestampError)?;
let marketplace =
<Marketplaces<T>>::get(marketplace_id).ok_or(Error::<T>::MarketplaceNotFound)?;
let offer_data = OfferData::<T> {
marketplace_id,
collection_id,
item_id,
creator: authority.clone(),
price,
fee: price * Permill::deconstruct(marketplace.sell_fee).into() / 1_000_000u32.into(),
percentage: Permill::from_percent(percentage),
creation_date,
status: OfferStatus::Open,
offer_type: OfferType::SellOrder,
buyer: None,
};
let offer_id = offer_data.using_encoded(blake2_256);
<OffersByItem<T>>::try_mutate(collection_id, item_id, |offers| offers.try_push(offer_id))
.map_err(|_| Error::<T>::OfferStorageError)?;
<OffersByAccount<T>>::try_mutate(authority, |offers| offers.try_push(offer_id))
.map_err(|_| Error::<T>::OfferStorageError)?;
ensure!(!<OffersInfo<T>>::contains_key(offer_id), Error::<T>::OfferAlreadyExists);
<OffersInfo<T>>::insert(offer_id, offer_data);
<OffersByMarketplace<T>>::try_mutate(marketplace_id, |offers| offers.try_push(offer_id))
.map_err(|_| Error::<T>::OfferStorageError)?;
pallet_fruniques::Pallet::<T>::do_freeze(&collection_id, item_id)?;
Self::deposit_event(Event::OfferStored(collection_id, item_id, offer_id));
Ok(offer_id)
}
pub fn do_enlist_buy_offer(
authority: T::AccountId,
marketplace_id: [u8; 32],
collection_id: T::CollectionId,
item_id: T::ItemId,
price: T::Balance,
percentage: u32,
) -> Result<[u8; 32], DispatchError> {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
if let Some(a) = pallet_uniques::Pallet::<T>::owner(collection_id, item_id) {
ensure!(a != authority, Error::<T>::CannotCreateOffer);
} else {
return Err(Error::<T>::CollectionNotFound.into());
}
Self::can_this_item_receive_buy_orders(
&marketplace_id,
authority.clone(),
&collection_id,
&item_id,
)?;
let asset_id = <Marketplaces<T>>::get(marketplace_id)
.ok_or(Error::<T>::MarketplaceNotFound)?
.asset_id;
let total_user_balance =
pallet_mapped_assets::Pallet::<T>::balance(asset_id, authority.clone());
ensure!(total_user_balance >= price, Error::<T>::NotEnoughBalance);
Self::is_the_offer_valid(price, Permill::from_percent(percentage))?;
let creation_date = Self::get_timestamp_in_milliseconds().ok_or(Error::<T>::TimestampError)?;
let marketplace =
<Marketplaces<T>>::get(marketplace_id).ok_or(Error::<T>::MarketplaceNotFound)?;
let offer_data = OfferData::<T> {
marketplace_id,
collection_id,
item_id,
creator: authority.clone(),
price,
fee: price * Permill::deconstruct(marketplace.buy_fee).into() / 1_000_000u32.into(),
percentage: Permill::from_percent(percentage),
creation_date,
status: OfferStatus::Open,
offer_type: OfferType::BuyOrder,
buyer: None,
};
let offer_id = offer_data.using_encoded(blake2_256);
<OffersByItem<T>>::try_mutate(collection_id, item_id, |offers| offers.try_push(offer_id))
.map_err(|_| Error::<T>::OfferStorageError)?;
<OffersByAccount<T>>::try_mutate(authority, |offers| offers.try_push(offer_id))
.map_err(|_| Error::<T>::OfferStorageError)?;
ensure!(!<OffersInfo<T>>::contains_key(offer_id), Error::<T>::OfferAlreadyExists);
<OffersInfo<T>>::insert(offer_id, offer_data);
<OffersByMarketplace<T>>::try_mutate(marketplace_id, |offers| offers.try_push(offer_id))
.map_err(|_| Error::<T>::OfferStorageError)?;
Self::deposit_event(Event::OfferStored(collection_id, item_id, offer_id));
Ok(offer_id)
}
pub fn do_take_sell_offer(origin: OriginFor<T>, offer_id: [u8; 32]) -> DispatchResult
where
<T as pallet_uniques::Config>::ItemId: From<u32>,
{
let buyer = ensure_signed(origin.clone())?;
let offer_data = <OffersInfo<T>>::get(offer_id).ok_or(Error::<T>::OfferNotFound)?;
let marketplace_id = offer_data.marketplace_id;
Self::is_authorized(buyer.clone(), &offer_data.marketplace_id, Permission::TakeSellOffer)?;
let owner_item =
pallet_uniques::Pallet::<T>::owner(offer_data.collection_id, offer_data.item_id)
.ok_or(Error::<T>::OwnerNotFound)?;
ensure!(owner_item != buyer, Error::<T>::CannotTakeOffer);
Self::does_exist_offer_id_for_this_item(
offer_data.collection_id,
offer_data.item_id,
offer_id,
)?;
ensure!(offer_data.status == OfferStatus::Open, Error::<T>::OfferIsNotAvailable);
let asset_id = <Marketplaces<T>>::get(marketplace_id)
.ok_or(Error::<T>::MarketplaceNotFound)?
.asset_id;
let total_amount_buyer =
pallet_mapped_assets::Pallet::<T>::balance(asset_id.clone(), buyer.clone());
ensure!(total_amount_buyer > offer_data.price, Error::<T>::NotEnoughBalance);
let marketplace =
<Marketplaces<T>>::get(offer_data.marketplace_id).ok_or(Error::<T>::OfferNotFound)?;
let owners_cut: T::Balance = offer_data.price - offer_data.fee;
pallet_mapped_assets::Pallet::<T>::transfer(
origin.clone(),
asset_id.clone().into(),
T::Lookup::unlookup(owner_item.clone()),
owners_cut,
)?;
pallet_mapped_assets::Pallet::<T>::transfer(
origin.clone(),
asset_id.clone().into(),
T::Lookup::unlookup(marketplace.creator.clone()),
offer_data.fee,
)?;
pallet_fruniques::Pallet::<T>::do_thaw(&offer_data.collection_id, offer_data.item_id)?;
if offer_data.percentage == Permill::from_percent(100) {
pallet_uniques::Pallet::<T>::do_transfer(
offer_data.collection_id,
offer_data.item_id,
buyer.clone(),
|_, _| Ok(()),
)?;
} else {
let parent_info = pallet_fruniques::types::ParentInfo {
collection_id: offer_data.collection_id,
parent_id: offer_data.item_id,
parent_weight: offer_data.percentage,
is_hierarchical: true,
};
let metadata = pallet_fruniques::Pallet::<T>::get_nft_metadata(
offer_data.collection_id,
offer_data.item_id,
);
pallet_fruniques::Pallet::<T>::do_spawn(
offer_data.collection_id,
buyer.clone(),
metadata,
None,
Some(parent_info),
)?;
}
Self::update_offers_status(
buyer.clone(),
offer_data.collection_id,
offer_data.item_id,
offer_data.marketplace_id,
)?;
Self::delete_all_offers_for_this_item(offer_data.collection_id, offer_data.item_id)?;
Self::deposit_event(Event::OfferWasAccepted(offer_id, buyer));
Ok(())
}
pub fn do_take_buy_offer(authority: T::AccountId, offer_id: [u8; 32]) -> DispatchResult
where
<T as pallet_uniques::Config>::ItemId: From<u32>,
{
let offer_data = <OffersInfo<T>>::get(offer_id).ok_or(Error::<T>::OfferNotFound)?;
Self::is_authorized(authority.clone(), &offer_data.marketplace_id, Permission::TakeBuyOffer)?;
let owner_item =
pallet_uniques::Pallet::<T>::owner(offer_data.collection_id, offer_data.item_id)
.ok_or(Error::<T>::OwnerNotFound)?;
ensure!(owner_item == authority, Error::<T>::NotOwner);
ensure!(owner_item != offer_data.creator, Error::<T>::CannotTakeOffer);
Self::does_exist_offer_id_for_this_item(
offer_data.collection_id,
offer_data.item_id,
offer_id,
)?;
ensure!(offer_data.status == OfferStatus::Open, Error::<T>::OfferIsNotAvailable);
let marketplace_id = offer_data.marketplace_id;
let asset_id = <Marketplaces<T>>::get(marketplace_id)
.ok_or(Error::<T>::MarketplaceNotFound)?
.asset_id;
let total_amount_buyer =
pallet_mapped_assets::Pallet::<T>::balance(asset_id.clone(), offer_data.creator.clone());
ensure!(total_amount_buyer > offer_data.price, Error::<T>::NotEnoughBalance);
let marketplace =
<Marketplaces<T>>::get(offer_data.marketplace_id).ok_or(Error::<T>::OfferNotFound)?;
let owners_cut: T::Balance = offer_data.price - offer_data.fee;
pallet_mapped_assets::Pallet::<T>::transfer(
RawOrigin::Signed(offer_data.creator.clone()).into(),
asset_id.clone().into(),
T::Lookup::unlookup(owner_item.clone()),
owners_cut,
)?;
pallet_mapped_assets::Pallet::<T>::transfer(
RawOrigin::Signed(offer_data.creator.clone()).into(),
asset_id.clone().into(),
T::Lookup::unlookup(marketplace.creator.clone()),
offer_data.fee,
)?;
pallet_fruniques::Pallet::<T>::do_thaw(&offer_data.collection_id, offer_data.item_id)?;
if offer_data.percentage == Permill::from_percent(100) {
pallet_uniques::Pallet::<T>::do_transfer(
offer_data.collection_id,
offer_data.item_id,
offer_data.creator.clone(),
|_, _| Ok(()),
)?;
} else {
let parent_info = pallet_fruniques::types::ParentInfo {
collection_id: offer_data.collection_id,
parent_id: offer_data.item_id,
parent_weight: offer_data.percentage,
is_hierarchical: true,
};
let metadata = pallet_fruniques::Pallet::<T>::get_nft_metadata(
offer_data.collection_id,
offer_data.item_id,
);
pallet_fruniques::Pallet::<T>::do_spawn(
offer_data.collection_id,
offer_data.creator.clone(),
metadata,
None,
Some(parent_info),
)?;
}
Self::update_offers_status(
offer_data.creator.clone(),
offer_data.collection_id,
offer_data.item_id,
offer_data.marketplace_id,
)?;
Self::delete_all_offers_for_this_item(offer_data.collection_id, offer_data.item_id)?;
Self::deposit_event(Event::OfferWasAccepted(offer_id, offer_data.creator));
Ok(())
}
pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8; 32]) -> DispatchResult {
ensure!(<OffersInfo<T>>::contains_key(offer_id), Error::<T>::OfferNotFound);
let offer_data = <OffersInfo<T>>::get(offer_id).ok_or(Error::<T>::OfferNotFound)?;
Self::is_authorized(authority.clone(), &offer_data.marketplace_id, Permission::RemoveOffer)?;
ensure!(offer_data.status == OfferStatus::Open, Error::<T>::CannotDeleteOffer);
ensure!(offer_data.creator == authority, Error::<T>::CannotRemoveOffer);
Self::does_exist_offer_id_for_this_item(
offer_data.collection_id,
offer_data.item_id,
offer_id,
)?;
if offer_data.offer_type == OfferType::SellOrder {
pallet_fruniques::Pallet::<T>::do_thaw(&offer_data.collection_id, offer_data.item_id)?;
}
<OffersInfo<T>>::remove(offer_id);
<OffersByMarketplace<T>>::try_mutate(offer_data.marketplace_id, |offers| {
let offer_index =
offers.iter().position(|x| *x == offer_id).ok_or(Error::<T>::OfferNotFound)?;
offers.remove(offer_index);
Ok(())
})
.map_err(|_: Error<T>| Error::<T>::OfferNotFound)?;
<OffersByAccount<T>>::try_mutate(authority, |offers| {
let offer_index =
offers.iter().position(|x| *x == offer_id).ok_or(Error::<T>::OfferNotFound)?;
offers.remove(offer_index);
Ok(())
})
.map_err(|_: Error<T>| Error::<T>::OfferNotFound)?;
<OffersByItem<T>>::try_mutate(offer_data.collection_id, offer_data.item_id, |offers| {
let offer_index =
offers.iter().position(|x| *x == offer_id).ok_or(Error::<T>::OfferNotFound)?;
offers.remove(offer_index);
Ok(())
})
.map_err(|_: Error<T>| Error::<T>::OfferNotFound)?;
Self::deposit_event(Event::OfferRemoved(offer_id, offer_data.marketplace_id));
Ok(())
}
pub fn set_up_application(
fields: Fields<T>,
custodian_fields: Option<CustodianFields<T>>,
) -> (Option<T::AccountId>, BoundedVec<ApplicationField, T::MaxFiles>) {
let mut f: Vec<ApplicationField> = fields
.iter()
.map(|tuple| ApplicationField {
display_name: tuple.0.clone(),
cid: tuple.1.clone(),
custodian_cid: None,
})
.collect();
let custodian = match custodian_fields {
Some(c_fields) => {
for (i, field) in f.iter_mut().enumerate() {
field.custodian_cid = Some(c_fields.1[i].clone());
}
Some(c_fields.0)
},
_ => None,
};
(custodian, BoundedVec::<ApplicationField, T::MaxFiles>::try_from(f).unwrap_or_default())
}
fn insert_in_auth_market_lists(
authority: T::AccountId,
role: MarketplaceRole,
marketplace_id: [u8; 32],
) -> DispatchResult {
<T as pallet::Config>::Rbac::assign_role_to_user(
authority,
Self::pallet_id(),
&marketplace_id,
role.id(),
)?;
Ok(())
}
fn insert_in_applicants_lists(
applicant: T::AccountId,
status: ApplicationStatus,
marketplace_id: [u8; 32],
) -> DispatchResult {
<ApplicantsByMarketplace<T>>::try_mutate(marketplace_id, status, |applicants| {
applicants.try_push(applicant)
})
.map_err(|_| Error::<T>::ExceedMaxApplicants)?;
Ok(())
}
fn insert_custodian(
custodian: T::AccountId,
marketplace_id: [u8; 32],
applicant: T::AccountId,
) -> DispatchResult {
<Custodians<T>>::try_mutate(custodian, marketplace_id, |applications| {
applications.try_push(applicant)
})
.map_err(|_| Error::<T>::ExceedMaxApplicationsPerCustodian)?;
Ok(())
}
fn remove_from_applicants_lists(
applicant: T::AccountId,
status: ApplicationStatus,
marketplace_id: [u8; 32],
) -> DispatchResult {
<ApplicantsByMarketplace<T>>::try_mutate::<_, _, _, DispatchError, _>(
marketplace_id,
status,
|applicants| {
let applicant_index = applicants
.iter()
.position(|a| *a == applicant.clone())
.ok_or(Error::<T>::ApplicantNotFound)?;
applicants.remove(applicant_index);
Ok(())
},
)
}
pub fn remove_from_market_lists(
account: T::AccountId,
author_type: MarketplaceRole,
marketplace_id: [u8; 32],
) -> DispatchResult {
<T as pallet::Config>::Rbac::remove_role_from_user(
account,
Self::pallet_id(),
&marketplace_id,
author_type.id(),
)?;
Ok(())
}
fn change_applicant_status(
applicant: T::AccountId,
marketplace_id: [u8; 32],
next_status: ApplicationStatus,
feedback: BoundedVec<u8, T::MaxFeedbackLen>,
) -> DispatchResult {
let mut prev_status = ApplicationStatus::default();
let app_id = <ApplicationsByAccount<T>>::get(applicant.clone(), marketplace_id)
.ok_or(Error::<T>::ApplicationNotFound)?;
<Applications<T>>::try_mutate::<_, _, DispatchError, _>(app_id, |application| {
application.as_ref().ok_or(Error::<T>::ApplicationNotFound)?;
if let Some(a) = application {
prev_status.clone_from(&a.status);
a.feedback = feedback;
a.status.clone_from(&next_status)
}
Ok(())
})?;
ensure!(prev_status != next_status, Error::<T>::AlreadyEnrolled);
Self::remove_from_applicants_lists(applicant.clone(), prev_status, marketplace_id)?;
Self::insert_in_applicants_lists(applicant.clone(), next_status, marketplace_id)?;
if prev_status == ApplicationStatus::Approved {
<T as pallet::Config>::Rbac::remove_role_from_user(
applicant.clone(),
Self::pallet_id(),
&marketplace_id,
MarketplaceRole::Participant.id(),
)?;
}
if next_status == ApplicationStatus::Approved {
<T as pallet::Config>::Rbac::assign_role_to_user(
applicant,
Self::pallet_id(),
&marketplace_id,
MarketplaceRole::Participant.id(),
)?
}
Ok(())
}
pub fn do_block_user(
authority: T::AccountId,
marketplace_id: [u8; 32],
user: T::AccountId,
) -> DispatchResult {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
Self::is_authorized(authority.clone(), &marketplace_id, Permission::BlockUser)?;
ensure!(!Self::has_any_role(user.clone(), &marketplace_id), Error::<T>::UserAlreadyParticipant);
ensure!(!Self::is_user_blocked(user.clone(), marketplace_id), Error::<T>::UserAlreadyBlocked);
<BlockedUsersByMarketplace<T>>::try_mutate(marketplace_id, |blocked_list| {
blocked_list.try_push(user.clone())
})
.map_err(|_| Error::<T>::ExceedMaxBlockedUsers)?;
Self::deposit_event(Event::UserBlocked(marketplace_id, user.clone()));
Ok(())
}
pub fn do_unblock_user(
authority: T::AccountId,
marketplace_id: [u8; 32],
user: T::AccountId,
) -> DispatchResult {
ensure!(<Marketplaces<T>>::contains_key(marketplace_id), Error::<T>::MarketplaceNotFound);
Self::is_authorized(authority.clone(), &marketplace_id, Permission::BlockUser)?;
ensure!(!Self::has_any_role(user.clone(), &marketplace_id), Error::<T>::UserAlreadyParticipant);
ensure!(Self::is_user_blocked(user.clone(), marketplace_id), Error::<T>::UserIsNotBlocked);
<BlockedUsersByMarketplace<T>>::try_mutate::<_, _, DispatchError, _>(
marketplace_id,
|blocked_list| {
let user_index = blocked_list
.iter()
.position(|a| *a == user.clone())
.ok_or(Error::<T>::UserNotFound)?;
blocked_list.remove(user_index);
Ok(())
},
)?;
Self::deposit_event(Event::UserUnblocked(marketplace_id, user.clone()));
Ok(())
}
fn is_user_blocked(user: T::AccountId, marketplace_id: [u8; 32]) -> bool {
<BlockedUsersByMarketplace<T>>::get(marketplace_id).contains(&user)
}
fn is_authorized(
authority: T::AccountId,
marketplace_id: &[u8; 32],
permission: Permission,
) -> DispatchResult {
<T as pallet::Config>::Rbac::is_authorized(
authority,
Self::pallet_id(),
marketplace_id,
&permission.id(),
)
}
fn has_any_role(account: T::AccountId, marketplace_id: &[u8; 32]) -> bool {
let pallet_id = Self::pallet_id();
<T as pallet::Config>::Rbac::does_user_have_any_role_in_scope(
account,
pallet_id,
marketplace_id,
)
}
fn is_admin(account: T::AccountId, marketplace_id: [u8; 32]) -> bool {
<T as pallet::Config>::Rbac::has_role(
account,
Self::pallet_id(),
&marketplace_id,
[MarketplaceRole::Admin.id()].to_vec(),
)
.is_ok()
}
fn owner_exist(marketplace_id: [u8; 32]) -> bool {
<T as pallet::Config>::Rbac::get_role_users_len(
Self::pallet_id(),
&marketplace_id,
&MarketplaceRole::Owner.id(),
) == 1
}
fn update_label(
marketplace_id: [u8; 32],
new_label: BoundedVec<u8, T::LabelMaxLen>,
) -> DispatchResult {
<Marketplaces<T>>::try_mutate(marketplace_id, |marketplace| {
let market = marketplace.as_mut().ok_or(Error::<T>::MarketplaceNotFound)?;
market.label = new_label;
Ok(())
})
}
fn remove_selected_marketplace(marketplace_id: [u8; 32]) -> DispatchResult {
let mut applications = Vec::new();
for ele in <ApplicationsByAccount<T>>::iter() {
if ele.1 == marketplace_id {
applications.push(ele.2);
}
}
for application in applications {
<Applications<T>>::remove(application);
}
<ApplicationsByAccount<T>>::iter().for_each(|(_k1, _k2, _k3)| {
<ApplicationsByAccount<T>>::remove(_k1, marketplace_id);
});
let _ = <ApplicantsByMarketplace<T>>::clear_prefix(marketplace_id, 1000, None);
<Custodians<T>>::iter().for_each(|(_k1, _k2, _k3)| {
<Custodians<T>>::remove(_k1, marketplace_id);
});
<Marketplaces<T>>::remove(marketplace_id);
<T as pallet::Config>::Rbac::remove_scope(Self::pallet_id(), marketplace_id)?;
Ok(())
}
pub fn is_application_in_rejected_status(
account: T::AccountId,
marketplace_id: [u8; 32],
) -> DispatchResult {
ensure!(!Self::is_user_blocked(account.clone(), marketplace_id), Error::<T>::UserIsBlocked);
let application_id = <ApplicationsByAccount<T>>::try_get(account.clone(), marketplace_id)
.map_err(|_| Error::<T>::ApplicationIdNotFound)?;
let application =
<Applications<T>>::try_get(application_id).map_err(|_| Error::<T>::ApplicationNotFound)?;
match application.status {
ApplicationStatus::Pending => return Err(Error::<T>::ApplicationStatusStillPending.into()),
ApplicationStatus::Approved => {
return Err(Error::<T>::ApplicationHasAlreadyBeenApproved.into())
},
ApplicationStatus::Rejected => {
<Applications<T>>::remove(application_id);
<ApplicationsByAccount<T>>::remove(account.clone(), marketplace_id);
Self::remove_from_applicants_lists(account, ApplicationStatus::Rejected, marketplace_id)?;
},
}
Ok(())
}
fn get_timestamp_in_milliseconds() -> Option<u64> {
let timestamp: u64 = T::Timestamp::now().into();
Some(timestamp)
}
fn _is_offer_status(offer_id: [u8; 32], offer_status: OfferStatus) -> bool {
if let Some(offer) = <OffersInfo<T>>::get(offer_id) {
offer.status == offer_status
} else {
false
}
}
fn does_exist_offer_id_for_this_item(
collection_id: T::CollectionId,
item_id: T::ItemId,
offer_id: [u8; 32],
) -> DispatchResult {
let offers =
<OffersByItem<T>>::try_get(collection_id, item_id).map_err(|_| Error::<T>::OfferNotFound)?;
offers.iter().find(|&x| *x == offer_id).ok_or(Error::<T>::OfferNotFound)?;
Ok(())
}
fn update_offers_status(
buyer: T::AccountId,
collection_id: T::CollectionId,
item_id: T::ItemId,
marketplace_id: [u8; 32],
) -> DispatchResult {
let offer_ids =
<OffersByItem<T>>::try_get(collection_id, item_id).map_err(|_| Error::<T>::OfferNotFound)?;
for offer_id in offer_ids {
<OffersInfo<T>>::try_mutate::<_, _, DispatchError, _>(offer_id, |offer| {
let offer = offer.as_mut().ok_or(Error::<T>::OfferNotFound)?;
offer.status = OfferStatus::Closed;
offer.buyer = Some((buyer.clone(), marketplace_id));
Ok(())
})?;
}
Ok(())
}
fn is_the_offer_valid(price: T::Balance, percentage: Permill) -> DispatchResult {
let minimun_amount: T::Balance = 0u32.into();
ensure!(price > minimun_amount, Error::<T>::PriceMustBeGreaterThanZero);
ensure!(percentage <= Permill::from_percent(100), Error::<T>::ExceedMaxPercentage);
ensure!(percentage >= Permill::from_percent(1), Error::<T>::ExceedMinPercentage);
Ok(())
}
fn can_this_item_receive_sell_orders(
collection_id: T::CollectionId,
item_id: T::ItemId,
marketplace_id: [u8; 32],
) -> DispatchResult {
let offers = <OffersByItem<T>>::get(collection_id, item_id);
if offers.len() > 0 {
for offer in offers {
let offer_info = <OffersInfo<T>>::get(offer).ok_or(Error::<T>::OfferNotFound)?;
if offer_info.marketplace_id == marketplace_id
&& offer_info.offer_type == OfferType::SellOrder
{
return Err(Error::<T>::OfferAlreadyExists.into());
}
}
}
Ok(())
}
fn can_this_item_receive_buy_orders(
marketplace_id: &[u8; 32],
buyer: T::AccountId,
class_id: &T::CollectionId,
instance_id: &T::ItemId,
) -> DispatchResult {
Self::is_authorized(buyer, marketplace_id, Permission::EnlistBuyOffer)?;
if let Some(owner) = pallet_uniques::Pallet::<T>::owner(*class_id, *instance_id) {
if Self::is_authorized(owner, marketplace_id, Permission::EnlistSellOffer).is_ok() {
return Ok(());
}
}
Err(Error::<T>::OwnerNotInMarketplace.into())
}
fn _delete_all_sell_orders_for_this_item(
collection_id: T::CollectionId,
item_id: T::ItemId,
) -> DispatchResult {
ensure!(<OffersByItem<T>>::contains_key(collection_id, item_id), Error::<T>::OfferNotFound);
let offers_ids = <OffersByItem<T>>::take(collection_id, item_id);
let mut buy_offer_ids: BoundedVec<[u8; 32], T::MaxOffersPerMarket> = BoundedVec::default();
for offer_id in offers_ids {
let offer_info = <OffersInfo<T>>::get(offer_id).ok_or(Error::<T>::OfferNotFound)?;
if offer_info.offer_type != OfferType::SellOrder {
buy_offer_ids.try_push(offer_id).map_err(|_| Error::<T>::LimitExceeded)?;
}
}
ensure!(!<OffersByItem<T>>::contains_key(collection_id, item_id), Error::<T>::OfferNotFound);
<OffersByItem<T>>::insert(collection_id, item_id, buy_offer_ids);
Ok(())
}
fn delete_all_offers_for_this_item(
collection_id: T::CollectionId,
item_id: T::ItemId,
) -> DispatchResult {
pallet_fruniques::Pallet::<T>::do_thaw(&collection_id, item_id)?;
<OffersByItem<T>>::remove(collection_id, item_id);
Ok(())
}
pub fn do_ask_for_redeem(
who: T::AccountId,
marketplace: MarketplaceId,
collection_id: T::CollectionId,
item_id: T::ItemId,
) -> DispatchResult {
ensure!(<Marketplaces<T>>::contains_key(marketplace), Error::<T>::MarketplaceNotFound);
Self::is_authorized(who.clone(), &marketplace, Permission::AskForRedemption)?;
if let Some(a) = pallet_uniques::Pallet::<T>::owner(collection_id, item_id) {
ensure!(a == who, Error::<T>::NotOwner);
} else {
return Err(Error::<T>::CollectionNotFound.into());
}
let redemption_data: RedemptionData<T> = RedemptionData {
creator: who.clone(),
redeemed_by: None,
collection_id,
item_id,
is_redeemed: false,
};
let redemption_id = redemption_data.using_encoded(blake2_256);
ensure!(
!<AskingForRedemption<T>>::contains_key(marketplace, redemption_id),
Error::<T>::RedemptionRequestAlreadyExists
);
<AskingForRedemption<T>>::insert(marketplace, redemption_id, redemption_data);
Self::deposit_event(Event::RedemptionRequested(marketplace, redemption_id, who));
Ok(())
}
pub fn do_accept_redeem(
who: T::AccountId,
marketplace: MarketplaceId,
redemption_id: RedemptionId,
) -> DispatchResult
where
<T as pallet_uniques::Config>::ItemId: From<u32>,
{
ensure!(<Marketplaces<T>>::contains_key(marketplace), Error::<T>::MarketplaceNotFound);
Self::is_authorized(who.clone(), &marketplace, Permission::AcceptRedemption)?;
ensure!(
<AskingForRedemption<T>>::contains_key(marketplace, redemption_id),
Error::<T>::RedemptionRequestNotFound
);
<AskingForRedemption<T>>::try_mutate::<_, _, _, DispatchError, _>(
marketplace,
redemption_id,
|redemption_data| -> DispatchResult {
let redemption_data =
redemption_data.as_mut().ok_or(Error::<T>::RedemptionRequestNotFound)?;
ensure!(redemption_data.is_redeemed == false, Error::<T>::RedemptionRequestAlreadyRedeemed);
ensure!(redemption_data.is_redeemed == false, Error::<T>::RedemptionRequestAlreadyRedeemed);
redemption_data.is_redeemed = true;
redemption_data.redeemed_by = Some(who.clone());
Self::deposit_event(Event::RedemptionAccepted(marketplace, redemption_id, who));
pallet_fruniques::Pallet::<T>::do_redeem(
redemption_data.collection_id,
redemption_data.item_id,
)?;
Ok(())
},
)?;
Ok(())
}
pub fn pallet_id() -> IdOrVec {
IdOrVec::Vec(Self::module_name().as_bytes().to_vec())
}
}